From 395d54bf867803acddc8012c4c44bf8b56531165 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 23 Jan 2025 12:46:09 +0100 Subject: [PATCH 001/122] Create Base.qll --- cpp/ql/lib/experimental/Quantum/Base.qll | 165 +++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 cpp/ql/lib/experimental/Quantum/Base.qll diff --git a/cpp/ql/lib/experimental/Quantum/Base.qll b/cpp/ql/lib/experimental/Quantum/Base.qll new file mode 100644 index 000000000000..bfdd509174e5 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/Base.qll @@ -0,0 +1,165 @@ +/** + * A language-independent library for reasoning about cryptography. + */ + +import codeql.util.Location +import codeql.util.Option + +signature module InputSig { + class KnownUnknownLocation extends Location; + + class LocatableElement { + Location getLocation(); + } +} + +module CryptographyBase Input> { + final class LocatableElement = Input::LocatableElement; + + newtype TNode = + TNodeUnknown() or + TNodeAsset() or + TNodeValue() // currently unused + + class KnownNode = TNodeAsset or TNodeValue; + + abstract class NodeBase extends TNode { + /** + * Returns a string representation of this node, usually the name of the operation/algorithm/property. + */ + abstract string toString(); + + /** + * Returns the location of this node in the code. + */ + abstract Location getLocation(); + + /** + * Returns the child of this node with the given edge name. + * + * This predicate is used by derived classes to construct the graph of cryptographic operations. + */ + NodeBase getChild(string edgeName) { none() } + + /** + * Returns the parent of this node. + */ + final NodeBase getAParent() { result.getChild(_) = this } + } + + /** + * A node representing an unknown value. + * + * If a property should have a value but that value is unknown, `UnknownNode` to represent that value. + */ + final class UnknownNode extends NodeBase, TNodeUnknown { + override string toString() { result = "unknown" } + + override Location getLocation() { result instanceof Input::KnownUnknownLocation } + } + + /** + * A node with a known location in the code. + */ + abstract class LocatableNode extends NodeBase, TNodeAsset { + abstract LocatableElement toElement(); + + override Location getLocation() { result = this.toElement().getLocation() } + } + + /** + * A node representing a known asset, i.e., an algorithm, operation, or property. + */ + class Asset = LocatableNode; + + /** + * A cryptographic operation, such as hashing or encryption. + */ + abstract class Operation extends Asset { + /** + * Gets the algorithm associated with this operation. + */ + private NodeBase getAlgorithmOrUnknown() { + if exists(this.getAlgorithm()) + then result = this.getAlgorithm() + else result instanceof UnknownNode + } + + abstract Algorithm getAlgorithm(); + + /** + * Gets the name of this operation, e.g., "hash" or "encrypt". + */ + abstract string getOperationName(); + + final override string toString() { result = this.getOperationName() } + + override NodeBase getChild(string edgeName) { + edgeName = "algorithm" and + this.getAlgorithmOrUnknown() = result + } + } + + abstract class Algorithm extends Asset { + /** + * Gets the name of this algorithm, e.g., "AES" or "SHA". + */ + abstract string getAlgorithmName(); + + final override string toString() { result = this.getAlgorithmName() } + } + + /** + * A hashing operation that processes data to generate a hash value. + * This operation takes an input message of arbitrary content and length and produces a fixed-size + * hash value as the output using a specified hashing algorithm. + */ + abstract class HashOperation extends Operation { + abstract override HashAlgorithm getAlgorithm(); + + override string getOperationName() { result = "hash" } + } + + /** + * A hashing algorithm that transforms variable-length input into a fixed-size hash value. + */ + abstract class HashAlgorithm extends Algorithm { } + + /** + * An operation that derives one or more keys from an input value. + */ + abstract class KeyDerivationOperation extends Operation { + override string getOperationName() { result = "key derivation" } + } + + /** + * An algorithm that derives one or more keys from an input value. + */ + abstract class KeyDerivationAlgorithm extends Algorithm { + abstract override string getAlgorithmName(); + } + + /** + * HKDF Extract+Expand key derivation function. + */ + abstract class HKDFAlgorithm extends KeyDerivationAlgorithm { + final override string getAlgorithmName() { result = "HKDF" } + + private NodeBase getHashAlgorithmOrUnknown() { + if exists(this.getHashAlgorithm()) + then result = this.getHashAlgorithm() + else result instanceof UnknownNode + } + + abstract HashAlgorithm getHashAlgorithm(); + + /** + * digest:HashAlgorithm + */ + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + edgeName = "digest" and result = this.getHashAlgorithmOrUnknown() + } + } +} From 1a7d8cb99db194624238ab0d2880bd6984c96904 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Fri, 24 Jan 2025 17:33:03 +0100 Subject: [PATCH 002/122] WIP --- cpp/ql/lib/experimental/Quantum/Base.qll | 80 ++++------- .../lib/experimental/Quantum/BaseBackup.qll | 125 ++++++++++++++++++ cpp/ql/lib/experimental/Quantum/Language.qll | 10 ++ cpp/ql/lib/experimental/Quantum/OpenSSL.qll | 100 ++++++++++++++ cpp/ql/src/experimental/Quantum/Test.ql | 17 +++ cpp/ql/src/experimental/Quantum/Test2.ql | 8 ++ 6 files changed, 288 insertions(+), 52 deletions(-) create mode 100644 cpp/ql/lib/experimental/Quantum/BaseBackup.qll create mode 100644 cpp/ql/lib/experimental/Quantum/Language.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL.qll create mode 100644 cpp/ql/src/experimental/Quantum/Test.ql create mode 100644 cpp/ql/src/experimental/Quantum/Test2.ql diff --git a/cpp/ql/lib/experimental/Quantum/Base.qll b/cpp/ql/lib/experimental/Quantum/Base.qll index bfdd509174e5..8a910eb44143 100644 --- a/cpp/ql/lib/experimental/Quantum/Base.qll +++ b/cpp/ql/lib/experimental/Quantum/Base.qll @@ -6,8 +6,6 @@ import codeql.util.Location import codeql.util.Option signature module InputSig { - class KnownUnknownLocation extends Location; - class LocatableElement { Location getLocation(); } @@ -16,14 +14,7 @@ signature module InputSig { module CryptographyBase Input> { final class LocatableElement = Input::LocatableElement; - newtype TNode = - TNodeUnknown() or - TNodeAsset() or - TNodeValue() // currently unused - - class KnownNode = TNodeAsset or TNodeValue; - - abstract class NodeBase extends TNode { + abstract class NodeBase instanceof LocatableElement { /** * Returns a string representation of this node, usually the name of the operation/algorithm/property. */ @@ -32,14 +23,19 @@ module CryptographyBase Input> { /** * Returns the location of this node in the code. */ - abstract Location getLocation(); + Location getLocation() { result = super.getLocation() } /** * Returns the child of this node with the given edge name. * * This predicate is used by derived classes to construct the graph of cryptographic operations. */ - NodeBase getChild(string edgeName) { none() } + NodeBase getChild(string edgeName) { edgeName = "origin" and result = this.getOrigin() } + + /** + * Gets the origin of this node, e.g., a string literal in source describing it. + */ + NodeBase getOrigin() { none() } /** * Returns the parent of this node. @@ -47,30 +43,7 @@ module CryptographyBase Input> { final NodeBase getAParent() { result.getChild(_) = this } } - /** - * A node representing an unknown value. - * - * If a property should have a value but that value is unknown, `UnknownNode` to represent that value. - */ - final class UnknownNode extends NodeBase, TNodeUnknown { - override string toString() { result = "unknown" } - - override Location getLocation() { result instanceof Input::KnownUnknownLocation } - } - - /** - * A node with a known location in the code. - */ - abstract class LocatableNode extends NodeBase, TNodeAsset { - abstract LocatableElement toElement(); - - override Location getLocation() { result = this.toElement().getLocation() } - } - - /** - * A node representing a known asset, i.e., an algorithm, operation, or property. - */ - class Asset = LocatableNode; + class Asset = NodeBase; /** * A cryptographic operation, such as hashing or encryption. @@ -79,12 +52,6 @@ module CryptographyBase Input> { /** * Gets the algorithm associated with this operation. */ - private NodeBase getAlgorithmOrUnknown() { - if exists(this.getAlgorithm()) - then result = this.getAlgorithm() - else result instanceof UnknownNode - } - abstract Algorithm getAlgorithm(); /** @@ -95,8 +62,10 @@ module CryptographyBase Input> { final override string toString() { result = this.getOperationName() } override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or edgeName = "algorithm" and - this.getAlgorithmOrUnknown() = result + if exists(this.getAlgorithm()) then result = this.getAlgorithm() else result = this } } @@ -125,6 +94,10 @@ module CryptographyBase Input> { */ abstract class HashAlgorithm extends Algorithm { } + abstract class SHA1 extends HashAlgorithm { + override string getAlgorithmName() { result = "SHA1" } + } + /** * An operation that derives one or more keys from an input value. */ @@ -142,24 +115,27 @@ module CryptographyBase Input> { /** * HKDF Extract+Expand key derivation function. */ - abstract class HKDFAlgorithm extends KeyDerivationAlgorithm { + abstract class HKDF extends KeyDerivationAlgorithm { final override string getAlgorithmName() { result = "HKDF" } - private NodeBase getHashAlgorithmOrUnknown() { - if exists(this.getHashAlgorithm()) - then result = this.getHashAlgorithm() - else result instanceof UnknownNode + abstract HashAlgorithm getHashAlgorithm(); + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + edgeName = "digest" and result = this.getHashAlgorithm() } + } + + abstract class PKCS12KDF extends KeyDerivationAlgorithm { + final override string getAlgorithmName() { result = "PKCS12KDF" } abstract HashAlgorithm getHashAlgorithm(); - /** - * digest:HashAlgorithm - */ override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or - edgeName = "digest" and result = this.getHashAlgorithmOrUnknown() + edgeName = "digest" and result = this.getHashAlgorithm() } } } diff --git a/cpp/ql/lib/experimental/Quantum/BaseBackup.qll b/cpp/ql/lib/experimental/Quantum/BaseBackup.qll new file mode 100644 index 000000000000..821c89eb8f57 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/BaseBackup.qll @@ -0,0 +1,125 @@ +/** + * A language-independent library for reasoning about cryptography. + */ + +import codeql.util.Location +import codeql.util.Option + +signature module InputSig { + class KnownUnknownLocation extends Location; + + class LocatableElement { + Location getLocation(); + } +} + +// An operation = a specific loc in code +// An algorithm +// Properties +// Node -> Operation -> Algorithm -> Symmetric -> SpecificSymmetricAlgo +// -[Language-Specific]-> LibrarySymmetricAlgo -> Properties +// For example (nsted newtypes): +/* + * newtype for each algo, and each one of those would have params for their properties + * implementation: optional/range for example + * + * + * + * /** + * Constructs an `Option` type that is a disjoint union of the given type and an + * additional singleton element. + */ + +module CryptographyBase Input> { + newtype TNode = + TNodeUnknown() or + TNodeAlgorithm() or + TNodeOperation() + + /* + * A cryptographic asset in code, i.e., an algorithm, operation, property, or known unknown. + */ + + abstract class Node extends TNode { + // this would then extend LanguageNode + abstract Location getLocation(); + + abstract string toString(); + + abstract Node getChild(int childIndex); + + final Node getAChild() { result = this.getChild(_) } + + final Node getAParent() { result.getAChild() = this } + } + + final class KnownUnknown extends Node, TNodeUnknown { + override string toString() { result = "unknown" } + + override Node getChild(int childIndex) { none() } + + override Location getLocation() { result instanceof Input::KnownUnknownLocation } + } + + abstract class Operation extends Node, TNodeOperation { + /** + * Gets the algorithm associated with this operation. + */ + abstract Node getAlgorithm(); + + /** + * Gets the name of this operation, e.g., "hash" or "encrypt". + */ + abstract string getOperationName(); + + final override Node getChild(int childIndex) { childIndex = 0 and result = this.getAlgorithm() } + + final override string toString() { result = this.getOperationName() } + } + + abstract class Algorithm extends Node, TNodeAlgorithm { + /** + * Gets the name of this algorithm, e.g., "AES" or "SHA". + */ + abstract string getAlgorithmName(); + } + + /** + * A hashing operation that processes data to generate a hash value. + * This operation takes an input message of arbitrary content and length and produces a fixed-size + * hash value as the output using a specified hashing algorithm. + */ + abstract class HashOperation extends Operation { + abstract override HashAlgorithm getAlgorithm(); + + override string getOperationName() { result = "hash" } + } + + /** + * A hashing algorithm that transforms variable-length input into a fixed-size hash value. + */ + abstract class HashAlgorithm extends Algorithm { } + + /** + * An operation that derives one or more keys from an input value. + */ + abstract class KeyDerivationOperation extends Operation { + override string getOperationName() { result = "key derivation" } + } + + /** + * An algorithm that derives one or more keys from an input value. + */ + abstract class KeyDerivationAlgorithm extends Algorithm { + abstract override string getAlgorithmName(); + } + + /** + * HKDF Extract+Expand key derivation function. + */ + abstract class HKDFAlgorithm extends KeyDerivationAlgorithm { + final override string getAlgorithmName() { result = "HKDF" } + + abstract Node getDigestAlgorithm(); + } +} diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll new file mode 100644 index 000000000000..108993fb134c --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -0,0 +1,10 @@ +private import Base +private import cpp as Lang + +module CryptoInput implements InputSig { + class LocatableElement = Lang::Locatable; +} + +module Crypto = CryptographyBase; + +import OpenSSL diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll new file mode 100644 index 000000000000..b489a6630a0e --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll @@ -0,0 +1,100 @@ +import cpp +import semmle.code.cpp.dataflow.new.DataFlow + +module OpenSSLModel { + import Language + + abstract class KeyDerivationOperation extends Crypto::KeyDerivationOperation { } + + class SHA1Algo extends Crypto::SHA1 instanceof MacroAccess { + SHA1Algo() { this.getMacro().getName() = "SN_sha1" } + } + + module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof KeyDerivationAlgorithm } + + predicate isSink(DataFlow::Node sink) { + exists(EVP_KDF_derive kdo | sink.asExpr() = kdo.getAlgorithmArg()) + } + } + + module AlgorithmToEVPKeyDeriveFlow = DataFlow::Global; + + predicate algorithm_to_EVP_KDF_derive(Crypto::Algorithm algo, EVP_KDF_derive derive) { + algo.(Expr).getEnclosingFunction() = derive.(Expr).getEnclosingFunction() + } + + class EVP_KDF_derive extends KeyDerivationOperation instanceof FunctionCall { + EVP_KDF_derive() { this.getTarget().getName() = "EVP_KDF_derive" } + + override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) } + + Expr getAlgorithmArg() { result = this.(FunctionCall).getArgument(3) } + } + + abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm { } + + class EVP_KDF_fetch_Call extends FunctionCall { + EVP_KDF_fetch_Call() { this.getTarget().getName() = "EVP_KDF_fetch" } + + Expr getAlgorithmArg() { result = this.getArgument(1) } + } + + predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF"] } + + class KDFAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLiteral { + KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) } + + override string toString() { result = this.(StringLiteral).toString() } + + string getValue() { result = this.(StringLiteral).getValue() } + } + + private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KDFAlgorithmStringLiteral } + + predicate isSink(DataFlow::Node sink) { + exists(EVP_KDF_fetch_Call call | sink.asExpr() = call.getAlgorithmArg()) + } + } + + module AlgorithmStringToFetchFlow = DataFlow::Global; + + predicate algorithmStringToKDFFetchArgFlow(string name, KDFAlgorithmStringLiteral origin, Expr arg) { + exists(EVP_KDF_fetch_Call sinkCall | + origin.getValue().toUpperCase() = name and + arg = sinkCall.getAlgorithmArg() and + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) + ) + } + + class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF instanceof Expr { + KDFAlgorithmStringLiteral origin; + + HKDF() { algorithmStringToKDFFetchArgFlow("HKDF", origin, this) } + + override Crypto::HashAlgorithm getHashAlgorithm() { none() } + + override Crypto::NodeBase getOrigin() { result = origin } + } + + class TestKeyDerivationOperationHacky extends KeyDerivationOperation instanceof FunctionCall { + HKDF hkdf; + + TestKeyDerivationOperationHacky() { + this.getEnclosingFunction() = hkdf.(Expr).getEnclosingFunction() + } + + override Crypto::KeyDerivationAlgorithm getAlgorithm() { result = hkdf } + } + + class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF instanceof Expr { + KDFAlgorithmStringLiteral origin; + + PKCS12KDF() { algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, this) } + + override Crypto::HashAlgorithm getHashAlgorithm() { none() } + + override Crypto::NodeBase getOrigin() { result = origin } + } +} diff --git a/cpp/ql/src/experimental/Quantum/Test.ql b/cpp/ql/src/experimental/Quantum/Test.ql new file mode 100644 index 000000000000..a9ad5021a295 --- /dev/null +++ b/cpp/ql/src/experimental/Quantum/Test.ql @@ -0,0 +1,17 @@ +/** + * @name "PQC Test" + * @kind graph + */ + +import experimental.Quantum.Language + +query predicate nodes(Crypto::NodeBase node) { any() } + +query predicate edges(Crypto::NodeBase source, Crypto::NodeBase target, string key, string value) { + target = source.getChild(value) and + key = "semmle.label" +} + +query predicate graphProperties(string key, string value) { + key = "semmle.graphKind" and value = "tree" +} diff --git a/cpp/ql/src/experimental/Quantum/Test2.ql b/cpp/ql/src/experimental/Quantum/Test2.ql new file mode 100644 index 000000000000..f5971da96443 --- /dev/null +++ b/cpp/ql/src/experimental/Quantum/Test2.ql @@ -0,0 +1,8 @@ +/** + * @name "PQC Test" + */ + +import experimental.Quantum.Language + +from Crypto::KeyDerivationAlgorithm n +select n From 78362341fff9adb73deaf5a3a3f85f1da66481b0 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Fri, 24 Jan 2025 22:32:32 +0100 Subject: [PATCH 003/122] WIP: hash types example and documentation --- cpp/ql/lib/experimental/Quantum/Base.qll | 38 ++++++++++++++++++--- cpp/ql/lib/experimental/Quantum/OpenSSL.qll | 4 ++- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/Base.qll b/cpp/ql/lib/experimental/Quantum/Base.qll index 8a910eb44143..22e810f4aff4 100644 --- a/cpp/ql/lib/experimental/Quantum/Base.qll +++ b/cpp/ql/lib/experimental/Quantum/Base.qll @@ -64,7 +64,7 @@ module CryptographyBase Input> { override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or - edgeName = "algorithm" and + edgeName = "uses" and if exists(this.getAlgorithm()) then result = this.getAlgorithm() else result = this } } @@ -89,13 +89,43 @@ module CryptographyBase Input> { override string getOperationName() { result = "hash" } } + // Rule: no newtype representing a type of algorithm should be modelled with multiple interfaces + // + // Example: HKDF and PKCS12KDF are both key derivation algorithms. + // However, PKCS12KDF also has a property: the iteration count. + // + // If we have HKDF and PKCS12KDF under TKeyDerivationType, + // someone modelling a library might try to make a generic identification of both of those algorithms. + // + // They will therefore not use the specialized type for PKCS12KDF, + // meaning "from PKCS12KDF algo select algo" will have no results. + // + newtype THashType = + // We're saying by this that all of these have an identical interface / properties / edges + MD5() or + SHA1() or + SHA256() or + SHA512() + + class HashAlgorithmType extends THashType { + string toString() { hashTypeToNameMapping(this, result) } + } + + predicate hashTypeToNameMapping(THashType type, string name) { + type instanceof SHA1 and name = "SHA-1" + or + type instanceof SHA256 and name = "SHA-256" + or + type instanceof SHA512 and name = "SHA-512" + } + /** * A hashing algorithm that transforms variable-length input into a fixed-size hash value. */ - abstract class HashAlgorithm extends Algorithm { } + abstract class HashAlgorithm extends Algorithm { + abstract HashAlgorithmType getHashType(); - abstract class SHA1 extends HashAlgorithm { - override string getAlgorithmName() { result = "SHA1" } + override string getAlgorithmName() { hashTypeToNameMapping(this.getHashType(), result) } } /** diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll index b489a6630a0e..17de5ddab604 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll @@ -6,8 +6,10 @@ module OpenSSLModel { abstract class KeyDerivationOperation extends Crypto::KeyDerivationOperation { } - class SHA1Algo extends Crypto::SHA1 instanceof MacroAccess { + class SHA1Algo extends Crypto::HashAlgorithm instanceof MacroAccess { SHA1Algo() { this.getMacro().getName() = "SN_sha1" } + + override Crypto::HashAlgorithmType getHashType() { result instanceof Crypto::SHA1 } } module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig { From e027b0e9a0e56f21226d832bcd87751a8c0ee47f Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Tue, 28 Jan 2025 02:02:06 +0100 Subject: [PATCH 004/122] WIP: add properties --- cpp/ql/lib/experimental/Quantum/Base.qll | 95 +++++++++++++++----- cpp/ql/lib/experimental/Quantum/Language.qll | 2 + cpp/ql/lib/experimental/Quantum/OpenSSL.qll | 12 ++- cpp/ql/src/experimental/Quantum/Test.ql | 28 +++++- cpp/ql/src/experimental/Quantum/Test2.ql | 4 +- 5 files changed, 114 insertions(+), 27 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/Base.qll b/cpp/ql/lib/experimental/Quantum/Base.qll index 22e810f4aff4..b3f4d619f4d8 100644 --- a/cpp/ql/lib/experimental/Quantum/Base.qll +++ b/cpp/ql/lib/experimental/Quantum/Base.qll @@ -9,11 +9,19 @@ signature module InputSig { class LocatableElement { Location getLocation(); } + + class UnknownLocation instanceof Location; } module CryptographyBase Input> { final class LocatableElement = Input::LocatableElement; + final class UnknownLocation = Input::UnknownLocation; + + final class UnknownPropertyValue extends string { + UnknownPropertyValue() { this = "" } + } + abstract class NodeBase instanceof LocatableElement { /** * Returns a string representation of this node, usually the name of the operation/algorithm/property. @@ -25,17 +33,26 @@ module CryptographyBase Input> { */ Location getLocation() { result = super.getLocation() } + /** + * Gets the origin of this node, e.g., a string literal in source describing it. + */ + LocatableElement getOrigin(string value) { none() } + /** * Returns the child of this node with the given edge name. * * This predicate is used by derived classes to construct the graph of cryptographic operations. */ - NodeBase getChild(string edgeName) { edgeName = "origin" and result = this.getOrigin() } + NodeBase getChild(string edgeName) { none() } /** - * Gets the origin of this node, e.g., a string literal in source describing it. + * Defines properties of this node by name and either a value or location or both. + * + * This predicate is used by derived classes to construct the graph of cryptographic operations. */ - NodeBase getOrigin() { none() } + predicate properties(string key, string value, Location location) { + key = "origin" and location = this.getOrigin(value).getLocation() + } /** * Returns the parent of this node. @@ -86,7 +103,7 @@ module CryptographyBase Input> { abstract class HashOperation extends Operation { abstract override HashAlgorithm getAlgorithm(); - override string getOperationName() { result = "hash" } + override string getOperationName() { result = "HASH" } } // Rule: no newtype representing a type of algorithm should be modelled with multiple interfaces @@ -105,34 +122,40 @@ module CryptographyBase Input> { MD5() or SHA1() or SHA256() or - SHA512() - - class HashAlgorithmType extends THashType { - string toString() { hashTypeToNameMapping(this, result) } - } - - predicate hashTypeToNameMapping(THashType type, string name) { - type instanceof SHA1 and name = "SHA-1" - or - type instanceof SHA256 and name = "SHA-256" - or - type instanceof SHA512 and name = "SHA-512" - } + SHA512() or + OtherHashType() /** * A hashing algorithm that transforms variable-length input into a fixed-size hash value. */ abstract class HashAlgorithm extends Algorithm { - abstract HashAlgorithmType getHashType(); + final predicate hashTypeToNameMapping(THashType type, string name) { + type instanceof MD5 and name = "MD5" + or + type instanceof SHA1 and name = "SHA-1" + or + type instanceof SHA256 and name = "SHA-256" + or + type instanceof SHA512 and name = "SHA-512" + or + type instanceof OtherHashType and name = this.getRawAlgorithmName() + } + + abstract THashType getHashType(); + + override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } - override string getAlgorithmName() { hashTypeToNameMapping(this.getHashType(), result) } + /** + * Gets the raw name of this hash algorithm from source. + */ + abstract string getRawAlgorithmName(); } /** * An operation that derives one or more keys from an input value. */ abstract class KeyDerivationOperation extends Operation { - override string getOperationName() { result = "key derivation" } + override string getOperationName() { result = "KEY_DERIVATION" } } /** @@ -143,7 +166,7 @@ module CryptographyBase Input> { } /** - * HKDF Extract+Expand key derivation function. + * HKDF key derivation function */ abstract class HKDF extends KeyDerivationAlgorithm { final override string getAlgorithmName() { result = "HKDF" } @@ -157,6 +180,9 @@ module CryptographyBase Input> { } } + /** + * PKCS #12 key derivation function + */ abstract class PKCS12KDF extends KeyDerivationAlgorithm { final override string getAlgorithmName() { result = "PKCS12KDF" } @@ -168,4 +194,31 @@ module CryptographyBase Input> { edgeName = "digest" and result = this.getHashAlgorithm() } } + + /** + * Elliptic curve algorithm + */ + abstract class EllipticCurve extends Algorithm { + abstract string getVersion(Location location); + + abstract string getKeySize(Location location); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + key = "version" and + if exists(this.getVersion(location)) + then value = this.getVersion(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + or + key = "key_size" and + if exists(this.getKeySize(location)) + then value = this.getKeySize(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + } + } } diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index 108993fb134c..c9398c9e3245 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -3,6 +3,8 @@ private import cpp as Lang module CryptoInput implements InputSig { class LocatableElement = Lang::Locatable; + + class UnknownLocation = Lang::UnknownDefaultLocation; } module Crypto = CryptographyBase; diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll index 17de5ddab604..852fedf646a8 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll @@ -9,7 +9,9 @@ module OpenSSLModel { class SHA1Algo extends Crypto::HashAlgorithm instanceof MacroAccess { SHA1Algo() { this.getMacro().getName() = "SN_sha1" } - override Crypto::HashAlgorithmType getHashType() { result instanceof Crypto::SHA1 } + override string getRawAlgorithmName() { result = "SN_sha1" } + + override Crypto::THashType getHashType() { result instanceof Crypto::SHA1 } } module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig { @@ -77,7 +79,9 @@ module OpenSSLModel { override Crypto::HashAlgorithm getHashAlgorithm() { none() } - override Crypto::NodeBase getOrigin() { result = origin } + override Crypto::LocatableElement getOrigin(string name) { + result = origin and name = origin.toString() + } } class TestKeyDerivationOperationHacky extends KeyDerivationOperation instanceof FunctionCall { @@ -97,6 +101,8 @@ module OpenSSLModel { override Crypto::HashAlgorithm getHashAlgorithm() { none() } - override Crypto::NodeBase getOrigin() { result = origin } + override Crypto::NodeBase getOrigin(string name) { + result = origin and name = origin.toString() + } } } diff --git a/cpp/ql/src/experimental/Quantum/Test.ql b/cpp/ql/src/experimental/Quantum/Test.ql index a9ad5021a295..87341a6b612c 100644 --- a/cpp/ql/src/experimental/Quantum/Test.ql +++ b/cpp/ql/src/experimental/Quantum/Test.ql @@ -5,7 +5,33 @@ import experimental.Quantum.Language -query predicate nodes(Crypto::NodeBase node) { any() } +string getValueAndLocationPairs(Crypto::NodeBase node, string key) { + exists(string value, Location location | + node.properties(key, value, location) and + result = "(" + value + "," + location.toString() + ")" + ) +} + +string properties(Crypto::NodeBase node) { + forex(string key | node.properties(key, _, _) | + result = key + ":" + strictconcat(getValueAndLocationPairs(node, key), ",") + ) +} + +string getLabel(Crypto::NodeBase node) { + result = + "[" + node.toString() + "]" + + any(string prop | + if exists(properties(node)) then prop = " " + properties(node) else prop = "" + | + prop + ) +} + +query predicate nodes(Crypto::NodeBase node, string key, string value) { + key = "semmle.label" and + value = getLabel(node) +} query predicate edges(Crypto::NodeBase source, Crypto::NodeBase target, string key, string value) { target = source.getChild(value) and diff --git a/cpp/ql/src/experimental/Quantum/Test2.ql b/cpp/ql/src/experimental/Quantum/Test2.ql index f5971da96443..3f48f156a430 100644 --- a/cpp/ql/src/experimental/Quantum/Test2.ql +++ b/cpp/ql/src/experimental/Quantum/Test2.ql @@ -4,5 +4,5 @@ import experimental.Quantum.Language -from Crypto::KeyDerivationAlgorithm n -select n +from Crypto::NodeBase node +select node From 0cd3df9d2685d9aef55b2305c2ef2d478d3e1223 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Wed, 29 Jan 2025 10:27:46 -0500 Subject: [PATCH 005/122] Concepts for elliptic cureve and misc. updates. --- cpp/ql/lib/experimental/Quantum/Base.qll | 50 +++- python/ql/lib/experimental/Quantum/Base.qll | 250 ++++++++++++++++++ .../ql/lib/experimental/Quantum/Language.qll | 12 + .../experimental/Quantum/PycaCryptography.qll | 55 ++++ python/ql/lib/semmle/python/Files.qll | 37 +++ 5 files changed, 392 insertions(+), 12 deletions(-) create mode 100644 python/ql/lib/experimental/Quantum/Base.qll create mode 100644 python/ql/lib/experimental/Quantum/Language.qll create mode 100644 python/ql/lib/experimental/Quantum/PycaCryptography.qll diff --git a/cpp/ql/lib/experimental/Quantum/Base.qll b/cpp/ql/lib/experimental/Quantum/Base.qll index b3f4d619f4d8..9be24ca6efaa 100644 --- a/cpp/ql/lib/experimental/Quantum/Base.qll +++ b/cpp/ql/lib/experimental/Quantum/Base.qll @@ -92,6 +92,11 @@ module CryptographyBase Input> { */ abstract string getAlgorithmName(); + /** + * Gets the raw name of this algorithm from source (no parsing or formatting) + */ + abstract string getRawAlgorithmName(); + final override string toString() { result = this.getAlgorithmName() } } @@ -145,10 +150,6 @@ module CryptographyBase Input> { override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } - /** - * Gets the raw name of this hash algorithm from source. - */ - abstract string getRawAlgorithmName(); } /** @@ -195,30 +196,55 @@ module CryptographyBase Input> { } } + newtype TEllipticCurveFamilyType = + // We're saying by this that all of these have an identical interface / properties / edges + NIST() or + SEC() or + NUMS() or + PRIME() or + BRAINPOOL() or + CURVE25519() or + CURVE448() or + C2() or + SM2() or + ES() or + OtherEllipticCurveFamilyType() + + /** * Elliptic curve algorithm */ abstract class EllipticCurve extends Algorithm { - abstract string getVersion(Location location); + abstract string getKeySize(Location location); + abstract TEllipticCurveFamilyType getCurveFamilyType(); + override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or - key = "version" and - if exists(this.getVersion(location)) - then value = this.getVersion(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - or key = "key_size" and if exists(this.getKeySize(location)) then value = this.getKeySize(location) else ( value instanceof UnknownPropertyValue and location instanceof UnknownLocation ) + // other properties, like field type are possible, but not modeled until considered necessary } + + override string getAlgorithmName() { result = this.getRawAlgorithmName().toUpperCase()} + + /** + * Mandating that for Elliptic Curves specifically, users are responsible + * for providing as the 'raw' name, the official name of the algorithm. + * Casing doesn't matter, we will enforce further naming restrictions on + * `getAlgorithmName` by default. + * Rationale: elliptic curve names can have a lot of variation in their components + * (e.g., "secp256r1" vs "P-256"), trying to produce generalized set of properties + * is possible to capture all cases, but such modeling is likely not necessary. + * if all properties need to be captured, we can reassess how names are generated. + */ + override abstract string getRawAlgorithmName(); } } diff --git a/python/ql/lib/experimental/Quantum/Base.qll b/python/ql/lib/experimental/Quantum/Base.qll new file mode 100644 index 000000000000..9be24ca6efaa --- /dev/null +++ b/python/ql/lib/experimental/Quantum/Base.qll @@ -0,0 +1,250 @@ +/** + * A language-independent library for reasoning about cryptography. + */ + +import codeql.util.Location +import codeql.util.Option + +signature module InputSig { + class LocatableElement { + Location getLocation(); + } + + class UnknownLocation instanceof Location; +} + +module CryptographyBase Input> { + final class LocatableElement = Input::LocatableElement; + + final class UnknownLocation = Input::UnknownLocation; + + final class UnknownPropertyValue extends string { + UnknownPropertyValue() { this = "" } + } + + abstract class NodeBase instanceof LocatableElement { + /** + * Returns a string representation of this node, usually the name of the operation/algorithm/property. + */ + abstract string toString(); + + /** + * Returns the location of this node in the code. + */ + Location getLocation() { result = super.getLocation() } + + /** + * Gets the origin of this node, e.g., a string literal in source describing it. + */ + LocatableElement getOrigin(string value) { none() } + + /** + * Returns the child of this node with the given edge name. + * + * This predicate is used by derived classes to construct the graph of cryptographic operations. + */ + NodeBase getChild(string edgeName) { none() } + + /** + * Defines properties of this node by name and either a value or location or both. + * + * This predicate is used by derived classes to construct the graph of cryptographic operations. + */ + predicate properties(string key, string value, Location location) { + key = "origin" and location = this.getOrigin(value).getLocation() + } + + /** + * Returns the parent of this node. + */ + final NodeBase getAParent() { result.getChild(_) = this } + } + + class Asset = NodeBase; + + /** + * A cryptographic operation, such as hashing or encryption. + */ + abstract class Operation extends Asset { + /** + * Gets the algorithm associated with this operation. + */ + abstract Algorithm getAlgorithm(); + + /** + * Gets the name of this operation, e.g., "hash" or "encrypt". + */ + abstract string getOperationName(); + + final override string toString() { result = this.getOperationName() } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + edgeName = "uses" and + if exists(this.getAlgorithm()) then result = this.getAlgorithm() else result = this + } + } + + abstract class Algorithm extends Asset { + /** + * Gets the name of this algorithm, e.g., "AES" or "SHA". + */ + abstract string getAlgorithmName(); + + /** + * Gets the raw name of this algorithm from source (no parsing or formatting) + */ + abstract string getRawAlgorithmName(); + + final override string toString() { result = this.getAlgorithmName() } + } + + /** + * A hashing operation that processes data to generate a hash value. + * This operation takes an input message of arbitrary content and length and produces a fixed-size + * hash value as the output using a specified hashing algorithm. + */ + abstract class HashOperation extends Operation { + abstract override HashAlgorithm getAlgorithm(); + + override string getOperationName() { result = "HASH" } + } + + // Rule: no newtype representing a type of algorithm should be modelled with multiple interfaces + // + // Example: HKDF and PKCS12KDF are both key derivation algorithms. + // However, PKCS12KDF also has a property: the iteration count. + // + // If we have HKDF and PKCS12KDF under TKeyDerivationType, + // someone modelling a library might try to make a generic identification of both of those algorithms. + // + // They will therefore not use the specialized type for PKCS12KDF, + // meaning "from PKCS12KDF algo select algo" will have no results. + // + newtype THashType = + // We're saying by this that all of these have an identical interface / properties / edges + MD5() or + SHA1() or + SHA256() or + SHA512() or + OtherHashType() + + /** + * A hashing algorithm that transforms variable-length input into a fixed-size hash value. + */ + abstract class HashAlgorithm extends Algorithm { + final predicate hashTypeToNameMapping(THashType type, string name) { + type instanceof MD5 and name = "MD5" + or + type instanceof SHA1 and name = "SHA-1" + or + type instanceof SHA256 and name = "SHA-256" + or + type instanceof SHA512 and name = "SHA-512" + or + type instanceof OtherHashType and name = this.getRawAlgorithmName() + } + + abstract THashType getHashType(); + + override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } + + } + + /** + * An operation that derives one or more keys from an input value. + */ + abstract class KeyDerivationOperation extends Operation { + override string getOperationName() { result = "KEY_DERIVATION" } + } + + /** + * An algorithm that derives one or more keys from an input value. + */ + abstract class KeyDerivationAlgorithm extends Algorithm { + abstract override string getAlgorithmName(); + } + + /** + * HKDF key derivation function + */ + abstract class HKDF extends KeyDerivationAlgorithm { + final override string getAlgorithmName() { result = "HKDF" } + + abstract HashAlgorithm getHashAlgorithm(); + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + edgeName = "digest" and result = this.getHashAlgorithm() + } + } + + /** + * PKCS #12 key derivation function + */ + abstract class PKCS12KDF extends KeyDerivationAlgorithm { + final override string getAlgorithmName() { result = "PKCS12KDF" } + + abstract HashAlgorithm getHashAlgorithm(); + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + edgeName = "digest" and result = this.getHashAlgorithm() + } + } + + newtype TEllipticCurveFamilyType = + // We're saying by this that all of these have an identical interface / properties / edges + NIST() or + SEC() or + NUMS() or + PRIME() or + BRAINPOOL() or + CURVE25519() or + CURVE448() or + C2() or + SM2() or + ES() or + OtherEllipticCurveFamilyType() + + + /** + * Elliptic curve algorithm + */ + abstract class EllipticCurve extends Algorithm { + + + abstract string getKeySize(Location location); + + abstract TEllipticCurveFamilyType getCurveFamilyType(); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + key = "key_size" and + if exists(this.getKeySize(location)) + then value = this.getKeySize(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + // other properties, like field type are possible, but not modeled until considered necessary + } + + override string getAlgorithmName() { result = this.getRawAlgorithmName().toUpperCase()} + + /** + * Mandating that for Elliptic Curves specifically, users are responsible + * for providing as the 'raw' name, the official name of the algorithm. + * Casing doesn't matter, we will enforce further naming restrictions on + * `getAlgorithmName` by default. + * Rationale: elliptic curve names can have a lot of variation in their components + * (e.g., "secp256r1" vs "P-256"), trying to produce generalized set of properties + * is possible to capture all cases, but such modeling is likely not necessary. + * if all properties need to be captured, we can reassess how names are generated. + */ + override abstract string getRawAlgorithmName(); + } +} diff --git a/python/ql/lib/experimental/Quantum/Language.qll b/python/ql/lib/experimental/Quantum/Language.qll new file mode 100644 index 000000000000..9abf3e7fa7e4 --- /dev/null +++ b/python/ql/lib/experimental/Quantum/Language.qll @@ -0,0 +1,12 @@ +private import Base +private import python as Lang + +module CryptoInput implements InputSig { + class LocatableElement = Lang::Expr; + + class UnknownLocation = Lang::UnknownDefaultLocation; +} + +module Crypto = CryptographyBase; + +import PycaCryptography diff --git a/python/ql/lib/experimental/Quantum/PycaCryptography.qll b/python/ql/lib/experimental/Quantum/PycaCryptography.qll new file mode 100644 index 000000000000..802ad23cf9d9 --- /dev/null +++ b/python/ql/lib/experimental/Quantum/PycaCryptography.qll @@ -0,0 +1,55 @@ +import python +import semmle.python.ApiGraphs + +module PycaCryptographyModule { + import Language + + /** + * Gets a predefined curve class constructor call from + * `cryptography.hazmat.primitives.asymmetric.ec` + * https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/#elliptic-curves + */ + DataFlow::Node predefinedCurveClass(string rawName, string curveName, Crypto::TEllipticCurveFamilyType family, int keySize) { + // getACall since the typical case is to construct the curve with initialization values, + // not to pass the mode uninitialized + result = + API::moduleImport("cryptography") + .getMember("hazmat") + .getMember("primitives") + .getMember("asymmetric") + .getMember("ec") + .getMember(rawName) + .getACall() + and + curveName = rawName.toUpperCase() + and + curveName.matches("SEC%") and family instanceof Crypto::SEC + and + curveName.matches("BRAINPOOL%") and family instanceof Crypto::BRAINPOOL + and + // Enumerating all key sizes known in the API + // TODO: should we dynamically extract them through a regex? + keySize in [160, 163, 192, 224, 233, 256, 283, 320, 384, 409, 512, 571] + and + curveName.matches("%" + keySize + "%") + } + + + class EllipticCurve extends Crypto::EllipticCurve instanceof Expr{ + int keySize; + string rawName; + string curveName; + Crypto::TEllipticCurveFamilyType family; + EllipticCurve() { + this = predefinedCurveClass(rawName, curveName, family, keySize).asExpr() + } + + override string getRawAlgorithmName() { result = rawName } + override string getAlgorithmName() { result = curveName } + Crypto::TEllipticCurveFamilyType getFamily() { result = family } + + override string getKeySize(Location location) { + location = this and + result = keySize.toString() } + } +} diff --git a/python/ql/lib/semmle/python/Files.qll b/python/ql/lib/semmle/python/Files.qll index 2da0dd61f885..67f21ad0b249 100644 --- a/python/ql/lib/semmle/python/Files.qll +++ b/python/ql/lib/semmle/python/Files.qll @@ -368,3 +368,40 @@ class EncodingError extends SyntaxError { override string toString() { result = "Encoding Error" } } + + + +/** + * 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. + */ +class UnknownLocation extends 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. + */ +class UnknownDefaultLocation extends UnknownLocation { + UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) } +} + +/** + * A dummy location which is used when an expression doesn't have a + * location in the source code but needs to have a `Location` associated + * with it. + */ +class UnknownExprLocation extends UnknownLocation { + UnknownExprLocation() { locations_default(this, _, 0, 0, 0, 0) } +} + +/** + * A dummy location which is used when a statement doesn't have a location + * in the source code but needs to have a `Location` associated with it. + */ +class UnknownStmtLocation extends UnknownLocation { + UnknownStmtLocation() { locations_default(this, _, 0, 0, 0, 0) } +} From 9af18bc1002e4b32b18a8f56d2fbf9c2ffa7405f Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Wed, 29 Jan 2025 19:45:04 +0100 Subject: [PATCH 006/122] WIP: add dgml/dot output/remove test code --- cpp/ql/lib/experimental/Quantum/Base.qll | 4 +- .../lib/experimental/Quantum/BaseBackup.qll | 125 ------------------ cpp/ql/lib/experimental/Quantum/OpenSSL.qll | 10 -- cpp/ql/src/experimental/Quantum/CBOMGraph.ql | 47 +++++++ cpp/ql/src/experimental/Quantum/Test.ql | 43 ------ cpp/ql/src/experimental/Quantum/cbom.sh | 8 ++ .../src/experimental/Quantum/generate_cbom.py | 104 +++++++++++++++ 7 files changed, 162 insertions(+), 179 deletions(-) delete mode 100644 cpp/ql/lib/experimental/Quantum/BaseBackup.qll create mode 100644 cpp/ql/src/experimental/Quantum/CBOMGraph.ql delete mode 100644 cpp/ql/src/experimental/Quantum/Test.ql create mode 100755 cpp/ql/src/experimental/Quantum/cbom.sh create mode 100644 cpp/ql/src/experimental/Quantum/generate_cbom.py diff --git a/cpp/ql/lib/experimental/Quantum/Base.qll b/cpp/ql/lib/experimental/Quantum/Base.qll index b3f4d619f4d8..cb0f53f468e7 100644 --- a/cpp/ql/lib/experimental/Quantum/Base.qll +++ b/cpp/ql/lib/experimental/Quantum/Base.qll @@ -51,7 +51,9 @@ module CryptographyBase Input> { * This predicate is used by derived classes to construct the graph of cryptographic operations. */ predicate properties(string key, string value, Location location) { - key = "origin" and location = this.getOrigin(value).getLocation() + key = "origin" and + location = this.getOrigin(value).getLocation() and + not location = this.getLocation() } /** diff --git a/cpp/ql/lib/experimental/Quantum/BaseBackup.qll b/cpp/ql/lib/experimental/Quantum/BaseBackup.qll deleted file mode 100644 index 821c89eb8f57..000000000000 --- a/cpp/ql/lib/experimental/Quantum/BaseBackup.qll +++ /dev/null @@ -1,125 +0,0 @@ -/** - * A language-independent library for reasoning about cryptography. - */ - -import codeql.util.Location -import codeql.util.Option - -signature module InputSig { - class KnownUnknownLocation extends Location; - - class LocatableElement { - Location getLocation(); - } -} - -// An operation = a specific loc in code -// An algorithm -// Properties -// Node -> Operation -> Algorithm -> Symmetric -> SpecificSymmetricAlgo -// -[Language-Specific]-> LibrarySymmetricAlgo -> Properties -// For example (nsted newtypes): -/* - * newtype for each algo, and each one of those would have params for their properties - * implementation: optional/range for example - * - * - * - * /** - * Constructs an `Option` type that is a disjoint union of the given type and an - * additional singleton element. - */ - -module CryptographyBase Input> { - newtype TNode = - TNodeUnknown() or - TNodeAlgorithm() or - TNodeOperation() - - /* - * A cryptographic asset in code, i.e., an algorithm, operation, property, or known unknown. - */ - - abstract class Node extends TNode { - // this would then extend LanguageNode - abstract Location getLocation(); - - abstract string toString(); - - abstract Node getChild(int childIndex); - - final Node getAChild() { result = this.getChild(_) } - - final Node getAParent() { result.getAChild() = this } - } - - final class KnownUnknown extends Node, TNodeUnknown { - override string toString() { result = "unknown" } - - override Node getChild(int childIndex) { none() } - - override Location getLocation() { result instanceof Input::KnownUnknownLocation } - } - - abstract class Operation extends Node, TNodeOperation { - /** - * Gets the algorithm associated with this operation. - */ - abstract Node getAlgorithm(); - - /** - * Gets the name of this operation, e.g., "hash" or "encrypt". - */ - abstract string getOperationName(); - - final override Node getChild(int childIndex) { childIndex = 0 and result = this.getAlgorithm() } - - final override string toString() { result = this.getOperationName() } - } - - abstract class Algorithm extends Node, TNodeAlgorithm { - /** - * Gets the name of this algorithm, e.g., "AES" or "SHA". - */ - abstract string getAlgorithmName(); - } - - /** - * A hashing operation that processes data to generate a hash value. - * This operation takes an input message of arbitrary content and length and produces a fixed-size - * hash value as the output using a specified hashing algorithm. - */ - abstract class HashOperation extends Operation { - abstract override HashAlgorithm getAlgorithm(); - - override string getOperationName() { result = "hash" } - } - - /** - * A hashing algorithm that transforms variable-length input into a fixed-size hash value. - */ - abstract class HashAlgorithm extends Algorithm { } - - /** - * An operation that derives one or more keys from an input value. - */ - abstract class KeyDerivationOperation extends Operation { - override string getOperationName() { result = "key derivation" } - } - - /** - * An algorithm that derives one or more keys from an input value. - */ - abstract class KeyDerivationAlgorithm extends Algorithm { - abstract override string getAlgorithmName(); - } - - /** - * HKDF Extract+Expand key derivation function. - */ - abstract class HKDFAlgorithm extends KeyDerivationAlgorithm { - final override string getAlgorithmName() { result = "HKDF" } - - abstract Node getDigestAlgorithm(); - } -} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll index 852fedf646a8..18feda6c8c6c 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll @@ -84,16 +84,6 @@ module OpenSSLModel { } } - class TestKeyDerivationOperationHacky extends KeyDerivationOperation instanceof FunctionCall { - HKDF hkdf; - - TestKeyDerivationOperationHacky() { - this.getEnclosingFunction() = hkdf.(Expr).getEnclosingFunction() - } - - override Crypto::KeyDerivationAlgorithm getAlgorithm() { result = hkdf } - } - class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF instanceof Expr { KDFAlgorithmStringLiteral origin; diff --git a/cpp/ql/src/experimental/Quantum/CBOMGraph.ql b/cpp/ql/src/experimental/Quantum/CBOMGraph.ql new file mode 100644 index 000000000000..1df6611aab9a --- /dev/null +++ b/cpp/ql/src/experimental/Quantum/CBOMGraph.ql @@ -0,0 +1,47 @@ +/** + * @name "Outputs a graph representation of the cryptographic bill of materials." + * @kind graph + * @id cbomgraph + */ + +import experimental.Quantum.Language + +string getPropertyString(Crypto::NodeBase node, string key) { + result = + strictconcat(any(string value, Location location, string parsed | + node.properties(key, value, location) and + parsed = "(" + value + "," + location.toString() + ")" + | + parsed + ), "," + ) +} + +string getLabel(Crypto::NodeBase node) { result = node.toString() } + +query predicate nodes(Crypto::NodeBase node, string key, string value) { + key = "semmle.label" and + value = getLabel(node) + or + // CodeQL's DGML output does not include a location + key = "Location" and + value = node.getLocation().toString() + or + // Known unknown edges should be reported as properties rather than edges + node = node.getChild(key) and + value = "" + or + // Report properties + value = getPropertyString(node, key) +} + +query predicate edges(Crypto::NodeBase source, Crypto::NodeBase target, string key, string value) { + key = "semmle.label" and + target = source.getChild(value) and + // Known unknowns are reported as properties rather than edges + not source = target +} + +query predicate graphProperties(string key, string value) { + key = "semmle.graphKind" and value = "graph" +} diff --git a/cpp/ql/src/experimental/Quantum/Test.ql b/cpp/ql/src/experimental/Quantum/Test.ql deleted file mode 100644 index 87341a6b612c..000000000000 --- a/cpp/ql/src/experimental/Quantum/Test.ql +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @name "PQC Test" - * @kind graph - */ - -import experimental.Quantum.Language - -string getValueAndLocationPairs(Crypto::NodeBase node, string key) { - exists(string value, Location location | - node.properties(key, value, location) and - result = "(" + value + "," + location.toString() + ")" - ) -} - -string properties(Crypto::NodeBase node) { - forex(string key | node.properties(key, _, _) | - result = key + ":" + strictconcat(getValueAndLocationPairs(node, key), ",") - ) -} - -string getLabel(Crypto::NodeBase node) { - result = - "[" + node.toString() + "]" + - any(string prop | - if exists(properties(node)) then prop = " " + properties(node) else prop = "" - | - prop - ) -} - -query predicate nodes(Crypto::NodeBase node, string key, string value) { - key = "semmle.label" and - value = getLabel(node) -} - -query predicate edges(Crypto::NodeBase source, Crypto::NodeBase target, string key, string value) { - target = source.getChild(value) and - key = "semmle.label" -} - -query predicate graphProperties(string key, string value) { - key = "semmle.graphKind" and value = "tree" -} diff --git a/cpp/ql/src/experimental/Quantum/cbom.sh b/cpp/ql/src/experimental/Quantum/cbom.sh new file mode 100755 index 000000000000..df1c58b1efb8 --- /dev/null +++ b/cpp/ql/src/experimental/Quantum/cbom.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +CODEQL_PATH="/Users/nicolaswill/Library/Application Support/Code/User/globalStorage/github.vscode-codeql/distribution5/codeql/codeql" +DATABASE_PATH="/Users/nicolaswill/openssl_codeql/openssl/openssl_db" +QUERY_FILE="CBOMGraph.ql" +OUTPUT_DIR="graph_output" + +python3 generate_cbom.py -c "$CODEQL_PATH" -d "$DATABASE_PATH" -q "$QUERY_FILE" -o "$OUTPUT_DIR" diff --git a/cpp/ql/src/experimental/Quantum/generate_cbom.py b/cpp/ql/src/experimental/Quantum/generate_cbom.py new file mode 100644 index 000000000000..10ec895dc629 --- /dev/null +++ b/cpp/ql/src/experimental/Quantum/generate_cbom.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 + +import os +import sys +import argparse +import subprocess +import xml.etree.ElementTree as ET + +def run_codeql_analysis(codeql_path, database_path, query_path, output_dir): + """Runs the CodeQL analysis and generates a DGML file.""" + os.makedirs(output_dir, exist_ok=True) + command = [ + codeql_path, "database", "analyze", database_path, query_path, + "--rerun", "--format=dgml", "--output", output_dir + ] + + print(f"Running CodeQL analysis: {' '.join(command)}") + result = subprocess.run(command, capture_output=True, text=True) + + if result.returncode == 0: + print("Analysis completed successfully.") + else: + print("Analysis failed.") + print(result.stderr) + sys.exit(1) + + return result.returncode + + +def convert_dgml_to_dot(dgml_file, dot_file): + """Converts the DGML file to DOT format using the exact original implementation.""" + print(f"Processing DGML file: {dgml_file}") + + # Read source DGML + with open(dgml_file, "r", encoding="utf-8") as f: + xml_content = f.read() + + root = ET.fromstring(xml_content) + + # Form dot element sequence + body_l = ["digraph cbom {", + "node [shape=box];", + "rankdir=LR;" + ] + + # Process nodes + for node in root.find("{http://schemas.microsoft.com/vs/2009/dgml}Nodes"): + att = node.attrib + node_id = att['Id'] + label_parts = [] + for key, value in att.items(): + if key == 'Id': + continue + elif key == 'Label': + label_parts.append(value) + else: + label_parts.append(f"{key}={value}") + label = "\\n".join(label_parts) + prop_l = [f'label="{label}"'] + node_s = f'nd_{node_id} [{", ".join(prop_l)}];' + body_l.append(node_s) + + # Process edges + for edge in root.find("{http://schemas.microsoft.com/vs/2009/dgml}Links"): + att = edge.attrib + edge_s = 'nd_{} -> nd_{} [label="{}"];'.format( + att["Source"], att["Target"], att.get("Label", "")) + body_l.append(edge_s) + + body_l.append("}") + + # Write DOT output + with open(dot_file, "w", encoding="utf-8") as f: + f.write("\n".join(body_l)) + + print(f"DGML file successfully converted to DOT format: {dot_file}") + + +def main(): + parser = argparse.ArgumentParser(description="Run CodeQL analysis and convert DGML to DOT.") + parser.add_argument("-c", "--codeql", required=True, help="Path to CodeQL CLI executable.") + parser.add_argument("-d", "--database", required=True, help="Path to the CodeQL database.") + parser.add_argument("-q", "--query", required=True, help="Path to the .ql query file.") + parser.add_argument("-o", "--output", required=True, help="Output directory for analysis results.") + + args = parser.parse_args() + + # Run CodeQL analysis + run_codeql_analysis(args.codeql, args.database, args.query, args.output) + + # Locate DGML file + dgml_file = os.path.join(args.output, "cbomgraph.dgml") + dot_file = dgml_file.replace(".dgml", ".dot") + + if os.path.exists(dgml_file): + # Convert DGML to DOT + convert_dgml_to_dot(dgml_file, dot_file) + else: + print(f"No DGML file found in {args.output}.") + sys.exit(1) + + +if __name__ == "__main__": + main() From 69a63855cc07c7a5d4b0b48f2e4b3f51b274ed3a Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Wed, 29 Jan 2025 21:55:57 +0100 Subject: [PATCH 007/122] Update CBOMGraph.ql --- cpp/ql/src/experimental/Quantum/CBOMGraph.ql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Quantum/CBOMGraph.ql b/cpp/ql/src/experimental/Quantum/CBOMGraph.ql index 1df6611aab9a..edcc40aca6a4 100644 --- a/cpp/ql/src/experimental/Quantum/CBOMGraph.ql +++ b/cpp/ql/src/experimental/Quantum/CBOMGraph.ql @@ -1,5 +1,6 @@ /** - * @name "Outputs a graph representation of the cryptographic bill of materials." + * @name "Print CBOM Graph" + * @description "Outputs a graph representation of the cryptographic bill of materials." * @kind graph * @id cbomgraph */ From 5f355c7f555748cb47b9cbaab862a0a05daf211c Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Tue, 4 Feb 2025 11:55:09 -0500 Subject: [PATCH 008/122] Add first sample JCA encryption model --- java/ql/lib/experimental/Quantum/Base.qll | 235 ++++++++++++++++++ java/ql/lib/experimental/Quantum/JCA.qll | 105 ++++++++ java/ql/lib/experimental/Quantum/Language.qll | 30 +++ java/ql/src/experimental/Quantum/Test.ql | 13 + 4 files changed, 383 insertions(+) create mode 100644 java/ql/lib/experimental/Quantum/Base.qll create mode 100644 java/ql/lib/experimental/Quantum/JCA.qll create mode 100644 java/ql/lib/experimental/Quantum/Language.qll create mode 100644 java/ql/src/experimental/Quantum/Test.ql diff --git a/java/ql/lib/experimental/Quantum/Base.qll b/java/ql/lib/experimental/Quantum/Base.qll new file mode 100644 index 000000000000..bbcd60ee891a --- /dev/null +++ b/java/ql/lib/experimental/Quantum/Base.qll @@ -0,0 +1,235 @@ +/** + * A language-independent library for reasoning about cryptography. + */ + + import codeql.util.Location + import codeql.util.Option + + signature module InputSig { + class LocatableElement { + Location getLocation(); + } + + class UnknownLocation instanceof Location; + } + + module CryptographyBase Input> { + final class LocatableElement = Input::LocatableElement; + + final class UnknownLocation = Input::UnknownLocation; + + final class UnknownPropertyValue extends string { + UnknownPropertyValue() { this = "" } + } + + abstract class NodeBase instanceof LocatableElement { + /** + * Returns a string representation of this node, usually the name of the operation/algorithm/property. + */ + abstract string toString(); + + /** + * Returns the location of this node in the code. + */ + Location getLocation() { result = super.getLocation() } + + /** + * Gets the origin of this node, e.g., a string literal in source describing it. + */ + LocatableElement getOrigin(string value) { none() } + + /** + * Returns the child of this node with the given edge name. + * + * This predicate is used by derived classes to construct the graph of cryptographic operations. + */ + NodeBase getChild(string edgeName) { none() } + + /** + * Defines properties of this node by name and either a value or location or both. + * + * This predicate is used by derived classes to construct the graph of cryptographic operations. + */ + predicate properties(string key, string value, Location location) { + key = "origin" and location = this.getOrigin(value).getLocation() + } + + /** + * Returns the parent of this node. + */ + final NodeBase getAParent() { result.getChild(_) = this } + } + + class Asset = NodeBase; + + /** + * A cryptographic operation, such as hashing or encryption. + */ + abstract class Operation extends Asset { + /** + * Gets the algorithm associated with this operation. + */ + abstract Algorithm getAlgorithm(); + + /** + * Gets the name of this operation, e.g., "hash" or "encrypt". + */ + abstract string getOperationName(); + + final override string toString() { result = this.getOperationName() } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + edgeName = "uses" and + if exists(this.getAlgorithm()) then result = this.getAlgorithm() else result = this + } + } + + abstract class Algorithm extends Asset { + /** + * Gets the name of this algorithm, e.g., "AES" or "SHA". + */ + abstract string getAlgorithmName(); + + final override string toString() { result = this.getAlgorithmName() } + } + + /** + * A hashing operation that processes data to generate a hash value. + * This operation takes an input message of arbitrary content and length and produces a fixed-size + * hash value as the output using a specified hashing algorithm. + */ + abstract class HashOperation extends Operation { + abstract override HashAlgorithm getAlgorithm(); + + override string getOperationName() { result = "HASH" } + } + + // Rule: no newtype representing a type of algorithm should be modelled with multiple interfaces + // + // Example: HKDF and PKCS12KDF are both key derivation algorithms. + // However, PKCS12KDF also has a property: the iteration count. + // + // If we have HKDF and PKCS12KDF under TKeyDerivationType, + // someone modelling a library might try to make a generic identification of both of those algorithms. + // + // They will therefore not use the specialized type for PKCS12KDF, + // meaning "from PKCS12KDF algo select algo" will have no results. + // + newtype THashType = + // We're saying by this that all of these have an identical interface / properties / edges + MD5() or + SHA1() or + SHA256() or + SHA512() or + OtherHashType() + + /** + * A hashing algorithm that transforms variable-length input into a fixed-size hash value. + */ + abstract class HashAlgorithm extends Algorithm { + final predicate hashTypeToNameMapping(THashType type, string name) { + type instanceof MD5 and name = "MD5" + or + type instanceof SHA1 and name = "SHA-1" + or + type instanceof SHA256 and name = "SHA-256" + or + type instanceof SHA512 and name = "SHA-512" + or + type instanceof OtherHashType and name = this.getRawAlgorithmName() + } + + abstract THashType getHashType(); + + override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } + + /** + * Gets the raw name of this hash algorithm from source. + */ + abstract string getRawAlgorithmName(); + } + + /** + * An operation that derives one or more keys from an input value. + */ + abstract class KeyDerivationOperation extends Operation { + override string getOperationName() { result = "KEY_DERIVATION" } + } + + /** + * An algorithm that derives one or more keys from an input value. + */ + abstract class KeyDerivationAlgorithm extends Algorithm { + abstract override string getAlgorithmName(); + } + + /** + * HKDF key derivation function + */ + abstract class HKDF extends KeyDerivationAlgorithm { + final override string getAlgorithmName() { result = "HKDF" } + + abstract HashAlgorithm getHashAlgorithm(); + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + edgeName = "digest" and result = this.getHashAlgorithm() + } + } + + /** + * PKCS #12 key derivation function + */ + abstract class PKCS12KDF extends KeyDerivationAlgorithm { + final override string getAlgorithmName() { result = "PKCS12KDF" } + + abstract HashAlgorithm getHashAlgorithm(); + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + edgeName = "digest" and result = this.getHashAlgorithm() + } + } + + /** + * Elliptic curve algorithm + */ + abstract class EllipticCurve extends Algorithm { + abstract string getVersion(Location location); + + abstract string getKeySize(Location location); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + key = "version" and + if exists(this.getVersion(location)) + then value = this.getVersion(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + or + key = "key_size" and + if exists(this.getKeySize(location)) + then value = this.getKeySize(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + } + } + + /** + * An encryption operation that processes plaintext to generate a ciphertext. + * This operation takes an input message (plaintext) of arbitrary content and length and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). + */ + abstract class EncryptionOperation extends Operation { + abstract override Algorithm getAlgorithm(); + + override string getOperationName() { result = "ENCRYPTION" } + } + } + \ No newline at end of file diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll new file mode 100644 index 000000000000..bef648d290a1 --- /dev/null +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -0,0 +1,105 @@ +import java +import semmle.code.java.dataflow.DataFlow + +module JCAModel { + import Language + + abstract class EncryptionOperation extends Crypto::EncryptionOperation { } + + //TODO PBEWith can have suffixes. how to do? enumerate? or match a pattern? +predicate cipher_names(string algo) { algo = ["AES", "AESWrap", "AESWrapPad", "ARCFOUR", "Blowfish", "ChaCha20", "ChaCha20-Poly1305", "DES", "DESede", "DESedeWrap", "ECIES", "PBEWith", "RC2", "RC4", "RC5", "RSA"] } +//TODO solve the fact that x is an int of various values. same as above... enumerate? +predicate cipher_modes(string mode) {mode = ["NONE", "CBC", "CCM", "CFB", "CFBx", "CTR", "CTS", "ECB", "GCM", "KW", "KWP", "OFB", "OFBx", "PCBC"]} +//todo same as above, OAEPWith has asuffix type +predicate cipher_padding(string padding) {padding = ["NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith", "PKCS1Padding", "PKCS5Padding", "SSL3Padding"]} + + /** + * Symmetric algorithms + */ + abstract class SymmetricAlgorithm extends Crypto::Algorithm { + + + //TODO figure out how to get this from the Cipher interface, is it explicit? + //abstract string getKeySize(Location location); + + // override predicate properties(string key, string value, Location location) { + // super.properties(key, value, location) + // or + // key = "key_size" and + // if exists(this.getKeySize(location)) + // then value = this.getKeySize(location) + // else ( + // value instanceof Crypto::UnknownPropertyValue and location instanceof UnknownLocation + // ) + // // other properties, like field type are possible, but not modeled until considered necessary + // } + + abstract override string getAlgorithmName(); +} + +////cipher specifics ---------------------------------------- + +class CipherInstance extends Call { + CipherInstance() { this.getCallee().hasQualifiedName("javax.crypto","Cipher", "getInstance") } + + Expr getAlgorithmArg() { result = this.getArgument(0) } + } + +class CipherAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLiteral { + CipherAlgorithmStringLiteral() { cipher_names(this.getValue().splitAt("/"))} + + override string toString() { result = this.(StringLiteral).toString() } + + string getValue() { result = this.(StringLiteral).getValue() } + } + + class CipherAlgorithmModeStringLiteral extends Crypto::NodeBase instanceof StringLiteral { + CipherAlgorithmModeStringLiteral() { cipher_modes(this.getValue().splitAt("/"))} + + override string toString() { result = this.(StringLiteral).toString() } + + string getValue() { result = this.(StringLiteral).getValue() } + } + + class CipherAlgorithmPaddingStringLiteral extends Crypto::NodeBase instanceof StringLiteral { + CipherAlgorithmPaddingStringLiteral() { cipher_padding(this.getValue().splitAt("/"))} + + override string toString() { result = this.(StringLiteral).toString() } + + string getValue() { result = this.(StringLiteral).getValue() } + } + + private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherAlgorithmStringLiteral } + + predicate isSink(DataFlow::Node sink) { + exists(CipherInstance call | sink.asExpr() = call.getAlgorithmArg()) + } + } + + module AlgorithmStringToFetchFlow = DataFlow::Global; + + predicate algorithmStringToCipherInstanceArgFlow(string name, CipherAlgorithmStringLiteral origin, Expr arg) { + exists(CipherInstance sinkCall | + origin.getValue().toUpperCase() = name and + arg = sinkCall.getAlgorithmArg() and + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) + ) + } + + class AES extends SymmetricAlgorithm instanceof Expr { + CipherAlgorithmStringLiteral origin; + + AES() { algorithmStringToCipherInstanceArgFlow("AES", origin, this) } + + override Crypto::LocatableElement getOrigin(string name) { + result = origin and name = origin.toString() + } + + override string getAlgorithmName(){ result = "AES"} + } + + + + +} \ No newline at end of file diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll new file mode 100644 index 000000000000..246f2b86367c --- /dev/null +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -0,0 +1,30 @@ +private import Base +private import java as Lang + +/** + * 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. + */ +class UnknownLocation extends 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. + */ +class UnknownDefaultLocation extends UnknownLocation { + UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) } +} + +module CryptoInput implements InputSig { + class LocatableElement = Lang::Element; + + class UnknownLocation = UnknownDefaultLocation; +} + +module Crypto = CryptographyBase; + +import JCA diff --git a/java/ql/src/experimental/Quantum/Test.ql b/java/ql/src/experimental/Quantum/Test.ql new file mode 100644 index 000000000000..672ad14e5dfa --- /dev/null +++ b/java/ql/src/experimental/Quantum/Test.ql @@ -0,0 +1,13 @@ +/** + * @name "PQC Test" + */ + + import experimental.Quantum.Language + //import java + + from Crypto::NodeBase node + select node + +// from Class t +// where t.hasQualifiedName("javax.crypto", "CipherSpi") +// select t, t.getADescendant*() \ No newline at end of file From 86e51dad8a4d2ead6638f1d5cfb0f54309bf81fa Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Wed, 5 Feb 2025 13:39:48 -0500 Subject: [PATCH 009/122] Improve JCA aes alg model, add test --- java/ql/lib/experimental/Quantum/JCA.qll | 58 ++++++++++++++++++------ java/ql/src/experimental/Quantum/Test.ql | 9 +--- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index bef648d290a1..fae5c4f806e9 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -13,6 +13,18 @@ predicate cipher_modes(string mode) {mode = ["NONE", "CBC", "CCM", "CFB", "CFBx" //todo same as above, OAEPWith has asuffix type predicate cipher_padding(string padding) {padding = ["NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith", "PKCS1Padding", "PKCS5Padding", "SSL3Padding"]} + +abstract class BlockCiper extends Crypto::Algorithm { + CipherAlgorithmStringLiteral alg; + CipherAlgorithmMode mode; + CipherAlgorithmPadding padding; + + + CipherAlgorithmStringLiteral getAlg() {result = alg } + CipherAlgorithmMode getMode() {result = mode } + + CipherAlgorithmPadding getPadding() {result =padding} +} /** * Symmetric algorithms */ @@ -45,6 +57,9 @@ class CipherInstance extends Call { Expr getAlgorithmArg() { result = this.getArgument(0) } } + /** + * this may be specified either in the ALG/MODE/PADDING or just ALG format + */ class CipherAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLiteral { CipherAlgorithmStringLiteral() { cipher_names(this.getValue().splitAt("/"))} @@ -53,20 +68,28 @@ class CipherAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLit string getValue() { result = this.(StringLiteral).getValue() } } - class CipherAlgorithmModeStringLiteral extends Crypto::NodeBase instanceof StringLiteral { - CipherAlgorithmModeStringLiteral() { cipher_modes(this.getValue().splitAt("/"))} +abstract class CipherAlgorithmMode extends Crypto::NodeBase { + string getValue() {result = ""} +} + + class CipherAlgorithmModeStringLiteral extends CipherAlgorithmMode instanceof StringLiteral { + CipherAlgorithmModeStringLiteral() { cipher_modes(this.(StringLiteral).getValue().splitAt("/"))} override string toString() { result = this.(StringLiteral).toString() } - string getValue() { result = this.(StringLiteral).getValue() } + override string getValue() { result = this.(StringLiteral).getValue().regexpCapture(".*/(.*)/.*",1) } } - class CipherAlgorithmPaddingStringLiteral extends Crypto::NodeBase instanceof StringLiteral { - CipherAlgorithmPaddingStringLiteral() { cipher_padding(this.getValue().splitAt("/"))} + abstract class CipherAlgorithmPadding extends Crypto::NodeBase { + string getValue() {result = ""} + } + + class CipherAlgorithmPaddingStringLiteral extends CipherAlgorithmPadding instanceof StringLiteral { + CipherAlgorithmPaddingStringLiteral() { cipher_padding(this.(StringLiteral).getValue().splitAt("/"))} override string toString() { result = this.(StringLiteral).toString() } - string getValue() { result = this.(StringLiteral).getValue() } + override string getValue() { result = this.(StringLiteral).getValue().regexpCapture(".*/.*/(.*)",1) } } private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { @@ -79,27 +102,32 @@ class CipherAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLit module AlgorithmStringToFetchFlow = DataFlow::Global; - predicate algorithmStringToCipherInstanceArgFlow(string name, CipherAlgorithmStringLiteral origin, Expr arg) { + predicate algorithmStringToCipherInstanceArgFlow(string name, CipherAlgorithmStringLiteral origin, CipherAlgorithmModeStringLiteral mode, CipherAlgorithmPaddingStringLiteral padding, Expr arg) { exists(CipherInstance sinkCall | - origin.getValue().toUpperCase() = name and + origin.getValue().splitAt("/") = name and + origin = mode and + origin = padding and arg = sinkCall.getAlgorithmArg() and AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) ) } - class AES extends SymmetricAlgorithm instanceof Expr { - CipherAlgorithmStringLiteral origin; + /** + * A class to represent when AES is used AND it has literal mode and padding provided + * this does not capture the use without + */ + class AESLiteral extends SymmetricAlgorithm, BlockCiper instanceof Expr { - AES() { algorithmStringToCipherInstanceArgFlow("AES", origin, this) } + + AESLiteral() { algorithmStringToCipherInstanceArgFlow("AES", alg, mode, padding, this) +} override Crypto::LocatableElement getOrigin(string name) { - result = origin and name = origin.toString() + result = alg and name = alg.toString() } - override string getAlgorithmName(){ result = "AES"} + override string getAlgorithmName(){ result = this.getAlgorithmName()} } - - } \ No newline at end of file diff --git a/java/ql/src/experimental/Quantum/Test.ql b/java/ql/src/experimental/Quantum/Test.ql index 672ad14e5dfa..f35336a846f2 100644 --- a/java/ql/src/experimental/Quantum/Test.ql +++ b/java/ql/src/experimental/Quantum/Test.ql @@ -3,11 +3,6 @@ */ import experimental.Quantum.Language - //import java - from Crypto::NodeBase node - select node - -// from Class t -// where t.hasQualifiedName("javax.crypto", "CipherSpi") -// select t, t.getADescendant*() \ No newline at end of file +from JCAModel::AESLiteral l +select l, l.getAlg(), l.getMode().getValue(), l.getPadding().getValue() \ No newline at end of file From efcf7eab0cad504376e19ea96145007df3d929a5 Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Wed, 5 Feb 2025 17:24:25 -0500 Subject: [PATCH 010/122] Add broken crypto query --- java/ql/lib/experimental/Quantum/JCA.qll | 2 +- .../src/experimental/Quantum/BrokenCrypto.ql | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 java/ql/src/experimental/Quantum/BrokenCrypto.ql diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index fae5c4f806e9..8a613b66d022 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -126,7 +126,7 @@ abstract class CipherAlgorithmMode extends Crypto::NodeBase { result = alg and name = alg.toString() } - override string getAlgorithmName(){ result = this.getAlgorithmName()} + override string getAlgorithmName(){ result = alg.getValue()} } diff --git a/java/ql/src/experimental/Quantum/BrokenCrypto.ql b/java/ql/src/experimental/Quantum/BrokenCrypto.ql new file mode 100644 index 000000000000..020ac1b8925a --- /dev/null +++ b/java/ql/src/experimental/Quantum/BrokenCrypto.ql @@ -0,0 +1,77 @@ +/** +* @name Use of a broken or risky cryptographic algorithm +* @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security. +* @kind problem +* @problem.severity warning +* @security-severity 7.5 +* @precision high +* @id java/weak-cryptographic-algorithm-new-model +* @tags security +* external/cwe/cwe-327 +* external/cwe/cwe-328 +*/ + + + +//THIS QUERY IS A REPLICA OF: https://github.com/github/codeql/blob/main/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +//but uses the **NEW MODELLING** +import experimental.Quantum.Language + + +/** + * Gets the name of an algorithm that is known to be insecure. + */ +string getAnInsecureAlgorithmName() { + result = + [ + "DES", "RC2", "RC4", "RC5", + // ARCFOUR is a variant of RC4 + "ARCFOUR", + // Encryption mode ECB like AES/ECB/NoPadding is vulnerable to replay and other attacks + "ECB", + // CBC mode of operation with PKCS#5 or PKCS#7 padding is vulnerable to padding oracle attacks + "AES/CBC/PKCS[57]Padding" + ] + } + + private string rankedInsecureAlgorithm(int i) { + result = rank[i](string s | s = getAnInsecureAlgorithmName()) + } + + private string insecureAlgorithmString(int i) { + i = 1 and result = rankedInsecureAlgorithm(i) + or + result = rankedInsecureAlgorithm(i) + "|" + insecureAlgorithmString(i - 1) + } + + /** + * Gets the regular expression used for matching strings that look like they + * contain an algorithm that is known to be insecure. + */ + string getInsecureAlgorithmRegex() { + result = algorithmRegex(insecureAlgorithmString(max(int i | exists(rankedInsecureAlgorithm(i))))) + } + + bindingset[algorithmString] +private string algorithmRegex(string algorithmString) { + // Algorithms usually appear in names surrounded by characters that are not + // alphabetical characters in the same case. This handles the upper and lower + // case cases. + result = + "((^|.*[^A-Z])(" + algorithmString + ")([^A-Z].*|$))" + + // or... + "|" + + // For lowercase, we want to be careful to avoid being confused by camelCase + // hence we require two preceding uppercase letters to be sure of a case switch, + // or a preceding non-alphabetic character + "((^|.*[A-Z]{2}|.*[^a-zA-Z])(" + algorithmString.toLowerCase() + ")([^a-z].*|$))" +} + +from Crypto::Algorithm alg +where alg.getAlgorithmName().regexpMatch(getInsecureAlgorithmRegex()) and +// Exclude RSA/ECB/.* ciphers. +not alg.getAlgorithmName().regexpMatch("RSA/ECB.*") and +// Exclude German and French sentences. +not alg.getAlgorithmName().regexpMatch(".*\\p{IsLowercase} des \\p{IsLetter}.*") +select alg, "Cryptographic algorithm $@ is weak and should not be used.", alg, +alg.getAlgorithmName() From 3dc28c2d17d95c9bdbf2e7127d365e3673770363 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 6 Feb 2025 21:54:18 +0100 Subject: [PATCH 011/122] Move language-agnostic model to shared library --- cpp/ql/lib/experimental/Quantum/Language.qll | 2 +- cpp/ql/lib/experimental/Quantum/OpenSSL.qll | 4 ++ cpp/ql/lib/qlpack.yml | 1 + .../scripts/cryptography}/cbom.sh | 2 +- .../scripts/cryptography}/generate_cbom.py | 0 .../codeql/cryptography/Model.qll | 44 +++++++++---------- shared/cryptography/qlpack.yml | 7 +++ 7 files changed, 34 insertions(+), 26 deletions(-) rename {cpp/ql/src/experimental/Quantum => misc/scripts/cryptography}/cbom.sh (79%) rename {cpp/ql/src/experimental/Quantum => misc/scripts/cryptography}/generate_cbom.py (100%) rename cpp/ql/lib/experimental/Quantum/Base.qll => shared/cryptography/codeql/cryptography/Model.qll (93%) create mode 100644 shared/cryptography/qlpack.yml diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index c9398c9e3245..8f400858f07c 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -1,4 +1,4 @@ -private import Base +private import codeql.cryptography.Model private import cpp as Lang module CryptoInput implements InputSig { diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll index 18feda6c8c6c..bf38e5c0ab5d 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll @@ -77,6 +77,8 @@ module OpenSSLModel { HKDF() { algorithmStringToKDFFetchArgFlow("HKDF", origin, this) } + override string getRawAlgorithmName() { result = origin.getValue() } + override Crypto::HashAlgorithm getHashAlgorithm() { none() } override Crypto::LocatableElement getOrigin(string name) { @@ -89,6 +91,8 @@ module OpenSSLModel { PKCS12KDF() { algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, this) } + override string getRawAlgorithmName() { result = origin.getValue() } + override Crypto::HashAlgorithm getHashAlgorithm() { none() } override Crypto::NodeBase getOrigin(string name) { diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index 6ffc77714d47..489de9f6cef4 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ extractor: cpp library: true upgrades: upgrades dependencies: + codeql/cryptography: ${workspace} codeql/dataflow: ${workspace} codeql/mad: ${workspace} codeql/rangeanalysis: ${workspace} diff --git a/cpp/ql/src/experimental/Quantum/cbom.sh b/misc/scripts/cryptography/cbom.sh similarity index 79% rename from cpp/ql/src/experimental/Quantum/cbom.sh rename to misc/scripts/cryptography/cbom.sh index df1c58b1efb8..9cbef951bbf4 100755 --- a/cpp/ql/src/experimental/Quantum/cbom.sh +++ b/misc/scripts/cryptography/cbom.sh @@ -2,7 +2,7 @@ CODEQL_PATH="/Users/nicolaswill/Library/Application Support/Code/User/globalStorage/github.vscode-codeql/distribution5/codeql/codeql" DATABASE_PATH="/Users/nicolaswill/openssl_codeql/openssl/openssl_db" -QUERY_FILE="CBOMGraph.ql" +QUERY_FILE="/Users/nicolaswill/pqc/codeql/cpp/ql/src/experimental/Quantum/CBOMGraph.ql" OUTPUT_DIR="graph_output" python3 generate_cbom.py -c "$CODEQL_PATH" -d "$DATABASE_PATH" -q "$QUERY_FILE" -o "$OUTPUT_DIR" diff --git a/cpp/ql/src/experimental/Quantum/generate_cbom.py b/misc/scripts/cryptography/generate_cbom.py similarity index 100% rename from cpp/ql/src/experimental/Quantum/generate_cbom.py rename to misc/scripts/cryptography/generate_cbom.py diff --git a/cpp/ql/lib/experimental/Quantum/Base.qll b/shared/cryptography/codeql/cryptography/Model.qll similarity index 93% rename from cpp/ql/lib/experimental/Quantum/Base.qll rename to shared/cryptography/codeql/cryptography/Model.qll index 68b3bc08cf70..eba25db60d24 100644 --- a/cpp/ql/lib/experimental/Quantum/Base.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -94,7 +94,7 @@ module CryptographyBase Input> { */ abstract string getAlgorithmName(); - /** + /** * Gets the raw name of this algorithm from source (no parsing or formatting) */ abstract string getRawAlgorithmName(); @@ -151,7 +151,6 @@ module CryptographyBase Input> { abstract THashType getHashType(); override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } - } /** @@ -199,26 +198,23 @@ module CryptographyBase Input> { } newtype TEllipticCurveFamilyType = - // We're saying by this that all of these have an identical interface / properties / edges - NIST() or - SEC() or - NUMS() or - PRIME() or - BRAINPOOL() or - CURVE25519() or - CURVE448() or - C2() or - SM2() or - ES() or - OtherEllipticCurveFamilyType() - + // We're saying by this that all of these have an identical interface / properties / edges + NIST() or + SEC() or + NUMS() or + PRIME() or + BRAINPOOL() or + CURVE25519() or + CURVE448() or + C2() or + SM2() or + ES() or + OtherEllipticCurveFamilyType() /** * Elliptic curve algorithm */ abstract class EllipticCurve extends Algorithm { - - abstract string getKeySize(Location location); abstract TEllipticCurveFamilyType getCurveFamilyType(); @@ -235,18 +231,18 @@ module CryptographyBase Input> { // other properties, like field type are possible, but not modeled until considered necessary } - override string getAlgorithmName() { result = this.getRawAlgorithmName().toUpperCase()} + override string getAlgorithmName() { result = this.getRawAlgorithmName().toUpperCase() } /** * Mandating that for Elliptic Curves specifically, users are responsible - * for providing as the 'raw' name, the official name of the algorithm. - * Casing doesn't matter, we will enforce further naming restrictions on - * `getAlgorithmName` by default. + * for providing as the 'raw' name, the official name of the algorithm. + * Casing doesn't matter, we will enforce further naming restrictions on + * `getAlgorithmName` by default. * Rationale: elliptic curve names can have a lot of variation in their components * (e.g., "secp256r1" vs "P-256"), trying to produce generalized set of properties - * is possible to capture all cases, but such modeling is likely not necessary. - * if all properties need to be captured, we can reassess how names are generated. + * is possible to capture all cases, but such modeling is likely not necessary. + * if all properties need to be captured, we can reassess how names are generated. */ - override abstract string getRawAlgorithmName(); + abstract override string getRawAlgorithmName(); } } diff --git a/shared/cryptography/qlpack.yml b/shared/cryptography/qlpack.yml new file mode 100644 index 000000000000..768c64a0704e --- /dev/null +++ b/shared/cryptography/qlpack.yml @@ -0,0 +1,7 @@ +name: codeql/cryptography +version: 0.0.0-dev +groups: shared +library: true +dependencies: + codeql/util: ${workspace} +warnOnImplicitThis: true \ No newline at end of file From 60d931af9f98ffda9185ed17bc17e9ad870887ab Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Fri, 7 Feb 2025 15:46:13 -0500 Subject: [PATCH 012/122] Update progress on JCA --- java/ql/lib/experimental/Quantum/Base.qll | 94 +++++++++++++++++++- java/ql/lib/experimental/Quantum/JCA.qll | 100 ++++++++++------------ 2 files changed, 136 insertions(+), 58 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/Base.qll b/java/ql/lib/experimental/Quantum/Base.qll index bbcd60ee891a..a14f3b6836ad 100644 --- a/java/ql/lib/experimental/Quantum/Base.qll +++ b/java/ql/lib/experimental/Quantum/Base.qll @@ -11,6 +11,8 @@ } class UnknownLocation instanceof Location; + + } module CryptographyBase Input> { @@ -91,6 +93,11 @@ * Gets the name of this algorithm, e.g., "AES" or "SHA". */ abstract string getAlgorithmName(); + + /** + * Gets the raw name of this algorithm from source (no parsing or formatting) + */ + abstract string getRawAlgorithmName(); final override string toString() { result = this.getAlgorithmName() } } @@ -148,7 +155,7 @@ /** * Gets the raw name of this hash algorithm from source. */ - abstract string getRawAlgorithmName(); + override abstract string getRawAlgorithmName(); } /** @@ -231,5 +238,90 @@ override string getOperationName() { result = "ENCRYPTION" } } + +// newtype TBlockCipherFamilyType = +// // We're saying by this that all of these have an identical interface / properties / edges +// CBC() or ECB() + + +// abstract class BlockCiper extends Algorithm { + +// abstract string getKeySize(Location location); + +// abstract TBlockCipherFamilyType getBlockCipherFamilyType(); + +// override predicate properties(string key, string value, Location location) { +// super.properties(key, value, location) +// or +// key = "key_size" and +// if exists(this.getKeySize(location)) +// then value = this.getKeySize(location) +// else ( +// value instanceof UnknownPropertyValue and location instanceof UnknownLocation +// ) +// // other properties, like field type are possible, but not modeled until considered necessary +// } + +// override string getAlgorithmName() { result = this.getRawAlgorithmName().toUpperCase()} + +// override abstract string getRawAlgorithmName(); +// } + +newtype TModeOperation = +ECB() or OtherMode() + +abstract class ModeOfOperation extends Algorithm { + string getValue() {result = ""} + + final private predicate modeToNameMapping(TModeOperation type, string name) { + type instanceof ECB and name = "ECB" + or + type instanceof OtherMode and name = this.getRawAlgorithmName() + } + + abstract TModeOperation getModeType(); + + override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } + +} + +newtype TCipherStructure = +Block() or Stream() + + newtype TSymmetricCipherFamilyType = +// We're saying by this that all of these have an identical interface / properties / edges +AES() + + /** + * Symmetric algorithms + */ + abstract class SymmetricAlgorithm extends Algorithm { + + + abstract TSymmetricCipherFamilyType getSymmetricCipherFamilyType(); + + abstract string getKeySize(Location location); + + abstract TCipherStructure getCipherType(); + + + //mode, padding scheme, keysize, block/stream, auth'd + //nodes = mode, padding scheme + //properties = keysize, block/stream, auth'd + //leave authd to lang specific + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + key = "key_size" and + if exists(this.getKeySize(location)) + then value = this.getKeySize(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + //add more keys to index props + } + + abstract ModeOfOperation getModeOfOperation(); +} } \ No newline at end of file diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 8a613b66d022..389235964b5f 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -14,41 +14,6 @@ predicate cipher_modes(string mode) {mode = ["NONE", "CBC", "CCM", "CFB", "CFBx" predicate cipher_padding(string padding) {padding = ["NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith", "PKCS1Padding", "PKCS5Padding", "SSL3Padding"]} -abstract class BlockCiper extends Crypto::Algorithm { - CipherAlgorithmStringLiteral alg; - CipherAlgorithmMode mode; - CipherAlgorithmPadding padding; - - - CipherAlgorithmStringLiteral getAlg() {result = alg } - CipherAlgorithmMode getMode() {result = mode } - - CipherAlgorithmPadding getPadding() {result =padding} -} - /** - * Symmetric algorithms - */ - abstract class SymmetricAlgorithm extends Crypto::Algorithm { - - - //TODO figure out how to get this from the Cipher interface, is it explicit? - //abstract string getKeySize(Location location); - - // override predicate properties(string key, string value, Location location) { - // super.properties(key, value, location) - // or - // key = "key_size" and - // if exists(this.getKeySize(location)) - // then value = this.getKeySize(location) - // else ( - // value instanceof Crypto::UnknownPropertyValue and location instanceof UnknownLocation - // ) - // // other properties, like field type are possible, but not modeled until considered necessary - // } - - abstract override string getAlgorithmName(); -} - ////cipher specifics ---------------------------------------- class CipherInstance extends Call { @@ -60,24 +25,26 @@ class CipherInstance extends Call { /** * this may be specified either in the ALG/MODE/PADDING or just ALG format */ -class CipherAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLiteral { +class CipherAlgorithmStringLiteral extends StringLiteral { CipherAlgorithmStringLiteral() { cipher_names(this.getValue().splitAt("/"))} - - override string toString() { result = this.(StringLiteral).toString() } - - string getValue() { result = this.(StringLiteral).getValue() } } -abstract class CipherAlgorithmMode extends Crypto::NodeBase { - string getValue() {result = ""} -} - class CipherAlgorithmModeStringLiteral extends CipherAlgorithmMode instanceof StringLiteral { - CipherAlgorithmModeStringLiteral() { cipher_modes(this.(StringLiteral).getValue().splitAt("/"))} + class ModeOfOperationStringLiteral extends Crypto::ModeOfOperation instanceof StringLiteral { + ModeOfOperationStringLiteral() { cipher_modes(this.(StringLiteral).getValue().splitAt("/"))} - override string toString() { result = this.(StringLiteral).toString() } + override string getRawAlgorithmName() { result = this.(StringLiteral).getValue().regexpCapture(".*/(.*)/.*",1) } override string getValue() { result = this.(StringLiteral).getValue().regexpCapture(".*/(.*)/.*",1) } + + + predicate modeToNameMapping(Crypto::TModeOperation type, string name) { + name = "ECB" and type instanceof Crypto::ECB + } + + override Crypto::TModeOperation getModeType(){ + modeToNameMapping(result, this.getRawAlgorithmName()) + } } abstract class CipherAlgorithmPadding extends Crypto::NodeBase { @@ -102,32 +69,51 @@ abstract class CipherAlgorithmMode extends Crypto::NodeBase { module AlgorithmStringToFetchFlow = DataFlow::Global; - predicate algorithmStringToCipherInstanceArgFlow(string name, CipherAlgorithmStringLiteral origin, CipherAlgorithmModeStringLiteral mode, CipherAlgorithmPaddingStringLiteral padding, Expr arg) { + predicate algorithmStringToCipherInstanceArgFlow(string name, CipherAlgorithmStringLiteral origin, Expr arg) { exists(CipherInstance sinkCall | origin.getValue().splitAt("/") = name and - origin = mode and - origin = padding and arg = sinkCall.getAlgorithmArg() and AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) ) } + + predicate modeStringToCipherInstanceArgFlow(string name, ModeOfOperationStringLiteral mode, Expr arg) { + exists(CipherInstance sinkCall | + mode.getRawAlgorithmName() = name and + arg = sinkCall.getAlgorithmArg() and + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(mode), DataFlow::exprNode(arg)) + ) + } + /** * A class to represent when AES is used AND it has literal mode and padding provided * this does not capture the use without */ - class AESLiteral extends SymmetricAlgorithm, BlockCiper instanceof Expr { +// class AESLiteral extends Crypto::SymmetricAlgorithm instanceof Expr { +// CipherAlgorithmStringLiteral alg; +// AESLiteral() { algorithmStringToCipherInstanceArgFlow("AES", alg, this) +// } +// override Crypto::ModeOfOperation getModeOfOperation(){ modeStringToCipherInstanceArgFlow(result.getAlgorithmName(), result, this)} - AESLiteral() { algorithmStringToCipherInstanceArgFlow("AES", alg, mode, padding, this) -} +// override Crypto::LocatableElement getOrigin(string name) { +// result = alg and name = alg.toString() +// } - override Crypto::LocatableElement getOrigin(string name) { - result = alg and name = alg.toString() - } +// override string getAlgorithmName(){ result = "AES" } - override string getAlgorithmName(){ result = alg.getValue()} - } +// override string getRawAlgorithmName(){ result = alg.getValue()} + +// override Crypto::TSymmetricCipherFamilyType getSymmetricCipherFamilyType() { result instanceof Crypto::AES} + +// //temp hacks for testing +// override string getKeySize(Location location){ +// result = "" +// } + +// override Crypto::TCipherStructure getCipherType(){ none()} +// } } \ No newline at end of file From 6005437001c27e022d6f9d29c738f5fdb05ad481 Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Mon, 10 Feb 2025 11:26:48 -0500 Subject: [PATCH 013/122] Update JCA model with flow to call as AESuse and format JCA model --- java/ql/lib/experimental/Quantum/JCA.qll | 132 ++++++++++++++--------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 389235964b5f..f75cfe7fd083 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -7,17 +7,35 @@ module JCAModel { abstract class EncryptionOperation extends Crypto::EncryptionOperation { } //TODO PBEWith can have suffixes. how to do? enumerate? or match a pattern? -predicate cipher_names(string algo) { algo = ["AES", "AESWrap", "AESWrapPad", "ARCFOUR", "Blowfish", "ChaCha20", "ChaCha20-Poly1305", "DES", "DESede", "DESedeWrap", "ECIES", "PBEWith", "RC2", "RC4", "RC5", "RSA"] } -//TODO solve the fact that x is an int of various values. same as above... enumerate? -predicate cipher_modes(string mode) {mode = ["NONE", "CBC", "CCM", "CFB", "CFBx", "CTR", "CTS", "ECB", "GCM", "KW", "KWP", "OFB", "OFBx", "PCBC"]} -//todo same as above, OAEPWith has asuffix type -predicate cipher_padding(string padding) {padding = ["NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith", "PKCS1Padding", "PKCS5Padding", "SSL3Padding"]} + predicate cipher_names(string algo) { + algo = + [ + "AES", "AESWrap", "AESWrapPad", "ARCFOUR", "Blowfish", "ChaCha20", "ChaCha20-Poly1305", + "DES", "DESede", "DESedeWrap", "ECIES", "PBEWith", "RC2", "RC4", "RC5", "RSA" + ] + } + //TODO solve the fact that x is an int of various values. same as above... enumerate? + predicate cipher_modes(string mode) { + mode = + [ + "NONE", "CBC", "CCM", "CFB", "CFBx", "CTR", "CTS", "ECB", "GCM", "KW", "KWP", "OFB", "OFBx", + "PCBC" + ] + } -////cipher specifics ---------------------------------------- + //todo same as above, OAEPWith has asuffix type + predicate cipher_padding(string padding) { + padding = + [ + "NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith", "PKCS1Padding", "PKCS5Padding", + "SSL3Padding" + ] + } -class CipherInstance extends Call { - CipherInstance() { this.getCallee().hasQualifiedName("javax.crypto","Cipher", "getInstance") } + ////cipher specifics ---------------------------------------- + class CipherInstance extends Call { + CipherInstance() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") } Expr getAlgorithmArg() { result = this.getArgument(0) } } @@ -25,38 +43,44 @@ class CipherInstance extends Call { /** * this may be specified either in the ALG/MODE/PADDING or just ALG format */ -class CipherAlgorithmStringLiteral extends StringLiteral { - CipherAlgorithmStringLiteral() { cipher_names(this.getValue().splitAt("/"))} + class CipherAlgorithmStringLiteral extends StringLiteral { + CipherAlgorithmStringLiteral() { cipher_names(this.getValue().splitAt("/")) } } - class ModeOfOperationStringLiteral extends Crypto::ModeOfOperation instanceof StringLiteral { - ModeOfOperationStringLiteral() { cipher_modes(this.(StringLiteral).getValue().splitAt("/"))} + ModeOfOperationStringLiteral() { cipher_modes(this.(StringLiteral).getValue().splitAt("/")) } - override string getRawAlgorithmName() { result = this.(StringLiteral).getValue().regexpCapture(".*/(.*)/.*",1) } + override string getRawAlgorithmName() { + result = this.(StringLiteral).getValue().regexpCapture(".*/(.*)/.*", 1) + } - override string getValue() { result = this.(StringLiteral).getValue().regexpCapture(".*/(.*)/.*",1) } + override string getValue() { + result = this.(StringLiteral).getValue().regexpCapture(".*/(.*)/.*", 1) + } + predicate modeToNameMapping(Crypto::TModeOperation type, string name) { + name = "ECB" and type instanceof Crypto::ECB + } - predicate modeToNameMapping(Crypto::TModeOperation type, string name) { - name = "ECB" and type instanceof Crypto::ECB - } - - override Crypto::TModeOperation getModeType(){ + override Crypto::TModeOperation getModeType() { modeToNameMapping(result, this.getRawAlgorithmName()) } } abstract class CipherAlgorithmPadding extends Crypto::NodeBase { - string getValue() {result = ""} + string getValue() { result = "" } } class CipherAlgorithmPaddingStringLiteral extends CipherAlgorithmPadding instanceof StringLiteral { - CipherAlgorithmPaddingStringLiteral() { cipher_padding(this.(StringLiteral).getValue().splitAt("/"))} + CipherAlgorithmPaddingStringLiteral() { + cipher_padding(this.(StringLiteral).getValue().splitAt("/")) + } override string toString() { result = this.(StringLiteral).toString() } - override string getValue() { result = this.(StringLiteral).getValue().regexpCapture(".*/.*/(.*)",1) } + override string getValue() { + result = this.(StringLiteral).getValue().regexpCapture(".*/.*/(.*)", 1) + } } private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { @@ -69,51 +93,59 @@ class CipherAlgorithmStringLiteral extends StringLiteral { module AlgorithmStringToFetchFlow = DataFlow::Global; - predicate algorithmStringToCipherInstanceArgFlow(string name, CipherAlgorithmStringLiteral origin, Expr arg) { + predicate algorithmStringToCipherInstanceArgFlow( + string name, CipherAlgorithmStringLiteral origin, Expr arg + ) { exists(CipherInstance sinkCall | origin.getValue().splitAt("/") = name and - arg = sinkCall.getAlgorithmArg() and - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) + arg = sinkCall and + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), + DataFlow::exprNode(sinkCall.getAlgorithmArg())) ) } - - predicate modeStringToCipherInstanceArgFlow(string name, ModeOfOperationStringLiteral mode, Expr arg) { + predicate modeStringToCipherInstanceArgFlow( + string name, ModeOfOperationStringLiteral mode, Expr arg + ) { exists(CipherInstance sinkCall | mode.getRawAlgorithmName() = name and - arg = sinkCall.getAlgorithmArg() and - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(mode), DataFlow::exprNode(arg)) + arg = sinkCall and + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(mode), + DataFlow::exprNode(sinkCall.getAlgorithmArg())) ) } /** - * A class to represent when AES is used AND it has literal mode and padding provided - * this does not capture the use without + * A class to represent when AES is used + * AND currently it has literal mode and padding provided + * + * this currently does not capture the use without a literal + * though should be extended to */ -// class AESLiteral extends Crypto::SymmetricAlgorithm instanceof Expr { -// CipherAlgorithmStringLiteral alg; -// AESLiteral() { algorithmStringToCipherInstanceArgFlow("AES", alg, this) -// } + class AESAlgo extends Crypto::SymmetricAlgorithm instanceof Expr { + CipherAlgorithmStringLiteral alg; -// override Crypto::ModeOfOperation getModeOfOperation(){ modeStringToCipherInstanceArgFlow(result.getAlgorithmName(), result, this)} + AESAlgo() { algorithmStringToCipherInstanceArgFlow("AES", alg, this) } -// override Crypto::LocatableElement getOrigin(string name) { -// result = alg and name = alg.toString() -// } + override Crypto::ModeOfOperation getModeOfOperation() { + modeStringToCipherInstanceArgFlow(result.getAlgorithmName(), result, this) + } -// override string getAlgorithmName(){ result = "AES" } + override Crypto::LocatableElement getOrigin(string name) { + result = alg and name = alg.toString() + } -// override string getRawAlgorithmName(){ result = alg.getValue()} + override string getAlgorithmName() { result = "AES" } -// override Crypto::TSymmetricCipherFamilyType getSymmetricCipherFamilyType() { result instanceof Crypto::AES} + override string getRawAlgorithmName() { result = alg.getValue() } -// //temp hacks for testing -// override string getKeySize(Location location){ -// result = "" -// } + override Crypto::TSymmetricCipherFamilyType getSymmetricCipherFamilyType() { + result instanceof Crypto::AES + } -// override Crypto::TCipherStructure getCipherType(){ none()} -// } + //temp hacks for testing + override string getKeySize(Location location) { result = "" } - -} \ No newline at end of file + override Crypto::TCipherStructure getCipherType() { none() } + } +} From 59208bdb85adb0b5fb14c7b2d9bf94d393ff11ea Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Mon, 10 Feb 2025 12:22:22 -0500 Subject: [PATCH 014/122] Update JCA model to use shared lib --- java/ql/lib/experimental/Quantum/Base.qll | 327 ------------------ java/ql/lib/experimental/Quantum/Language.qll | 2 +- java/ql/lib/qlpack.yml | 1 + .../codeql/cryptography/Model.qll | 65 ++++ 4 files changed, 67 insertions(+), 328 deletions(-) delete mode 100644 java/ql/lib/experimental/Quantum/Base.qll diff --git a/java/ql/lib/experimental/Quantum/Base.qll b/java/ql/lib/experimental/Quantum/Base.qll deleted file mode 100644 index a14f3b6836ad..000000000000 --- a/java/ql/lib/experimental/Quantum/Base.qll +++ /dev/null @@ -1,327 +0,0 @@ -/** - * A language-independent library for reasoning about cryptography. - */ - - import codeql.util.Location - import codeql.util.Option - - signature module InputSig { - class LocatableElement { - Location getLocation(); - } - - class UnknownLocation instanceof Location; - - - } - - module CryptographyBase Input> { - final class LocatableElement = Input::LocatableElement; - - final class UnknownLocation = Input::UnknownLocation; - - final class UnknownPropertyValue extends string { - UnknownPropertyValue() { this = "" } - } - - abstract class NodeBase instanceof LocatableElement { - /** - * Returns a string representation of this node, usually the name of the operation/algorithm/property. - */ - abstract string toString(); - - /** - * Returns the location of this node in the code. - */ - Location getLocation() { result = super.getLocation() } - - /** - * Gets the origin of this node, e.g., a string literal in source describing it. - */ - LocatableElement getOrigin(string value) { none() } - - /** - * Returns the child of this node with the given edge name. - * - * This predicate is used by derived classes to construct the graph of cryptographic operations. - */ - NodeBase getChild(string edgeName) { none() } - - /** - * Defines properties of this node by name and either a value or location or both. - * - * This predicate is used by derived classes to construct the graph of cryptographic operations. - */ - predicate properties(string key, string value, Location location) { - key = "origin" and location = this.getOrigin(value).getLocation() - } - - /** - * Returns the parent of this node. - */ - final NodeBase getAParent() { result.getChild(_) = this } - } - - class Asset = NodeBase; - - /** - * A cryptographic operation, such as hashing or encryption. - */ - abstract class Operation extends Asset { - /** - * Gets the algorithm associated with this operation. - */ - abstract Algorithm getAlgorithm(); - - /** - * Gets the name of this operation, e.g., "hash" or "encrypt". - */ - abstract string getOperationName(); - - final override string toString() { result = this.getOperationName() } - - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - edgeName = "uses" and - if exists(this.getAlgorithm()) then result = this.getAlgorithm() else result = this - } - } - - abstract class Algorithm extends Asset { - /** - * Gets the name of this algorithm, e.g., "AES" or "SHA". - */ - abstract string getAlgorithmName(); - - /** - * Gets the raw name of this algorithm from source (no parsing or formatting) - */ - abstract string getRawAlgorithmName(); - - final override string toString() { result = this.getAlgorithmName() } - } - - /** - * A hashing operation that processes data to generate a hash value. - * This operation takes an input message of arbitrary content and length and produces a fixed-size - * hash value as the output using a specified hashing algorithm. - */ - abstract class HashOperation extends Operation { - abstract override HashAlgorithm getAlgorithm(); - - override string getOperationName() { result = "HASH" } - } - - // Rule: no newtype representing a type of algorithm should be modelled with multiple interfaces - // - // Example: HKDF and PKCS12KDF are both key derivation algorithms. - // However, PKCS12KDF also has a property: the iteration count. - // - // If we have HKDF and PKCS12KDF under TKeyDerivationType, - // someone modelling a library might try to make a generic identification of both of those algorithms. - // - // They will therefore not use the specialized type for PKCS12KDF, - // meaning "from PKCS12KDF algo select algo" will have no results. - // - newtype THashType = - // We're saying by this that all of these have an identical interface / properties / edges - MD5() or - SHA1() or - SHA256() or - SHA512() or - OtherHashType() - - /** - * A hashing algorithm that transforms variable-length input into a fixed-size hash value. - */ - abstract class HashAlgorithm extends Algorithm { - final predicate hashTypeToNameMapping(THashType type, string name) { - type instanceof MD5 and name = "MD5" - or - type instanceof SHA1 and name = "SHA-1" - or - type instanceof SHA256 and name = "SHA-256" - or - type instanceof SHA512 and name = "SHA-512" - or - type instanceof OtherHashType and name = this.getRawAlgorithmName() - } - - abstract THashType getHashType(); - - override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } - - /** - * Gets the raw name of this hash algorithm from source. - */ - override abstract string getRawAlgorithmName(); - } - - /** - * An operation that derives one or more keys from an input value. - */ - abstract class KeyDerivationOperation extends Operation { - override string getOperationName() { result = "KEY_DERIVATION" } - } - - /** - * An algorithm that derives one or more keys from an input value. - */ - abstract class KeyDerivationAlgorithm extends Algorithm { - abstract override string getAlgorithmName(); - } - - /** - * HKDF key derivation function - */ - abstract class HKDF extends KeyDerivationAlgorithm { - final override string getAlgorithmName() { result = "HKDF" } - - abstract HashAlgorithm getHashAlgorithm(); - - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - edgeName = "digest" and result = this.getHashAlgorithm() - } - } - - /** - * PKCS #12 key derivation function - */ - abstract class PKCS12KDF extends KeyDerivationAlgorithm { - final override string getAlgorithmName() { result = "PKCS12KDF" } - - abstract HashAlgorithm getHashAlgorithm(); - - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - edgeName = "digest" and result = this.getHashAlgorithm() - } - } - - /** - * Elliptic curve algorithm - */ - abstract class EllipticCurve extends Algorithm { - abstract string getVersion(Location location); - - abstract string getKeySize(Location location); - - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - key = "version" and - if exists(this.getVersion(location)) - then value = this.getVersion(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - or - key = "key_size" and - if exists(this.getKeySize(location)) - then value = this.getKeySize(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - } - } - - /** - * An encryption operation that processes plaintext to generate a ciphertext. - * This operation takes an input message (plaintext) of arbitrary content and length and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). - */ - abstract class EncryptionOperation extends Operation { - abstract override Algorithm getAlgorithm(); - - override string getOperationName() { result = "ENCRYPTION" } - } - -// newtype TBlockCipherFamilyType = -// // We're saying by this that all of these have an identical interface / properties / edges -// CBC() or ECB() - - -// abstract class BlockCiper extends Algorithm { - -// abstract string getKeySize(Location location); - -// abstract TBlockCipherFamilyType getBlockCipherFamilyType(); - -// override predicate properties(string key, string value, Location location) { -// super.properties(key, value, location) -// or -// key = "key_size" and -// if exists(this.getKeySize(location)) -// then value = this.getKeySize(location) -// else ( -// value instanceof UnknownPropertyValue and location instanceof UnknownLocation -// ) -// // other properties, like field type are possible, but not modeled until considered necessary -// } - -// override string getAlgorithmName() { result = this.getRawAlgorithmName().toUpperCase()} - -// override abstract string getRawAlgorithmName(); -// } - -newtype TModeOperation = -ECB() or OtherMode() - -abstract class ModeOfOperation extends Algorithm { - string getValue() {result = ""} - - final private predicate modeToNameMapping(TModeOperation type, string name) { - type instanceof ECB and name = "ECB" - or - type instanceof OtherMode and name = this.getRawAlgorithmName() - } - - abstract TModeOperation getModeType(); - - override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } - -} - -newtype TCipherStructure = -Block() or Stream() - - newtype TSymmetricCipherFamilyType = -// We're saying by this that all of these have an identical interface / properties / edges -AES() - - /** - * Symmetric algorithms - */ - abstract class SymmetricAlgorithm extends Algorithm { - - - abstract TSymmetricCipherFamilyType getSymmetricCipherFamilyType(); - - abstract string getKeySize(Location location); - - abstract TCipherStructure getCipherType(); - - - //mode, padding scheme, keysize, block/stream, auth'd - //nodes = mode, padding scheme - //properties = keysize, block/stream, auth'd - //leave authd to lang specific - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - key = "key_size" and - if exists(this.getKeySize(location)) - then value = this.getKeySize(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - //add more keys to index props - } - - abstract ModeOfOperation getModeOfOperation(); -} - } - \ No newline at end of file diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 246f2b86367c..485b3e716e69 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -1,4 +1,4 @@ -private import Base +private import codeql.cryptography.Model private import java as Lang /** diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 18b74a919dd2..ae7f13ad7ab3 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ extractor: java library: true upgrades: upgrades dependencies: + codeql/cryptography: ${workspace} codeql/dataflow: ${workspace} codeql/mad: ${workspace} codeql/rangeanalysis: ${workspace} diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index eba25db60d24..aa626066684b 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -245,4 +245,69 @@ module CryptographyBase Input> { */ abstract override string getRawAlgorithmName(); } + + /** + * An encryption operation that processes plaintext to generate a ciphertext. + * This operation takes an input message (plaintext) of arbitrary content and length and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). + */ + abstract class EncryptionOperation extends Operation { + abstract override Algorithm getAlgorithm(); + + override string getOperationName() { result = "ENCRYPTION" } + } + + newtype TModeOperation = + ECB() or + OtherMode() + + abstract class ModeOfOperation extends Algorithm { + string getValue() { result = "" } + + final private predicate modeToNameMapping(TModeOperation type, string name) { + type instanceof ECB and name = "ECB" + or + type instanceof OtherMode and name = this.getRawAlgorithmName() + } + + abstract TModeOperation getModeType(); + + override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } + } + + newtype TCipherStructure = + Block() or + Stream() + + newtype TSymmetricCipherFamilyType = + // We're saying by this that all of these have an identical interface / properties / edges + AES() + + /** + * Symmetric algorithms + */ + abstract class SymmetricAlgorithm extends Algorithm { + abstract TSymmetricCipherFamilyType getSymmetricCipherFamilyType(); + + abstract string getKeySize(Location location); + + abstract TCipherStructure getCipherType(); + + //mode, padding scheme, keysize, block/stream, auth'd + //nodes = mode, padding scheme + //properties = keysize, block/stream, auth'd + //leave authd to lang specific + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + key = "key_size" and + if exists(this.getKeySize(location)) + then value = this.getKeySize(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + //add more keys to index props + } + + abstract ModeOfOperation getModeOfOperation(); + } } From 1a12fb309902fa6111c443051ab56359d6867f48 Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Mon, 10 Feb 2025 13:49:32 -0500 Subject: [PATCH 015/122] Update JCA model, refactor modes --- java/ql/lib/experimental/Quantum/JCA.qll | 47 +++++++++++-------- .../codeql/cryptography/Model.qll | 1 + 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index f75cfe7fd083..4443ce85159c 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -47,16 +47,18 @@ module JCAModel { CipherAlgorithmStringLiteral() { cipher_names(this.getValue().splitAt("/")) } } - class ModeOfOperationStringLiteral extends Crypto::ModeOfOperation instanceof StringLiteral { + class ModeOfOperationStringLiteral extends StringLiteral { ModeOfOperationStringLiteral() { cipher_modes(this.(StringLiteral).getValue().splitAt("/")) } - override string getRawAlgorithmName() { - result = this.(StringLiteral).getValue().regexpCapture(".*/(.*)/.*", 1) - } + string getRawAlgorithmName() { result = this.getValue().regexpCapture(".*/(.*)/.*", 1) } + } - override string getValue() { - result = this.(StringLiteral).getValue().regexpCapture(".*/(.*)/.*", 1) - } + class ECBMode extends Crypto::ModeOfOperation { + ModeOfOperationStringLiteral mode; + + ECBMode() { modeStringToCipherInstanceArgFlow("ECB", mode, this) } + + override string getRawAlgorithmName() { result = mode.getRawAlgorithmName() } predicate modeToNameMapping(Crypto::TModeOperation type, string name) { name = "ECB" and type instanceof Crypto::ECB @@ -65,24 +67,26 @@ module JCAModel { override Crypto::TModeOperation getModeType() { modeToNameMapping(result, this.getRawAlgorithmName()) } + + override Crypto::LocatableElement getOrigin(string name) { + result = mode and name = mode.toString() + } } abstract class CipherAlgorithmPadding extends Crypto::NodeBase { string getValue() { result = "" } } - class CipherAlgorithmPaddingStringLiteral extends CipherAlgorithmPadding instanceof StringLiteral { - CipherAlgorithmPaddingStringLiteral() { - cipher_padding(this.(StringLiteral).getValue().splitAt("/")) - } - - override string toString() { result = this.(StringLiteral).toString() } - - override string getValue() { - result = this.(StringLiteral).getValue().regexpCapture(".*/.*/(.*)", 1) - } - } - + //todo refactor + // class CipherAlgorithmPaddingStringLiteral extends CipherAlgorithmPadding instanceof StringLiteral { + // CipherAlgorithmPaddingStringLiteral() { + // cipher_padding(this.(StringLiteral).getValue().splitAt("/")) + // } + // override string toString() { result = this.(StringLiteral).toString() } + // override string getValue() { + // result = this.(StringLiteral).getValue().regexpCapture(".*/.*/(.*)", 1) + // } + // } private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherAlgorithmStringLiteral } @@ -108,6 +112,7 @@ module JCAModel { string name, ModeOfOperationStringLiteral mode, Expr arg ) { exists(CipherInstance sinkCall | + //consider if this should be a more specific predicate mode.getRawAlgorithmName() = name and arg = sinkCall and AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(mode), @@ -127,8 +132,10 @@ module JCAModel { AESAlgo() { algorithmStringToCipherInstanceArgFlow("AES", alg, this) } + //todo this is really not correct yet override Crypto::ModeOfOperation getModeOfOperation() { - modeStringToCipherInstanceArgFlow(result.getAlgorithmName(), result, this) + none() + //exists(Crypto::ModeOfOperation mode | mode = this and result = this) } override Crypto::LocatableElement getOrigin(string name) { diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index aa626066684b..2b2919b6d3f8 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -258,6 +258,7 @@ module CryptographyBase Input> { newtype TModeOperation = ECB() or + CBC() or OtherMode() abstract class ModeOfOperation extends Algorithm { From 4d447559457edacc18dad249417e2855e2b7269d Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Tue, 11 Feb 2025 15:37:15 +0100 Subject: [PATCH 016/122] Refactor Model and CBOM print queries --- cpp/ql/src/experimental/Quantum/CBOMGraph.ql | 48 --------- .../experimental/Quantum/PrintCBOMGraph.ql | 21 ++++ java/ql/lib/experimental/Quantum/JCA.qll | 27 ++--- .../experimental/Quantum/PrintCBOMGraph.ql | 21 ++++ .../codeql/cryptography/Model.qll | 101 +++++++++++++----- 5 files changed, 134 insertions(+), 84 deletions(-) delete mode 100644 cpp/ql/src/experimental/Quantum/CBOMGraph.ql create mode 100644 cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql create mode 100644 java/ql/src/experimental/Quantum/PrintCBOMGraph.ql diff --git a/cpp/ql/src/experimental/Quantum/CBOMGraph.ql b/cpp/ql/src/experimental/Quantum/CBOMGraph.ql deleted file mode 100644 index edcc40aca6a4..000000000000 --- a/cpp/ql/src/experimental/Quantum/CBOMGraph.ql +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @name "Print CBOM Graph" - * @description "Outputs a graph representation of the cryptographic bill of materials." - * @kind graph - * @id cbomgraph - */ - -import experimental.Quantum.Language - -string getPropertyString(Crypto::NodeBase node, string key) { - result = - strictconcat(any(string value, Location location, string parsed | - node.properties(key, value, location) and - parsed = "(" + value + "," + location.toString() + ")" - | - parsed - ), "," - ) -} - -string getLabel(Crypto::NodeBase node) { result = node.toString() } - -query predicate nodes(Crypto::NodeBase node, string key, string value) { - key = "semmle.label" and - value = getLabel(node) - or - // CodeQL's DGML output does not include a location - key = "Location" and - value = node.getLocation().toString() - or - // Known unknown edges should be reported as properties rather than edges - node = node.getChild(key) and - value = "" - or - // Report properties - value = getPropertyString(node, key) -} - -query predicate edges(Crypto::NodeBase source, Crypto::NodeBase target, string key, string value) { - key = "semmle.label" and - target = source.getChild(value) and - // Known unknowns are reported as properties rather than edges - not source = target -} - -query predicate graphProperties(string key, string value) { - key = "semmle.graphKind" and value = "graph" -} diff --git a/cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql b/cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql new file mode 100644 index 000000000000..d9658105aebf --- /dev/null +++ b/cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql @@ -0,0 +1,21 @@ +/** + * @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 cpp/print-cbom-graph + */ + +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/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 4443ce85159c..44a753bb7db2 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -7,12 +7,14 @@ module JCAModel { abstract class EncryptionOperation extends Crypto::EncryptionOperation { } //TODO PBEWith can have suffixes. how to do? enumerate? or match a pattern? + bindingset[algo] predicate cipher_names(string algo) { - algo = - [ - "AES", "AESWrap", "AESWrapPad", "ARCFOUR", "Blowfish", "ChaCha20", "ChaCha20-Poly1305", - "DES", "DESede", "DESedeWrap", "ECIES", "PBEWith", "RC2", "RC4", "RC5", "RSA" - ] + // "Standard names are not case-sensitive." + algo.toUpperCase() + .matches([ + "AES", "AESWrap", "AESWrapPad", "ARCFOUR", "Blowfish", "ChaCha20", "ChaCha20-Poly1305", + "DES", "DESede", "DESedeWrap", "ECIES", "PBEWith%", "RC2", "RC4", "RC5", "RSA" + ].toUpperCase()) } //TODO solve the fact that x is an int of various values. same as above... enumerate? @@ -33,9 +35,10 @@ module JCAModel { ] } - ////cipher specifics ---------------------------------------- - class CipherInstance extends Call { - CipherInstance() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") } + class CipherGetInstanceCall extends Call { + CipherGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") + } Expr getAlgorithmArg() { result = this.getArgument(0) } } @@ -65,7 +68,7 @@ module JCAModel { } override Crypto::TModeOperation getModeType() { - modeToNameMapping(result, this.getRawAlgorithmName()) + this.modeToNameMapping(result, this.getRawAlgorithmName()) } override Crypto::LocatableElement getOrigin(string name) { @@ -91,7 +94,7 @@ module JCAModel { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherAlgorithmStringLiteral } predicate isSink(DataFlow::Node sink) { - exists(CipherInstance call | sink.asExpr() = call.getAlgorithmArg()) + exists(CipherGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) } } @@ -100,7 +103,7 @@ module JCAModel { predicate algorithmStringToCipherInstanceArgFlow( string name, CipherAlgorithmStringLiteral origin, Expr arg ) { - exists(CipherInstance sinkCall | + exists(CipherGetInstanceCall sinkCall | origin.getValue().splitAt("/") = name and arg = sinkCall and AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), @@ -111,7 +114,7 @@ module JCAModel { predicate modeStringToCipherInstanceArgFlow( string name, ModeOfOperationStringLiteral mode, Expr arg ) { - exists(CipherInstance sinkCall | + exists(CipherGetInstanceCall sinkCall | //consider if this should be a more specific predicate mode.getRawAlgorithmName() = name and arg = sinkCall and diff --git a/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql b/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql new file mode 100644 index 000000000000..063cda564b60 --- /dev/null +++ b/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql @@ -0,0 +1,21 @@ +/** + * @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 java/print-cbom-graph + */ + +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/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 2b2919b6d3f8..ad117674dffe 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -22,6 +22,45 @@ module CryptographyBase Input> { UnknownPropertyValue() { this = "" } } + private string getPropertyAsGraphString(NodeBase node, string key) { + result = + strictconcat(any(string value, Location location, string parsed | + node.properties(key, value, location) and + parsed = "(" + value + "," + location.toString() + ")" + | + parsed + ), "," + ) + } + + predicate nodes_graph_impl(NodeBase node, string key, string value) { + key = "semmle.label" and + value = node.toString() + or + // CodeQL's DGML output does not include a location + key = "Location" and + value = node.getLocation().toString() + or + // Known unknown edges should be reported as properties rather than edges + node = node.getChild(key) and + value = "" + or + // Report properties + value = getPropertyAsGraphString(node, key) + } + + predicate edges_graph_impl(NodeBase source, NodeBase target, string key, string value) { + key = "semmle.label" and + target = source.getChild(value) and + // Known unknowns are reported as properties rather than edges + not source = target + } + + /** + * The base class for all cryptographic assets, such as operations and algorithms. + * + * Each `NodeBase` is a node in a graph of cryptographic operations, where the edges are the relationships between the nodes. + */ abstract class NodeBase instanceof LocatableElement { /** * Returns a string representation of this node, usually the name of the operation/algorithm/property. @@ -104,6 +143,7 @@ module CryptographyBase Input> { /** * A hashing operation that processes data to generate a hash value. + * * This operation takes an input message of arbitrary content and length and produces a fixed-size * hash value as the output using a specified hashing algorithm. */ @@ -113,19 +153,7 @@ module CryptographyBase Input> { override string getOperationName() { result = "HASH" } } - // Rule: no newtype representing a type of algorithm should be modelled with multiple interfaces - // - // Example: HKDF and PKCS12KDF are both key derivation algorithms. - // However, PKCS12KDF also has a property: the iteration count. - // - // If we have HKDF and PKCS12KDF under TKeyDerivationType, - // someone modelling a library might try to make a generic identification of both of those algorithms. - // - // They will therefore not use the specialized type for PKCS12KDF, - // meaning "from PKCS12KDF algo select algo" will have no results. - // newtype THashType = - // We're saying by this that all of these have an identical interface / properties / edges MD5() or SHA1() or SHA256() or @@ -197,8 +225,28 @@ module CryptographyBase Input> { } } - newtype TEllipticCurveFamilyType = - // We're saying by this that all of these have an identical interface / properties / edges + /* + * TODO: + * + * Rule: No newtype representing a type of algorithm should be modelled with multiple interfaces + * + * Example 1: HKDF and PKCS12KDF are both key derivation algorithms. + * However, PKCS12KDF also has a property: the iteration count. + * + * If we have HKDF and PKCS12KDF under TKeyDerivationType, + * someone modelling a library might try to make a generic identification of both of those algorithms. + * + * They will therefore not use the specialized type for PKCS12KDF, + * meaning "from PKCS12KDF algo select algo" will have no results. + * + * Example 2: Each type below represents a common family of elliptic curves, with a shared interface, i.e., + * predicates for library modellers to implement as well as the properties and edges reported. + */ + + /** + * Elliptic curve algorithms + */ + newtype TEllipticCurveFamily = NIST() or SEC() or NUMS() or @@ -211,13 +259,10 @@ module CryptographyBase Input> { ES() or OtherEllipticCurveFamilyType() - /** - * Elliptic curve algorithm - */ abstract class EllipticCurve extends Algorithm { abstract string getKeySize(Location location); - abstract TEllipticCurveFamilyType getCurveFamilyType(); + abstract TEllipticCurveFamily getCurveFamilyType(); override predicate properties(string key, string value, Location location) { super.properties(key, value, location) @@ -236,8 +281,10 @@ module CryptographyBase Input> { /** * Mandating that for Elliptic Curves specifically, users are responsible * for providing as the 'raw' name, the official name of the algorithm. + * * Casing doesn't matter, we will enforce further naming restrictions on * `getAlgorithmName` by default. + * * Rationale: elliptic curve names can have a lot of variation in their components * (e.g., "secp256r1" vs "P-256"), trying to produce generalized set of properties * is possible to capture all cases, but such modeling is likely not necessary. @@ -256,17 +303,20 @@ module CryptographyBase Input> { override string getOperationName() { result = "ENCRYPTION" } } + /** + * Block cipher modes of operation algorithms + */ newtype TModeOperation = ECB() or CBC() or OtherMode() abstract class ModeOfOperation extends Algorithm { - string getValue() { result = "" } - final private predicate modeToNameMapping(TModeOperation type, string name) { type instanceof ECB and name = "ECB" or + type instanceof CBC and name = "CBC" + or type instanceof OtherMode and name = this.getRawAlgorithmName() } @@ -275,17 +325,20 @@ module CryptographyBase Input> { override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } } + /** + * A helper type for distinguishing between block and stream ciphers. + */ newtype TCipherStructure = Block() or Stream() - newtype TSymmetricCipherFamilyType = - // We're saying by this that all of these have an identical interface / properties / edges - AES() - /** * Symmetric algorithms */ + newtype TSymmetricCipherFamilyType = + AES() or + OtherSymmetricCipherFamilyType() + abstract class SymmetricAlgorithm extends Algorithm { abstract TSymmetricCipherFamilyType getSymmetricCipherFamilyType(); From 874e3b5e063bdd403264eea80e367d6a2f4eccdc Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Wed, 12 Feb 2025 17:58:15 +0100 Subject: [PATCH 017/122] Modify model to use newtypes, expand modeling --- cpp/ql/lib/experimental/Quantum/OpenSSL.qll | 204 ++++++-- java/ql/lib/experimental/Quantum/JCA.qll | 143 +++--- java/ql/src/experimental/Quantum/Test.ql | 7 +- .../codeql/cryptography/Model.qll | 467 ++++++++++++++++-- 4 files changed, 665 insertions(+), 156 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll index bf38e5c0ab5d..cb19092f3fd7 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll @@ -4,78 +4,172 @@ import semmle.code.cpp.dataflow.new.DataFlow module OpenSSLModel { import Language - abstract class KeyDerivationOperation extends Crypto::KeyDerivationOperation { } + class FunctionCallOrMacroAccess extends Element { + FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess } + + string getTargetName() { + result = this.(FunctionCall).getTarget().getName() + or + result = this.(MacroAccess).getMacroName() + } + } + + /** + * Hash function references in OpenSSL. + */ + predicate hash_ref_type_mapping_known(string name, Crypto::THashType algo) { + // `ma` name has an LN_ or SN_ prefix, which we want to ignore + // capture any name after the _ prefix using regex matching + name = ["sha1", "sha160"] and algo instanceof Crypto::SHA1 + or + name = ["sha224", "sha256", "sha384", "sha512"] and algo instanceof Crypto::SHA2 + or + name = ["sha3-224", "sha3-256", "sha3-384", "sha3-512"] and algo instanceof Crypto::SHA3 + or + name = "md2" and algo instanceof Crypto::MD2 + or + name = "md4" and algo instanceof Crypto::MD4 + or + name = "md5" and algo instanceof Crypto::MD5 + or + name = "ripemd160" and algo instanceof Crypto::RIPEMD160 + or + name = "whirlpool" and algo instanceof Crypto::WHIRLPOOL + } + + predicate hash_ref_type_mapping(FunctionCallOrMacroAccess ref, string name, Crypto::THashType algo) { + name = ref.getTargetName().regexpCapture("(?:SN_|LN_|EVP_)([a-z0-9]+)", 1) and + hash_ref_type_mapping_known(name, algo) + } + + class HashAlgorithmRef extends Crypto::HashAlgorithm { + FunctionCallOrMacroAccess instance; + + HashAlgorithmRef() { + this = Crypto::THashAlgorithm(instance) and + hash_ref_type_mapping(instance, _, _) + } + + override string getSHA2OrSHA3DigestSize(Location location) { + ( + this.getHashType() instanceof Crypto::SHA2 or + this.getHashType() instanceof Crypto::SHA3 + ) and + exists(string name | + hash_ref_type_mapping(instance, name, this.getHashType()) and + result = name.regexpFind("\\d{3}", 0, _) and + location = instance.getLocation() + ) + } - class SHA1Algo extends Crypto::HashAlgorithm instanceof MacroAccess { - SHA1Algo() { this.getMacro().getName() = "SN_sha1" } + override string getRawAlgorithmName() { result = instance.getTargetName() } - override string getRawAlgorithmName() { result = "SN_sha1" } + override Crypto::THashType getHashType() { hash_ref_type_mapping(instance, _, result) } - override Crypto::THashType getHashType() { result instanceof Crypto::SHA1 } + Element getInstance() { result = instance } + + override Location getLocation() { result = instance.getLocation() } } + /** + * Data-flow configuration for key derivation algorithm flow to EVP_KDF_derive. + */ module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { source.asExpr() instanceof KeyDerivationAlgorithm } + predicate isSource(DataFlow::Node source) { + source.asExpr() = any(KeyDerivationAlgorithm a).getInstance() + } predicate isSink(DataFlow::Node sink) { - exists(EVP_KDF_derive kdo | sink.asExpr() = kdo.getAlgorithmArg()) + exists(EVP_KDF_derive kdo | + sink.asExpr() = kdo.getAlgorithmArg() + or + sink.asExpr() = kdo.getContextArg() // via `EVP_KDF_CTX_set_params` + ) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + none() // TODO } } module AlgorithmToEVPKeyDeriveFlow = DataFlow::Global; - predicate algorithm_to_EVP_KDF_derive(Crypto::Algorithm algo, EVP_KDF_derive derive) { - algo.(Expr).getEnclosingFunction() = derive.(Expr).getEnclosingFunction() + predicate algorithm_to_EVP_KDF_derive(KeyDerivationAlgorithm algo, EVP_KDF_derive derive) { + none() } - class EVP_KDF_derive extends KeyDerivationOperation instanceof FunctionCall { - EVP_KDF_derive() { this.getTarget().getName() = "EVP_KDF_derive" } + /** + * Key derivation operation (e.g., `EVP_KDF_derive`) + */ + abstract class KeyDerivationOperation extends Crypto::KeyDerivationOperation { } + + class EVP_KDF_derive extends KeyDerivationOperation { + FunctionCall instance; + + EVP_KDF_derive() { + this = Crypto::TKeyDerivationOperation(instance) and + instance.getTarget().getName() = "EVP_KDF_derive" + } override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) } - Expr getAlgorithmArg() { result = this.(FunctionCall).getArgument(3) } + Expr getAlgorithmArg() { result = instance.getArgument(3) } + + Expr getContextArg() { result = instance.getArgument(0) } } - abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm { } + /** + * Key derivation algorithm nodes + */ + abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm { + abstract Expr getInstance(); + } + /** + * `EVP_KDF_fetch` returns a key derivation algorithm. + */ class EVP_KDF_fetch_Call extends FunctionCall { EVP_KDF_fetch_Call() { this.getTarget().getName() = "EVP_KDF_fetch" } Expr getAlgorithmArg() { result = this.getArgument(1) } } - predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF"] } - - class KDFAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLiteral { - KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) } + class EVP_KDF_fetch_AlgorithmArg extends Expr { + EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) } + } - override string toString() { result = this.(StringLiteral).toString() } + predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF", "PBKDF2"] } - string getValue() { result = this.(StringLiteral).getValue() } + class KDFAlgorithmStringLiteral extends StringLiteral { + KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) } } private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KDFAlgorithmStringLiteral } - predicate isSink(DataFlow::Node sink) { - exists(EVP_KDF_fetch_Call call | sink.asExpr() = call.getAlgorithmArg()) - } + predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof EVP_KDF_fetch_AlgorithmArg } } module AlgorithmStringToFetchFlow = DataFlow::Global; - predicate algorithmStringToKDFFetchArgFlow(string name, KDFAlgorithmStringLiteral origin, Expr arg) { - exists(EVP_KDF_fetch_Call sinkCall | - origin.getValue().toUpperCase() = name and - arg = sinkCall.getAlgorithmArg() and - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) - ) + predicate algorithmStringToKDFFetchArgFlow( + string name, KDFAlgorithmStringLiteral origin, EVP_KDF_fetch_AlgorithmArg arg + ) { + origin.getValue().toUpperCase() = name and + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) } - class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF instanceof Expr { + /** + * HKDF key derivation algorithm. + */ + class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF { KDFAlgorithmStringLiteral origin; + EVP_KDF_fetch_AlgorithmArg instance; - HKDF() { algorithmStringToKDFFetchArgFlow("HKDF", origin, this) } + HKDF() { + this = Crypto::TKeyDerivationAlgorithm(instance) and + algorithmStringToKDFFetchArgFlow("HKDF", origin, instance) + } override string getRawAlgorithmName() { result = origin.getValue() } @@ -84,19 +178,61 @@ module OpenSSLModel { override Crypto::LocatableElement getOrigin(string name) { result = origin and name = origin.toString() } + + override Expr getInstance() { result = origin } } - class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF instanceof Expr { + /** + * PBKDF2 key derivation algorithm. + */ + class PBKDF2 extends KeyDerivationAlgorithm, Crypto::PBKDF2 { KDFAlgorithmStringLiteral origin; + EVP_KDF_fetch_AlgorithmArg instance; - PKCS12KDF() { algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, this) } + PBKDF2() { + this = Crypto::TKeyDerivationAlgorithm(instance) and + algorithmStringToKDFFetchArgFlow("PBKDF2", origin, instance) + } override string getRawAlgorithmName() { result = origin.getValue() } - override Crypto::HashAlgorithm getHashAlgorithm() { none() } + override string getIterationCount(Location location) { none() } // TODO + + override string getKeyLength(Location location) { none() } // TODO + + override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO + + override Crypto::LocatableElement getOrigin(string name) { + result = origin and name = origin.toString() + } - override Crypto::NodeBase getOrigin(string name) { + override Expr getInstance() { result = instance } + } + + /** + * PKCS12KDF key derivation algorithm. + */ + class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF { + KDFAlgorithmStringLiteral origin; + EVP_KDF_fetch_AlgorithmArg instance; + + PKCS12KDF() { + this = Crypto::TKeyDerivationAlgorithm(instance) and + algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, instance) + } + + override string getRawAlgorithmName() { result = origin.getValue() } + + override string getIterationCount(Location location) { none() } // TODO + + override string getIDByte(Location location) { none() } // TODO + + override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO + + override Crypto::LocatableElement getOrigin(string name) { result = origin and name = origin.toString() } + + override Expr getInstance() { result = instance } } } diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 44a753bb7db2..807dec96ba40 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -35,6 +35,19 @@ module JCAModel { ] } + /** + * this may be specified either in the ALG/MODE/PADDING or just ALG format + */ + class CipherStringLiteral extends StringLiteral { + CipherStringLiteral() { cipher_names(this.getValue().splitAt("/")) } + + string getAlgorithmName() { result = this.getValue().splitAt("/", 0) } + + string getMode() { result = this.getValue().splitAt("/", 1) } + + string getPadding() { result = this.getValue().splitAt("/", 2) } + } + class CipherGetInstanceCall extends Call { CipherGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") @@ -43,41 +56,48 @@ module JCAModel { Expr getAlgorithmArg() { result = this.getArgument(0) } } - /** - * this may be specified either in the ALG/MODE/PADDING or just ALG format - */ - class CipherAlgorithmStringLiteral extends StringLiteral { - CipherAlgorithmStringLiteral() { cipher_names(this.getValue().splitAt("/")) } + private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral } + + predicate isSink(DataFlow::Node sink) { + exists(CipherGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) + } } - class ModeOfOperationStringLiteral extends StringLiteral { - ModeOfOperationStringLiteral() { cipher_modes(this.(StringLiteral).getValue().splitAt("/")) } + module AlgorithmStringToFetchFlow = DataFlow::Global; - string getRawAlgorithmName() { result = this.getValue().regexpCapture(".*/(.*)/.*", 1) } + class CipherGetInstanceAlgorithmArg extends Expr { + CipherGetInstanceAlgorithmArg() { + exists(CipherGetInstanceCall call | this = call.getArgument(0)) + } + + StringLiteral getOrigin() { + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), DataFlow::exprNode(this)) + } } - class ECBMode extends Crypto::ModeOfOperation { - ModeOfOperationStringLiteral mode; + class ModeStringLiteral extends Crypto::ModeOfOperation { + CipherStringLiteral instance; - ECBMode() { modeStringToCipherInstanceArgFlow("ECB", mode, this) } + ModeStringLiteral() { + this = Crypto::TModeOfOperationAlgorithm(instance) and + exists(instance.getMode()) and + instance = any(CipherGetInstanceAlgorithmArg call).getOrigin() + } - override string getRawAlgorithmName() { result = mode.getRawAlgorithmName() } + override Location getLocation() { result = instance.getLocation() } - predicate modeToNameMapping(Crypto::TModeOperation type, string name) { - name = "ECB" and type instanceof Crypto::ECB - } + override string getRawAlgorithmName() { result = instance.getMode() } - override Crypto::TModeOperation getModeType() { - this.modeToNameMapping(result, this.getRawAlgorithmName()) + predicate modeToNameMapping(Crypto::TModeOperationType type, string name) { + super.modeToNameMapping(type, name) } - override Crypto::LocatableElement getOrigin(string name) { - result = mode and name = mode.toString() + override Crypto::TModeOperationType getModeType() { + this.modeToNameMapping(result, instance.getMode().toUpperCase()) } - } - abstract class CipherAlgorithmPadding extends Crypto::NodeBase { - string getValue() { result = "" } + CipherStringLiteral getInstance() { result = instance } } //todo refactor @@ -90,39 +110,6 @@ module JCAModel { // result = this.(StringLiteral).getValue().regexpCapture(".*/.*/(.*)", 1) // } // } - private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherAlgorithmStringLiteral } - - predicate isSink(DataFlow::Node sink) { - exists(CipherGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) - } - } - - module AlgorithmStringToFetchFlow = DataFlow::Global; - - predicate algorithmStringToCipherInstanceArgFlow( - string name, CipherAlgorithmStringLiteral origin, Expr arg - ) { - exists(CipherGetInstanceCall sinkCall | - origin.getValue().splitAt("/") = name and - arg = sinkCall and - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), - DataFlow::exprNode(sinkCall.getAlgorithmArg())) - ) - } - - predicate modeStringToCipherInstanceArgFlow( - string name, ModeOfOperationStringLiteral mode, Expr arg - ) { - exists(CipherGetInstanceCall sinkCall | - //consider if this should be a more specific predicate - mode.getRawAlgorithmName() = name and - arg = sinkCall and - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(mode), - DataFlow::exprNode(sinkCall.getAlgorithmArg())) - ) - } - /** * A class to represent when AES is used * AND currently it has literal mode and padding provided @@ -130,32 +117,48 @@ module JCAModel { * this currently does not capture the use without a literal * though should be extended to */ - class AESAlgo extends Crypto::SymmetricAlgorithm instanceof Expr { - CipherAlgorithmStringLiteral alg; + class CipherAlgorithm extends Crypto::SymmetricAlgorithm { + CipherStringLiteral origin; + CipherGetInstanceAlgorithmArg instance; + + CipherAlgorithm() { + this = Crypto::TSymmetricAlgorithm(instance) and + instance.getOrigin() = origin + } - AESAlgo() { algorithmStringToCipherInstanceArgFlow("AES", alg, this) } + override Location getLocation() { result = instance.getLocation() } - //todo this is really not correct yet override Crypto::ModeOfOperation getModeOfOperation() { - none() - //exists(Crypto::ModeOfOperation mode | mode = this and result = this) + result.(ModeStringLiteral).getInstance() = origin } override Crypto::LocatableElement getOrigin(string name) { - result = alg and name = alg.toString() + result = origin and name = origin.toString() } - override string getAlgorithmName() { result = "AES" } - - override string getRawAlgorithmName() { result = alg.getValue() } + override string getRawAlgorithmName() { result = origin.getValue() } - override Crypto::TSymmetricCipherFamilyType getSymmetricCipherFamilyType() { - result instanceof Crypto::AES + override Crypto::TSymmetricCipherType getCipherFamily() { + this.cipherNameMapping(result, origin.getAlgorithmName()) } - //temp hacks for testing - override string getKeySize(Location location) { result = "" } + override string getKeySize(Location location) { none() } - override Crypto::TCipherStructure getCipherType() { none() } + bindingset[name] + private predicate cipherNameMappingKnown(Crypto::TSymmetricCipherType type, string name) { + name = "AES" and + type instanceof Crypto::AES + or + name = "RC4" and + type instanceof Crypto::RC4 + } + + bindingset[name] + predicate cipherNameMapping(Crypto::TSymmetricCipherType type, string name) { + this.cipherNameMappingKnown(type, name) + or + not this.cipherNameMappingKnown(_, name) and + type instanceof Crypto::OtherSymmetricCipherType + } } } diff --git a/java/ql/src/experimental/Quantum/Test.ql b/java/ql/src/experimental/Quantum/Test.ql index f35336a846f2..f3ae23a2ccac 100644 --- a/java/ql/src/experimental/Quantum/Test.ql +++ b/java/ql/src/experimental/Quantum/Test.ql @@ -2,7 +2,8 @@ * @name "PQC Test" */ - import experimental.Quantum.Language +import experimental.Quantum.Language -from JCAModel::AESLiteral l -select l, l.getAlg(), l.getMode().getValue(), l.getPadding().getValue() \ No newline at end of file +from Crypto::SymmetricAlgorithm a, Crypto::ModeOfOperation mode +where a.getModeOfOperation() = mode +select a, a.getAlgorithmName(), a.getRawAlgorithmName(), mode, mode.getAlgorithmName() diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index ad117674dffe..dd570a89ef6e 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -56,12 +56,22 @@ module CryptographyBase Input> { not source = target } + newtype TNode = + THashOperation(LocatableElement e) or + THashAlgorithm(LocatableElement e) or + TKeyDerivationOperation(LocatableElement e) or + TKeyDerivationAlgorithm(LocatableElement e) or + TEncryptionOperation(LocatableElement e) or + TSymmetricAlgorithm(LocatableElement e) or + TEllipticCurveAlgorithm(LocatableElement e) or + TModeOfOperationAlgorithm(LocatableElement e) + /** * The base class for all cryptographic assets, such as operations and algorithms. * * Each `NodeBase` is a node in a graph of cryptographic operations, where the edges are the relationships between the nodes. */ - abstract class NodeBase instanceof LocatableElement { + abstract class NodeBase extends TNode { /** * Returns a string representation of this node, usually the name of the operation/algorithm/property. */ @@ -70,7 +80,7 @@ module CryptographyBase Input> { /** * Returns the location of this node in the code. */ - Location getLocation() { result = super.getLocation() } + abstract Location getLocation(); /** * Gets the origin of this node, e.g., a string literal in source describing it. @@ -128,6 +138,8 @@ module CryptographyBase Input> { } abstract class Algorithm extends Asset { + final override string toString() { result = this.getAlgorithmType() } + /** * Gets the name of this algorithm, e.g., "AES" or "SHA". */ @@ -138,7 +150,20 @@ module CryptographyBase Input> { */ abstract string getRawAlgorithmName(); - final override string toString() { result = this.getAlgorithmName() } + /** + * Gets the type of this algorithm, e.g., "hash" or "key derivation". + */ + abstract string getAlgorithmType(); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [ONLY_KNOWN] + key = "name" and value = this.getAlgorithmName() and location = this.getLocation() + or + // [ONLY_KNOWN] + key = "raw_name" and value = this.getRawAlgorithmName() and location = this.getLocation() + } } /** @@ -147,81 +172,318 @@ module CryptographyBase Input> { * This operation takes an input message of arbitrary content and length and produces a fixed-size * hash value as the output using a specified hashing algorithm. */ - abstract class HashOperation extends Operation { + abstract class HashOperation extends Operation, THashOperation { abstract override HashAlgorithm getAlgorithm(); - override string getOperationName() { result = "HASH" } + override string getOperationName() { result = "HashOperation" } } newtype THashType = + MD2() or + MD4() or MD5() or SHA1() or - SHA256() or - SHA512() or + SHA2() or + SHA3() or + RIPEMD160() or + WHIRLPOOL() or OtherHashType() /** * A hashing algorithm that transforms variable-length input into a fixed-size hash value. */ - abstract class HashAlgorithm extends Algorithm { + abstract class HashAlgorithm extends Algorithm, THashAlgorithm { + override string getAlgorithmType() { result = "HashAlgorithm" } + final predicate hashTypeToNameMapping(THashType type, string name) { + type instanceof MD2 and name = "MD2" + or + type instanceof MD4 and name = "MD4" + or type instanceof MD5 and name = "MD5" or - type instanceof SHA1 and name = "SHA-1" + type instanceof SHA1 and name = "SHA1" + or + type instanceof SHA2 and name = "SHA2" or - type instanceof SHA256 and name = "SHA-256" + type instanceof SHA3 and name = "SHA3" or - type instanceof SHA512 and name = "SHA-512" + type instanceof RIPEMD160 and name = "RIPEMD160" + or + type instanceof WHIRLPOOL and name = "WHIRLPOOL" or type instanceof OtherHashType and name = this.getRawAlgorithmName() } + /** + * Gets the type of this hashing algorithm, e.g., MD5 or SHA. + * + * When modeling a new hashing algorithm, use this predicate to specify the type of the algorithm. + */ abstract THashType getHashType(); override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } + + /** + * Gets the digest size of SHA2 or SHA3 algorithms. + * + * This predicate does not need to hold for other algorithms, + * as the digest size is already known based on the algorithm itself. + * + * For `OtherHashType` algorithms where a digest size should be reported, `THashType` + * should be extended to explicitly model that algorithm. If the algorithm has variable + * or multiple digest size variants, a similar predicate to this one must be defined + * for that algorithm to report the digest size. + */ + abstract string getSHA2OrSHA3DigestSize(Location location); + + bindingset[type] + private string getDigestSize(THashType type, Location location) { + type instanceof MD2 and result = "128" + or + type instanceof MD4 and result = "128" + or + type instanceof MD5 and result = "128" + or + type instanceof SHA1 and result = "160" + or + type instanceof SHA2 and result = this.getSHA2OrSHA3DigestSize(location) + or + type instanceof SHA3 and result = this.getSHA2OrSHA3DigestSize(location) + or + type instanceof RIPEMD160 and result = "160" + or + type instanceof WHIRLPOOL and result = "512" + } + + final override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [KNOWN_OR_UNKNOWN] + key = "digest_size" and + if exists(this.getDigestSize(this.getHashType(), location)) + then value = this.getDigestSize(this.getHashType(), location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + } } /** * An operation that derives one or more keys from an input value. */ - abstract class KeyDerivationOperation extends Operation { - override string getOperationName() { result = "KEY_DERIVATION" } + abstract class KeyDerivationOperation extends Operation, TKeyDerivationOperation { + final override Location getLocation() { + exists(LocatableElement le | this = TKeyDerivationOperation(le) and result = le.getLocation()) + } + + override string getOperationName() { result = "KeyDerivationOperation" } } /** * An algorithm that derives one or more keys from an input value. + * + * Only use this class to model UNKNOWN key derivation algorithms. + * + * For known algorithms, use the specialized classes, e.g., `HKDF` and `PKCS12KDF`. */ - abstract class KeyDerivationAlgorithm extends Algorithm { - abstract override string getAlgorithmName(); + abstract class KeyDerivationAlgorithm extends Algorithm, TKeyDerivationAlgorithm { + final override Location getLocation() { + exists(LocatableElement le | this = TKeyDerivationAlgorithm(le) and result = le.getLocation()) + } + + override string getAlgorithmType() { result = "KeyDerivationAlgorithm" } + + override string getAlgorithmName() { result = this.getRawAlgorithmName() } } /** - * HKDF key derivation function + * An algorithm that derives one or more keys from an input value, using a configurable digest algorithm. */ - abstract class HKDF extends KeyDerivationAlgorithm { - final override string getAlgorithmName() { result = "HKDF" } - + abstract private class KeyDerivationWithDigestParameter extends KeyDerivationAlgorithm { abstract HashAlgorithm getHashAlgorithm(); override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or - edgeName = "digest" and result = this.getHashAlgorithm() + ( + // [KNOWN_OR_UNKNOWN] + edgeName = "uses" and + if exists(this.getHashAlgorithm()) then result = this.getHashAlgorithm() else result = this + ) } } /** - * PKCS #12 key derivation function + * HKDF key derivation function */ - abstract class PKCS12KDF extends KeyDerivationAlgorithm { - final override string getAlgorithmName() { result = "PKCS12KDF" } + abstract class HKDF extends KeyDerivationWithDigestParameter { + final override string getAlgorithmName() { result = "HKDF" } + } - abstract HashAlgorithm getHashAlgorithm(); + /** + * PBKDF2 key derivation function + */ + abstract class PBKDF2 extends KeyDerivationWithDigestParameter { + final override string getAlgorithmName() { result = "PBKDF2" } - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) + /** + * Gets the iteration count of this key derivation algorithm. + */ + abstract string getIterationCount(Location location); + + /** + * Gets the bit-length of the derived key. + */ + abstract string getKeyLength(Location location); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) or - edgeName = "digest" and result = this.getHashAlgorithm() + ( + // [KNOWN_OR_UNKNOWN] + key = "iterations" and + if exists(this.getIterationCount(location)) + then value = this.getIterationCount(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "key_len" and + if exists(this.getKeyLength(location)) + then value = this.getKeyLength(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + } + } + + /** + * PKCS12KDF key derivation function + */ + abstract class PKCS12KDF extends KeyDerivationWithDigestParameter { + override string getAlgorithmName() { result = "PKCS12KDF" } + + /** + * Gets the iteration count of this key derivation algorithm. + */ + abstract string getIterationCount(Location location); + + /** + * Gets the raw ID argument specifying the intended use of the derived key. + * + * The intended use is defined in RFC 7292, appendix B.3, as follows: + * + * This standard specifies 3 different values for the ID byte mentioned above: + * + * 1. If ID=1, then the pseudorandom bits being produced are to be used + * as key material for performing encryption or decryption. + * + * 2. If ID=2, then the pseudorandom bits being produced are to be used + * as an IV (Initial Value) for encryption or decryption. + * + * 3. If ID=3, then the pseudorandom bits being produced are to be used + * as an integrity key for MACing. + */ + abstract string getIDByte(Location location); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "iterations" and + if exists(this.getIterationCount(location)) + then value = this.getIterationCount(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "id_byte" and + if exists(this.getIDByte(location)) + then value = this.getIDByte(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + } + } + + /** + * scrypt key derivation function + */ + abstract class SCRYPT extends KeyDerivationAlgorithm { + final override string getAlgorithmName() { result = "scrypt" } + + /** + * Gets the iteration count (`N`) argument + */ + abstract string get_N(Location location); + + /** + * Gets the block size (`r`) argument + */ + abstract string get_r(Location location); + + /** + * Gets the parallelization factor (`p`) argument + */ + abstract string get_p(Location location); + + /** + * Gets the derived key length argument + */ + abstract string getDerivedKeyLength(Location location); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "N" and + if exists(this.get_N(location)) + then value = this.get_N(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "r" and + if exists(this.get_r(location)) + then value = this.get_r(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "p" and + if exists(this.get_p(location)) + then value = this.get_p(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "key_len" and + if exists(this.getDerivedKeyLength(location)) + then value = this.getDerivedKeyLength(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) } } @@ -246,7 +508,7 @@ module CryptographyBase Input> { /** * Elliptic curve algorithms */ - newtype TEllipticCurveFamily = + newtype TEllipticCurveType = NIST() or SEC() or NUMS() or @@ -257,16 +519,17 @@ module CryptographyBase Input> { C2() or SM2() or ES() or - OtherEllipticCurveFamilyType() + OtherEllipticCurveType() - abstract class EllipticCurve extends Algorithm { + abstract class EllipticCurve extends Algorithm, TEllipticCurveAlgorithm { abstract string getKeySize(Location location); - abstract TEllipticCurveFamily getCurveFamilyType(); + abstract TEllipticCurveType getCurveFamily(); override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or + // [KNOWN_OR_UNKNOWN] key = "key_size" and if exists(this.getKeySize(location)) then value = this.getKeySize(location) @@ -306,62 +569,168 @@ module CryptographyBase Input> { /** * Block cipher modes of operation algorithms */ - newtype TModeOperation = + newtype TModeOperationType = ECB() or CBC() or + CFB() or + OFB() or + CTR() or + GCM() or + CCM() or + XTS() or OtherMode() abstract class ModeOfOperation extends Algorithm { - final private predicate modeToNameMapping(TModeOperation type, string name) { + override string getAlgorithmType() { result = "ModeOfOperation" } + + /** + * Gets the type of this mode of operation, e.g., "ECB" or "CBC". + * + * When modeling a new mode of operation, use this predicate to specify the type of the mode. + */ + abstract TModeOperationType getModeType(); + + bindingset[type] + final predicate modeToNameMapping(TModeOperationType type, string name) { type instanceof ECB and name = "ECB" or type instanceof CBC and name = "CBC" or + type instanceof CFB and name = "CFB" + or + type instanceof OFB and name = "OFB" + or + type instanceof CTR and name = "CTR" + or + type instanceof GCM and name = "GCM" + or + type instanceof CCM and name = "CCM" + or + type instanceof XTS and name = "XTS" + or type instanceof OtherMode and name = this.getRawAlgorithmName() } - abstract TModeOperation getModeType(); - override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } } /** * A helper type for distinguishing between block and stream ciphers. */ - newtype TCipherStructure = + newtype TCipherStructureType = Block() or - Stream() + Stream() or + UnknownCipherStructureType() + + private string getCipherStructureTypeString(TCipherStructureType type) { + type instanceof Block and result = "Block" + or + type instanceof Stream and result = "Stream" + or + type instanceof UnknownCipherStructureType and result instanceof UnknownPropertyValue + } /** * Symmetric algorithms */ - newtype TSymmetricCipherFamilyType = + newtype TSymmetricCipherType = AES() or - OtherSymmetricCipherFamilyType() + Camellia() or + DES() or + TripleDES() or + IDEA() or + CAST5() or + ChaCha20() or + RC4() or + RC5() or + OtherSymmetricCipherType() abstract class SymmetricAlgorithm extends Algorithm { - abstract TSymmetricCipherFamilyType getSymmetricCipherFamilyType(); + final TCipherStructureType getCipherStructure() { + this.cipherFamilyToNameAndStructure(this.getCipherFamily(), _, result) + } + + final override string getAlgorithmName() { + this.cipherFamilyToNameAndStructure(this.getCipherFamily(), result, _) + } + final override string getAlgorithmType() { result = "SymmetricAlgorithm" } + + /** + * Gets the key size of this symmetric cipher, e.g., "128" or "256". + */ abstract string getKeySize(Location location); - abstract TCipherStructure getCipherType(); + /** + * Gets the type of this symmetric cipher, e.g., "AES" or "ChaCha20". + */ + abstract TSymmetricCipherType getCipherFamily(); + + /** + * Gets the mode of operation of this symmetric cipher, e.g., "GCM" or "CBC". + */ + abstract ModeOfOperation getModeOfOperation(); + + bindingset[type] + final private predicate cipherFamilyToNameAndStructure( + TSymmetricCipherType type, string name, TCipherStructureType s + ) { + type instanceof AES and name = "AES" and s = Block() + or + type instanceof Camellia and name = "Camellia" and s = Block() + or + type instanceof DES and name = "DES" and s = Block() + or + type instanceof TripleDES and name = "TripleDES" and s = Block() + or + type instanceof IDEA and name = "IDEA" and s = Block() + or + type instanceof CAST5 and name = "CAST5" and s = Block() + or + type instanceof ChaCha20 and name = "ChaCha20" and s = Stream() + or + type instanceof RC4 and name = "RC4" and s = Stream() + or + type instanceof RC5 and name = "RC5" and s = Block() + or + type instanceof OtherSymmetricCipherType and + name = this.getRawAlgorithmName() and + s = UnknownCipherStructureType() + } //mode, padding scheme, keysize, block/stream, auth'd //nodes = mode, padding scheme //properties = keysize, block/stream, auth'd //leave authd to lang specific + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + ( + // [KNOWN_OR_UNKNOWN] + edgeName = "mode" and + if exists(this.getModeOfOperation()) + then result = this.getModeOfOperation() + else result = this + ) + } + override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or - key = "key_size" and - if exists(this.getKeySize(location)) - then value = this.getKeySize(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation + // [ALWAYS_KNOWN]: unknown case is handled in `getCipherStructureTypeString` + key = "structure" and + getCipherStructureTypeString(this.getCipherStructure()) = value and + location instanceof UnknownLocation + or + ( + // [KNOWN_OR_UNKNOWN] + key = "key_size" and + if exists(this.getKeySize(location)) + then value = this.getKeySize(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) ) - //add more keys to index props } - - abstract ModeOfOperation getModeOfOperation(); } } From b777a22d3578a057e58082310b2ab9e7a01b522e Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Fri, 14 Feb 2025 23:43:07 +0100 Subject: [PATCH 018/122] Expand model and specialize newtype relations --- cpp/ql/lib/experimental/Quantum/OpenSSL.qll | 62 ++++---- java/ql/lib/experimental/Quantum/JCA.qll | 116 ++++++++------- java/ql/src/experimental/Quantum/Test.ql | 2 +- .../codeql/cryptography/Model.qll | 135 ++++++++++++++---- 4 files changed, 203 insertions(+), 112 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll index cb19092f3fd7..821fc0aec8ff 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll @@ -4,16 +4,6 @@ import semmle.code.cpp.dataflow.new.DataFlow module OpenSSLModel { import Language - class FunctionCallOrMacroAccess extends Element { - FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess } - - string getTargetName() { - result = this.(FunctionCall).getTarget().getName() - or - result = this.(MacroAccess).getMacroName() - } - } - /** * Hash function references in OpenSSL. */ @@ -42,13 +32,27 @@ module OpenSSLModel { hash_ref_type_mapping_known(name, algo) } - class HashAlgorithmRef extends Crypto::HashAlgorithm { - FunctionCallOrMacroAccess instance; + class FunctionCallOrMacroAccess extends Element { + FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess } - HashAlgorithmRef() { - this = Crypto::THashAlgorithm(instance) and - hash_ref_type_mapping(instance, _, _) + string getTargetName() { + result = this.(FunctionCall).getTarget().getName() + or + result = this.(MacroAccess).getMacroName() } + } + + class HashAlgorithmCallOrMacro extends Crypto::HashAlgorithmInstance instanceof FunctionCallOrMacroAccess + { + HashAlgorithmCallOrMacro() { hash_ref_type_mapping(this, _, _) } + + string getTargetName() { result = this.(FunctionCallOrMacroAccess).getTargetName() } + } + + class HashAlgorithm extends Crypto::HashAlgorithm { + HashAlgorithmCallOrMacro instance; + + HashAlgorithm() { this = Crypto::THashAlgorithm(instance) } override string getSHA2OrSHA3DigestSize(Location location) { ( @@ -81,9 +85,9 @@ module OpenSSLModel { predicate isSink(DataFlow::Node sink) { exists(EVP_KDF_derive kdo | - sink.asExpr() = kdo.getAlgorithmArg() + sink.asExpr() = kdo.getCall().getAlgorithmArg() or - sink.asExpr() = kdo.getContextArg() // via `EVP_KDF_CTX_set_params` + sink.asExpr() = kdo.getCall().getContextArg() // via `EVP_KDF_CTX_set_params` ) } @@ -101,21 +105,23 @@ module OpenSSLModel { /** * Key derivation operation (e.g., `EVP_KDF_derive`) */ - abstract class KeyDerivationOperation extends Crypto::KeyDerivationOperation { } + class EVP_KDF_derive_FunctionCall extends Crypto::KeyDerivationOperationInstance instanceof FunctionCall + { + EVP_KDF_derive_FunctionCall() { this.getTarget().getName() = "EVP_KDF_derive" } - class EVP_KDF_derive extends KeyDerivationOperation { - FunctionCall instance; + Expr getAlgorithmArg() { result = super.getArgument(3) } - EVP_KDF_derive() { - this = Crypto::TKeyDerivationOperation(instance) and - instance.getTarget().getName() = "EVP_KDF_derive" - } + Expr getContextArg() { result = super.getArgument(0) } + } - override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) } + class EVP_KDF_derive extends Crypto::KeyDerivationOperation { + EVP_KDF_derive_FunctionCall instance; + + EVP_KDF_derive() { this = Crypto::TKeyDerivationOperation(instance) } - Expr getAlgorithmArg() { result = instance.getArgument(3) } + override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) } - Expr getContextArg() { result = instance.getArgument(0) } + EVP_KDF_derive_FunctionCall getCall() { result = instance } } /** @@ -134,7 +140,7 @@ module OpenSSLModel { Expr getAlgorithmArg() { result = this.getArgument(1) } } - class EVP_KDF_fetch_AlgorithmArg extends Expr { + class EVP_KDF_fetch_AlgorithmArg extends Crypto::KeyDerivationAlgorithmInstance instanceof Expr { EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) } } diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 807dec96ba40..299d9558ee10 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -4,12 +4,9 @@ import semmle.code.java.dataflow.DataFlow module JCAModel { import Language - abstract class EncryptionOperation extends Crypto::EncryptionOperation { } - - //TODO PBEWith can have suffixes. how to do? enumerate? or match a pattern? + // TODO: Verify that the PBEWith% case works correctly bindingset[algo] predicate cipher_names(string algo) { - // "Standard names are not case-sensitive." algo.toUpperCase() .matches([ "AES", "AESWrap", "AESWrapPad", "ARCFOUR", "Blowfish", "ChaCha20", "ChaCha20-Poly1305", @@ -17,26 +14,29 @@ module JCAModel { ].toUpperCase()) } - //TODO solve the fact that x is an int of various values. same as above... enumerate? + // TODO: Verify that the CFB% case works correctly + bindingset[mode] predicate cipher_modes(string mode) { - mode = - [ - "NONE", "CBC", "CCM", "CFB", "CFBx", "CTR", "CTS", "ECB", "GCM", "KW", "KWP", "OFB", "OFBx", - "PCBC" - ] + mode.toUpperCase() + .matches([ + "NONE", "CBC", "CCM", "CFB", "CFB%", "CTR", "CTS", "ECB", "GCM", "KW", "KWP", "OFB", + "OFB%", "PCBC" + ].toUpperCase()) } - //todo same as above, OAEPWith has asuffix type + // TODO: Verify that the OAEPWith% case works correctly + bindingset[padding] predicate cipher_padding(string padding) { - padding = - [ - "NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith", "PKCS1Padding", "PKCS5Padding", - "SSL3Padding" - ] + padding + .toUpperCase() + .matches([ + "NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith%", "PKCS1Padding", + "PKCS5Padding", "SSL3Padding" + ].toUpperCase()) } /** - * this may be specified either in the ALG/MODE/PADDING or just ALG format + * A `StringLiteral` in the `"ALG/MODE/PADDING"` or `"ALG"` format */ class CipherStringLiteral extends StringLiteral { CipherStringLiteral() { cipher_names(this.getValue().splitAt("/")) } @@ -56,6 +56,9 @@ module JCAModel { Expr getAlgorithmArg() { result = this.getArgument(0) } } + /** + * Data-flow configuration modelling flow from a cipher string literal to a `CipherGetInstanceCall` argument. + */ private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral } @@ -66,70 +69,77 @@ module JCAModel { module AlgorithmStringToFetchFlow = DataFlow::Global; - class CipherGetInstanceAlgorithmArg extends Expr { + /** + * The cipher algorithm argument to a `CipherGetInstanceCall`. + * + * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`. + */ + class CipherGetInstanceAlgorithmArg extends Crypto::EncryptionAlgorithmInstance, + Crypto::ModeOfOperationAlgorithmInstance instanceof Expr + { CipherGetInstanceAlgorithmArg() { exists(CipherGetInstanceCall call | this = call.getArgument(0)) } - StringLiteral getOrigin() { - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), DataFlow::exprNode(this)) + /** + * Returns the `StringLiteral` from which this argument is derived, if known. + */ + CipherStringLiteral getOrigin() { + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), + DataFlow::exprNode(this.(Expr).getAChildExpr*())) } } - class ModeStringLiteral extends Crypto::ModeOfOperation { - CipherStringLiteral instance; + /** + * A block cipher mode of operation, where the mode is specified in the ALG or ALG/MODE/PADDING format. + * + * This class will only exist when the mode (*and its type*) is determinable. + * This is because the mode will always be specified alongside the algorithm and never independently. + * Therefore, we can always assume that a determinable algorithm will have a determinable mode. + * + * In the case that only an algorithm is specified, e.g., "AES", the provider provides a default mode. + * + * TODO: Model the case of relying on a provider default, but alert on it as a bad practice. + */ + class ModeOfOperation extends Crypto::ModeOfOperationAlgorithm { + CipherGetInstanceAlgorithmArg instance; - ModeStringLiteral() { + ModeOfOperation() { this = Crypto::TModeOfOperationAlgorithm(instance) and - exists(instance.getMode()) and - instance = any(CipherGetInstanceAlgorithmArg call).getOrigin() + // TODO: this currently only holds for explicitly defined modes in a string literal. + // Cases with defaults, e.g., "AES", are not yet modelled. + // For these cases, in a CBOM, the AES node would have an unknown edge to its mode child. + exists(instance.getOrigin().getMode()) } override Location getLocation() { result = instance.getLocation() } - override string getRawAlgorithmName() { result = instance.getMode() } + override string getRawAlgorithmName() { result = instance.getOrigin().getValue() } predicate modeToNameMapping(Crypto::TModeOperationType type, string name) { super.modeToNameMapping(type, name) } override Crypto::TModeOperationType getModeType() { - this.modeToNameMapping(result, instance.getMode().toUpperCase()) + this.modeToNameMapping(result, instance.getOrigin().getMode()) } CipherStringLiteral getInstance() { result = instance } } - //todo refactor - // class CipherAlgorithmPaddingStringLiteral extends CipherAlgorithmPadding instanceof StringLiteral { - // CipherAlgorithmPaddingStringLiteral() { - // cipher_padding(this.(StringLiteral).getValue().splitAt("/")) - // } - // override string toString() { result = this.(StringLiteral).toString() } - // override string getValue() { - // result = this.(StringLiteral).getValue().regexpCapture(".*/.*/(.*)", 1) - // } - // } - /** - * A class to represent when AES is used - * AND currently it has literal mode and padding provided - * - * this currently does not capture the use without a literal - * though should be extended to - */ - class CipherAlgorithm extends Crypto::SymmetricAlgorithm { + class EncryptionAlgorithm extends Crypto::EncryptionAlgorithm { CipherStringLiteral origin; CipherGetInstanceAlgorithmArg instance; - CipherAlgorithm() { - this = Crypto::TSymmetricAlgorithm(instance) and + EncryptionAlgorithm() { + this = Crypto::TEncryptionAlgorithm(instance) and instance.getOrigin() = origin } override Location getLocation() { result = instance.getLocation() } - override Crypto::ModeOfOperation getModeOfOperation() { - result.(ModeStringLiteral).getInstance() = origin + override Crypto::ModeOfOperationAlgorithm getModeOfOperation() { + result.(ModeOfOperation).getInstance() = origin } override Crypto::LocatableElement getOrigin(string name) { @@ -138,23 +148,25 @@ module JCAModel { override string getRawAlgorithmName() { result = origin.getValue() } - override Crypto::TSymmetricCipherType getCipherFamily() { + override Crypto::TCipherType getCipherFamily() { this.cipherNameMapping(result, origin.getAlgorithmName()) } override string getKeySize(Location location) { none() } bindingset[name] - private predicate cipherNameMappingKnown(Crypto::TSymmetricCipherType type, string name) { + private predicate cipherNameMappingKnown(Crypto::TCipherType type, string name) { name = "AES" and type instanceof Crypto::AES or name = "RC4" and type instanceof Crypto::RC4 + // or + // TODO } bindingset[name] - predicate cipherNameMapping(Crypto::TSymmetricCipherType type, string name) { + predicate cipherNameMapping(Crypto::TCipherType type, string name) { this.cipherNameMappingKnown(type, name) or not this.cipherNameMappingKnown(_, name) and diff --git a/java/ql/src/experimental/Quantum/Test.ql b/java/ql/src/experimental/Quantum/Test.ql index f3ae23a2ccac..5496e5a70cf9 100644 --- a/java/ql/src/experimental/Quantum/Test.ql +++ b/java/ql/src/experimental/Quantum/Test.ql @@ -4,6 +4,6 @@ import experimental.Quantum.Language -from Crypto::SymmetricAlgorithm a, Crypto::ModeOfOperation mode +from Crypto::EncryptionAlgorithm a, Crypto::ModeOfOperationAlgorithm mode where a.getModeOfOperation() = mode select a, a.getAlgorithmName(), a.getRawAlgorithmName(), mode, mode.getAlgorithmName() diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index dd570a89ef6e..38ea6e475c05 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -8,6 +8,8 @@ import codeql.util.Option signature module InputSig { class LocatableElement { Location getLocation(); + + string toString(); } class UnknownLocation instanceof Location; @@ -56,15 +58,67 @@ module CryptographyBase Input> { not source = target } + /** + * All elements in the database that are mapped to nodes must extend the following classes + */ + abstract class HashOperationInstance extends LocatableElement { } + + abstract class HashAlgorithmInstance extends LocatableElement { } + + abstract class KeyDerivationOperationInstance extends LocatableElement { } + + abstract class KeyDerivationAlgorithmInstance extends LocatableElement { } + + abstract class EncryptionOperationInstance extends LocatableElement { } + + abstract class EncryptionAlgorithmInstance extends LocatableElement { } + + abstract class KeyEncapsulationOperationInstance extends LocatableElement { } + + abstract class KeyEncapsulationAlgorithmInstance extends LocatableElement { } + + abstract class EllipticCurveAlgorithmInstance extends LocatableElement { } + + // Non-standalone algorithms + abstract class ModeOfOperationAlgorithmInstance extends LocatableElement { } + + abstract class PaddingAlgorithmInstance extends LocatableElement { } + + // Artifacts + abstract class DigestArtifactInstance extends LocatableElement { } + + abstract class KeyArtifactInstance extends LocatableElement { } + + abstract class InitializationVectorArtifactInstance extends LocatableElement { } + + abstract class NonceArtifactInstance extends LocatableElement { } + newtype TNode = - THashOperation(LocatableElement e) or - THashAlgorithm(LocatableElement e) or - TKeyDerivationOperation(LocatableElement e) or - TKeyDerivationAlgorithm(LocatableElement e) or - TEncryptionOperation(LocatableElement e) or - TSymmetricAlgorithm(LocatableElement e) or - TEllipticCurveAlgorithm(LocatableElement e) or - TModeOfOperationAlgorithm(LocatableElement e) + // Artifacts (data that is not an operation or algorithm, e.g., a key) + TDigest(DigestArtifactInstance e) or + TKey(KeyArtifactInstance e) or + TInitializationVector(InitializationVectorArtifactInstance e) or + TNonce(NonceArtifactInstance e) or + // Operations (e.g., hashing, encryption) + THashOperation(HashOperationInstance e) or + TKeyDerivationOperation(KeyDerivationOperationInstance e) or + TEncryptionOperation(EncryptionOperationInstance e) or + TKeyEncapsulationOperation(KeyEncapsulationOperationInstance e) or + // Algorithms (e.g., SHA-256, AES) + TEncryptionAlgorithm(EncryptionAlgorithmInstance e) or + TEllipticCurveAlgorithm(EllipticCurveAlgorithmInstance e) or + THashAlgorithm(HashAlgorithmInstance e) or + TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstance e) or + TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or + // Non-standalone Algorithms (e.g., Mode, Padding) + TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or + TPaddingAlgorithm(PaddingAlgorithmInstance e) or + // Composite and hybrid cryptosystems (e.g., RSA-OAEP used with AES, post-quantum hybrid cryptosystems) + // These nodes are always parent nodes and are not modeled but rather defined via library-agnostic patterns. + TKemDemHybridCryptosystem(EncryptionAlgorithmInstance dem) or + TKeyAgreementHybridCryptosystem(EncryptionAlgorithmInstance ka) or + TAsymmetricEncryptionMacHybridCryptosystem(EncryptionAlgorithmInstance enc) or + TPostQuantumHybridCryptosystem(EncryptionAlgorithmInstance enc) /** * The base class for all cryptographic assets, such as operations and algorithms. @@ -90,14 +144,14 @@ module CryptographyBase Input> { /** * Returns the child of this node with the given edge name. * - * This predicate is used by derived classes to construct the graph of cryptographic operations. + * This predicate is overriden by derived classes to construct the graph of cryptographic operations. */ NodeBase getChild(string edgeName) { none() } /** * Defines properties of this node by name and either a value or location or both. * - * This predicate is used by derived classes to construct the graph of cryptographic operations. + * This predicate is overriden by derived classes to construct the graph of cryptographic operations. */ predicate properties(string key, string value, Location location) { key = "origin" and @@ -113,6 +167,8 @@ module CryptographyBase Input> { class Asset = NodeBase; + class Artifact = NodeBase; + /** * A cryptographic operation, such as hashing or encryption. */ @@ -125,9 +181,9 @@ module CryptographyBase Input> { /** * Gets the name of this operation, e.g., "hash" or "encrypt". */ - abstract string getOperationName(); + abstract string getOperationType(); - final override string toString() { result = this.getOperationName() } + final override string toString() { result = this.getOperationType() } override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) @@ -175,7 +231,7 @@ module CryptographyBase Input> { abstract class HashOperation extends Operation, THashOperation { abstract override HashAlgorithm getAlgorithm(); - override string getOperationName() { result = "HashOperation" } + override string getOperationType() { result = "HashOperation" } } newtype THashType = @@ -238,7 +294,7 @@ module CryptographyBase Input> { abstract string getSHA2OrSHA3DigestSize(Location location); bindingset[type] - private string getDigestSize(THashType type, Location location) { + private string type_to_digest_size_fixed(THashType type) { type instanceof MD2 and result = "128" or type instanceof MD4 and result = "128" @@ -247,15 +303,20 @@ module CryptographyBase Input> { or type instanceof SHA1 and result = "160" or - type instanceof SHA2 and result = this.getSHA2OrSHA3DigestSize(location) - or - type instanceof SHA3 and result = this.getSHA2OrSHA3DigestSize(location) - or type instanceof RIPEMD160 and result = "160" or type instanceof WHIRLPOOL and result = "512" } + bindingset[type] + private string getDigestSize(THashType type, Location location) { + result = this.type_to_digest_size_fixed(type) and location = this.getLocation() + or + type instanceof SHA2 and result = this.getSHA2OrSHA3DigestSize(location) + or + type instanceof SHA3 and result = this.getSHA2OrSHA3DigestSize(location) + } + final override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or @@ -277,7 +338,7 @@ module CryptographyBase Input> { exists(LocatableElement le | this = TKeyDerivationOperation(le) and result = le.getLocation()) } - override string getOperationName() { result = "KeyDerivationOperation" } + override string getOperationType() { result = "KeyDerivationOperation" } } /** @@ -560,10 +621,10 @@ module CryptographyBase Input> { * An encryption operation that processes plaintext to generate a ciphertext. * This operation takes an input message (plaintext) of arbitrary content and length and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). */ - abstract class EncryptionOperation extends Operation { + abstract class EncryptionOperation extends Operation, TEncryptionOperation { abstract override Algorithm getAlgorithm(); - override string getOperationName() { result = "ENCRYPTION" } + override string getOperationType() { result = "EncryptionOperation" } } /** @@ -578,15 +639,18 @@ module CryptographyBase Input> { GCM() or CCM() or XTS() or + OAEP() or OtherMode() - abstract class ModeOfOperation extends Algorithm { + abstract class ModeOfOperationAlgorithm extends Algorithm, TModeOfOperationAlgorithm { override string getAlgorithmType() { result = "ModeOfOperation" } /** * Gets the type of this mode of operation, e.g., "ECB" or "CBC". * * When modeling a new mode of operation, use this predicate to specify the type of the mode. + * + * If a type cannot be determined, the result is `OtherMode`. */ abstract TModeOperationType getModeType(); @@ -633,7 +697,7 @@ module CryptographyBase Input> { /** * Symmetric algorithms */ - newtype TSymmetricCipherType = + newtype TCipherType = AES() or Camellia() or DES() or @@ -643,9 +707,12 @@ module CryptographyBase Input> { ChaCha20() or RC4() or RC5() or + RSA() or OtherSymmetricCipherType() - abstract class SymmetricAlgorithm extends Algorithm { + abstract class EncryptionAlgorithm extends Algorithm, TEncryptionAlgorithm { + final LocatableElement getInstance() { this = TEncryptionAlgorithm(result) } + final TCipherStructureType getCipherStructure() { this.cipherFamilyToNameAndStructure(this.getCipherFamily(), _, result) } @@ -654,26 +721,26 @@ module CryptographyBase Input> { this.cipherFamilyToNameAndStructure(this.getCipherFamily(), result, _) } - final override string getAlgorithmType() { result = "SymmetricAlgorithm" } + final override string getAlgorithmType() { result = "EncryptionAlgorithm" } /** - * Gets the key size of this symmetric cipher, e.g., "128" or "256". + * Gets the key size of this cipher, e.g., "128" or "256". */ abstract string getKeySize(Location location); /** - * Gets the type of this symmetric cipher, e.g., "AES" or "ChaCha20". + * Gets the type of this cipher, e.g., "AES" or "ChaCha20". */ - abstract TSymmetricCipherType getCipherFamily(); + abstract TCipherType getCipherFamily(); /** - * Gets the mode of operation of this symmetric cipher, e.g., "GCM" or "CBC". + * Gets the mode of operation of this cipher, e.g., "GCM" or "CBC". */ - abstract ModeOfOperation getModeOfOperation(); + abstract ModeOfOperationAlgorithm getModeOfOperation(); bindingset[type] final private predicate cipherFamilyToNameAndStructure( - TSymmetricCipherType type, string name, TCipherStructureType s + TCipherType type, string name, TCipherStructureType s ) { type instanceof AES and name = "AES" and s = Block() or @@ -693,6 +760,8 @@ module CryptographyBase Input> { or type instanceof RC5 and name = "RC5" and s = Block() or + type instanceof RSA and name = "RSA" and s = Block() + or type instanceof OtherSymmetricCipherType and name = this.getRawAlgorithmName() and s = UnknownCipherStructureType() @@ -733,4 +802,8 @@ module CryptographyBase Input> { ) } } + + abstract class KEMAlgorithm extends TKeyEncapsulationAlgorithm, Algorithm { + final override string getAlgorithmType() { result = "KeyEncapsulationAlgorithm" } + } } From df01fa7a9cd6a3b8c750c47d658ec4f6e37cd3c6 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Mon, 17 Feb 2025 00:16:08 +0100 Subject: [PATCH 019/122] Expand model and JCA modeling --- java/ql/lib/experimental/Quantum/JCA.qll | 149 ++++++++++++++++-- java/ql/src/experimental/Quantum/Test.ql | 6 +- .../codeql/cryptography/Model.qll | 124 ++++++++++++--- 3 files changed, 237 insertions(+), 42 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 299d9558ee10..5a30e96b13ca 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -54,6 +54,8 @@ module JCAModel { } Expr getAlgorithmArg() { result = this.getArgument(0) } + + Expr getProviderArg() { result = this.getArgument(1) } } /** @@ -75,7 +77,7 @@ module JCAModel { * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`. */ class CipherGetInstanceAlgorithmArg extends Crypto::EncryptionAlgorithmInstance, - Crypto::ModeOfOperationAlgorithmInstance instanceof Expr + Crypto::ModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof Expr { CipherGetInstanceAlgorithmArg() { exists(CipherGetInstanceCall call | this = call.getArgument(0)) @@ -114,14 +116,62 @@ module JCAModel { override Location getLocation() { result = instance.getLocation() } - override string getRawAlgorithmName() { result = instance.getOrigin().getValue() } + // In this case, the raw name is still only the /MODE/ part. + // TODO: handle defaults + override string getRawAlgorithmName() { result = instance.getOrigin().getMode() } - predicate modeToNameMapping(Crypto::TModeOperationType type, string name) { - super.modeToNameMapping(type, name) + private predicate modeToNameMappingKnown(Crypto::TModeOperationType type, string name) { + type instanceof Crypto::ECB and name = "ECB" + or + type instanceof Crypto::CBC and name = "CBC" + or + type instanceof Crypto::GCM and name = "GCM" + or + type instanceof Crypto::CTR and name = "CTR" + or + type instanceof Crypto::XTS and name = "XTS" + or + type instanceof Crypto::CCM and name = "CCM" + or + type instanceof Crypto::SIV and name = "SIV" + or + type instanceof Crypto::OCB and name = "OCB" } override Crypto::TModeOperationType getModeType() { - this.modeToNameMapping(result, instance.getOrigin().getMode()) + if this.modeToNameMappingKnown(_, instance.getOrigin().getMode()) + then this.modeToNameMappingKnown(result, instance.getOrigin().getMode()) + else result instanceof Crypto::OtherMode + } + + CipherStringLiteral getInstance() { result = instance } + } + + class PaddingAlgorithm extends Crypto::PaddingAlgorithm { + CipherGetInstanceAlgorithmArg instance; + + PaddingAlgorithm() { + this = Crypto::TPaddingAlgorithm(instance) and + exists(instance.getOrigin().getPadding()) + } + + override Location getLocation() { result = instance.getLocation() } + + override string getRawAlgorithmName() { result = instance.getOrigin().getPadding() } + + bindingset[name] + private predicate paddingToNameMappingKnown(Crypto::TPaddingType type, string name) { + type instanceof Crypto::NoPadding and name = "NOPADDING" + or + type instanceof Crypto::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? + or + type instanceof Crypto::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% + } + + override Crypto::TPaddingType getPaddingType() { + if this.paddingToNameMappingKnown(_, instance.getOrigin().getPadding()) + then this.paddingToNameMappingKnown(result, instance.getOrigin().getPadding()) + else result instanceof Crypto::OtherPadding } CipherStringLiteral getInstance() { result = instance } @@ -142,6 +192,10 @@ module JCAModel { result.(ModeOfOperation).getInstance() = origin } + override Crypto::PaddingAlgorithm getPadding() { + result.(PaddingAlgorithm).getInstance() = origin + } + override Crypto::LocatableElement getOrigin(string name) { result = origin and name = origin.toString() } @@ -149,7 +203,9 @@ module JCAModel { override string getRawAlgorithmName() { result = origin.getValue() } override Crypto::TCipherType getCipherFamily() { - this.cipherNameMapping(result, origin.getAlgorithmName()) + if this.cipherNameMappingKnown(_, origin.getAlgorithmName()) + then this.cipherNameMappingKnown(result, origin.getAlgorithmName()) + else result instanceof Crypto::OtherSymmetricCipherType } override string getKeySize(Location location) { none() } @@ -159,18 +215,83 @@ module JCAModel { name = "AES" and type instanceof Crypto::AES or + name = "DES" and + type instanceof Crypto::DES + or + name = "TripleDES" and + type instanceof Crypto::TripleDES + or + name = "IDEA" and + type instanceof Crypto::IDEA + or + name = "CAST5" and + type instanceof Crypto::CAST5 + or + name = "ChaCha20" and + type instanceof Crypto::ChaCha20 + or name = "RC4" and type instanceof Crypto::RC4 - // or - // TODO + or + name = "RC5" and + type instanceof Crypto::RC5 + or + name = "RSA" and + type instanceof Crypto::RSA } + } - bindingset[name] - predicate cipherNameMapping(Crypto::TCipherType type, string name) { - this.cipherNameMappingKnown(type, name) - or - not this.cipherNameMappingKnown(_, name) and - type instanceof Crypto::OtherSymmetricCipherType + /** + * Initialiation vectors + */ + abstract class IVParameterInstantiation extends ClassInstanceExpr { + abstract Expr getIV(); + } + + class IvParameterSpecInstance extends IVParameterInstantiation { + IvParameterSpecInstance() { + this.getConstructedType().hasQualifiedName("javax.crypto.spec", "IvParameterSpec") + } + + override Expr getIV() { result = super.getArgument(0) } + } + + class GCMParameterSpecInstance extends IVParameterInstantiation { + GCMParameterSpecInstance() { + this.getConstructedType().hasQualifiedName("javax.crypto.spec", "GCMParameterSpec") } + + override Expr getIV() { result = super.getArgument(1) } + } + + class CipherInitCall extends MethodCall { + CipherInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "init") } + + Expr getModeArg() { result = this.getArgument(0) } + + Expr getKey() { + result = this.getArgument(1) and this.getMethod().getParameterType(1).hasName("Key") + } + + Expr getIV() { + result = this.getArgument(2) and + this.getMethod().getParameterType(2).hasName("AlgorithmParameterSpec") + } + } + + // TODO: cipher.getParameters().getParameterSpec(GCMParameterSpec.class); + class InitializationVectorExpr extends Crypto::InitializationVectorArtifactInstance instanceof Expr + { + CipherInitCall call; // TODO: add origin to known sources (e.g. RNG, etc.) + + InitializationVectorExpr() { this = call.getIV() } + } + + class InitializationVector extends Crypto::InitializationVector { + InitializationVectorExpr instance; + + InitializationVector() { this = Crypto::TInitializationVector(instance) } + + override Location getLocation() { result = instance.getLocation() } } } diff --git a/java/ql/src/experimental/Quantum/Test.ql b/java/ql/src/experimental/Quantum/Test.ql index 5496e5a70cf9..ba76213132c3 100644 --- a/java/ql/src/experimental/Quantum/Test.ql +++ b/java/ql/src/experimental/Quantum/Test.ql @@ -4,6 +4,6 @@ import experimental.Quantum.Language -from Crypto::EncryptionAlgorithm a, Crypto::ModeOfOperationAlgorithm mode -where a.getModeOfOperation() = mode -select a, a.getAlgorithmName(), a.getRawAlgorithmName(), mode, mode.getAlgorithmName() +from Crypto::EncryptionAlgorithm a, Crypto::ModeOfOperationAlgorithm m, Crypto::PaddingAlgorithm p +where m = a.getModeOfOperation() and p = a.getPadding() +select a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), p, p.getRawAlgorithmName() diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 38ea6e475c05..9814f1519950 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -115,7 +115,7 @@ module CryptographyBase Input> { TPaddingAlgorithm(PaddingAlgorithmInstance e) or // Composite and hybrid cryptosystems (e.g., RSA-OAEP used with AES, post-quantum hybrid cryptosystems) // These nodes are always parent nodes and are not modeled but rather defined via library-agnostic patterns. - TKemDemHybridCryptosystem(EncryptionAlgorithmInstance dem) or + TKemDemHybridCryptosystem(EncryptionAlgorithmInstance dem) or // TODO, change this relation and the below ones TKeyAgreementHybridCryptosystem(EncryptionAlgorithmInstance ka) or TAsymmetricEncryptionMacHybridCryptosystem(EncryptionAlgorithmInstance enc) or TPostQuantumHybridCryptosystem(EncryptionAlgorithmInstance enc) @@ -131,6 +131,11 @@ module CryptographyBase Input> { */ abstract string toString(); + /** + * Returns a string representation of the internal type of this node, usually the name of the class. + */ + abstract string getInternalType(); + /** * Returns the location of this node in the code. */ @@ -169,6 +174,15 @@ module CryptographyBase Input> { class Artifact = NodeBase; + /** + * An initialization vector + */ + abstract class InitializationVector extends Asset, TInitializationVector { + final override string getInternalType() { result = "InitializationVector" } + + final override string toString() { result = this.getInternalType() } + } + /** * A cryptographic operation, such as hashing or encryption. */ @@ -185,6 +199,8 @@ module CryptographyBase Input> { final override string toString() { result = this.getOperationType() } + final override string getInternalType() { result = this.getOperationType() } + override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or @@ -196,6 +212,8 @@ module CryptographyBase Input> { abstract class Algorithm extends Asset { final override string toString() { result = this.getAlgorithmType() } + final override string getInternalType() { result = this.getAlgorithmType() } + /** * Gets the name of this algorithm, e.g., "AES" or "SHA". */ @@ -294,7 +312,7 @@ module CryptographyBase Input> { abstract string getSHA2OrSHA3DigestSize(Location location); bindingset[type] - private string type_to_digest_size_fixed(THashType type) { + private string getTypeDigestSizeFixed(THashType type) { type instanceof MD2 and result = "128" or type instanceof MD4 and result = "128" @@ -309,21 +327,25 @@ module CryptographyBase Input> { } bindingset[type] - private string getDigestSize(THashType type, Location location) { - result = this.type_to_digest_size_fixed(type) and location = this.getLocation() + private string getTypeDigestSize(THashType type, Location location) { + result = this.getTypeDigestSizeFixed(type) and location = this.getLocation() or type instanceof SHA2 and result = this.getSHA2OrSHA3DigestSize(location) or type instanceof SHA3 and result = this.getSHA2OrSHA3DigestSize(location) } + string getDigestSize(Location location) { + result = this.getTypeDigestSize(this.getHashType(), location) + } + final override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or // [KNOWN_OR_UNKNOWN] key = "digest_size" and - if exists(this.getDigestSize(this.getHashType(), location)) - then value = this.getDigestSize(this.getHashType(), location) + if exists(this.getDigestSize(location)) + then value = this.getDigestSize(location) else ( value instanceof UnknownPropertyValue and location instanceof UnknownLocation ) @@ -619,27 +641,33 @@ module CryptographyBase Input> { /** * An encryption operation that processes plaintext to generate a ciphertext. - * This operation takes an input message (plaintext) of arbitrary content and length and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). + * This operation takes an input message (plaintext) of arbitrary content and length + * and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). */ abstract class EncryptionOperation extends Operation, TEncryptionOperation { - abstract override Algorithm getAlgorithm(); - override string getOperationType() { result = "EncryptionOperation" } + + /** + * Gets the initialization vector associated with this encryption operation. + * + * This predicate does not need to hold for all encryption operations, + * as the initialization vector is not always required. + */ + abstract InitializationVector getInitializationVector(); } /** * Block cipher modes of operation algorithms */ newtype TModeOperationType = - ECB() or - CBC() or - CFB() or - OFB() or - CTR() or - GCM() or - CCM() or - XTS() or - OAEP() or + ECB() or // Not secure, widely used + CBC() or // Vulnerable to padding oracle attacks + GCM() or // Widely used AEAD mode (TLS 1.3, SSH, IPsec) + CTR() or // Fast stream-like encryption (SSH, disk encryption) + XTS() or // Standard for full-disk encryption (BitLocker, LUKS, FileVault) + CCM() or // Used in lightweight cryptography (IoT, WPA2) + SIV() or // Misuse-resistant encryption, used in secure storage + OCB() or // Efficient AEAD mode OtherMode() abstract class ModeOfOperationAlgorithm extends Algorithm, TModeOfOperationAlgorithm { @@ -655,22 +683,22 @@ module CryptographyBase Input> { abstract TModeOperationType getModeType(); bindingset[type] - final predicate modeToNameMapping(TModeOperationType type, string name) { + final private predicate modeToNameMapping(TModeOperationType type, string name) { type instanceof ECB and name = "ECB" or type instanceof CBC and name = "CBC" or - type instanceof CFB and name = "CFB" - or - type instanceof OFB and name = "OFB" + type instanceof GCM and name = "GCM" or type instanceof CTR and name = "CTR" or - type instanceof GCM and name = "GCM" + type instanceof XTS and name = "XTS" or type instanceof CCM and name = "CCM" or - type instanceof XTS and name = "XTS" + type instanceof SIV and name = "SIV" + or + type instanceof OCB and name = "OCB" or type instanceof OtherMode and name = this.getRawAlgorithmName() } @@ -678,12 +706,51 @@ module CryptographyBase Input> { override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } } + newtype TPaddingType = + PKCS1_v1_5() or // RSA encryption/signing padding + PKCS7() or // Standard block cipher padding (PKCS5 for 8-byte blocks) + ANSI_X9_23() or // Zero-padding except last byte = padding length + NoPadding() or // Explicit no-padding + OAEP() or // RSA OAEP padding + OtherPadding() + + abstract class PaddingAlgorithm extends Algorithm, TPaddingAlgorithm { + override string getAlgorithmType() { result = "PaddingAlgorithm" } + + /** + * Gets the type of this padding algorithm, e.g., "PKCS7" or "OAEP". + * + * When modeling a new padding algorithm, use this predicate to specify the type of the padding. + * + * If a type cannot be determined, the result is `OtherPadding`. + */ + abstract TPaddingType getPaddingType(); + + bindingset[type] + final private predicate paddingToNameMapping(TPaddingType type, string name) { + type instanceof PKCS1_v1_5 and name = "PKCS1_v1_5" + or + type instanceof PKCS7 and name = "PKCS7" + or + type instanceof ANSI_X9_23 and name = "ANSI_X9_23" + or + type instanceof NoPadding and name = "NoPadding" + or + type instanceof OAEP and name = "OAEP" + or + type instanceof OtherPadding and name = this.getRawAlgorithmName() + } + + override string getAlgorithmName() { this.paddingToNameMapping(this.getPaddingType(), result) } + } + /** * A helper type for distinguishing between block and stream ciphers. */ newtype TCipherStructureType = Block() or Stream() or + Asymmetric() or UnknownCipherStructureType() private string getCipherStructureTypeString(TCipherStructureType type) { @@ -691,6 +758,8 @@ module CryptographyBase Input> { or type instanceof Stream and result = "Stream" or + type instanceof Asymmetric and result = "Asymmetric" + or type instanceof UnknownCipherStructureType and result instanceof UnknownPropertyValue } @@ -738,6 +807,11 @@ module CryptographyBase Input> { */ abstract ModeOfOperationAlgorithm getModeOfOperation(); + /** + * Gets the padding scheme of this cipher, e.g., "PKCS7" or "NoPadding". + */ + abstract PaddingAlgorithm getPadding(); + bindingset[type] final private predicate cipherFamilyToNameAndStructure( TCipherType type, string name, TCipherStructureType s @@ -760,7 +834,7 @@ module CryptographyBase Input> { or type instanceof RC5 and name = "RC5" and s = Block() or - type instanceof RSA and name = "RSA" and s = Block() + type instanceof RSA and name = "RSA" and s = Asymmetric() or type instanceof OtherSymmetricCipherType and name = this.getRawAlgorithmName() and From 8707e4d9a394aea6081fbcf52dffc0d29e0c6e8b Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Tue, 18 Feb 2025 18:35:49 +0100 Subject: [PATCH 020/122] Continue Artifact data-flow WIP --- java/ql/lib/experimental/Quantum/JCA.qll | 37 +++++---- java/ql/lib/experimental/Quantum/Language.qll | 76 ++++++++++++++++--- .../codeql/cryptography/Model.qll | 62 ++++++++++++--- 3 files changed, 141 insertions(+), 34 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 5a30e96b13ca..e961310334f9 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -244,24 +244,29 @@ module JCAModel { /** * Initialiation vectors */ - abstract class IVParameterInstantiation extends ClassInstanceExpr { - abstract Expr getIV(); + abstract class IVParameterInstantiation extends Crypto::InitializationVectorArtifactInstance instanceof ClassInstanceExpr + { + abstract Expr getInput(); } class IvParameterSpecInstance extends IVParameterInstantiation { IvParameterSpecInstance() { - this.getConstructedType().hasQualifiedName("javax.crypto.spec", "IvParameterSpec") + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("javax.crypto.spec", "IvParameterSpec") } - override Expr getIV() { result = super.getArgument(0) } + override Expr getInput() { result = this.(ClassInstanceExpr).getArgument(0) } } class GCMParameterSpecInstance extends IVParameterInstantiation { GCMParameterSpecInstance() { - this.getConstructedType().hasQualifiedName("javax.crypto.spec", "GCMParameterSpec") + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("javax.crypto.spec", "GCMParameterSpec") } - override Expr getIV() { result = super.getArgument(1) } + override Expr getInput() { result = this.(ClassInstanceExpr).getArgument(1) } } class CipherInitCall extends MethodCall { @@ -280,18 +285,24 @@ module JCAModel { } // TODO: cipher.getParameters().getParameterSpec(GCMParameterSpec.class); - class InitializationVectorExpr extends Crypto::InitializationVectorArtifactInstance instanceof Expr - { - CipherInitCall call; // TODO: add origin to known sources (e.g. RNG, etc.) - - InitializationVectorExpr() { this = call.getIV() } - } + /* + * class InitializationVectorArg extends Crypto::InitializationVectorArtifactInstance instanceof Expr + * { + * IVParameterInstantiation creation; + * + * InitializationVectorArg() { this = creation.getInput() } + * } + */ class InitializationVector extends Crypto::InitializationVector { - InitializationVectorExpr instance; + IVParameterInstantiation instance; InitializationVector() { this = Crypto::TInitializationVector(instance) } override Location getLocation() { result = instance.getLocation() } + + override Crypto::DataFlowNode asOutputData() { result.asExpr() = instance } + + override Crypto::DataFlowNode getInputData() { result.asExpr() = instance.getInput() } } } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 485b3e716e69..5801815109b0 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -1,5 +1,11 @@ private import codeql.cryptography.Model -private import java as Lang +private import java as Language +private import semmle.code.java.security.InsecureRandomnessQuery +private import semmle.code.java.security.RandomQuery + +private class UnknownLocation extends Language::Location { + UnknownLocation() { this.getFile().getAbsolutePath() = "" } +} /** * A dummy location which is used when something doesn't have a location in @@ -7,24 +13,72 @@ private import java as Lang * may be several distinct kinds of unknown locations. For example: one for * expressions, one for statements and one for other program elements. */ -class UnknownLocation extends Location { - UnknownLocation() { this.getFile().getAbsolutePath() = "" } +private class UnknownDefaultLocation extends UnknownLocation { + UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) } +} + +module CryptoInput implements InputSig { + class DataFlowNode = DataFlow::Node; + + class LocatableElement = Language::Element; + + class UnknownLocation = UnknownDefaultLocation; + + predicate rngToIvFlow(DataFlowNode rng, DataFlowNode iv) { none() } } /** - * 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. + * Instantiate the model */ -class UnknownDefaultLocation extends UnknownLocation { - UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) } +module Crypto = CryptographyBase; + +/** + * Random number generation, where each instance is modelled as the expression + * tied to an output node (i.e., the result of the source of randomness) + */ +abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance { + abstract Crypto::RNGSourceSecurity getSourceSecurity(); + + Crypto::TRNGSeedSecurity getSeedSecurity(Location location) { none() } } -module CryptoInput implements InputSig { - class LocatableElement = Lang::Element; +class SecureRandomnessInstance extends RandomnessInstance { + SecureRandomnessInstance() { + exists(RandomDataSource s | this = s.getOutput() | + s.getSourceOfRandomness() instanceof SecureRandomNumberGenerator + ) + } - class UnknownLocation = UnknownDefaultLocation; + override Crypto::RNGSourceSecurity getSourceSecurity() { + result instanceof Crypto::RNGSourceSecure + } +} + +class InsecureRandomnessInstance extends RandomnessInstance { + InsecureRandomnessInstance() { exists(InsecureRandomnessSource node | this = node.asExpr()) } + + override Crypto::RNGSourceSecurity getSourceSecurity() { + result instanceof Crypto::RNGSourceInsecure + } } -module Crypto = CryptographyBase; +class RandomnessArtifact extends Crypto::RandomNumberGeneration { + RandomnessInstance instance; + + RandomnessArtifact() { this = Crypto::TRandomNumberGeneration(instance) } + + override Location getLocation() { result = instance.getLocation() } + + override Crypto::RNGSourceSecurity getSourceSecurity() { result = instance.getSourceSecurity() } + + override Crypto::TRNGSeedSecurity getSeedSecurity(Location location) { + result = instance.getSeedSecurity(location) + } + + override Crypto::DataFlowNode asOutputData() { result.asExpr() = instance } + + override Crypto::DataFlowNode getInputData() { none() } +} +// Import library-specific modeling import JCA diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 9814f1519950..205400e8a3ca 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -12,7 +12,15 @@ signature module InputSig { string toString(); } + class DataFlowNode { + Location getLocation(); + + string toString(); + } + class UnknownLocation instanceof Location; + + predicate rngToIvFlow(DataFlowNode rng, DataFlowNode iv); } module CryptographyBase Input> { @@ -20,6 +28,8 @@ module CryptographyBase Input> { final class UnknownLocation = Input::UnknownLocation; + final class DataFlowNode = Input::DataFlowNode; + final class UnknownPropertyValue extends string { UnknownPropertyValue() { this = "" } } @@ -93,12 +103,15 @@ module CryptographyBase Input> { abstract class NonceArtifactInstance extends LocatableElement { } + abstract class RandomNumberGenerationInstance extends LocatableElement { } + newtype TNode = // Artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or TInitializationVector(InitializationVectorArtifactInstance e) or TNonce(NonceArtifactInstance e) or + TRandomNumberGeneration(RandomNumberGenerationInstance e) or // Operations (e.g., hashing, encryption) THashOperation(HashOperationInstance e) or TKeyDerivationOperation(KeyDerivationOperationInstance e) or @@ -115,7 +128,7 @@ module CryptographyBase Input> { TPaddingAlgorithm(PaddingAlgorithmInstance e) or // Composite and hybrid cryptosystems (e.g., RSA-OAEP used with AES, post-quantum hybrid cryptosystems) // These nodes are always parent nodes and are not modeled but rather defined via library-agnostic patterns. - TKemDemHybridCryptosystem(EncryptionAlgorithmInstance dem) or // TODO, change this relation and the below ones + TKemDemHybridCryptosystem(EncryptionAlgorithm dem) or // TODO, change this relation and the below ones TKeyAgreementHybridCryptosystem(EncryptionAlgorithmInstance ka) or TAsymmetricEncryptionMacHybridCryptosystem(EncryptionAlgorithmInstance enc) or TPostQuantumHybridCryptosystem(EncryptionAlgorithmInstance enc) @@ -127,9 +140,9 @@ module CryptographyBase Input> { */ abstract class NodeBase extends TNode { /** - * Returns a string representation of this node, usually the name of the operation/algorithm/property. + * Returns a string representation of this node. */ - abstract string toString(); + string toString() { result = this.getInternalType() } /** * Returns a string representation of the internal type of this node, usually the name of the class. @@ -172,15 +185,48 @@ module CryptographyBase Input> { class Asset = NodeBase; - class Artifact = NodeBase; + abstract class Artifact extends NodeBase { + abstract DataFlowNode asOutputData(); + + abstract DataFlowNode getInputData(); + } /** * An initialization vector */ - abstract class InitializationVector extends Asset, TInitializationVector { + abstract class InitializationVector extends Artifact, TInitializationVector { final override string getInternalType() { result = "InitializationVector" } - final override string toString() { result = this.getInternalType() } + RandomNumberGeneration getRNGSource() { + Input::rngToIvFlow(result.asOutputData(), this.getInputData()) + } + } + + newtype TRNGSourceSecurity = + RNGSourceSecure() or // Secure RNG source (unrelated to seed) + RNGSourceInsecure() // Insecure RNG source (unrelated to seed) + + class RNGSourceSecurity extends TRNGSourceSecurity { + string toString() { + this instanceof RNGSourceSecure and result = "Secure RNG Source" + or + this instanceof RNGSourceInsecure and result = "Insecure RNG Source" + } + } + + newtype TRNGSeedSecurity = + RNGSeedSecure() or + RNGSeedInsecure() + + /** + * A source of random number generation + */ + abstract class RandomNumberGeneration extends Artifact, TRandomNumberGeneration { + final override string getInternalType() { result = "RandomNumberGeneration" } + + abstract RNGSourceSecurity getSourceSecurity(); + + abstract TRNGSeedSecurity getSeedSecurity(Location location); } /** @@ -197,8 +243,6 @@ module CryptographyBase Input> { */ abstract string getOperationType(); - final override string toString() { result = this.getOperationType() } - final override string getInternalType() { result = this.getOperationType() } override NodeBase getChild(string edgeName) { @@ -210,8 +254,6 @@ module CryptographyBase Input> { } abstract class Algorithm extends Asset { - final override string toString() { result = this.getAlgorithmType() } - final override string getInternalType() { result = this.getAlgorithmType() } /** From 3871c6a33ed3e3c0399de39b389e6cb7712eea64 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 18 Feb 2025 16:09:00 -0500 Subject: [PATCH 021/122] Adding support for encryption operation detection. --- java/ql/lib/experimental/Quantum/JCA.qll | 57 +++++++++++++++++++ .../codeql/cryptography/Model.qll | 15 ++--- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index e961310334f9..5b55090bd9e2 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -58,6 +58,10 @@ module JCAModel { Expr getProviderArg() { result = this.getArgument(1) } } + class CipherDoFinalCall extends Call { + CipherDoFinalCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "doFinal") } + } + /** * Data-flow configuration modelling flow from a cipher string literal to a `CipherGetInstanceCall` argument. */ @@ -92,6 +96,57 @@ module JCAModel { } } + // TODO: what if encrypt/decrypt mode isn't known + private module CipherGetInstanceToFinalizeConfig implements DataFlow::StateConfigSig { + class FlowState = string; + + predicate isSource(DataFlow::Node src, FlowState state) { + state = "UNKNOWN" and + src.asExpr() instanceof CipherGetInstanceCall + } + + predicate isSink(DataFlow::Node sink, FlowState state) { + state in ["ENCRYPT", "DECRYPT", "UNKNOWN"] and + exists(CipherDoFinalCall c | c.getQualifier() = sink.asExpr()) + } + + predicate isAdditionalFlowStep( + DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 + ) { + state1 in ["UNKNOWN", "ENCRYPT", "DECRYPT"] and + exists(CipherInitCall c | + c.getQualifier() = node1.asExpr() and + // TODO: not taking into consideration if the mode traces to this arg + exists(FieldAccess fa | + c.getModeArg() = fa and + ( + fa.getField().getName() = "ENCRYPT_MODE" and + state2 = "ENCRYPT" + or + fa.getField().getName() = "DECRYPT_MODE" and + state2 = "DECRYPT" + ) + ) + ) and + node2 = node1 + } + } + + module CipherGetInstanceToFinalizeFlow = + DataFlow::GlobalWithState; + + // TODO: what if the mode is UNKNOWN? + class CipherEncryptionOperation extends Crypto::EncryptionOperationInstance instanceof Call { + CipherEncryptionOperation() { + exists(CipherGetInstanceToFinalizeFlow::PathNode sink, CipherDoFinalCall c | + CipherGetInstanceToFinalizeFlow::flowPath(_, sink) and + sink.getNode().asExpr() = c.getQualifier() and + sink.getState() = "ENCRYPT" and + this = c + ) + } + } + /** * A block cipher mode of operation, where the mode is specified in the ALG or ALG/MODE/PADDING format. * @@ -272,6 +327,8 @@ module JCAModel { class CipherInitCall extends MethodCall { CipherInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "init") } + // TODO: this doesn't account for tracing the mode to this arg if expending this arg to have + // the actual mode directly Expr getModeArg() { result = this.getArgument(0) } Expr getKey() { diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 205400e8a3ca..8a5011489a67 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -689,13 +689,14 @@ module CryptographyBase Input> { abstract class EncryptionOperation extends Operation, TEncryptionOperation { override string getOperationType() { result = "EncryptionOperation" } - /** - * Gets the initialization vector associated with this encryption operation. - * - * This predicate does not need to hold for all encryption operations, - * as the initialization vector is not always required. - */ - abstract InitializationVector getInitializationVector(); + abstract override EncryptionAlgorithm getAlgorithm(); + // /** + // * Gets the initialization vector associated with this encryption operation. + // * + // * This predicate does not need to hold for all encryption operations, + // * as the initialization vector is not always required. + // */ + // abstract InitializationVector getInitializationVector(); } /** From 9ee4a7a7b8798efb33b18271527e5dd167d9bc94 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Thu, 20 Feb 2025 10:37:40 -0500 Subject: [PATCH 022/122] Adding a sketch for a CipherOperation concept to model encryption/decryption operations. --- java/ql/lib/experimental/Quantum/JCA.qll | 51 +++++++++++++------ .../codeql/cryptography/Model.qll | 51 +++++++++++++------ 2 files changed, 70 insertions(+), 32 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 5b55090bd9e2..0f505e400a06 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -83,9 +83,9 @@ module JCAModel { class CipherGetInstanceAlgorithmArg extends Crypto::EncryptionAlgorithmInstance, Crypto::ModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof Expr { - CipherGetInstanceAlgorithmArg() { - exists(CipherGetInstanceCall call | this = call.getArgument(0)) - } + CipherGetInstanceCall call; + + CipherGetInstanceAlgorithmArg() { this = call.getAlgorithmArg() } /** * Returns the `StringLiteral` from which this argument is derived, if known. @@ -94,26 +94,26 @@ module JCAModel { AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), DataFlow::exprNode(this.(Expr).getAChildExpr*())) } + + CipherGetInstanceCall getCall() { result = call } } // TODO: what if encrypt/decrypt mode isn't known private module CipherGetInstanceToFinalizeConfig implements DataFlow::StateConfigSig { - class FlowState = string; + class FlowState = Crypto::TCipherOperationMode; predicate isSource(DataFlow::Node src, FlowState state) { - state = "UNKNOWN" and + state = Crypto::UnknownCipherOperationMode() and src.asExpr() instanceof CipherGetInstanceCall } predicate isSink(DataFlow::Node sink, FlowState state) { - state in ["ENCRYPT", "DECRYPT", "UNKNOWN"] and exists(CipherDoFinalCall c | c.getQualifier() = sink.asExpr()) } predicate isAdditionalFlowStep( DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 ) { - state1 in ["UNKNOWN", "ENCRYPT", "DECRYPT"] and exists(CipherInitCall c | c.getQualifier() = node1.asExpr() and // TODO: not taking into consideration if the mode traces to this arg @@ -121,10 +121,16 @@ module JCAModel { c.getModeArg() = fa and ( fa.getField().getName() = "ENCRYPT_MODE" and - state2 = "ENCRYPT" + state2 = Crypto::EncryptionMode() or fa.getField().getName() = "DECRYPT_MODE" and - state2 = "DECRYPT" + state2 = Crypto::DecryptionMode() + or + fa.getField().getName() = "WRAP_MODE" and + state2 = Crypto::EncryptionMode() + or + fa.getField().getName() = "UNWRAP_MODE" and + state2 = Crypto::DecryptionMode() ) ) ) and @@ -135,16 +141,29 @@ module JCAModel { module CipherGetInstanceToFinalizeFlow = DataFlow::GlobalWithState; - // TODO: what if the mode is UNKNOWN? - class CipherEncryptionOperation extends Crypto::EncryptionOperationInstance instanceof Call { + class CipherEncryptionOperation extends Crypto::CipherOperationInstance instanceof Call { + Crypto::TCipherOperationMode mode; + Crypto::EncryptionAlgorithmInstance algorithm; + CipherEncryptionOperation() { - exists(CipherGetInstanceToFinalizeFlow::PathNode sink, CipherDoFinalCall c | - CipherGetInstanceToFinalizeFlow::flowPath(_, sink) and - sink.getNode().asExpr() = c.getQualifier() and - sink.getState() = "ENCRYPT" and - this = c + exists( + CipherGetInstanceToFinalizeFlow::PathNode sink, + CipherGetInstanceToFinalizeFlow::PathNode src, CipherGetInstanceCall getCipher, + CipherDoFinalCall doFinalize, CipherGetInstanceAlgorithmArg arg + | + CipherGetInstanceToFinalizeFlow::flowPath(src, sink) and + src.getNode().asExpr() = getCipher and + sink.getNode().asExpr() = doFinalize.getQualifier() and + sink.getState() = mode and + this = doFinalize and + arg.getCall() = getCipher and + algorithm = arg ) } + + override Crypto::EncryptionAlgorithmInstance getAlgorithm() { result = algorithm } + + override Crypto::TCipherOperationMode getCipherOperationMode() { result = mode } } /** diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 8a5011489a67..24e3d4f48ebd 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -79,7 +79,11 @@ module CryptographyBase Input> { abstract class KeyDerivationAlgorithmInstance extends LocatableElement { } - abstract class EncryptionOperationInstance extends LocatableElement { } + abstract class CipherOperationInstance extends LocatableElement { + abstract EncryptionAlgorithmInstance getAlgorithm(); + + abstract TCipherOperationMode getCipherOperationMode(); + } abstract class EncryptionAlgorithmInstance extends LocatableElement { } @@ -115,7 +119,7 @@ module CryptographyBase Input> { // Operations (e.g., hashing, encryption) THashOperation(HashOperationInstance e) or TKeyDerivationOperation(KeyDerivationOperationInstance e) or - TEncryptionOperation(EncryptionOperationInstance e) or + TCipherOperation(CipherOperationInstance e) or TKeyEncapsulationOperation(KeyEncapsulationOperationInstance e) or // Algorithms (e.g., SHA-256, AES) TEncryptionAlgorithm(EncryptionAlgorithmInstance e) or @@ -238,13 +242,14 @@ module CryptographyBase Input> { */ abstract Algorithm getAlgorithm(); - /** - * Gets the name of this operation, e.g., "hash" or "encrypt". - */ - abstract string getOperationType(); - - final override string getInternalType() { result = this.getOperationType() } - + // TODO: I only removed this because I want the operation type to be non-string + // since for CipherOperations the user will have to pick the right type, + // and I want to force them to use a type that is restricted. In this case to a TCipherOperationType + // /** + // * Gets the name of this operation, e.g., "hash" or "encrypt". + // */ + // abstract string getOperationType(); + // final override string getInternalType() { result = this.getOperationType() } override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or @@ -290,8 +295,7 @@ module CryptographyBase Input> { */ abstract class HashOperation extends Operation, THashOperation { abstract override HashAlgorithm getAlgorithm(); - - override string getOperationType() { result = "HashOperation" } + //override string getOperationType() { result = "HashOperation" } } newtype THashType = @@ -401,8 +405,7 @@ module CryptographyBase Input> { final override Location getLocation() { exists(LocatableElement le | this = TKeyDerivationOperation(le) and result = le.getLocation()) } - - override string getOperationType() { result = "KeyDerivationOperation" } + //override string getOperationType() { result = "KeyDerivationOperation" } } /** @@ -681,15 +684,31 @@ module CryptographyBase Input> { abstract override string getRawAlgorithmName(); } + newtype TCipherOperationMode = + EncryptionMode() or + DecryptionMode() or + UnknownCipherOperationMode() + /** * An encryption operation that processes plaintext to generate a ciphertext. * This operation takes an input message (plaintext) of arbitrary content and length * and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). */ - abstract class EncryptionOperation extends Operation, TEncryptionOperation { - override string getOperationType() { result = "EncryptionOperation" } + // NOTE FOR NICK: making this concrete here as I don't think users need to worry about making/extending these operations, just instances + class CipherOperation extends Operation, TCipherOperation { + CipherOperationInstance instance; + + CipherOperation() { this = TCipherOperation(instance) } + + override Location getLocation() { result = instance.getLocation() } + + final TCipherOperationMode getCipherOperationMode() { + result = instance.getCipherOperationMode() + } + + final override EncryptionAlgorithm getAlgorithm() { result = instance.getAlgorithm() } - abstract override EncryptionAlgorithm getAlgorithm(); + override string getInternalType() { result = "CipherOperation" } // /** // * Gets the initialization vector associated with this encryption operation. // * From 83dc5b99065f8d4482899bb05d8736e8d6e0d13d Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Thu, 20 Feb 2025 10:45:33 -0500 Subject: [PATCH 023/122] Fixing type bug --- shared/cryptography/codeql/cryptography/Model.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 24e3d4f48ebd..17834343e6d5 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -706,7 +706,7 @@ module CryptographyBase Input> { result = instance.getCipherOperationMode() } - final override EncryptionAlgorithm getAlgorithm() { result = instance.getAlgorithm() } + final override EncryptionAlgorithm getAlgorithm() { result.getInstance() = instance.getAlgorithm() } override string getInternalType() { result = "CipherOperation" } // /** From 011ed3fbfd11a6cd7ab5b8aa13c3945881a37faf Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Thu, 20 Feb 2025 11:10:24 -0500 Subject: [PATCH 024/122] Simplifying additional flow step logic. --- java/ql/lib/experimental/Quantum/JCA.qll | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 0f505e400a06..0a7903aa2722 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -120,17 +120,13 @@ module JCAModel { exists(FieldAccess fa | c.getModeArg() = fa and ( - fa.getField().getName() = "ENCRYPT_MODE" and - state2 = Crypto::EncryptionMode() - or - fa.getField().getName() = "DECRYPT_MODE" and - state2 = Crypto::DecryptionMode() - or - fa.getField().getName() = "WRAP_MODE" and - state2 = Crypto::EncryptionMode() - or - fa.getField().getName() = "UNWRAP_MODE" and - state2 = Crypto::DecryptionMode() + if fa.getField().getName() in ["ENCRYPT_MODE", "WRAP_MODE"] + then state2 = Crypto::EncryptionMode() + else ( + if fa.getField().getName() in ["DECRYPT_MODE", "UNWRAP_MODE"] + then state2 = Crypto::DecryptionMode() + else state2 = Crypto::UnknownCipherOperationMode() + ) ) ) ) and From 9ac9252f7548d1c6f59cd2b4f159ebe57db60aa8 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Thu, 20 Feb 2025 11:11:41 -0500 Subject: [PATCH 025/122] Adding a todo --- java/ql/lib/experimental/Quantum/JCA.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 0a7903aa2722..3c37d1d717e1 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -346,6 +346,8 @@ module JCAModel { // the actual mode directly Expr getModeArg() { result = this.getArgument(0) } + // TODO: need a getModeOrigin + Expr getKey() { result = this.getArgument(1) and this.getMethod().getParameterType(1).hasName("Key") } From 86cab46b8d7bd15213ce40d57cc1fa86b01d0e43 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 21 Feb 2025 12:53:35 -0500 Subject: [PATCH 026/122] Misc. updates to support all JCA cipher operations, including wrap, unwrap and doFinal calls. Corrected pathing for init tracing to detect what mode is being set along a path. Added support for tracing the init operation mode argument to source. Since this involved creating an Operation Mode, changes were also made to make cipher block modes (CBC) more explicit (previously just called mode, but now that term is used for various purposes). --- java/ql/lib/experimental/Quantum/JCA.qll | 193 ++++++++++++++---- .../codeql/cryptography/Model.qll | 41 +++- 2 files changed, 181 insertions(+), 53 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 3c37d1d717e1..acbe6c67888f 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -58,8 +58,12 @@ module JCAModel { Expr getProviderArg() { result = this.getArgument(1) } } - class CipherDoFinalCall extends Call { - CipherDoFinalCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "doFinal") } + private class JCACipherOperationCall extends Call { + JCACipherOperationCall() { + exists(string s | s in ["doFinal", "wrap", "unwrap"] | + this.getCallee().hasQualifiedName("javax.crypto", "Cipher", s) + ) + } } /** @@ -81,7 +85,7 @@ module JCAModel { * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`. */ class CipherGetInstanceAlgorithmArg extends Crypto::EncryptionAlgorithmInstance, - Crypto::ModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof Expr + Crypto::BlockCipherModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof Expr { CipherGetInstanceCall call; @@ -98,68 +102,136 @@ module JCAModel { CipherGetInstanceCall getCall() { result = call } } - // TODO: what if encrypt/decrypt mode isn't known - private module CipherGetInstanceToFinalizeConfig implements DataFlow::StateConfigSig { - class FlowState = Crypto::TCipherOperationMode; + /** + * An access to the `javax.crypto.Cipher` class. + */ + private class CipherAccess extends TypeAccess { + CipherAccess() { this.getType().(Class).hasQualifiedName("javax.crypto", "Cipher") } + } + + /** + * An access to a cipher mode field of the `javax.crypto.Cipher` class, + * specifically `ENCRYPT_MODE`, `DECRYPT_MODE`, `WRAP_MODE`, or `UNWRAP_MODE`. + */ + private class JavaxCryptoCipherOperationModeAccess extends FieldAccess { + JavaxCryptoCipherOperationModeAccess() { + this.getQualifier() instanceof CipherAccess and + this.getField().getName() in ["ENCRYPT_MODE", "DECRYPT_MODE", "WRAP_MODE", "UNWRAP_MODE"] + } + } + + private newtype TCipherModeFlowState = + TUninitializedCipherModeFlowState() or + TInitializedCipherModeFlowState(CipherInitCall call) + + abstract private class CipherModeFlowState extends TCipherModeFlowState { + string toString() { + this = TUninitializedCipherModeFlowState() and result = "uninitialized" + or + this = TInitializedCipherModeFlowState(_) and result = "initialized" + } + + abstract Crypto::CipherOperationMode getCipherOperationMode(); + } + + private class UninitializedCipherModeFlowState extends CipherModeFlowState, + TUninitializedCipherModeFlowState + { + override Crypto::CipherOperationMode getCipherOperationMode() { + result instanceof Crypto::UnknownCipherOperationMode + } + } + + private class InitializedCipherModeFlowState extends CipherModeFlowState, + TInitializedCipherModeFlowState + { + CipherInitCall call; + DataFlow::Node node1; + DataFlow::Node node2; + Crypto::CipherOperationMode mode; + + InitializedCipherModeFlowState() { + this = TInitializedCipherModeFlowState(call) and + DataFlow::localFlowStep(node1, node2) and + node2.asExpr() = call.getQualifier() and + // I would imagine this would make this predicate horribly horribly inefficient + // it now binds with anything + not node1.asExpr() = call.getQualifier() and + mode = call.getCipherOperationModeType() + } + + CipherInitCall getCall() { result = call } + + DataFlow::Node getFstNode() { result = node1 } + + /** + * Returns the node *to* which the state-changing step occurs + */ + DataFlow::Node getSndNode() { result = node2 } + + override Crypto::CipherOperationMode getCipherOperationMode() { result = mode } + } + + /** + * Trace to a cryptographic operation, + * specifically `Cipher.doFinal()`, `Cipher.wrap()`, or `Cipher.unwrap()`. + */ + private module CipherGetInstanceToCipherOperationConfig implements DataFlow::StateConfigSig { + class FlowState = TCipherModeFlowState; predicate isSource(DataFlow::Node src, FlowState state) { - state = Crypto::UnknownCipherOperationMode() and + state instanceof UninitializedCipherModeFlowState and src.asExpr() instanceof CipherGetInstanceCall } - predicate isSink(DataFlow::Node sink, FlowState state) { - exists(CipherDoFinalCall c | c.getQualifier() = sink.asExpr()) + predicate isSink(DataFlow::Node sink, FlowState state) { none() } + + predicate isSink(DataFlow::Node sink) { + exists(JCACipherOperationCall c | c.getQualifier() = sink.asExpr()) } predicate isAdditionalFlowStep( DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 ) { - exists(CipherInitCall c | - c.getQualifier() = node1.asExpr() and - // TODO: not taking into consideration if the mode traces to this arg - exists(FieldAccess fa | - c.getModeArg() = fa and - ( - if fa.getField().getName() in ["ENCRYPT_MODE", "WRAP_MODE"] - then state2 = Crypto::EncryptionMode() - else ( - if fa.getField().getName() in ["DECRYPT_MODE", "UNWRAP_MODE"] - then state2 = Crypto::DecryptionMode() - else state2 = Crypto::UnknownCipherOperationMode() - ) - ) - ) - ) and - node2 = node1 + node1 = state2.(InitializedCipherModeFlowState).getFstNode() and + node2 = state2.(InitializedCipherModeFlowState).getSndNode() + } + + predicate isBarrier(DataFlow::Node node, FlowState state) { + exists(CipherInitCall call | node.asExpr() = call.getQualifier() | + state instanceof UninitializedCipherModeFlowState + or + state.(InitializedCipherModeFlowState).getCall() != call + ) } } - module CipherGetInstanceToFinalizeFlow = - DataFlow::GlobalWithState; + module CipherGetInstanceToCipherOperationFlow = + DataFlow::GlobalWithState; class CipherEncryptionOperation extends Crypto::CipherOperationInstance instanceof Call { - Crypto::TCipherOperationMode mode; + Crypto::CipherOperationMode mode; Crypto::EncryptionAlgorithmInstance algorithm; CipherEncryptionOperation() { exists( - CipherGetInstanceToFinalizeFlow::PathNode sink, - CipherGetInstanceToFinalizeFlow::PathNode src, CipherGetInstanceCall getCipher, - CipherDoFinalCall doFinalize, CipherGetInstanceAlgorithmArg arg + CipherGetInstanceToCipherOperationFlow::PathNode sink, + CipherGetInstanceToCipherOperationFlow::PathNode src, CipherGetInstanceCall getCipher, + JCACipherOperationCall doFinalize, CipherGetInstanceAlgorithmArg arg | - CipherGetInstanceToFinalizeFlow::flowPath(src, sink) and + CipherGetInstanceToCipherOperationFlow::flowPath(src, sink) and src.getNode().asExpr() = getCipher and sink.getNode().asExpr() = doFinalize.getQualifier() and - sink.getState() = mode and + sink.getState().(CipherModeFlowState).getCipherOperationMode() = mode and this = doFinalize and - arg.getCall() = getCipher and + arg.getCall() = getCipher and algorithm = arg ) } override Crypto::EncryptionAlgorithmInstance getAlgorithm() { result = algorithm } - override Crypto::TCipherOperationMode getCipherOperationMode() { result = mode } + override Crypto::CipherOperationMode getCipherOperationMode() { result = mode } } /** @@ -177,7 +249,7 @@ module JCAModel { CipherGetInstanceAlgorithmArg instance; ModeOfOperation() { - this = Crypto::TModeOfOperationAlgorithm(instance) and + this = Crypto::TBlockCipherModeOfOperationAlgorithm(instance) and // TODO: this currently only holds for explicitly defined modes in a string literal. // Cases with defaults, e.g., "AES", are not yet modelled. // For these cases, in a CBOM, the AES node would have an unknown edge to its mode child. @@ -190,7 +262,7 @@ module JCAModel { // TODO: handle defaults override string getRawAlgorithmName() { result = instance.getOrigin().getMode() } - private predicate modeToNameMappingKnown(Crypto::TModeOperationType type, string name) { + private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { type instanceof Crypto::ECB and name = "ECB" or type instanceof Crypto::CBC and name = "CBC" @@ -208,7 +280,7 @@ module JCAModel { type instanceof Crypto::OCB and name = "OCB" } - override Crypto::TModeOperationType getModeType() { + override Crypto::TBlockCipherModeOperationType getModeType() { if this.modeToNameMappingKnown(_, instance.getOrigin().getMode()) then this.modeToNameMappingKnown(result, instance.getOrigin().getMode()) else result instanceof Crypto::OtherMode @@ -339,14 +411,51 @@ module JCAModel { override Expr getInput() { result = this.(ClassInstanceExpr).getArgument(1) } } + private module JavaxCipherModeAccessToInitConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + src.asExpr() instanceof JavaxCryptoCipherOperationModeAccess + } + + predicate isSink(DataFlow::Node sink) { + exists(CipherInitCall c | c.getModeArg() = sink.asExpr()) + } + } + + module JavaxCipherModeAccessToInitFlow = DataFlow::Global; + class CipherInitCall extends MethodCall { CipherInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "init") } - // TODO: this doesn't account for tracing the mode to this arg if expending this arg to have - // the actual mode directly + /** + * Returns the mode argument to the `init` method + * that is used to determine the cipher operation mode. + * Note this is the raw expr and not necessarily a direct access + * of a mode. Use `getModeOrigin()` to get the field access origin + * flowing to this argument, if one exists (is known). + */ Expr getModeArg() { result = this.getArgument(0) } - // TODO: need a getModeOrigin + JavaxCryptoCipherOperationModeAccess getModeOrigin() { + exists(DataFlow::Node src, DataFlow::Node sink | + JavaxCipherModeAccessToInitFlow::flow(src, sink) and + src.asExpr() = result and + this.getModeArg() = sink.asExpr() + ) + } + + Crypto::CipherOperationMode getCipherOperationModeType() { + if not exists(this.getModeOrigin()) + then result instanceof Crypto::UnknownCipherOperationMode + else + if this.getModeOrigin().getField().getName() in ["ENCRYPT_MODE", "WRAP_MODE"] + then result instanceof Crypto::EncryptionMode + else + if this.getModeOrigin().getField().getName() in ["DECRYPT_MODE", "UNWRAP_MODE"] + then result instanceof Crypto::DecryptionMode + else + // TODO/Question: distinguish between unknown vs unspecified? (the field access is not recognized, vs no field access is found) + result instanceof Crypto::UnknownCipherOperationMode + } Expr getKey() { result = this.getArgument(1) and this.getMethod().getParameterType(1).hasName("Key") diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 17834343e6d5..a0f22d0229b6 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -82,7 +82,7 @@ module CryptographyBase Input> { abstract class CipherOperationInstance extends LocatableElement { abstract EncryptionAlgorithmInstance getAlgorithm(); - abstract TCipherOperationMode getCipherOperationMode(); + abstract CipherOperationMode getCipherOperationMode(); } abstract class EncryptionAlgorithmInstance extends LocatableElement { } @@ -94,7 +94,7 @@ module CryptographyBase Input> { abstract class EllipticCurveAlgorithmInstance extends LocatableElement { } // Non-standalone algorithms - abstract class ModeOfOperationAlgorithmInstance extends LocatableElement { } + abstract class BlockCipherModeOfOperationAlgorithmInstance extends LocatableElement { } abstract class PaddingAlgorithmInstance extends LocatableElement { } @@ -128,7 +128,8 @@ module CryptographyBase Input> { TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstance e) or TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or // Non-standalone Algorithms (e.g., Mode, Padding) - TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or + // TODO: need to rename this, as "mode" is getting reused in different contexts, be precise + TBlockCipherModeOfOperationAlgorithm(BlockCipherModeOfOperationAlgorithmInstance e) or TPaddingAlgorithm(PaddingAlgorithmInstance e) or // Composite and hybrid cryptosystems (e.g., RSA-OAEP used with AES, post-quantum hybrid cryptosystems) // These nodes are always parent nodes and are not modeled but rather defined via library-agnostic patterns. @@ -685,9 +686,25 @@ module CryptographyBase Input> { } newtype TCipherOperationMode = - EncryptionMode() or - DecryptionMode() or - UnknownCipherOperationMode() + TEncryptionMode() or + TDecryptionMode() or + TUnknownCipherOperationMode() + + abstract class CipherOperationMode extends TCipherOperationMode { + abstract string toString(); + } + + class EncryptionMode extends CipherOperationMode, TEncryptionMode { + override string toString() { result = "Encryption" } + } + + class DecryptionMode extends CipherOperationMode, TDecryptionMode { + override string toString() { result = "Decryption" } + } + + class UnknownCipherOperationMode extends CipherOperationMode, TUnknownCipherOperationMode { + override string toString() { result = "Unknown" } + } /** * An encryption operation that processes plaintext to generate a ciphertext. @@ -706,7 +723,9 @@ module CryptographyBase Input> { result = instance.getCipherOperationMode() } - final override EncryptionAlgorithm getAlgorithm() { result.getInstance() = instance.getAlgorithm() } + final override EncryptionAlgorithm getAlgorithm() { + result.getInstance() = instance.getAlgorithm() + } override string getInternalType() { result = "CipherOperation" } // /** @@ -721,7 +740,7 @@ module CryptographyBase Input> { /** * Block cipher modes of operation algorithms */ - newtype TModeOperationType = + newtype TBlockCipherModeOperationType = ECB() or // Not secure, widely used CBC() or // Vulnerable to padding oracle attacks GCM() or // Widely used AEAD mode (TLS 1.3, SSH, IPsec) @@ -732,7 +751,7 @@ module CryptographyBase Input> { OCB() or // Efficient AEAD mode OtherMode() - abstract class ModeOfOperationAlgorithm extends Algorithm, TModeOfOperationAlgorithm { + abstract class ModeOfOperationAlgorithm extends Algorithm, TBlockCipherModeOfOperationAlgorithm { override string getAlgorithmType() { result = "ModeOfOperation" } /** @@ -742,10 +761,10 @@ module CryptographyBase Input> { * * If a type cannot be determined, the result is `OtherMode`. */ - abstract TModeOperationType getModeType(); + abstract TBlockCipherModeOperationType getModeType(); bindingset[type] - final private predicate modeToNameMapping(TModeOperationType type, string name) { + final private predicate modeToNameMapping(TBlockCipherModeOperationType type, string name) { type instanceof ECB and name = "ECB" or type instanceof CBC and name = "CBC" From 2b0b927b0b14ad819f2e07588d2b75042cbd2545 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Mon, 24 Feb 2025 17:37:41 +0100 Subject: [PATCH 027/122] Add Nonce association to Operation, update graph --- java/ql/lib/experimental/Quantum/JCA.qll | 176 ++++++++----- java/ql/lib/experimental/Quantum/Language.qll | 32 +-- .../src/experimental/Quantum/ReusedNonce.ql | 23 ++ java/ql/src/experimental/Quantum/Test.ql | 9 - .../ql/src/experimental/Quantum/TestCipher.ql | 16 ++ misc/scripts/cryptography/cbom.sh | 6 +- misc/scripts/cryptography/generate_cbom.py | 10 +- .../codeql/cryptography/Model.qll | 233 ++++++++++-------- 8 files changed, 297 insertions(+), 208 deletions(-) create mode 100644 java/ql/src/experimental/Quantum/ReusedNonce.ql delete mode 100644 java/ql/src/experimental/Quantum/Test.ql create mode 100644 java/ql/src/experimental/Quantum/TestCipher.ql diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index acbe6c67888f..9892b445312a 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -1,5 +1,6 @@ import java import semmle.code.java.dataflow.DataFlow +import semmle.code.java.controlflow.Dominance module JCAModel { import Language @@ -64,6 +65,8 @@ module JCAModel { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", s) ) } + + DataFlow::Node getInputData() { result.asExpr() = this.getArgument(0) } } /** @@ -84,7 +87,7 @@ module JCAModel { * * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`. */ - class CipherGetInstanceAlgorithmArg extends Crypto::EncryptionAlgorithmInstance, + class CipherGetInstanceAlgorithmArg extends Crypto::CipherAlgorithmInstance, Crypto::BlockCipherModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof Expr { CipherGetInstanceCall call; @@ -116,13 +119,22 @@ module JCAModel { private class JavaxCryptoCipherOperationModeAccess extends FieldAccess { JavaxCryptoCipherOperationModeAccess() { this.getQualifier() instanceof CipherAccess and - this.getField().getName() in ["ENCRYPT_MODE", "DECRYPT_MODE", "WRAP_MODE", "UNWRAP_MODE"] + this.getField().getName().toUpperCase() in [ + "ENCRYPT_MODE", "DECRYPT_MODE", "WRAP_MODE", "UNWRAP_MODE" + ] } } + class CipherUpdateCall extends MethodCall { + CipherUpdateCall() { this.getMethod().hasQualifiedName("javax.crypto", "Cipher", "update") } + + DataFlow::Node getInputData() { result.asExpr() = this.getArgument(0) } + } + private newtype TCipherModeFlowState = TUninitializedCipherModeFlowState() or - TInitializedCipherModeFlowState(CipherInitCall call) + TInitializedCipherModeFlowState(CipherInitCall call) or + TUsedCipherModeFlowState(CipherInitCall init, CipherUpdateCall update) abstract private class CipherModeFlowState extends TCipherModeFlowState { string toString() { @@ -131,13 +143,13 @@ module JCAModel { this = TInitializedCipherModeFlowState(_) and result = "initialized" } - abstract Crypto::CipherOperationMode getCipherOperationMode(); + abstract Crypto::CipherOperationSubtype getCipherOperationMode(); } private class UninitializedCipherModeFlowState extends CipherModeFlowState, TUninitializedCipherModeFlowState { - override Crypto::CipherOperationMode getCipherOperationMode() { + override Crypto::CipherOperationSubtype getCipherOperationMode() { result instanceof Crypto::UnknownCipherOperationMode } } @@ -148,19 +160,18 @@ module JCAModel { CipherInitCall call; DataFlow::Node node1; DataFlow::Node node2; - Crypto::CipherOperationMode mode; + Crypto::CipherOperationSubtype mode; InitializedCipherModeFlowState() { this = TInitializedCipherModeFlowState(call) and DataFlow::localFlowStep(node1, node2) and node2.asExpr() = call.getQualifier() and - // I would imagine this would make this predicate horribly horribly inefficient - // it now binds with anything + // TODO: does this make this predicate inefficient as it binds with anything? not node1.asExpr() = call.getQualifier() and mode = call.getCipherOperationModeType() } - CipherInitCall getCall() { result = call } + CipherInitCall getInitCall() { result = call } DataFlow::Node getFstNode() { result = node1 } @@ -169,12 +180,14 @@ module JCAModel { */ DataFlow::Node getSndNode() { result = node2 } - override Crypto::CipherOperationMode getCipherOperationMode() { result = mode } + override Crypto::CipherOperationSubtype getCipherOperationMode() { result = mode } } /** - * Trace to a cryptographic operation, + * Trace from cipher initialization to a cryptographic operation, * specifically `Cipher.doFinal()`, `Cipher.wrap()`, or `Cipher.unwrap()`. + * + * TODO: handle `Cipher.update()` */ private module CipherGetInstanceToCipherOperationConfig implements DataFlow::StateConfigSig { class FlowState = TCipherModeFlowState; @@ -201,7 +214,7 @@ module JCAModel { exists(CipherInitCall call | node.asExpr() = call.getQualifier() | state instanceof UninitializedCipherModeFlowState or - state.(InitializedCipherModeFlowState).getCall() != call + state.(InitializedCipherModeFlowState).getInitCall() != call ) } } @@ -209,15 +222,16 @@ module JCAModel { module CipherGetInstanceToCipherOperationFlow = DataFlow::GlobalWithState; - class CipherEncryptionOperation extends Crypto::CipherOperationInstance instanceof Call { - Crypto::CipherOperationMode mode; - Crypto::EncryptionAlgorithmInstance algorithm; + class CipherOperationInstance extends Crypto::CipherOperationInstance instanceof Call { + Crypto::CipherOperationSubtype mode; + Crypto::CipherAlgorithmInstance algorithm; + CipherGetInstanceToCipherOperationFlow::PathNode sink; + JCACipherOperationCall doFinalize; - CipherEncryptionOperation() { + CipherOperationInstance() { exists( - CipherGetInstanceToCipherOperationFlow::PathNode sink, CipherGetInstanceToCipherOperationFlow::PathNode src, CipherGetInstanceCall getCipher, - JCACipherOperationCall doFinalize, CipherGetInstanceAlgorithmArg arg + CipherGetInstanceAlgorithmArg arg | CipherGetInstanceToCipherOperationFlow::flowPath(src, sink) and src.getNode().asExpr() = getCipher and @@ -229,9 +243,19 @@ module JCAModel { ) } - override Crypto::EncryptionAlgorithmInstance getAlgorithm() { result = algorithm } + override Crypto::CipherAlgorithmInstance getAlgorithm() { result = algorithm } - override Crypto::CipherOperationMode getCipherOperationMode() { result = mode } + override Crypto::CipherOperationSubtype getCipherOperationSubtype() { result = mode } + + override Crypto::NonceArtifactInstance getNonce() { + NonceArtifactToCipherInitCallFlow::flow(result.asOutputData(), + DataFlow::exprNode(sink.getState() + .(InitializedCipherModeFlowState) + .getInitCall() + .getNonceArg())) + } + + override DataFlow::Node getInputData() { result = doFinalize.getInputData() } } /** @@ -319,12 +343,12 @@ module JCAModel { CipherStringLiteral getInstance() { result = instance } } - class EncryptionAlgorithm extends Crypto::EncryptionAlgorithm { + class EncryptionAlgorithm extends Crypto::CipherAlgorithm { CipherStringLiteral origin; CipherGetInstanceAlgorithmArg instance; EncryptionAlgorithm() { - this = Crypto::TEncryptionAlgorithm(instance) and + this = Crypto::TCipherAlgorithm(instance) and instance.getOrigin() = origin } @@ -347,7 +371,7 @@ module JCAModel { override Crypto::TCipherType getCipherFamily() { if this.cipherNameMappingKnown(_, origin.getAlgorithmName()) then this.cipherNameMappingKnown(result, origin.getAlgorithmName()) - else result instanceof Crypto::OtherSymmetricCipherType + else result instanceof Crypto::OtherCipherType } override string getKeySize(Location location) { none() } @@ -384,33 +408,71 @@ module JCAModel { } /** - * Initialiation vectors + * Initialization vectors and other nonce artifacts */ - abstract class IVParameterInstantiation extends Crypto::InitializationVectorArtifactInstance instanceof ClassInstanceExpr + abstract class NonceParameterInstantiation extends Crypto::NonceArtifactInstance instanceof ClassInstanceExpr { - abstract Expr getInput(); + override DataFlow::Node asOutputData() { result.asExpr() = this } } - class IvParameterSpecInstance extends IVParameterInstantiation { + class IvParameterSpecInstance extends NonceParameterInstantiation { IvParameterSpecInstance() { this.(ClassInstanceExpr) .getConstructedType() .hasQualifiedName("javax.crypto.spec", "IvParameterSpec") } - override Expr getInput() { result = this.(ClassInstanceExpr).getArgument(0) } + override DataFlow::Node getInput() { result.asExpr() = this.(ClassInstanceExpr).getArgument(0) } } - class GCMParameterSpecInstance extends IVParameterInstantiation { + // TODO: this also specifies the tag length for GCM + class GCMParameterSpecInstance extends NonceParameterInstantiation { GCMParameterSpecInstance() { this.(ClassInstanceExpr) .getConstructedType() .hasQualifiedName("javax.crypto.spec", "GCMParameterSpec") } - override Expr getInput() { result = this.(ClassInstanceExpr).getArgument(1) } + override DataFlow::Node getInput() { result.asExpr() = this.(ClassInstanceExpr).getArgument(1) } + } + + class IvParameterSpecGetIvCall extends MethodCall { + IvParameterSpecGetIvCall() { + this.getMethod().hasQualifiedName("javax.crypto.spec", "IvParameterSpec", "getIV") + } } + module NonceArtifactToCipherInitCallConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + exists(NonceParameterInstantiation n | + src = n.asOutputData() and + not exists(IvParameterSpecGetIvCall m | n.getInput().asExpr() = m) + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(CipherInitCall c | c.getNonceArg() = sink.asExpr()) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(IvParameterSpecGetIvCall m | + node1.asExpr() = m.getQualifier() and + node2.asExpr() = m + ) + or + exists(NonceParameterInstantiation n | + node1 = n.getInput() and + node2.asExpr() = n + ) + } + } + + module NonceArtifactToCipherInitCallFlow = DataFlow::Global; + + /** + * A data-flow configuration to track flow from a mode field access to + * the mode argument of the `init` method of the `javax.crypto.Cipher` class. + */ private module JavaxCipherModeAccessToInitConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof JavaxCryptoCipherOperationModeAccess @@ -423,6 +485,18 @@ module JCAModel { module JavaxCipherModeAccessToInitFlow = DataFlow::Global; + private predicate cipher_mode_str_to_cipher_mode_known( + string mode, Crypto::CipherOperationSubtype cipher_mode + ) { + mode = "ENCRYPT_MODE" and cipher_mode instanceof Crypto::EncryptionMode + or + mode = "WRAP_MODE" and cipher_mode instanceof Crypto::WrapMode + or + mode = "DECRYPT_MODE" and cipher_mode instanceof Crypto::DecryptionMode + or + mode = "UNWRAP_MODE" and cipher_mode instanceof Crypto::UnwrapMode + } + class CipherInitCall extends MethodCall { CipherInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "init") } @@ -443,49 +517,19 @@ module JCAModel { ) } - Crypto::CipherOperationMode getCipherOperationModeType() { - if not exists(this.getModeOrigin()) - then result instanceof Crypto::UnknownCipherOperationMode - else - if this.getModeOrigin().getField().getName() in ["ENCRYPT_MODE", "WRAP_MODE"] - then result instanceof Crypto::EncryptionMode - else - if this.getModeOrigin().getField().getName() in ["DECRYPT_MODE", "UNWRAP_MODE"] - then result instanceof Crypto::DecryptionMode - else - // TODO/Question: distinguish between unknown vs unspecified? (the field access is not recognized, vs no field access is found) - result instanceof Crypto::UnknownCipherOperationMode + Crypto::CipherOperationSubtype getCipherOperationModeType() { + if cipher_mode_str_to_cipher_mode_known(this.getModeOrigin().getField().getName(), _) + then cipher_mode_str_to_cipher_mode_known(this.getModeOrigin().getField().getName(), result) + else result instanceof Crypto::UnknownCipherOperationMode } - Expr getKey() { + Expr getKeyArg() { result = this.getArgument(1) and this.getMethod().getParameterType(1).hasName("Key") } - Expr getIV() { + Expr getNonceArg() { result = this.getArgument(2) and this.getMethod().getParameterType(2).hasName("AlgorithmParameterSpec") } } - - // TODO: cipher.getParameters().getParameterSpec(GCMParameterSpec.class); - /* - * class InitializationVectorArg extends Crypto::InitializationVectorArtifactInstance instanceof Expr - * { - * IVParameterInstantiation creation; - * - * InitializationVectorArg() { this = creation.getInput() } - * } - */ - - class InitializationVector extends Crypto::InitializationVector { - IVParameterInstantiation instance; - - InitializationVector() { this = Crypto::TInitializationVector(instance) } - - override Location getLocation() { result = instance.getLocation() } - - override Crypto::DataFlowNode asOutputData() { result.asExpr() = instance } - - override Crypto::DataFlowNode getInputData() { result.asExpr() = instance.getInput() } - } } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 5801815109b0..e88bce6d9959 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -23,8 +23,6 @@ module CryptoInput implements InputSig { class LocatableElement = Language::Element; class UnknownLocation = UnknownDefaultLocation; - - predicate rngToIvFlow(DataFlowNode rng, DataFlowNode iv) { none() } } /** @@ -37,9 +35,7 @@ module Crypto = CryptographyBase; * tied to an output node (i.e., the result of the source of randomness) */ abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance { - abstract Crypto::RNGSourceSecurity getSourceSecurity(); - - Crypto::TRNGSeedSecurity getSeedSecurity(Location location) { none() } + override DataFlow::Node asOutputData() { result.asExpr() = this } } class SecureRandomnessInstance extends RandomnessInstance { @@ -48,36 +44,10 @@ class SecureRandomnessInstance extends RandomnessInstance { s.getSourceOfRandomness() instanceof SecureRandomNumberGenerator ) } - - override Crypto::RNGSourceSecurity getSourceSecurity() { - result instanceof Crypto::RNGSourceSecure - } } class InsecureRandomnessInstance extends RandomnessInstance { InsecureRandomnessInstance() { exists(InsecureRandomnessSource node | this = node.asExpr()) } - - override Crypto::RNGSourceSecurity getSourceSecurity() { - result instanceof Crypto::RNGSourceInsecure - } -} - -class RandomnessArtifact extends Crypto::RandomNumberGeneration { - RandomnessInstance instance; - - RandomnessArtifact() { this = Crypto::TRandomNumberGeneration(instance) } - - override Location getLocation() { result = instance.getLocation() } - - override Crypto::RNGSourceSecurity getSourceSecurity() { result = instance.getSourceSecurity() } - - override Crypto::TRNGSeedSecurity getSeedSecurity(Location location) { - result = instance.getSeedSecurity(location) - } - - override Crypto::DataFlowNode asOutputData() { result.asExpr() = instance } - - override Crypto::DataFlowNode getInputData() { none() } } // Import library-specific modeling diff --git a/java/ql/src/experimental/Quantum/ReusedNonce.ql b/java/ql/src/experimental/Quantum/ReusedNonce.ql new file mode 100644 index 000000000000..cb0d6ff9d610 --- /dev/null +++ b/java/ql/src/experimental/Quantum/ReusedNonce.ql @@ -0,0 +1,23 @@ +/** + * @name Unsafe nonce source or reuse + * @id java/unsafe-nonce-source-or-reuse + */ + +import experimental.Quantum.Language +import semmle.code.java.dataflow.DataFlow + +Element getNonceOrigin(Crypto::NonceArtifactInstance nonce) { + // TODO: this check is currently ultra hacky just for demoing + result = nonce.getInput().asExpr().(VarAccess).getVariable() +} + +from + Crypto::CipherOperationInstance op, Crypto::NonceArtifactInstance nonce1, + Crypto::NonceArtifactInstance nonce2 +where + op.(Expr).getEnclosingCallable().getName() = "encrypt" and + nonce1 = op.getNonce() and + nonce2 = op.getNonce() and + not nonce1 = nonce2 and + getNonceOrigin(nonce1) = getNonceOrigin(nonce2) +select op, nonce1, nonce2 diff --git a/java/ql/src/experimental/Quantum/Test.ql b/java/ql/src/experimental/Quantum/Test.ql deleted file mode 100644 index ba76213132c3..000000000000 --- a/java/ql/src/experimental/Quantum/Test.ql +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @name "PQC Test" - */ - -import experimental.Quantum.Language - -from Crypto::EncryptionAlgorithm a, Crypto::ModeOfOperationAlgorithm m, Crypto::PaddingAlgorithm p -where m = a.getModeOfOperation() and p = a.getPadding() -select a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), p, p.getRawAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/TestCipher.ql b/java/ql/src/experimental/Quantum/TestCipher.ql new file mode 100644 index 000000000000..6be308c25606 --- /dev/null +++ b/java/ql/src/experimental/Quantum/TestCipher.ql @@ -0,0 +1,16 @@ +/** + * @name "PQC Test" + */ + +import experimental.Quantum.Language + +from + Crypto::CipherOperation op, Crypto::CipherAlgorithm a, Crypto::ModeOfOperationAlgorithm m, + Crypto::PaddingAlgorithm p, Crypto::Nonce nonce +where + a = op.getAlgorithm() and + m = a.getModeOfOperation() and + p = a.getPadding() and + nonce = op.getNonce() +select op, op.getCipherOperationMode(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), p, + p.getRawAlgorithmName(), nonce, nonce.getInputData() diff --git a/misc/scripts/cryptography/cbom.sh b/misc/scripts/cryptography/cbom.sh index 9cbef951bbf4..ef558ccf432e 100755 --- a/misc/scripts/cryptography/cbom.sh +++ b/misc/scripts/cryptography/cbom.sh @@ -1,8 +1,8 @@ #!/bin/bash -CODEQL_PATH="/Users/nicolaswill/Library/Application Support/Code/User/globalStorage/github.vscode-codeql/distribution5/codeql/codeql" -DATABASE_PATH="/Users/nicolaswill/openssl_codeql/openssl/openssl_db" -QUERY_FILE="/Users/nicolaswill/pqc/codeql/cpp/ql/src/experimental/Quantum/CBOMGraph.ql" +CODEQL_PATH="/Users/nicolaswill/.local/share/gh/extensions/gh-codeql/dist/release/v2.20.4/codeql" +DATABASE_PATH="/Users/nicolaswill/pqc/gpt-crypto-test-cases/gpt_ai_gen_jca_test_cases_db" +QUERY_FILE="/Users/nicolaswill/pqc/codeql/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql" OUTPUT_DIR="graph_output" python3 generate_cbom.py -c "$CODEQL_PATH" -d "$DATABASE_PATH" -q "$QUERY_FILE" -o "$OUTPUT_DIR" diff --git a/misc/scripts/cryptography/generate_cbom.py b/misc/scripts/cryptography/generate_cbom.py index 10ec895dc629..07400d6cd6a3 100644 --- a/misc/scripts/cryptography/generate_cbom.py +++ b/misc/scripts/cryptography/generate_cbom.py @@ -56,6 +56,9 @@ def convert_dgml_to_dot(dgml_file, dot_file): else: label_parts.append(f"{key}={value}") label = "\\n".join(label_parts) + # Escape forward slashes and double quotes + label = label.replace("/", "\\/") + label = label.replace("\"", "\\\"") prop_l = [f'label="{label}"'] node_s = f'nd_{node_id} [{", ".join(prop_l)}];' body_l.append(node_s) @@ -63,8 +66,11 @@ def convert_dgml_to_dot(dgml_file, dot_file): # Process edges for edge in root.find("{http://schemas.microsoft.com/vs/2009/dgml}Links"): att = edge.attrib + edge_label = att.get("Label", "") + edge_label = edge_label.replace("/", "\\/") + edge_label = edge_label.replace("\"", "\\\"") edge_s = 'nd_{} -> nd_{} [label="{}"];'.format( - att["Source"], att["Target"], att.get("Label", "")) + att["Source"], att["Target"], edge_label) body_l.append(edge_s) body_l.append("}") @@ -89,7 +95,7 @@ def main(): run_codeql_analysis(args.codeql, args.database, args.query, args.output) # Locate DGML file - dgml_file = os.path.join(args.output, "cbomgraph.dgml") + dgml_file = os.path.join(args.output, "java", "print-cbom-graph.dgml") dot_file = dgml_file.replace(".dgml", ".dot") if os.path.exists(dgml_file): diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index a0f22d0229b6..e5b91e198eaf 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -19,8 +19,6 @@ signature module InputSig { } class UnknownLocation instanceof Location; - - predicate rngToIvFlow(DataFlowNode rng, DataFlowNode iv); } module CryptographyBase Input> { @@ -34,11 +32,13 @@ module CryptographyBase Input> { UnknownPropertyValue() { this = "" } } - private string getPropertyAsGraphString(NodeBase node, string key) { + private string getPropertyAsGraphString(NodeBase node, string key, Location root) { result = strictconcat(any(string value, Location location, string parsed | node.properties(key, value, location) and - parsed = "(" + value + "," + location.toString() + ")" + if location = root or location instanceof UnknownLocation + then parsed = value + else parsed = "(" + value + "," + location.toString() + ")" | parsed ), "," @@ -46,19 +46,26 @@ module CryptographyBase Input> { } predicate nodes_graph_impl(NodeBase node, string key, string value) { - key = "semmle.label" and - value = node.toString() - or - // CodeQL's DGML output does not include a location - key = "Location" and - value = node.getLocation().toString() - or - // Known unknown edges should be reported as properties rather than edges - node = node.getChild(key) and - value = "" - or - // Report properties - value = getPropertyAsGraphString(node, key) + not ( + // exclude Artifact nodes with no edges to or from them + node instanceof Artifact and + not (edges_graph_impl(node, _, _, _) or edges_graph_impl(_, node, _, _)) + ) and + ( + key = "semmle.label" and + value = node.toString() + or + // CodeQL's DGML output does not include a location + key = "Location" and + value = node.getLocation().toString() + or + // Known unknown edges should be reported as properties rather than edges + node = node.getChild(key) and + value = "" + or + // Report properties + value = getPropertyAsGraphString(node, key, node.getLocation()) + ) } predicate edges_graph_impl(NodeBase source, NodeBase target, string key, string value) { @@ -80,12 +87,16 @@ module CryptographyBase Input> { abstract class KeyDerivationAlgorithmInstance extends LocatableElement { } abstract class CipherOperationInstance extends LocatableElement { - abstract EncryptionAlgorithmInstance getAlgorithm(); + abstract CipherAlgorithmInstance getAlgorithm(); + + abstract CipherOperationSubtype getCipherOperationSubtype(); - abstract CipherOperationMode getCipherOperationMode(); + abstract NonceArtifactInstance getNonce(); + + abstract DataFlowNode getInputData(); } - abstract class EncryptionAlgorithmInstance extends LocatableElement { } + abstract class CipherAlgorithmInstance extends LocatableElement { } abstract class KeyEncapsulationOperationInstance extends LocatableElement { } @@ -99,21 +110,26 @@ module CryptographyBase Input> { abstract class PaddingAlgorithmInstance extends LocatableElement { } // Artifacts - abstract class DigestArtifactInstance extends LocatableElement { } + abstract private class ArtifactLocatableElement extends LocatableElement { + abstract DataFlowNode asOutputData(); - abstract class KeyArtifactInstance extends LocatableElement { } + abstract DataFlowNode getInput(); + } - abstract class InitializationVectorArtifactInstance extends LocatableElement { } + abstract class DigestArtifactInstance extends ArtifactLocatableElement { } - abstract class NonceArtifactInstance extends LocatableElement { } + abstract class KeyArtifactInstance extends ArtifactLocatableElement { } - abstract class RandomNumberGenerationInstance extends LocatableElement { } + abstract class NonceArtifactInstance extends ArtifactLocatableElement { } + + abstract class RandomNumberGenerationInstance extends ArtifactLocatableElement { + final override DataFlowNode getInput() { none() } + } newtype TNode = // Artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or - TInitializationVector(InitializationVectorArtifactInstance e) or TNonce(NonceArtifactInstance e) or TRandomNumberGeneration(RandomNumberGenerationInstance e) or // Operations (e.g., hashing, encryption) @@ -122,7 +138,7 @@ module CryptographyBase Input> { TCipherOperation(CipherOperationInstance e) or TKeyEncapsulationOperation(KeyEncapsulationOperationInstance e) or // Algorithms (e.g., SHA-256, AES) - TEncryptionAlgorithm(EncryptionAlgorithmInstance e) or + TCipherAlgorithm(CipherAlgorithmInstance e) or TEllipticCurveAlgorithm(EllipticCurveAlgorithmInstance e) or THashAlgorithm(HashAlgorithmInstance e) or TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstance e) or @@ -133,10 +149,10 @@ module CryptographyBase Input> { TPaddingAlgorithm(PaddingAlgorithmInstance e) or // Composite and hybrid cryptosystems (e.g., RSA-OAEP used with AES, post-quantum hybrid cryptosystems) // These nodes are always parent nodes and are not modeled but rather defined via library-agnostic patterns. - TKemDemHybridCryptosystem(EncryptionAlgorithm dem) or // TODO, change this relation and the below ones - TKeyAgreementHybridCryptosystem(EncryptionAlgorithmInstance ka) or - TAsymmetricEncryptionMacHybridCryptosystem(EncryptionAlgorithmInstance enc) or - TPostQuantumHybridCryptosystem(EncryptionAlgorithmInstance enc) + TKemDemHybridCryptosystem(CipherAlgorithm dem) or // TODO, change this relation and the below ones + TKeyAgreementHybridCryptosystem(CipherAlgorithmInstance ka) or + TAsymmetricEncryptionMacHybridCryptosystem(CipherAlgorithmInstance enc) or + TPostQuantumHybridCryptosystem(CipherAlgorithmInstance enc) /** * The base class for all cryptographic assets, such as operations and algorithms. @@ -183,7 +199,7 @@ module CryptographyBase Input> { } /** - * Returns the parent of this node. + * Returns a parent of this node. */ final NodeBase getAParent() { result.getChild(_) = this } } @@ -197,43 +213,43 @@ module CryptographyBase Input> { } /** - * An initialization vector + * A nonce or initialization vector */ - abstract class InitializationVector extends Artifact, TInitializationVector { - final override string getInternalType() { result = "InitializationVector" } + private class NonceImpl extends Artifact, TNonce { + NonceArtifactInstance instance; - RandomNumberGeneration getRNGSource() { - Input::rngToIvFlow(result.asOutputData(), this.getInputData()) - } - } + NonceImpl() { this = TNonce(instance) } - newtype TRNGSourceSecurity = - RNGSourceSecure() or // Secure RNG source (unrelated to seed) - RNGSourceInsecure() // Insecure RNG source (unrelated to seed) + final override string getInternalType() { result = "Nonce" } - class RNGSourceSecurity extends TRNGSourceSecurity { - string toString() { - this instanceof RNGSourceSecure and result = "Secure RNG Source" - or - this instanceof RNGSourceInsecure and result = "Insecure RNG Source" - } + override Location getLocation() { result = instance.getLocation() } + + override DataFlowNode asOutputData() { result = instance.asOutputData() } + + override DataFlowNode getInputData() { result = instance.getInput() } } - newtype TRNGSeedSecurity = - RNGSeedSecure() or - RNGSeedInsecure() + final class Nonce = NonceImpl; /** * A source of random number generation */ - abstract class RandomNumberGeneration extends Artifact, TRandomNumberGeneration { + final private class RandomNumberGenerationImpl extends Artifact, TRandomNumberGeneration { + RandomNumberGenerationInstance instance; + + RandomNumberGenerationImpl() { this = TRandomNumberGeneration(instance) } + final override string getInternalType() { result = "RandomNumberGeneration" } - abstract RNGSourceSecurity getSourceSecurity(); + override Location getLocation() { result = instance.getLocation() } + + override DataFlowNode asOutputData() { result = instance.asOutputData() } - abstract TRNGSeedSecurity getSeedSecurity(Location location); + override DataFlowNode getInputData() { result = instance.getInput() } } + final class RandomNumberGeneration = RandomNumberGenerationImpl; + /** * A cryptographic operation, such as hashing or encryption. */ @@ -243,14 +259,6 @@ module CryptographyBase Input> { */ abstract Algorithm getAlgorithm(); - // TODO: I only removed this because I want the operation type to be non-string - // since for CipherOperations the user will have to pick the right type, - // and I want to force them to use a type that is restricted. In this case to a TCipherOperationType - // /** - // * Gets the name of this operation, e.g., "hash" or "encrypt". - // */ - // abstract string getOperationType(); - // final override string getInternalType() { result = this.getOperationType() } override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or @@ -685,24 +693,34 @@ module CryptographyBase Input> { abstract override string getRawAlgorithmName(); } - newtype TCipherOperationMode = + newtype TCipherOperationSubtype = TEncryptionMode() or TDecryptionMode() or + TWrapMode() or + TUnwrapMode() or TUnknownCipherOperationMode() - abstract class CipherOperationMode extends TCipherOperationMode { + abstract class CipherOperationSubtype extends TCipherOperationSubtype { abstract string toString(); } - class EncryptionMode extends CipherOperationMode, TEncryptionMode { - override string toString() { result = "Encryption" } + class EncryptionMode extends CipherOperationSubtype, TEncryptionMode { + override string toString() { result = "Encrypt" } } - class DecryptionMode extends CipherOperationMode, TDecryptionMode { - override string toString() { result = "Decryption" } + class DecryptionMode extends CipherOperationSubtype, TDecryptionMode { + override string toString() { result = "Decrypt" } } - class UnknownCipherOperationMode extends CipherOperationMode, TUnknownCipherOperationMode { + class WrapMode extends CipherOperationSubtype, TWrapMode { + override string toString() { result = "Wrap" } + } + + class UnwrapMode extends CipherOperationSubtype, TUnwrapMode { + override string toString() { result = "Unwrap" } + } + + class UnknownCipherOperationMode extends CipherOperationSubtype, TUnknownCipherOperationMode { override string toString() { result = "Unknown" } } @@ -711,32 +729,51 @@ module CryptographyBase Input> { * This operation takes an input message (plaintext) of arbitrary content and length * and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). */ - // NOTE FOR NICK: making this concrete here as I don't think users need to worry about making/extending these operations, just instances - class CipherOperation extends Operation, TCipherOperation { + class CipherOperationImpl extends Operation, TCipherOperation { CipherOperationInstance instance; - CipherOperation() { this = TCipherOperation(instance) } + CipherOperationImpl() { this = TCipherOperation(instance) } + + override string getInternalType() { result = "CipherOperation" } override Location getLocation() { result = instance.getLocation() } - final TCipherOperationMode getCipherOperationMode() { - result = instance.getCipherOperationMode() + CipherOperationSubtype getCipherOperationMode() { + result = instance.getCipherOperationSubtype() } - final override EncryptionAlgorithm getAlgorithm() { - result.getInstance() = instance.getAlgorithm() + final override CipherAlgorithm getAlgorithm() { result.getInstance() = instance.getAlgorithm() } + + override NodeBase getChild(string key) { + result = super.getChild(key) + or + // [KNOWN_OR_UNKNOWN] + key = "nonce" and + if exists(this.getNonce()) then result = this.getNonce() else result = this } - override string getInternalType() { result = "CipherOperation" } - // /** - // * Gets the initialization vector associated with this encryption operation. - // * - // * This predicate does not need to hold for all encryption operations, - // * as the initialization vector is not always required. - // */ - // abstract InitializationVector getInitializationVector(); + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [ALWAYS_KNOWN] - Unknown is handled in getCipherOperationMode() + key = "operation" and + value = this.getCipherOperationMode().toString() and + location = this.getLocation() + } + + /** + * Gets the initialization vector associated with this encryption operation. + * + * This predicate does not need to hold for all encryption operations, + * as the initialization vector is not always required. + */ + Nonce getNonce() { result = TNonce(instance.getNonce()) } + + DataFlowNode getInputData() { result = instance.getInputData() } } + final class CipherOperation = CipherOperationImpl; + /** * Block cipher modes of operation algorithms */ @@ -858,10 +895,10 @@ module CryptographyBase Input> { RC4() or RC5() or RSA() or - OtherSymmetricCipherType() + OtherCipherType() - abstract class EncryptionAlgorithm extends Algorithm, TEncryptionAlgorithm { - final LocatableElement getInstance() { this = TEncryptionAlgorithm(result) } + abstract class CipherAlgorithm extends Algorithm, TCipherAlgorithm { + final LocatableElement getInstance() { this = TCipherAlgorithm(result) } final TCipherStructureType getCipherStructure() { this.cipherFamilyToNameAndStructure(this.getCipherFamily(), _, result) @@ -871,7 +908,7 @@ module CryptographyBase Input> { this.cipherFamilyToNameAndStructure(this.getCipherFamily(), result, _) } - final override string getAlgorithmType() { result = "EncryptionAlgorithm" } + override string getAlgorithmType() { result = "CipherAlgorithm" } /** * Gets the key size of this cipher, e.g., "128" or "256". @@ -917,7 +954,7 @@ module CryptographyBase Input> { or type instanceof RSA and name = "RSA" and s = Asymmetric() or - type instanceof OtherSymmetricCipherType and + type instanceof OtherCipherType and name = this.getRawAlgorithmName() and s = UnknownCipherStructureType() } @@ -929,19 +966,21 @@ module CryptographyBase Input> { override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or - ( - // [KNOWN_OR_UNKNOWN] - edgeName = "mode" and - if exists(this.getModeOfOperation()) - then result = this.getModeOfOperation() - else result = this - ) + // [KNOWN_OR_UNKNOWN] + edgeName = "mode" and + if exists(this.getModeOfOperation()) + then result = this.getModeOfOperation() + else result = this + or + // [KNOWN_OR_UNKNOWN] + edgeName = "padding" and + if exists(this.getPadding()) then result = this.getPadding() else result = this } override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or - // [ALWAYS_KNOWN]: unknown case is handled in `getCipherStructureTypeString` + // [ALWAYS_KNOWN] - unknown case is handled in `getCipherStructureTypeString` key = "structure" and getCipherStructureTypeString(this.getCipherStructure()) = value and location instanceof UnknownLocation From eb91ecf1fb8864856a7dea33e513edb0a2bd7f68 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Tue, 25 Feb 2025 02:53:13 +0100 Subject: [PATCH 028/122] Add generic artifact data-flow The relation between RNG and other artifacts has been added Nonce has been completed to report its source --- java/ql/lib/experimental/Quantum/Language.qll | 20 ++++++ .../codeql/cryptography/Model.qll | 71 +++++++++++++++---- 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index e88bce6d9959..f8b7bf7a914c 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -2,6 +2,7 @@ private import codeql.cryptography.Model private import java as Language private import semmle.code.java.security.InsecureRandomnessQuery private import semmle.code.java.security.RandomQuery +private import semmle.code.java.dataflow.DataFlow private class UnknownLocation extends Language::Location { UnknownLocation() { this.getFile().getAbsolutePath() = "" } @@ -36,6 +37,10 @@ module Crypto = CryptographyBase; */ abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance { override DataFlow::Node asOutputData() { result.asExpr() = this } + + override predicate flowsTo(Crypto::ArtifactLocatableElement other) { + RNGToArtifactFlow::flow(this.asOutputData(), other.getInput()) + } } class SecureRandomnessInstance extends RandomnessInstance { @@ -50,5 +55,20 @@ class InsecureRandomnessInstance extends RandomnessInstance { InsecureRandomnessInstance() { exists(InsecureRandomnessSource node | this = node.asExpr()) } } +/** + * Random number generation artifact to other artifact flow configuration + */ +module RNGToArtifactFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::RandomNumberGenerationInstance rng).asOutputData() + } + + predicate isSink(DataFlow::Node sink) { + sink = any(Crypto::ArtifactLocatableElement other).getInput() + } +} + +module RNGToArtifactFlow = DataFlow::Global; + // Import library-specific modeling import JCA diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index e5b91e198eaf..4e8dae8aa02a 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -47,8 +47,9 @@ module CryptographyBase Input> { predicate nodes_graph_impl(NodeBase node, string key, string value) { not ( - // exclude Artifact nodes with no edges to or from them - node instanceof Artifact and + // exclude certain Artifact nodes with no edges to or from them + node instanceof RandomNumberGeneration and + // TODO: performance? not (edges_graph_impl(node, _, _, _) or edges_graph_impl(_, node, _, _)) ) and ( @@ -110,17 +111,47 @@ module CryptographyBase Input> { abstract class PaddingAlgorithmInstance extends LocatableElement { } // Artifacts - abstract private class ArtifactLocatableElement extends LocatableElement { + abstract class ArtifactLocatableElement extends LocatableElement { + /** + * Gets the output node for this artifact, which should usually be the same as `this`. + */ abstract DataFlowNode asOutputData(); + /** + * Gets the input node for this artifact. + * + * If `getInput` is implemented as `none()`, the artifact will not have inbound flow analysis. + */ abstract DataFlowNode getInput(); + + /** + * Holds if this artifact flows to `other`. + * + * This predicate should be defined generically per-language with library-specific extension support. + * The expected implementation is to perform flow analysis from this artifact's output to another artifact's input. + * The `other` argument should be one or more `ArtifactLocatableElement` that are sinks of the flow. + * + * If `flowsTo` is implemented as `none()`, the artifact will not have outbound flow analysis. + */ + abstract predicate flowsTo(ArtifactLocatableElement other); } + newtype TGenericDataSourceType = + FilesystemDataSource() or + ExternalLibraryDataSource() or + MemoryAllocationDataSource() or + ConstantDataSource() + + abstract class GenericDataSourceInstance extends ArtifactLocatableElement { } + abstract class DigestArtifactInstance extends ArtifactLocatableElement { } abstract class KeyArtifactInstance extends ArtifactLocatableElement { } - abstract class NonceArtifactInstance extends ArtifactLocatableElement { } + abstract class NonceArtifactInstance extends ArtifactLocatableElement { + // no implicit outbound data-flow (only modelled in cipher operations) + override predicate flowsTo(ArtifactLocatableElement other) { none() } + } abstract class RandomNumberGenerationInstance extends ArtifactLocatableElement { final override DataFlowNode getInput() { none() } @@ -206,10 +237,30 @@ module CryptographyBase Input> { class Asset = NodeBase; + /** + * An artifact is an instance of data that is used in a cryptographic operation or produced by one. + */ abstract class Artifact extends NodeBase { - abstract DataFlowNode asOutputData(); + /** + * Gets the artifact locatable element associated with this artifact. + * + * *Implementation note*: to avoid cross-products, the result *must* only bind to the + * `ArtifactLocatableElement` that is already associated with the node instance. + */ + abstract ArtifactLocatableElement asArtifactLocatableElement(); - abstract DataFlowNode getInputData(); + final Artifact getSourceArtifact() { + not result = this and + result.asArtifactLocatableElement().flowsTo(this.asArtifactLocatableElement()) + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [ONLY_KNOWN] - TODO: unknown case handled by reporting a generic source type or unknown as a property + edgeName = "source" and + result = this.getSourceArtifact() + } } /** @@ -224,9 +275,7 @@ module CryptographyBase Input> { override Location getLocation() { result = instance.getLocation() } - override DataFlowNode asOutputData() { result = instance.asOutputData() } - - override DataFlowNode getInputData() { result = instance.getInput() } + override ArtifactLocatableElement asArtifactLocatableElement() { result = instance } } final class Nonce = NonceImpl; @@ -243,9 +292,7 @@ module CryptographyBase Input> { override Location getLocation() { result = instance.getLocation() } - override DataFlowNode asOutputData() { result = instance.asOutputData() } - - override DataFlowNode getInputData() { result = instance.getInput() } + override ArtifactLocatableElement asArtifactLocatableElement() { result = instance } } final class RandomNumberGeneration = RandomNumberGenerationImpl; From f55f27b0d92297d314a2cccfb913032379edfbac Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Tue, 25 Feb 2025 18:22:38 +0100 Subject: [PATCH 029/122] Expand handling of generic artifact sources --- java/ql/lib/experimental/Quantum/JCA.qll | 8 +- java/ql/lib/experimental/Quantum/Language.qll | 35 +++++ .../ql/src/experimental/Quantum/TestCipher.ql | 2 +- .../codeql/cryptography/Model.qll | 138 ++++++++++++++---- 4 files changed, 147 insertions(+), 36 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 9892b445312a..df96a4460e2e 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -125,16 +125,10 @@ module JCAModel { } } - class CipherUpdateCall extends MethodCall { - CipherUpdateCall() { this.getMethod().hasQualifiedName("javax.crypto", "Cipher", "update") } - - DataFlow::Node getInputData() { result.asExpr() = this.getArgument(0) } - } - private newtype TCipherModeFlowState = TUninitializedCipherModeFlowState() or TInitializedCipherModeFlowState(CipherInitCall call) or - TUsedCipherModeFlowState(CipherInitCall init, CipherUpdateCall update) + TUsedCipherModeFlowState(CipherInitCall init) abstract private class CipherModeFlowState extends TCipherModeFlowState { string toString() { diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index f8b7bf7a914c..5abac9e9462f 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -3,6 +3,7 @@ private import java as Language private import semmle.code.java.security.InsecureRandomnessQuery private import semmle.code.java.security.RandomQuery private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSources private class UnknownLocation extends Language::Location { UnknownLocation() { this.getFile().getAbsolutePath() = "" } @@ -31,6 +32,25 @@ module CryptoInput implements InputSig { */ module Crypto = CryptographyBase; +/** + * Definitions of various generic data sources + */ +final class DefaultFlowSource = SourceNode; + +final class DefaultRemoteFlowSource = RemoteFlowSource; + +class GenericLocalDataSource extends Crypto::GenericRemoteDataSource { + GenericLocalDataSource() { + any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this + } + + override DataFlow::Node asOutputData() { result.asExpr() = this } + + override predicate flowsTo(Crypto::ArtifactLocatableElement other) { + DataSourceToArtifactFlow::flow(this.asOutputData(), other.getInput()) + } +} + /** * Random number generation, where each instance is modelled as the expression * tied to an output node (i.e., the result of the source of randomness) @@ -70,5 +90,20 @@ module RNGToArtifactFlowConfig implements DataFlow::ConfigSig { module RNGToArtifactFlow = DataFlow::Global; +/** + * Generic data source to artifact flow configuration + */ +module DataSourceToArtifactFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::GenericDataSourceInstance i).asOutputData() + } + + predicate isSink(DataFlow::Node sink) { + sink = any(Crypto::ArtifactLocatableElement other).getInput() + } +} + +module DataSourceToArtifactFlow = DataFlow::Global; + // Import library-specific modeling import JCA diff --git a/java/ql/src/experimental/Quantum/TestCipher.ql b/java/ql/src/experimental/Quantum/TestCipher.ql index 6be308c25606..14041cd3b224 100644 --- a/java/ql/src/experimental/Quantum/TestCipher.ql +++ b/java/ql/src/experimental/Quantum/TestCipher.ql @@ -13,4 +13,4 @@ where p = a.getPadding() and nonce = op.getNonce() select op, op.getCipherOperationMode(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), p, - p.getRawAlgorithmName(), nonce, nonce.getInputData() + p.getRawAlgorithmName(), nonce diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 4e8dae8aa02a..713c503ea655 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -136,13 +136,30 @@ module CryptographyBase Input> { abstract predicate flowsTo(ArtifactLocatableElement other); } - newtype TGenericDataSourceType = - FilesystemDataSource() or - ExternalLibraryDataSource() or - MemoryAllocationDataSource() or - ConstantDataSource() + // TODO: WARNING: + // If this overlaps with any other LocatableElement, there will be a cross-product + // This is never to be used for unknowns + abstract class GenericDataSourceInstance extends ArtifactLocatableElement { + final override DataFlowNode getInput() { none() } + + abstract string getLabel(); + } + + abstract class GenericConstantOrAllocationSource extends GenericDataSourceInstance { + final override string getLabel() { result = "ConstantData" } // TODO: toString of source? + } + + abstract class GenericExternalCallSource extends GenericDataSourceInstance { + final override string getLabel() { result = "ExternalCall" } // TODO: call target name or toString of source? + } - abstract class GenericDataSourceInstance extends ArtifactLocatableElement { } + abstract class GenericRemoteDataSource extends GenericDataSourceInstance { + final override string getLabel() { result = "RemoteData" } // TODO: toString of source? + } + + abstract class GenericLocalDataSource extends GenericDataSourceInstance { + final override string getLabel() { result = "LocalData" } // TODO: toString of source? + } abstract class DigestArtifactInstance extends ArtifactLocatableElement { } @@ -224,7 +241,7 @@ module CryptographyBase Input> { * This predicate is overriden by derived classes to construct the graph of cryptographic operations. */ predicate properties(string key, string value, Location location) { - key = "origin" and + key = "Origin" and location = this.getOrigin(value).getLocation() and not location = this.getLocation() } @@ -249,18 +266,65 @@ module CryptographyBase Input> { */ abstract ArtifactLocatableElement asArtifactLocatableElement(); + /** + * Gets the `Artifact` node that is the data source for this artifact. + */ final Artifact getSourceArtifact() { - not result = this and - result.asArtifactLocatableElement().flowsTo(this.asArtifactLocatableElement()) + result.asArtifactLocatableElement() = this.getSourceArtifactElement() + } + + /** + * Gets the `ArtifactLocatableElement` that is the data source for this artifact. + * + * This predicate is equivalent to `getSourceArtifact().asArtifactLocatableElement()`. + */ + final ArtifactLocatableElement getSourceArtifactElement() { + not result = this.asArtifactLocatableElement() and + result.flowsTo(this.asArtifactLocatableElement()) } override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or - // [ONLY_KNOWN] - TODO: unknown case handled by reporting a generic source type or unknown as a property - edgeName = "source" and + // [ONLY_KNOWN] - TODO: known-unknown case handled by reporting a generic source type or unknown as a property + edgeName = "Source" and result = this.getSourceArtifact() } + + // TODO: document the below + final private predicate src_generic_data_source_to_label_and_loc(string label, Location location) { + exists(GenericDataSourceInstance instance | + this.getSourceArtifactElement() = instance and + instance.getLabel() = label and + instance.getLocation() = location + ) + } + + final private predicate src_artifact_to_label_and_loc(string label, Location location) { + exists(Artifact a | + this.getSourceArtifact() = a and + a.getInternalType() = label and + a.getLocation() = location + ) + } + + final private predicate source_type_property(string key, string value, Location location) { + key = "SourceType" and + if this.src_artifact_to_label_and_loc(_, _) + then this.src_artifact_to_label_and_loc(value, location) + else + if + exists(this.asArtifactLocatableElement().getInput()) and + not this.src_generic_data_source_to_label_and_loc(_, _) + then value instanceof UnknownPropertyValue and location instanceof UnknownLocation + else this.src_generic_data_source_to_label_and_loc(value, location) + } + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + this.source_type_property(key, value, location) + } } /** @@ -309,7 +373,25 @@ module CryptographyBase Input> { override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or - edgeName = "uses" and + /* + * TODO: Consider a case with getProperty, where an unknown value is loaded from the filesystem, + * but a default is specified as such: + * String value = getProperty("property", "default_algorithm") + * In this case, getAlgorithm *could* be resolved to default_algorithm, but in that case, the known + * unknown case, i.e., what is loaded from `property`, would not be reported at all as a known unknown. + * + * Implementation brainstorming: + * We have two cases, and we only considered one before: the case where we can't point to the known unknown. + * The new case is pointing to a known unknown, e.g., "property" loaded via getProperty. + * A potential solution is to create a known unknown node for each node type (particularly algorithms) + * and model those elements in the database to associate with that known unknown type?? + * + * - Idea: use the generic data source concept as the definition of potential known unknowns. + * flow should be tracked from them to anything that could be a "sink" that specifies the relation. + * in this case, the sink would be an instantiaition of an algorithm where the value is not resolvable. + */ + + edgeName = "Uses" and if exists(this.getAlgorithm()) then result = this.getAlgorithm() else result = this } } @@ -336,10 +418,10 @@ module CryptographyBase Input> { super.properties(key, value, location) or // [ONLY_KNOWN] - key = "name" and value = this.getAlgorithmName() and location = this.getLocation() + key = "Name" and value = this.getAlgorithmName() and location = this.getLocation() or // [ONLY_KNOWN] - key = "raw_name" and value = this.getRawAlgorithmName() and location = this.getLocation() + key = "RawName" and value = this.getRawAlgorithmName() and location = this.getLocation() } } @@ -445,7 +527,7 @@ module CryptographyBase Input> { super.properties(key, value, location) or // [KNOWN_OR_UNKNOWN] - key = "digest_size" and + key = "DigestSize" and if exists(this.getDigestSize(location)) then value = this.getDigestSize(location) else ( @@ -492,7 +574,7 @@ module CryptographyBase Input> { or ( // [KNOWN_OR_UNKNOWN] - edgeName = "uses" and + edgeName = "Uses" and if exists(this.getHashAlgorithm()) then result = this.getHashAlgorithm() else result = this ) } @@ -526,7 +608,7 @@ module CryptographyBase Input> { or ( // [KNOWN_OR_UNKNOWN] - key = "iterations" and + key = "Iterations" and if exists(this.getIterationCount(location)) then value = this.getIterationCount(location) else ( @@ -536,7 +618,7 @@ module CryptographyBase Input> { or ( // [KNOWN_OR_UNKNOWN] - key = "key_len" and + key = "KeyLength" and if exists(this.getKeyLength(location)) then value = this.getKeyLength(location) else ( @@ -580,7 +662,7 @@ module CryptographyBase Input> { or ( // [KNOWN_OR_UNKNOWN] - key = "iterations" and + key = "Iterations" and if exists(this.getIterationCount(location)) then value = this.getIterationCount(location) else ( @@ -590,7 +672,7 @@ module CryptographyBase Input> { or ( // [KNOWN_OR_UNKNOWN] - key = "id_byte" and + key = "IdByte" and if exists(this.getIDByte(location)) then value = this.getIDByte(location) else ( @@ -661,7 +743,7 @@ module CryptographyBase Input> { or ( // [KNOWN_OR_UNKNOWN] - key = "key_len" and + key = "KeyLength" and if exists(this.getDerivedKeyLength(location)) then value = this.getDerivedKeyLength(location) else ( @@ -714,7 +796,7 @@ module CryptographyBase Input> { super.properties(key, value, location) or // [KNOWN_OR_UNKNOWN] - key = "key_size" and + key = "KeySize" and if exists(this.getKeySize(location)) then value = this.getKeySize(location) else ( @@ -795,7 +877,7 @@ module CryptographyBase Input> { result = super.getChild(key) or // [KNOWN_OR_UNKNOWN] - key = "nonce" and + key = "Nonce" and if exists(this.getNonce()) then result = this.getNonce() else result = this } @@ -803,7 +885,7 @@ module CryptographyBase Input> { super.properties(key, value, location) or // [ALWAYS_KNOWN] - Unknown is handled in getCipherOperationMode() - key = "operation" and + key = "Operation" and value = this.getCipherOperationMode().toString() and location = this.getLocation() } @@ -1014,13 +1096,13 @@ module CryptographyBase Input> { result = super.getChild(edgeName) or // [KNOWN_OR_UNKNOWN] - edgeName = "mode" and + edgeName = "Mode" and if exists(this.getModeOfOperation()) then result = this.getModeOfOperation() else result = this or // [KNOWN_OR_UNKNOWN] - edgeName = "padding" and + edgeName = "Padding" and if exists(this.getPadding()) then result = this.getPadding() else result = this } @@ -1028,13 +1110,13 @@ module CryptographyBase Input> { super.properties(key, value, location) or // [ALWAYS_KNOWN] - unknown case is handled in `getCipherStructureTypeString` - key = "structure" and + key = "Structure" and getCipherStructureTypeString(this.getCipherStructure()) = value and location instanceof UnknownLocation or ( // [KNOWN_OR_UNKNOWN] - key = "key_size" and + key = "KeySize" and if exists(this.getKeySize(location)) then value = this.getKeySize(location) else ( From 04f46833991dffedfb91d425cba61dc4146b6fac Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 27 Feb 2025 05:42:02 +0100 Subject: [PATCH 030/122] Rewrite handling of known unknowns and data-flow --- java/ql/lib/experimental/Quantum/JCA.qll | 344 ++-- java/ql/lib/experimental/Quantum/Language.qll | 64 +- .../src/experimental/Quantum/ReusedNonce.ql | 14 +- .../ql/src/experimental/Quantum/TestCipher.ql | 14 +- .../codeql/cryptography/Model.qll | 1534 +++++++++-------- 5 files changed, 1058 insertions(+), 912 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index df96a4460e2e..f1e1ce9ccdc3 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -66,7 +66,7 @@ module JCAModel { ) } - DataFlow::Node getInputData() { result.asExpr() = this.getArgument(0) } + DataFlow::Node getMessageArg() { result.asExpr() = this.getArgument(0) } } /** @@ -82,27 +82,137 @@ module JCAModel { module AlgorithmStringToFetchFlow = DataFlow::Global; + /** + * Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable. + * This is because the mode will always be specified alongside the algorithm and never independently. + * Therefore, we can always assume that a determinable algorithm will have a determinable mode. + * + * In the case that only an algorithm is specified, e.g., "AES", the provider provides a default mode. + * + * TODO: Model the case of relying on a provider default, but alert on it as a bad practice. + */ + class CipherStringLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance, + Crypto::ModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral + { + CipherGetInstanceAlgorithmArg consumer; + + CipherStringLiteralAlgorithmInstance() { + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(consumer)) + } + + CipherGetInstanceAlgorithmArg getConsumer() { result = consumer } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { + result = this and exists(this.getRawModeAlgorithmName()) // TODO: provider defaults + } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { + result = this and exists(this.getRawPaddingAlgorithmName()) // TODO: provider defaults + } + + override string getRawAlgorithmName() { result = super.getValue() } + + override Crypto::TCipherType getCipherFamily() { + if this.cipherNameMappingKnown(_, super.getAlgorithmName()) + then this.cipherNameMappingKnown(result, super.getAlgorithmName()) + else result instanceof Crypto::OtherCipherType + } + + bindingset[name] + private predicate cipherNameMappingKnown(Crypto::TCipherType type, string name) { + name = "AES" and + type instanceof Crypto::AES + or + name = "DES" and + type instanceof Crypto::DES + or + name = "TripleDES" and + type instanceof Crypto::TripleDES + or + name = "IDEA" and + type instanceof Crypto::IDEA + or + name = "CAST5" and + type instanceof Crypto::CAST5 + or + name = "ChaCha20" and + type instanceof Crypto::ChaCha20 + or + name = "RC4" and + type instanceof Crypto::RC4 + or + name = "RC5" and + type instanceof Crypto::RC5 + or + name = "RSA" and + type instanceof Crypto::RSA + } + + private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { + type instanceof Crypto::ECB and name = "ECB" + or + type instanceof Crypto::CBC and name = "CBC" + or + type instanceof Crypto::GCM and name = "GCM" + or + type instanceof Crypto::CTR and name = "CTR" + or + type instanceof Crypto::XTS and name = "XTS" + or + type instanceof Crypto::CCM and name = "CCM" + or + type instanceof Crypto::SIV and name = "SIV" + or + type instanceof Crypto::OCB and name = "OCB" + } + + override Crypto::TBlockCipherModeOperationType getModeType() { + if this.modeToNameMappingKnown(_, super.getMode()) + then this.modeToNameMappingKnown(result, super.getMode()) + else result instanceof Crypto::OtherMode + } + + override string getRawModeAlgorithmName() { result = super.getMode() } + + override string getRawPaddingAlgorithmName() { result = super.getPadding() } + + bindingset[name] + private predicate paddingToNameMappingKnown(Crypto::TPaddingType type, string name) { + type instanceof Crypto::NoPadding and name = "NOPADDING" + or + type instanceof Crypto::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? + or + type instanceof Crypto::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% + } + + override Crypto::TPaddingType getPaddingType() { + if this.paddingToNameMappingKnown(_, super.getPadding()) + then this.paddingToNameMappingKnown(result, super.getPadding()) + else result instanceof Crypto::OtherPadding + } + } + /** * The cipher algorithm argument to a `CipherGetInstanceCall`. * * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`. */ - class CipherGetInstanceAlgorithmArg extends Crypto::CipherAlgorithmInstance, - Crypto::BlockCipherModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof Expr - { + class CipherGetInstanceAlgorithmArg extends Crypto::AlgorithmConsumer instanceof Expr { CipherGetInstanceCall call; CipherGetInstanceAlgorithmArg() { this = call.getAlgorithmArg() } - /** - * Returns the `StringLiteral` from which this argument is derived, if known. - */ - CipherStringLiteral getOrigin() { + override DataFlow::Node getInputNode() { result.asExpr() = this } + + CipherStringLiteral getOrigin(string value) { AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), - DataFlow::exprNode(this.(Expr).getAChildExpr*())) + DataFlow::exprNode(this.(Expr).getAChildExpr*())) and + value = result.getValue() } - CipherGetInstanceCall getCall() { result = call } + override Crypto::AlgorithmElement getAKnownAlgorithmSource() { + result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this + } } /** @@ -144,7 +254,7 @@ module JCAModel { TUninitializedCipherModeFlowState { override Crypto::CipherOperationSubtype getCipherOperationMode() { - result instanceof Crypto::UnknownCipherOperationMode + result instanceof Crypto::UnknownCipherOperationSubtype } } @@ -200,6 +310,7 @@ module JCAModel { predicate isAdditionalFlowStep( DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 ) { + state1 = state1 and node1 = state2.(InitializedCipherModeFlowState).getFstNode() and node2 = state2.(InitializedCipherModeFlowState).getSndNode() } @@ -218,195 +329,40 @@ module JCAModel { class CipherOperationInstance extends Crypto::CipherOperationInstance instanceof Call { Crypto::CipherOperationSubtype mode; - Crypto::CipherAlgorithmInstance algorithm; CipherGetInstanceToCipherOperationFlow::PathNode sink; JCACipherOperationCall doFinalize; + CipherGetInstanceAlgorithmArg consumer; CipherOperationInstance() { - exists( - CipherGetInstanceToCipherOperationFlow::PathNode src, CipherGetInstanceCall getCipher, - CipherGetInstanceAlgorithmArg arg - | + exists(CipherGetInstanceToCipherOperationFlow::PathNode src, CipherGetInstanceCall getCipher | CipherGetInstanceToCipherOperationFlow::flowPath(src, sink) and src.getNode().asExpr() = getCipher and sink.getNode().asExpr() = doFinalize.getQualifier() and sink.getState().(CipherModeFlowState).getCipherOperationMode() = mode and this = doFinalize and - arg.getCall() = getCipher and - algorithm = arg + consumer = getCipher.getAlgorithmArg() ) } - override Crypto::CipherAlgorithmInstance getAlgorithm() { result = algorithm } - override Crypto::CipherOperationSubtype getCipherOperationSubtype() { result = mode } - override Crypto::NonceArtifactInstance getNonce() { - NonceArtifactToCipherInitCallFlow::flow(result.asOutputData(), - DataFlow::exprNode(sink.getState() - .(InitializedCipherModeFlowState) - .getInitCall() - .getNonceArg())) + override Crypto::ArtifactConsumer getNonceConsumer() { + result = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg() } - override DataFlow::Node getInputData() { result = doFinalize.getInputData() } - } - - /** - * A block cipher mode of operation, where the mode is specified in the ALG or ALG/MODE/PADDING format. - * - * This class will only exist when the mode (*and its type*) is determinable. - * This is because the mode will always be specified alongside the algorithm and never independently. - * Therefore, we can always assume that a determinable algorithm will have a determinable mode. - * - * In the case that only an algorithm is specified, e.g., "AES", the provider provides a default mode. - * - * TODO: Model the case of relying on a provider default, but alert on it as a bad practice. - */ - class ModeOfOperation extends Crypto::ModeOfOperationAlgorithm { - CipherGetInstanceAlgorithmArg instance; - - ModeOfOperation() { - this = Crypto::TBlockCipherModeOfOperationAlgorithm(instance) and - // TODO: this currently only holds for explicitly defined modes in a string literal. - // Cases with defaults, e.g., "AES", are not yet modelled. - // For these cases, in a CBOM, the AES node would have an unknown edge to its mode child. - exists(instance.getOrigin().getMode()) - } - - override Location getLocation() { result = instance.getLocation() } - - // In this case, the raw name is still only the /MODE/ part. - // TODO: handle defaults - override string getRawAlgorithmName() { result = instance.getOrigin().getMode() } - - private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { - type instanceof Crypto::ECB and name = "ECB" - or - type instanceof Crypto::CBC and name = "CBC" - or - type instanceof Crypto::GCM and name = "GCM" - or - type instanceof Crypto::CTR and name = "CTR" - or - type instanceof Crypto::XTS and name = "XTS" - or - type instanceof Crypto::CCM and name = "CCM" - or - type instanceof Crypto::SIV and name = "SIV" - or - type instanceof Crypto::OCB and name = "OCB" - } - - override Crypto::TBlockCipherModeOperationType getModeType() { - if this.modeToNameMappingKnown(_, instance.getOrigin().getMode()) - then this.modeToNameMappingKnown(result, instance.getOrigin().getMode()) - else result instanceof Crypto::OtherMode - } - - CipherStringLiteral getInstance() { result = instance } - } - - class PaddingAlgorithm extends Crypto::PaddingAlgorithm { - CipherGetInstanceAlgorithmArg instance; - - PaddingAlgorithm() { - this = Crypto::TPaddingAlgorithm(instance) and - exists(instance.getOrigin().getPadding()) - } - - override Location getLocation() { result = instance.getLocation() } - - override string getRawAlgorithmName() { result = instance.getOrigin().getPadding() } - - bindingset[name] - private predicate paddingToNameMappingKnown(Crypto::TPaddingType type, string name) { - type instanceof Crypto::NoPadding and name = "NOPADDING" - or - type instanceof Crypto::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? - or - type instanceof Crypto::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% + override Crypto::ArtifactConsumer getMessageConsumer() { + result = doFinalize.getMessageArg().asExpr() } - override Crypto::TPaddingType getPaddingType() { - if this.paddingToNameMappingKnown(_, instance.getOrigin().getPadding()) - then this.paddingToNameMappingKnown(result, instance.getOrigin().getPadding()) - else result instanceof Crypto::OtherPadding - } - - CipherStringLiteral getInstance() { result = instance } - } - - class EncryptionAlgorithm extends Crypto::CipherAlgorithm { - CipherStringLiteral origin; - CipherGetInstanceAlgorithmArg instance; - - EncryptionAlgorithm() { - this = Crypto::TCipherAlgorithm(instance) and - instance.getOrigin() = origin - } - - override Location getLocation() { result = instance.getLocation() } - - override Crypto::ModeOfOperationAlgorithm getModeOfOperation() { - result.(ModeOfOperation).getInstance() = origin - } - - override Crypto::PaddingAlgorithm getPadding() { - result.(PaddingAlgorithm).getInstance() = origin - } - - override Crypto::LocatableElement getOrigin(string name) { - result = origin and name = origin.toString() - } - - override string getRawAlgorithmName() { result = origin.getValue() } - - override Crypto::TCipherType getCipherFamily() { - if this.cipherNameMappingKnown(_, origin.getAlgorithmName()) - then this.cipherNameMappingKnown(result, origin.getAlgorithmName()) - else result instanceof Crypto::OtherCipherType - } - - override string getKeySize(Location location) { none() } - - bindingset[name] - private predicate cipherNameMappingKnown(Crypto::TCipherType type, string name) { - name = "AES" and - type instanceof Crypto::AES - or - name = "DES" and - type instanceof Crypto::DES - or - name = "TripleDES" and - type instanceof Crypto::TripleDES - or - name = "IDEA" and - type instanceof Crypto::IDEA - or - name = "CAST5" and - type instanceof Crypto::CAST5 - or - name = "ChaCha20" and - type instanceof Crypto::ChaCha20 - or - name = "RC4" and - type instanceof Crypto::RC4 - or - name = "RC5" and - type instanceof Crypto::RC5 - or - name = "RSA" and - type instanceof Crypto::RSA - } + override Crypto::AlgorithmConsumer getAlgorithmConsumer() { result = consumer } } /** * Initialization vectors and other nonce artifacts */ - abstract class NonceParameterInstantiation extends Crypto::NonceArtifactInstance instanceof ClassInstanceExpr + abstract class NonceParameterInstantiation extends NonceArtifactInstance instanceof ClassInstanceExpr { - override DataFlow::Node asOutputData() { result.asExpr() = this } + override DataFlow::Node getOutputNode() { result.asExpr() = this } } class IvParameterSpecInstance extends NonceParameterInstantiation { @@ -416,7 +372,9 @@ module JCAModel { .hasQualifiedName("javax.crypto.spec", "IvParameterSpec") } - override DataFlow::Node getInput() { result.asExpr() = this.(ClassInstanceExpr).getArgument(0) } + override DataFlow::Node getInputNode() { + result.asExpr() = this.(ClassInstanceExpr).getArgument(0) + } } // TODO: this also specifies the tag length for GCM @@ -427,7 +385,9 @@ module JCAModel { .hasQualifiedName("javax.crypto.spec", "GCMParameterSpec") } - override DataFlow::Node getInput() { result.asExpr() = this.(ClassInstanceExpr).getArgument(1) } + override DataFlow::Node getInputNode() { + result.asExpr() = this.(ClassInstanceExpr).getArgument(1) + } } class IvParameterSpecGetIvCall extends MethodCall { @@ -439,8 +399,8 @@ module JCAModel { module NonceArtifactToCipherInitCallConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { exists(NonceParameterInstantiation n | - src = n.asOutputData() and - not exists(IvParameterSpecGetIvCall m | n.getInput().asExpr() = m) + src = n.getOutputNode() and + not exists(IvParameterSpecGetIvCall m | n.getInputNode().asExpr() = m) ) } @@ -455,7 +415,7 @@ module JCAModel { ) or exists(NonceParameterInstantiation n | - node1 = n.getInput() and + node1 = n.getInputNode() and node2.asExpr() = n ) } @@ -482,13 +442,13 @@ module JCAModel { private predicate cipher_mode_str_to_cipher_mode_known( string mode, Crypto::CipherOperationSubtype cipher_mode ) { - mode = "ENCRYPT_MODE" and cipher_mode instanceof Crypto::EncryptionMode + mode = "ENCRYPT_MODE" and cipher_mode instanceof Crypto::EncryptionSubtype or - mode = "WRAP_MODE" and cipher_mode instanceof Crypto::WrapMode + mode = "WRAP_MODE" and cipher_mode instanceof Crypto::WrapSubtype or - mode = "DECRYPT_MODE" and cipher_mode instanceof Crypto::DecryptionMode + mode = "DECRYPT_MODE" and cipher_mode instanceof Crypto::DecryptionSubtype or - mode = "UNWRAP_MODE" and cipher_mode instanceof Crypto::UnwrapMode + mode = "UNWRAP_MODE" and cipher_mode instanceof Crypto::UnwrapSubtype } class CipherInitCall extends MethodCall { @@ -514,7 +474,7 @@ module JCAModel { Crypto::CipherOperationSubtype getCipherOperationModeType() { if cipher_mode_str_to_cipher_mode_known(this.getModeOrigin().getField().getName(), _) then cipher_mode_str_to_cipher_mode_known(this.getModeOrigin().getField().getName(), result) - else result instanceof Crypto::UnknownCipherOperationMode + else result instanceof Crypto::UnknownCipherOperationSubtype } Expr getKeyArg() { @@ -526,4 +486,16 @@ module JCAModel { this.getMethod().getParameterType(2).hasName("AlgorithmParameterSpec") } } + + class CipherInitCallNonceArgConsumer extends Crypto::ArtifactConsumer instanceof Expr { + CipherInitCallNonceArgConsumer() { this = any(CipherInitCall call).getNonceArg() } + + override DataFlow::Node getInputNode() { result.asExpr() = this } + } + + class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer instanceof Expr { + CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() } + + override DataFlow::Node getInputNode() { result.asExpr() = this } + } } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 5abac9e9462f..228f21bd69bf 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -39,16 +39,30 @@ final class DefaultFlowSource = SourceNode; final class DefaultRemoteFlowSource = RemoteFlowSource; -class GenericLocalDataSource extends Crypto::GenericRemoteDataSource { +class GenericLocalDataSource extends Crypto::GenericLocalDataSource { GenericLocalDataSource() { any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this } - override DataFlow::Node asOutputData() { result.asExpr() = this } + override DataFlow::Node getOutputNode() { result.asExpr() = this } - override predicate flowsTo(Crypto::ArtifactLocatableElement other) { - DataSourceToArtifactFlow::flow(this.asOutputData(), other.getInput()) + override predicate flowsTo(Crypto::FlowAwareElement other) { + GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } + + override string getAdditionalDescription() { result = this.toString() } +} + +class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { + GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this } + + override DataFlow::Node getOutputNode() { result.asExpr() = this } + + override predicate flowsTo(Crypto::FlowAwareElement other) { + GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + } + + override string getAdditionalDescription() { result = this.toString() } } /** @@ -56,10 +70,12 @@ class GenericLocalDataSource extends Crypto::GenericRemoteDataSource { * tied to an output node (i.e., the result of the source of randomness) */ abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance { - override DataFlow::Node asOutputData() { result.asExpr() = this } + override DataFlow::Node getOutputNode() { result.asExpr() = this } + + override DataFlow::Node getInputNode() { none() } // TODO: add seed - override predicate flowsTo(Crypto::ArtifactLocatableElement other) { - RNGToArtifactFlow::flow(this.asOutputData(), other.getInput()) + override predicate flowsTo(Crypto::FlowAwareElement other) { + ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } } @@ -76,34 +92,48 @@ class InsecureRandomnessInstance extends RandomnessInstance { } /** - * Random number generation artifact to other artifact flow configuration + * Artifact output to node input configuration */ -module RNGToArtifactFlowConfig implements DataFlow::ConfigSig { +module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { - source = any(Crypto::RandomNumberGenerationInstance rng).asOutputData() + source = any(Crypto::ArtifactElement artifact).getOutputNode() } predicate isSink(DataFlow::Node sink) { - sink = any(Crypto::ArtifactLocatableElement other).getInput() + sink = any(Crypto::FlowAwareElement other).getInputNode() + } + + predicate isBarrierIn(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getOutputNode() } } -module RNGToArtifactFlow = DataFlow::Global; +module ArtifactUniversalFlow = DataFlow::Global; + +abstract class NonceArtifactInstance extends Crypto::NonceArtifactInstance { + override predicate flowsTo(Crypto::FlowAwareElement other) { + ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + } +} /** - * Generic data source to artifact flow configuration + * Generic data source to node input configuration */ -module DataSourceToArtifactFlowConfig implements DataFlow::ConfigSig { +module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { - source = any(Crypto::GenericDataSourceInstance i).asOutputData() + source = any(Crypto::GenericDataSourceInstance i).getOutputNode() } predicate isSink(DataFlow::Node sink) { - sink = any(Crypto::ArtifactLocatableElement other).getInput() + sink = any(Crypto::FlowAwareElement other).getInputNode() + } + + predicate isBarrierIn(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getOutputNode() } } -module DataSourceToArtifactFlow = DataFlow::Global; +module GenericDataSourceUniversalFlow = DataFlow::Global; // Import library-specific modeling import JCA diff --git a/java/ql/src/experimental/Quantum/ReusedNonce.ql b/java/ql/src/experimental/Quantum/ReusedNonce.ql index cb0d6ff9d610..7847781e0be5 100644 --- a/java/ql/src/experimental/Quantum/ReusedNonce.ql +++ b/java/ql/src/experimental/Quantum/ReusedNonce.ql @@ -6,18 +6,16 @@ import experimental.Quantum.Language import semmle.code.java.dataflow.DataFlow -Element getNonceOrigin(Crypto::NonceArtifactInstance nonce) { +Crypto::NodeBase getNonceOrigin(Crypto::NonceNode nonce) { // TODO: this check is currently ultra hacky just for demoing - result = nonce.getInput().asExpr().(VarAccess).getVariable() + result = nonce.getSourceNode() } -from - Crypto::CipherOperationInstance op, Crypto::NonceArtifactInstance nonce1, - Crypto::NonceArtifactInstance nonce2 +from Crypto::CipherOperationNode op, Crypto::NonceNode nonce1, Crypto::NonceNode nonce2 where - op.(Expr).getEnclosingCallable().getName() = "encrypt" and - nonce1 = op.getNonce() and - nonce2 = op.getNonce() and + op.asElement().(Expr).getEnclosingCallable().getName() = "encrypt" and + nonce1 = op.getANonce() and + nonce2 = op.getANonce() and not nonce1 = nonce2 and getNonceOrigin(nonce1) = getNonceOrigin(nonce2) select op, nonce1, nonce2 diff --git a/java/ql/src/experimental/Quantum/TestCipher.ql b/java/ql/src/experimental/Quantum/TestCipher.ql index 14041cd3b224..fa04770d8549 100644 --- a/java/ql/src/experimental/Quantum/TestCipher.ql +++ b/java/ql/src/experimental/Quantum/TestCipher.ql @@ -5,12 +5,12 @@ import experimental.Quantum.Language from - Crypto::CipherOperation op, Crypto::CipherAlgorithm a, Crypto::ModeOfOperationAlgorithm m, - Crypto::PaddingAlgorithm p, Crypto::Nonce nonce + Crypto::CipherOperationNode op, Crypto::CipherAlgorithmNode a, + Crypto::ModeOfOperationAlgorithmNode m, Crypto::PaddingAlgorithmNode p, Crypto::NonceNode nonce where - a = op.getAlgorithm() and + a = op.getAKnownCipherAlgorithm() and m = a.getModeOfOperation() and - p = a.getPadding() and - nonce = op.getNonce() -select op, op.getCipherOperationMode(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), p, - p.getRawAlgorithmName(), nonce + p = a.getPaddingAlgorithm() and + nonce = op.getANonce() +select op, op.getCipherOperationSubtype(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), + p, p.getRawAlgorithmName(), nonce diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 713c503ea655..5889e9eb5e28 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -48,7 +48,7 @@ module CryptographyBase Input> { predicate nodes_graph_impl(NodeBase node, string key, string value) { not ( // exclude certain Artifact nodes with no edges to or from them - node instanceof RandomNumberGeneration and + node instanceof RandomNumberGenerationNode and // TODO: performance? not (edges_graph_impl(node, _, _, _) or edges_graph_impl(_, node, _, _)) ) and @@ -58,7 +58,7 @@ module CryptographyBase Input> { or // CodeQL's DGML output does not include a location key = "Location" and - value = node.getLocation().toString() + value = "" // node.getLocation().toString() or // Known unknown edges should be reported as properties rather than edges node = node.getChild(key) and @@ -77,52 +77,20 @@ module CryptographyBase Input> { } /** - * All elements in the database that are mapped to nodes must extend the following classes + * An element that is flow-aware, i.e., it has an input and output node implicitly used for data flow analysis. */ - abstract class HashOperationInstance extends LocatableElement { } - - abstract class HashAlgorithmInstance extends LocatableElement { } - - abstract class KeyDerivationOperationInstance extends LocatableElement { } - - abstract class KeyDerivationAlgorithmInstance extends LocatableElement { } - - abstract class CipherOperationInstance extends LocatableElement { - abstract CipherAlgorithmInstance getAlgorithm(); - - abstract CipherOperationSubtype getCipherOperationSubtype(); - - abstract NonceArtifactInstance getNonce(); - - abstract DataFlowNode getInputData(); - } - - abstract class CipherAlgorithmInstance extends LocatableElement { } - - abstract class KeyEncapsulationOperationInstance extends LocatableElement { } - - abstract class KeyEncapsulationAlgorithmInstance extends LocatableElement { } - - abstract class EllipticCurveAlgorithmInstance extends LocatableElement { } - - // Non-standalone algorithms - abstract class BlockCipherModeOfOperationAlgorithmInstance extends LocatableElement { } - - abstract class PaddingAlgorithmInstance extends LocatableElement { } - - // Artifacts - abstract class ArtifactLocatableElement extends LocatableElement { + abstract class FlowAwareElement extends LocatableElement { /** * Gets the output node for this artifact, which should usually be the same as `this`. */ - abstract DataFlowNode asOutputData(); + abstract DataFlowNode getOutputNode(); /** * Gets the input node for this artifact. * * If `getInput` is implemented as `none()`, the artifact will not have inbound flow analysis. */ - abstract DataFlowNode getInput(); + abstract DataFlowNode getInputNode(); /** * Holds if this artifact flows to `other`. @@ -133,53 +101,227 @@ module CryptographyBase Input> { * * If `flowsTo` is implemented as `none()`, the artifact will not have outbound flow analysis. */ - abstract predicate flowsTo(ArtifactLocatableElement other); + abstract predicate flowsTo(FlowAwareElement other); + } + + /** + * An element that represents a _known_ cryptographic asset. + */ + abstract class KnownElement extends LocatableElement { + final ConsumerElement getAConsumer() { result.getAKnownSource() = this } + } + + /** + * An element that represents a _known_ cryptographic operation. + */ + abstract class OperationElement extends KnownElement { + /** + * Gets the consumer of algorithms associated with this operation. + */ + abstract AlgorithmConsumer getAlgorithmConsumer(); } - // TODO: WARNING: - // If this overlaps with any other LocatableElement, there will be a cross-product - // This is never to be used for unknowns - abstract class GenericDataSourceInstance extends ArtifactLocatableElement { - final override DataFlowNode getInput() { none() } + /** + * An element that represents a _known_ cryptographic algorithm. + */ + abstract class AlgorithmElement extends KnownElement { } + + /** + * An element that represents a _known_ cryptographic artifact. + */ + abstract class ArtifactElement extends KnownElement, FlowAwareElement { } + + /** + * An element that represents an _unknown_ data-source with a non-statically determinable value. + */ + abstract class GenericDataSourceInstance extends FlowAwareElement { + final override DataFlowNode getInputNode() { none() } + + abstract string getInternalType(); - abstract string getLabel(); + string getAdditionalDescription() { none() } } abstract class GenericConstantOrAllocationSource extends GenericDataSourceInstance { - final override string getLabel() { result = "ConstantData" } // TODO: toString of source? + final override string getInternalType() { result = "ConstantData" } // TODO: toString of source? } abstract class GenericExternalCallSource extends GenericDataSourceInstance { - final override string getLabel() { result = "ExternalCall" } // TODO: call target name or toString of source? + final override string getInternalType() { result = "ExternalCall" } // TODO: call target name or toString of source? } abstract class GenericRemoteDataSource extends GenericDataSourceInstance { - final override string getLabel() { result = "RemoteData" } // TODO: toString of source? + final override string getInternalType() { result = "RemoteData" } // TODO: toString of source? } abstract class GenericLocalDataSource extends GenericDataSourceInstance { - final override string getLabel() { result = "LocalData" } // TODO: toString of source? + final override string getInternalType() { result = "LocalData" } // TODO: toString of source? + } + + /** + * An element that consumes _known_ or _unknown_ cryptographic assets. + * + * Note that known assets are to be modeled explicitly with the `getAKnownSource` predicate, whereas + * unknown assets are modeled implicitly via flow analysis from any `GenericDataSourceInstance` to this element. + * + * A consumer can consume multiple instances and types of assets at once, e.g., both a `PaddingAlgorithm` and `CipherAlgorithm`. + */ + abstract private class ConsumerElement extends FlowAwareElement { + override predicate flowsTo(FlowAwareElement other) { none() } + + override DataFlowNode getOutputNode() { none() } + + GenericDataSourceInstance getAnUnknownSource() { result.flowsTo(this) } + + GenericSourceNode getAnUnknownSourceNode() { result.asElement() = this.getAnUnknownSource() } + + abstract KnownElement getAKnownSource(); + + final NodeBase getAKnownSourceNode() { result.asElement() = this.getAKnownSource() } + + final LocatableElement getAKnownOrUnknownSource() { + result = this.getAKnownSource() + or + result = this.getAnUnknownSource() + } + + NodeBase getAKnownOrUnknownSourceNode() { result.asElement() = this.getAKnownOrUnknownSource() } + } + + /** + * An element that consumes _known_ and _unknown_ values. + * + * A value consumer can consume multiple values and multiple value sources at once. + */ + abstract class ValueConsumer extends ConsumerElement { + final override KnownElement getAKnownSource() { none() } + + abstract string getAKnownValue(Location location); + } + + abstract class AlgorithmConsumer extends ConsumerElement { + final override KnownElement getAKnownSource() { result = this.getAKnownAlgorithmSource() } + + abstract AlgorithmElement getAKnownAlgorithmSource(); } - abstract class DigestArtifactInstance extends ArtifactLocatableElement { } + abstract class ArtifactConsumer extends ConsumerElement { + final override KnownElement getAKnownSource() { result = this.getAKnownArtifactSource() } - abstract class KeyArtifactInstance extends ArtifactLocatableElement { } + final ArtifactElement getAKnownArtifactSource() { result.flowsTo(this) } + } + + abstract class CipherOperationInstance extends OperationElement { + /** + * Gets the subtype of this cipher operation, distinguishing encryption, decryption, key wrapping, and key unwrapping. + */ + abstract CipherOperationSubtype getCipherOperationSubtype(); + + /** + * Gets the consumer of nonces/IVs associated with this cipher operation. + */ + abstract ArtifactConsumer getNonceConsumer(); + + /** + * Gets the consumer of plaintext or ciphertext input associated with this cipher operation. + */ + abstract ArtifactConsumer getMessageConsumer(); + } + + abstract class CipherAlgorithmInstance extends AlgorithmElement { + /** + * Gets the raw name as it appears in source, e.g., "AES/CBC/PKCS7Padding". + * This name is not parsed or formatted. + */ + abstract string getRawAlgorithmName(); + + /** + * Gets the type of this cipher, e.g., "AES" or "ChaCha20". + */ + abstract TCipherType getCipherFamily(); + + /** + * Gets the mode of operation of this cipher, e.g., "GCM" or "CBC". + * + * IMPLEMENTATION NOTE: as a trade-off, this is not a consumer but always either an instance or unknown. + * A mode of operation is therefore assumed to always be part of the cipher algorithm itself. + */ + abstract ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm(); + + /** + * Gets the padding scheme of this cipher, e.g., "PKCS7" or "NoPadding". + * + * IMPLEMENTATION NOTE: as a trade-off, this is not a consumer but always either an instance or unknown. + * A padding algorithm is therefore assumed to always be defined as part of the cipher algorithm itself. + */ + abstract PaddingAlgorithmInstance getPaddingAlgorithm(); + } - abstract class NonceArtifactInstance extends ArtifactLocatableElement { - // no implicit outbound data-flow (only modelled in cipher operations) - override predicate flowsTo(ArtifactLocatableElement other) { none() } + abstract class ModeOfOperationAlgorithmInstance extends AlgorithmElement { + /** + * Gets the type of this mode of operation, e.g., "ECB" or "CBC". + * + * When modeling a new mode of operation, use this predicate to specify the type of the mode. + * + * If a type cannot be determined, the result is `OtherMode`. + */ + abstract TBlockCipherModeOperationType getModeType(); + + /** + * Gets the isolated name as it appears in source, e.g., "CBC" in "AES/CBC/PKCS7Padding". + * + * This name should not be parsed or formatted beyond isolating the raw mode name if necessary. + */ + abstract string getRawModeAlgorithmName(); } - abstract class RandomNumberGenerationInstance extends ArtifactLocatableElement { - final override DataFlowNode getInput() { none() } + abstract class PaddingAlgorithmInstance extends AlgorithmElement { + /** + * Gets the isolated name as it appears in source, e.g., "PKCS7Padding" in "AES/CBC/PKCS7Padding". + * + * This name should not be parsed or formatted beyond isolating the raw padding name if necessary. + */ + abstract string getRawPaddingAlgorithmName(); + + /** + * Gets the type of this padding algorithm, e.g., "PKCS7" or "OAEP". + * + * When modeling a new padding algorithm, use this predicate to specify the type of the padding. + * + * If a type cannot be determined, the result is `OtherPadding`. + */ + abstract TPaddingType getPaddingType(); } + abstract class KeyEncapsulationOperationInstance extends LocatableElement { } + + abstract class KeyEncapsulationAlgorithmInstance extends LocatableElement { } + + abstract class EllipticCurveAlgorithmInstance extends LocatableElement { } + + abstract class HashOperationInstance extends KnownElement { } + + abstract class HashAlgorithmInstance extends KnownElement { } + + abstract class KeyDerivationOperationInstance extends KnownElement { } + + abstract class KeyDerivationAlgorithmInstance extends KnownElement { } + + // Artifacts + abstract class DigestArtifactInstance extends ArtifactElement { } + + abstract class KeyArtifactInstance extends ArtifactElement { } + + abstract class NonceArtifactInstance extends ArtifactElement { } + + abstract class RandomNumberGenerationInstance extends ArtifactElement { } + newtype TNode = // Artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or TNonce(NonceArtifactInstance e) or - TRandomNumberGeneration(RandomNumberGenerationInstance e) or + TRandomNumberGeneration(RandomNumberGenerationInstance e) { e.flowsTo(_) } or // Operations (e.g., hashing, encryption) THashOperation(HashOperationInstance e) or TKeyDerivationOperation(KeyDerivationOperationInstance e) or @@ -193,19 +335,40 @@ module CryptographyBase Input> { TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or // Non-standalone Algorithms (e.g., Mode, Padding) // TODO: need to rename this, as "mode" is getting reused in different contexts, be precise - TBlockCipherModeOfOperationAlgorithm(BlockCipherModeOfOperationAlgorithmInstance e) or + TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or TPaddingAlgorithm(PaddingAlgorithmInstance e) or // Composite and hybrid cryptosystems (e.g., RSA-OAEP used with AES, post-quantum hybrid cryptosystems) // These nodes are always parent nodes and are not modeled but rather defined via library-agnostic patterns. - TKemDemHybridCryptosystem(CipherAlgorithm dem) or // TODO, change this relation and the below ones + TKemDemHybridCryptosystem(CipherAlgorithmNode dem) or // TODO, change this relation and the below ones TKeyAgreementHybridCryptosystem(CipherAlgorithmInstance ka) or TAsymmetricEncryptionMacHybridCryptosystem(CipherAlgorithmInstance enc) or - TPostQuantumHybridCryptosystem(CipherAlgorithmInstance enc) + TPostQuantumHybridCryptosystem(CipherAlgorithmInstance enc) or + // Unknown source node + TGenericSourceNode(GenericDataSourceInstance e) { e.flowsTo(_) } /** * The base class for all cryptographic assets, such as operations and algorithms. * * Each `NodeBase` is a node in a graph of cryptographic operations, where the edges are the relationships between the nodes. + * + * A node, as opposed to a property, is a construct that can reference or be referenced by more than one node. + * For example: a key size is a single value configuring a cipher algorithm, but a single mode of operation algorithm + * can be referenced by multiple disjoint cipher algorithms. For example, even if the same key size value is reused + * for multiple cipher algorithms, the key size holds no information when devolved to that simple value, and it is + * therefore not a "construct" or "element" being reused by multiple nodes. + * + * As a rule of thumb, a node is an algorithm or the use of an algorithm (an operation), as well as structured data + * consumed by or produced by an operation or algorithm (an artifact) that represents a construct beyond its data. + * + * _Example 1_: A seed of a random number generation algorithm has meaning beyond its value, as its reuse in multiple + * random number generation algorithms is more relevant than its underlying value. In contrast, a key size is only + * relevant to analysis in terms of its underlying value. Therefore, an RNG seed is a node; a key size is not. + * + * _Example 2_: A salt for a key derivation function *is* an `ArtifactNode`. + * + * _Example 3_: The iteration count of a key derivation function is *not* a node. + * + * _Example 4_: A nonce for a cipher operation *is* an `ArtifactNode`. */ abstract class NodeBase extends TNode { /** @@ -221,12 +384,7 @@ module CryptographyBase Input> { /** * Returns the location of this node in the code. */ - abstract Location getLocation(); - - /** - * Gets the origin of this node, e.g., a string literal in source describing it. - */ - LocatableElement getOrigin(string value) { none() } + Location getLocation() { result = this.asElement().getLocation() } /** * Returns the child of this node with the given edge name. @@ -240,165 +398,109 @@ module CryptographyBase Input> { * * This predicate is overriden by derived classes to construct the graph of cryptographic operations. */ - predicate properties(string key, string value, Location location) { - key = "Origin" and - location = this.getOrigin(value).getLocation() and - not location = this.getLocation() - } + predicate properties(string key, string value, Location location) { none() } /** * Returns a parent of this node. */ final NodeBase getAParent() { result.getChild(_) = this } + + /** + * Gets the element associated with this node. + */ + abstract LocatableElement asElement(); + } + + /** + * A generic source node is a source of data that is not resolvable to a specific value or type. + */ + private class GenericSourceNode extends NodeBase, TGenericSourceNode { + GenericDataSourceInstance instance; + + GenericSourceNode() { this = TGenericSourceNode(instance) } + + final override string getInternalType() { result = instance.getInternalType() } + + override LocatableElement asElement() { result = instance } + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [ONLY_KNOWN] + key = "Description" and + value = instance.getAdditionalDescription() and + location = this.getLocation() + } } - class Asset = NodeBase; + class AssetNode = NodeBase; /** * An artifact is an instance of data that is used in a cryptographic operation or produced by one. */ - abstract class Artifact extends NodeBase { - /** - * Gets the artifact locatable element associated with this artifact. - * - * *Implementation note*: to avoid cross-products, the result *must* only bind to the - * `ArtifactLocatableElement` that is already associated with the node instance. - */ - abstract ArtifactLocatableElement asArtifactLocatableElement(); - + abstract class ArtifactNode extends NodeBase { /** * Gets the `Artifact` node that is the data source for this artifact. */ - final Artifact getSourceArtifact() { - result.asArtifactLocatableElement() = this.getSourceArtifactElement() - } + final NodeBase getSourceNode() { result.asElement() = this.getSourceElement() } /** * Gets the `ArtifactLocatableElement` that is the data source for this artifact. * * This predicate is equivalent to `getSourceArtifact().asArtifactLocatableElement()`. */ - final ArtifactLocatableElement getSourceArtifactElement() { - not result = this.asArtifactLocatableElement() and - result.flowsTo(this.asArtifactLocatableElement()) + final FlowAwareElement getSourceElement() { + not result = this.asElement() and result.flowsTo(this.asElement()) } + /** + * Gets a string describing the relationship between this artifact and its source. + * + * If a child class defines this predicate as `none()`, no relationship will be reported. + */ + string getSourceNodeRelationship() { result = "Source" } + override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or - // [ONLY_KNOWN] - TODO: known-unknown case handled by reporting a generic source type or unknown as a property - edgeName = "Source" and - result = this.getSourceArtifact() - } - - // TODO: document the below - final private predicate src_generic_data_source_to_label_and_loc(string label, Location location) { - exists(GenericDataSourceInstance instance | - this.getSourceArtifactElement() = instance and - instance.getLabel() = label and - instance.getLocation() = location - ) - } - - final private predicate src_artifact_to_label_and_loc(string label, Location location) { - exists(Artifact a | - this.getSourceArtifact() = a and - a.getInternalType() = label and - a.getLocation() = location - ) - } - - final private predicate source_type_property(string key, string value, Location location) { - key = "SourceType" and - if this.src_artifact_to_label_and_loc(_, _) - then this.src_artifact_to_label_and_loc(value, location) - else - if - exists(this.asArtifactLocatableElement().getInput()) and - not this.src_generic_data_source_to_label_and_loc(_, _) - then value instanceof UnknownPropertyValue and location instanceof UnknownLocation - else this.src_generic_data_source_to_label_and_loc(value, location) - } - - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - this.source_type_property(key, value, location) + // [KNOWN_OR_UNKNOWN] + edgeName = this.getSourceNodeRelationship() and // only holds if not set to none() + if exists(this.getSourceNode()) then result = this.getSourceNode() else result = this } } /** * A nonce or initialization vector */ - private class NonceImpl extends Artifact, TNonce { + final class NonceNode extends ArtifactNode, TNonce { NonceArtifactInstance instance; - NonceImpl() { this = TNonce(instance) } + NonceNode() { this = TNonce(instance) } final override string getInternalType() { result = "Nonce" } - override Location getLocation() { result = instance.getLocation() } - - override ArtifactLocatableElement asArtifactLocatableElement() { result = instance } + override LocatableElement asElement() { result = instance } } - final class Nonce = NonceImpl; - /** * A source of random number generation */ - final private class RandomNumberGenerationImpl extends Artifact, TRandomNumberGeneration { + final class RandomNumberGenerationNode extends ArtifactNode, TRandomNumberGeneration { RandomNumberGenerationInstance instance; - RandomNumberGenerationImpl() { this = TRandomNumberGeneration(instance) } + RandomNumberGenerationNode() { this = TRandomNumberGeneration(instance) } final override string getInternalType() { result = "RandomNumberGeneration" } - override Location getLocation() { result = instance.getLocation() } - - override ArtifactLocatableElement asArtifactLocatableElement() { result = instance } + override LocatableElement asElement() { result = instance } } - final class RandomNumberGeneration = RandomNumberGenerationImpl; - /** * A cryptographic operation, such as hashing or encryption. */ - abstract class Operation extends Asset { - /** - * Gets the algorithm associated with this operation. - */ - abstract Algorithm getAlgorithm(); - - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - /* - * TODO: Consider a case with getProperty, where an unknown value is loaded from the filesystem, - * but a default is specified as such: - * String value = getProperty("property", "default_algorithm") - * In this case, getAlgorithm *could* be resolved to default_algorithm, but in that case, the known - * unknown case, i.e., what is loaded from `property`, would not be reported at all as a known unknown. - * - * Implementation brainstorming: - * We have two cases, and we only considered one before: the case where we can't point to the known unknown. - * The new case is pointing to a known unknown, e.g., "property" loaded via getProperty. - * A potential solution is to create a known unknown node for each node type (particularly algorithms) - * and model those elements in the database to associate with that known unknown type?? - * - * - Idea: use the generic data source concept as the definition of potential known unknowns. - * flow should be tracked from them to anything that could be a "sink" that specifies the relation. - * in this case, the sink would be an instantiaition of an algorithm where the value is not resolvable. - */ - - edgeName = "Uses" and - if exists(this.getAlgorithm()) then result = this.getAlgorithm() else result = this - } - } - - abstract class Algorithm extends Asset { - final override string getInternalType() { result = this.getAlgorithmType() } + abstract class OperationNode extends AssetNode { } + abstract class AlgorithmNode extends AssetNode { /** * Gets the name of this algorithm, e.g., "AES" or "SHA". */ @@ -409,11 +511,6 @@ module CryptographyBase Input> { */ abstract string getRawAlgorithmName(); - /** - * Gets the type of this algorithm, e.g., "hash" or "key derivation". - */ - abstract string getAlgorithmType(); - override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or @@ -425,256 +522,352 @@ module CryptographyBase Input> { } } - /** - * A hashing operation that processes data to generate a hash value. - * - * This operation takes an input message of arbitrary content and length and produces a fixed-size - * hash value as the output using a specified hashing algorithm. - */ - abstract class HashOperation extends Operation, THashOperation { - abstract override HashAlgorithm getAlgorithm(); - //override string getOperationType() { result = "HashOperation" } + newtype TCipherOperationSubtype = + TEncryptionMode() or + TDecryptionMode() or + TWrapMode() or + TUnwrapMode() or + TUnknownCipherOperationMode() + + abstract class CipherOperationSubtype extends TCipherOperationSubtype { + abstract string toString(); } - newtype THashType = - MD2() or - MD4() or - MD5() or - SHA1() or - SHA2() or - SHA3() or - RIPEMD160() or - WHIRLPOOL() or - OtherHashType() + class EncryptionSubtype extends CipherOperationSubtype, TEncryptionMode { + override string toString() { result = "Encrypt" } + } + + class DecryptionSubtype extends CipherOperationSubtype, TDecryptionMode { + override string toString() { result = "Decrypt" } + } + + class WrapSubtype extends CipherOperationSubtype, TWrapMode { + override string toString() { result = "Wrap" } + } + + class UnwrapSubtype extends CipherOperationSubtype, TUnwrapMode { + override string toString() { result = "Unwrap" } + } + + class UnknownCipherOperationSubtype extends CipherOperationSubtype, TUnknownCipherOperationMode { + override string toString() { result = "Unknown" } + } /** - * A hashing algorithm that transforms variable-length input into a fixed-size hash value. + * An encryption operation that processes plaintext to generate a ciphertext. + * This operation takes an input message (plaintext) of arbitrary content and length + * and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). */ - abstract class HashAlgorithm extends Algorithm, THashAlgorithm { - override string getAlgorithmType() { result = "HashAlgorithm" } + final class CipherOperationNode extends OperationNode, TCipherOperation { + CipherOperationInstance instance; - final predicate hashTypeToNameMapping(THashType type, string name) { - type instanceof MD2 and name = "MD2" - or - type instanceof MD4 and name = "MD4" - or - type instanceof MD5 and name = "MD5" - or - type instanceof SHA1 and name = "SHA1" - or - type instanceof SHA2 and name = "SHA2" - or - type instanceof SHA3 and name = "SHA3" + CipherOperationNode() { this = TCipherOperation(instance) } + + override LocatableElement asElement() { result = instance } + + override string getInternalType() { result = "CipherOperation" } + + /** + * Gets the algorithm or unknown source nodes consumed as an algorithm associated with this operation. + */ + NodeBase getACipherAlgorithmOrUnknown() { + result = this.getAKnownCipherAlgorithm() or + result = this.asElement().(OperationElement).getAlgorithmConsumer().getAnUnknownSourceNode() + } + + /** + * Gets a known algorithm associated with this operation + */ + CipherAlgorithmNode getAKnownCipherAlgorithm() { + result = this.asElement().(OperationElement).getAlgorithmConsumer().getAKnownSourceNode() + } + + CipherOperationSubtype getCipherOperationSubtype() { + result = instance.getCipherOperationSubtype() + } + + NodeBase getANonceOrUnknown() { + result = + this.asElement().(CipherOperationInstance).getNonceConsumer().getAKnownOrUnknownSourceNode() + } + + NonceNode getANonce() { + result = this.asElement().(CipherOperationInstance).getNonceConsumer().getAKnownSourceNode() + } + + NodeBase getAMessageOrUnknown() { + result = + this.asElement() + .(CipherOperationInstance) + .getMessageConsumer() + .getAKnownOrUnknownSourceNode() + } + + override NodeBase getChild(string key) { + result = super.getChild(key) or - type instanceof RIPEMD160 and name = "RIPEMD160" + // [KNOWN_OR_UNKNOWN] + key = "Algorithm" and + if exists(this.getACipherAlgorithmOrUnknown()) + then result = this.getACipherAlgorithmOrUnknown() + else result = this or - type instanceof WHIRLPOOL and name = "WHIRLPOOL" + // [KNOWN_OR_UNKNOWN] + key = "Nonce" and + if exists(this.getANonceOrUnknown()) + then result = this.getANonceOrUnknown() + else result = this or - type instanceof OtherHashType and name = this.getRawAlgorithmName() + // [KNOWN_OR_UNKNOWN] + key = "Message" and + if exists(this.getAMessageOrUnknown()) + then result = this.getAMessageOrUnknown() + else result = this } - /** - * Gets the type of this hashing algorithm, e.g., MD5 or SHA. - * - * When modeling a new hashing algorithm, use this predicate to specify the type of the algorithm. - */ - abstract THashType getHashType(); + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [ALWAYS_KNOWN] - Unknown is handled in getCipherOperationMode() + key = "Operation" and + value = this.getCipherOperationSubtype().toString() and + location = this.getLocation() + } + } - override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } + /** + * Block cipher modes of operation algorithms + */ + newtype TBlockCipherModeOperationType = + ECB() or // Not secure, widely used + CBC() or // Vulnerable to padding oracle attacks + GCM() or // Widely used AEAD mode (TLS 1.3, SSH, IPsec) + CTR() or // Fast stream-like encryption (SSH, disk encryption) + XTS() or // Standard for full-disk encryption (BitLocker, LUKS, FileVault) + CCM() or // Used in lightweight cryptography (IoT, WPA2) + SIV() or // Misuse-resistant encryption, used in secure storage + OCB() or // Efficient AEAD mode + OtherMode() + + class ModeOfOperationAlgorithmNode extends AlgorithmNode, TModeOfOperationAlgorithm { + ModeOfOperationAlgorithmInstance instance; + + ModeOfOperationAlgorithmNode() { this = TModeOfOperationAlgorithm(instance) } + + override LocatableElement asElement() { result = instance } + + override string getInternalType() { result = "ModeOfOperation" } + + override string getRawAlgorithmName() { result = instance.getRawModeAlgorithmName() } /** - * Gets the digest size of SHA2 or SHA3 algorithms. + * Gets the type of this mode of operation, e.g., "ECB" or "CBC". * - * This predicate does not need to hold for other algorithms, - * as the digest size is already known based on the algorithm itself. + * When modeling a new mode of operation, use this predicate to specify the type of the mode. * - * For `OtherHashType` algorithms where a digest size should be reported, `THashType` - * should be extended to explicitly model that algorithm. If the algorithm has variable - * or multiple digest size variants, a similar predicate to this one must be defined - * for that algorithm to report the digest size. + * If a type cannot be determined, the result is `OtherMode`. */ - abstract string getSHA2OrSHA3DigestSize(Location location); + TBlockCipherModeOperationType getModeType() { result = instance.getModeType() } bindingset[type] - private string getTypeDigestSizeFixed(THashType type) { - type instanceof MD2 and result = "128" + final private predicate modeToNameMapping(TBlockCipherModeOperationType type, string name) { + type instanceof ECB and name = "ECB" or - type instanceof MD4 and result = "128" + type instanceof CBC and name = "CBC" or - type instanceof MD5 and result = "128" + type instanceof GCM and name = "GCM" or - type instanceof SHA1 and result = "160" + type instanceof CTR and name = "CTR" or - type instanceof RIPEMD160 and result = "160" + type instanceof XTS and name = "XTS" or - type instanceof WHIRLPOOL and result = "512" + type instanceof CCM and name = "CCM" + or + type instanceof SIV and name = "SIV" + or + type instanceof OCB and name = "OCB" + or + type instanceof OtherMode and name = this.getRawAlgorithmName() } + override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } + } + + newtype TPaddingType = + PKCS1_v1_5() or // RSA encryption/signing padding + PKCS7() or // Standard block cipher padding (PKCS5 for 8-byte blocks) + ANSI_X9_23() or // Zero-padding except last byte = padding length + NoPadding() or // Explicit no-padding + OAEP() or // RSA OAEP padding + OtherPadding() + + class PaddingAlgorithmNode extends AlgorithmNode, TPaddingAlgorithm { + PaddingAlgorithmInstance instance; + + PaddingAlgorithmNode() { this = TPaddingAlgorithm(instance) } + + override string getInternalType() { result = "PaddingAlgorithm" } + + override LocatableElement asElement() { result = instance } + + TPaddingType getPaddingType() { result = instance.getPaddingType() } + bindingset[type] - private string getTypeDigestSize(THashType type, Location location) { - result = this.getTypeDigestSizeFixed(type) and location = this.getLocation() + final private predicate paddingToNameMapping(TPaddingType type, string name) { + type instanceof PKCS1_v1_5 and name = "PKCS1_v1_5" or - type instanceof SHA2 and result = this.getSHA2OrSHA3DigestSize(location) + type instanceof PKCS7 and name = "PKCS7" or - type instanceof SHA3 and result = this.getSHA2OrSHA3DigestSize(location) + type instanceof ANSI_X9_23 and name = "ANSI_X9_23" + or + type instanceof NoPadding and name = "NoPadding" + or + type instanceof OAEP and name = "OAEP" + or + type instanceof OtherPadding and name = this.getRawAlgorithmName() } - string getDigestSize(Location location) { - result = this.getTypeDigestSize(this.getHashType(), location) - } + override string getAlgorithmName() { this.paddingToNameMapping(this.getPaddingType(), result) } - final override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - // [KNOWN_OR_UNKNOWN] - key = "DigestSize" and - if exists(this.getDigestSize(location)) - then value = this.getDigestSize(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - } + override string getRawAlgorithmName() { result = instance.getRawPaddingAlgorithmName() } } /** - * An operation that derives one or more keys from an input value. + * A helper type for distinguishing between block and stream ciphers. */ - abstract class KeyDerivationOperation extends Operation, TKeyDerivationOperation { - final override Location getLocation() { - exists(LocatableElement le | this = TKeyDerivationOperation(le) and result = le.getLocation()) - } - //override string getOperationType() { result = "KeyDerivationOperation" } + newtype TCipherStructureType = + Block() or + Stream() or + Asymmetric() or + UnknownCipherStructureType() + + private string getCipherStructureTypeString(TCipherStructureType type) { + type instanceof Block and result = "Block" + or + type instanceof Stream and result = "Stream" + or + type instanceof Asymmetric and result = "Asymmetric" + or + type instanceof UnknownCipherStructureType and result instanceof UnknownPropertyValue } /** - * An algorithm that derives one or more keys from an input value. - * - * Only use this class to model UNKNOWN key derivation algorithms. - * - * For known algorithms, use the specialized classes, e.g., `HKDF` and `PKCS12KDF`. + * Symmetric algorithms */ - abstract class KeyDerivationAlgorithm extends Algorithm, TKeyDerivationAlgorithm { - final override Location getLocation() { - exists(LocatableElement le | this = TKeyDerivationAlgorithm(le) and result = le.getLocation()) - } + newtype TCipherType = + AES() or + Camellia() or + DES() or + TripleDES() or + IDEA() or + CAST5() or + ChaCha20() or + RC4() or + RC5() or + RSA() or + OtherCipherType() - override string getAlgorithmType() { result = "KeyDerivationAlgorithm" } + final class CipherAlgorithmNode extends AlgorithmNode, TCipherAlgorithm { + CipherAlgorithmInstance instance; - override string getAlgorithmName() { result = this.getRawAlgorithmName() } - } + CipherAlgorithmNode() { this = TCipherAlgorithm(instance) } - /** - * An algorithm that derives one or more keys from an input value, using a configurable digest algorithm. - */ - abstract private class KeyDerivationWithDigestParameter extends KeyDerivationAlgorithm { - abstract HashAlgorithm getHashAlgorithm(); + override LocatableElement asElement() { result = instance } - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - ( - // [KNOWN_OR_UNKNOWN] - edgeName = "Uses" and - if exists(this.getHashAlgorithm()) then result = this.getHashAlgorithm() else result = this - ) + override string getInternalType() { result = "CipherAlgorithm" } + + final TCipherStructureType getCipherStructure() { + this.cipherFamilyToNameAndStructure(this.getCipherFamily(), _, result) } - } - /** - * HKDF key derivation function - */ - abstract class HKDF extends KeyDerivationWithDigestParameter { - final override string getAlgorithmName() { result = "HKDF" } - } + final override string getAlgorithmName() { + this.cipherFamilyToNameAndStructure(this.getCipherFamily(), result, _) + } - /** - * PBKDF2 key derivation function - */ - abstract class PBKDF2 extends KeyDerivationWithDigestParameter { - final override string getAlgorithmName() { result = "PBKDF2" } + final override string getRawAlgorithmName() { result = instance.getRawAlgorithmName() } /** - * Gets the iteration count of this key derivation algorithm. + * Gets the key size of this cipher, e.g., "128" or "256". */ - abstract string getIterationCount(Location location); + string getKeySize(Location location) { none() } // TODO /** - * Gets the bit-length of the derived key. + * Gets the type of this cipher, e.g., "AES" or "ChaCha20". */ - abstract string getKeyLength(Location location); + TCipherType getCipherFamily() { result = instance.getCipherFamily() } - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "Iterations" and - if exists(this.getIterationCount(location)) - then value = this.getIterationCount(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "KeyLength" and - if exists(this.getKeyLength(location)) - then value = this.getKeyLength(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) + /** + * Gets the mode of operation of this cipher, e.g., "GCM" or "CBC". + */ + ModeOfOperationAlgorithmNode getModeOfOperation() { + result.asElement() = instance.getModeOfOperationAlgorithm() } - } - - /** - * PKCS12KDF key derivation function - */ - abstract class PKCS12KDF extends KeyDerivationWithDigestParameter { - override string getAlgorithmName() { result = "PKCS12KDF" } /** - * Gets the iteration count of this key derivation algorithm. + * Gets the padding scheme of this cipher, e.g., "PKCS7" or "NoPadding". */ - abstract string getIterationCount(Location location); + PaddingAlgorithmNode getPaddingAlgorithm() { + result.asElement() = instance.getPaddingAlgorithm() + } - /** - * Gets the raw ID argument specifying the intended use of the derived key. - * - * The intended use is defined in RFC 7292, appendix B.3, as follows: - * - * This standard specifies 3 different values for the ID byte mentioned above: - * - * 1. If ID=1, then the pseudorandom bits being produced are to be used - * as key material for performing encryption or decryption. - * - * 2. If ID=2, then the pseudorandom bits being produced are to be used - * as an IV (Initial Value) for encryption or decryption. - * - * 3. If ID=3, then the pseudorandom bits being produced are to be used - * as an integrity key for MACing. - */ - abstract string getIDByte(Location location); + bindingset[type] + final private predicate cipherFamilyToNameAndStructure( + TCipherType type, string name, TCipherStructureType s + ) { + type instanceof AES and name = "AES" and s = Block() + or + type instanceof Camellia and name = "Camellia" and s = Block() + or + type instanceof DES and name = "DES" and s = Block() + or + type instanceof TripleDES and name = "TripleDES" and s = Block() + or + type instanceof IDEA and name = "IDEA" and s = Block() + or + type instanceof CAST5 and name = "CAST5" and s = Block() + or + type instanceof ChaCha20 and name = "ChaCha20" and s = Stream() + or + type instanceof RC4 and name = "RC4" and s = Stream() + or + type instanceof RC5 and name = "RC5" and s = Block() + or + type instanceof RSA and name = "RSA" and s = Asymmetric() + or + type instanceof OtherCipherType and + name = this.getRawAlgorithmName() and + s = UnknownCipherStructureType() + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] + edgeName = "Mode" and + if exists(this.getModeOfOperation()) + then result = this.getModeOfOperation() + else result = this + or + // [KNOWN_OR_UNKNOWN] + edgeName = "Padding" and + if exists(this.getPaddingAlgorithm()) + then result = this.getPaddingAlgorithm() + else result = this + } override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or - ( - // [KNOWN_OR_UNKNOWN] - key = "Iterations" and - if exists(this.getIterationCount(location)) - then value = this.getIterationCount(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) + // [ALWAYS_KNOWN] - unknown case is handled in `getCipherStructureTypeString` + key = "Structure" and + getCipherStructureTypeString(this.getCipherStructure()) = value and + location instanceof UnknownLocation or ( // [KNOWN_OR_UNKNOWN] - key = "IdByte" and - if exists(this.getIDByte(location)) - then value = this.getIDByte(location) + key = "KeySize" and + if exists(this.getKeySize(location)) + then value = this.getKeySize(location) else ( value instanceof UnknownPropertyValue and location instanceof UnknownLocation ) @@ -683,39 +876,191 @@ module CryptographyBase Input> { } /** - * scrypt key derivation function + * A hashing operation that processes data to generate a hash value. + * + * This operation takes an input message of arbitrary content and length and produces a fixed-size + * hash value as the output using a specified hashing algorithm. */ - abstract class SCRYPT extends KeyDerivationAlgorithm { - final override string getAlgorithmName() { result = "scrypt" } + abstract class HashOperationNode extends OperationNode, THashOperation { + abstract HashAlgorithmNode getAlgorithm(); + } + + newtype THashType = + MD2() or + MD4() or + MD5() or + SHA1() or + SHA2() or + SHA3() or + RIPEMD160() or + WHIRLPOOL() or + OtherHashType() + + /** + * A hashing algorithm that transforms variable-length input into a fixed-size hash value. + */ + abstract class HashAlgorithmNode extends AlgorithmNode, THashAlgorithm { + override string getInternalType() { result = "HashAlgorithm" } + + final predicate hashTypeToNameMapping(THashType type, string name) { + type instanceof MD2 and name = "MD2" + or + type instanceof MD4 and name = "MD4" + or + type instanceof MD5 and name = "MD5" + or + type instanceof SHA1 and name = "SHA1" + or + type instanceof SHA2 and name = "SHA2" + or + type instanceof SHA3 and name = "SHA3" + or + type instanceof RIPEMD160 and name = "RIPEMD160" + or + type instanceof WHIRLPOOL and name = "WHIRLPOOL" + or + type instanceof OtherHashType and name = this.getRawAlgorithmName() + } /** - * Gets the iteration count (`N`) argument + * Gets the type of this hashing algorithm, e.g., MD5 or SHA. + * + * When modeling a new hashing algorithm, use this predicate to specify the type of the algorithm. */ - abstract string get_N(Location location); + abstract THashType getHashType(); + + override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } /** - * Gets the block size (`r`) argument + * Gets the digest size of SHA2 or SHA3 algorithms. + * + * This predicate does not need to hold for other algorithms, + * as the digest size is already known based on the algorithm itself. + * + * For `OtherHashType` algorithms where a digest size should be reported, `THashType` + * should be extended to explicitly model that algorithm. If the algorithm has variable + * or multiple digest size variants, a similar predicate to this one must be defined + * for that algorithm to report the digest size. */ - abstract string get_r(Location location); + abstract string getSHA2OrSHA3DigestSize(Location location); + + bindingset[type] + private string getTypeDigestSizeFixed(THashType type) { + type instanceof MD2 and result = "128" + or + type instanceof MD4 and result = "128" + or + type instanceof MD5 and result = "128" + or + type instanceof SHA1 and result = "160" + or + type instanceof RIPEMD160 and result = "160" + or + type instanceof WHIRLPOOL and result = "512" + } + + bindingset[type] + private string getTypeDigestSize(THashType type, Location location) { + result = this.getTypeDigestSizeFixed(type) and location = this.getLocation() + or + type instanceof SHA2 and result = this.getSHA2OrSHA3DigestSize(location) + or + type instanceof SHA3 and result = this.getSHA2OrSHA3DigestSize(location) + } + + string getDigestSize(Location location) { + result = this.getTypeDigestSize(this.getHashType(), location) + } + + final override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [KNOWN_OR_UNKNOWN] + key = "DigestSize" and + if exists(this.getDigestSize(location)) + then value = this.getDigestSize(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + } + } + + /** + * An operation that derives one or more keys from an input value. + */ + abstract class KeyDerivationOperationNode extends OperationNode, TKeyDerivationOperation { + final override Location getLocation() { + exists(LocatableElement le | this = TKeyDerivationOperation(le) and result = le.getLocation()) + } + + override string getInternalType() { result = "KeyDerivationOperation" } + } + + /** + * An algorithm that derives one or more keys from an input value. + * + * Only use this class to model UNKNOWN key derivation algorithms. + * + * For known algorithms, use the specialized classes, e.g., `HKDF` and `PKCS12KDF`. + */ + abstract class KeyDerivationAlgorithmNode extends AlgorithmNode, TKeyDerivationAlgorithm { + final override Location getLocation() { + exists(LocatableElement le | this = TKeyDerivationAlgorithm(le) and result = le.getLocation()) + } + + override string getInternalType() { result = "KeyDerivationAlgorithm" } + + override string getAlgorithmName() { result = this.getRawAlgorithmName() } + } + + /** + * An algorithm that derives one or more keys from an input value, using a configurable digest algorithm. + */ + abstract private class KeyDerivationWithDigestParameterNode extends KeyDerivationAlgorithmNode { + abstract HashAlgorithmNode getHashAlgorithm(); + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + ( + // [KNOWN_OR_UNKNOWN] + edgeName = "Uses" and + if exists(this.getHashAlgorithm()) then result = this.getHashAlgorithm() else result = this + ) + } + } + + /** + * HKDF key derivation function + */ + abstract class HKDFNode extends KeyDerivationWithDigestParameterNode { + final override string getAlgorithmName() { result = "HKDF" } + } + + /** + * PBKDF2 key derivation function + */ + abstract class PBKDF2Node extends KeyDerivationWithDigestParameterNode { + final override string getAlgorithmName() { result = "PBKDF2" } /** - * Gets the parallelization factor (`p`) argument + * Gets the iteration count of this key derivation algorithm. */ - abstract string get_p(Location location); + abstract string getIterationCount(Location location); /** - * Gets the derived key length argument + * Gets the bit-length of the derived key. */ - abstract string getDerivedKeyLength(Location location); + abstract string getKeyLength(Location location); override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or ( // [KNOWN_OR_UNKNOWN] - key = "N" and - if exists(this.get_N(location)) - then value = this.get_N(location) + key = "Iterations" and + if exists(this.getIterationCount(location)) + then value = this.getIterationCount(location) else ( value instanceof UnknownPropertyValue and location instanceof UnknownLocation ) @@ -723,21 +1068,126 @@ module CryptographyBase Input> { or ( // [KNOWN_OR_UNKNOWN] - key = "r" and - if exists(this.get_r(location)) - then value = this.get_r(location) + key = "KeyLength" and + if exists(this.getKeyLength(location)) + then value = this.getKeyLength(location) else ( value instanceof UnknownPropertyValue and location instanceof UnknownLocation ) ) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "p" and - if exists(this.get_p(location)) - then value = this.get_p(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation + } + } + + /** + * PKCS12KDF key derivation function + */ + abstract class PKCS12KDF extends KeyDerivationWithDigestParameterNode { + override string getAlgorithmName() { result = "PKCS12KDF" } + + /** + * Gets the iteration count of this key derivation algorithm. + */ + abstract string getIterationCount(Location location); + + /** + * Gets the raw ID argument specifying the intended use of the derived key. + * + * The intended use is defined in RFC 7292, appendix B.3, as follows: + * + * This standard specifies 3 different values for the ID byte mentioned above: + * + * 1. If ID=1, then the pseudorandom bits being produced are to be used + * as key material for performing encryption or decryption. + * + * 2. If ID=2, then the pseudorandom bits being produced are to be used + * as an IV (Initial Value) for encryption or decryption. + * + * 3. If ID=3, then the pseudorandom bits being produced are to be used + * as an integrity key for MACing. + */ + abstract string getIDByte(Location location); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "Iterations" and + if exists(this.getIterationCount(location)) + then value = this.getIterationCount(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "IdByte" and + if exists(this.getIDByte(location)) + then value = this.getIDByte(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + } + } + + /** + * scrypt key derivation function + */ + abstract class SCRYPT extends KeyDerivationAlgorithmNode { + final override string getAlgorithmName() { result = "scrypt" } + + /** + * Gets the iteration count (`N`) argument + */ + abstract string get_N(Location location); + + /** + * Gets the block size (`r`) argument + */ + abstract string get_r(Location location); + + /** + * Gets the parallelization factor (`p`) argument + */ + abstract string get_p(Location location); + + /** + * Gets the derived key length argument + */ + abstract string getDerivedKeyLength(Location location); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "N" and + if exists(this.get_N(location)) + then value = this.get_N(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "r" and + if exists(this.get_r(location)) + then value = this.get_r(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + ) + or + ( + // [KNOWN_OR_UNKNOWN] + key = "p" and + if exists(this.get_p(location)) + then value = this.get_p(location) + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation ) ) or @@ -787,7 +1237,7 @@ module CryptographyBase Input> { ES() or OtherEllipticCurveType() - abstract class EllipticCurve extends Algorithm, TEllipticCurveAlgorithm { + abstract class EllipticCurve extends AlgorithmNode, TEllipticCurveAlgorithm { abstract string getKeySize(Location location); abstract TEllipticCurveType getCurveFamily(); @@ -822,311 +1272,7 @@ module CryptographyBase Input> { abstract override string getRawAlgorithmName(); } - newtype TCipherOperationSubtype = - TEncryptionMode() or - TDecryptionMode() or - TWrapMode() or - TUnwrapMode() or - TUnknownCipherOperationMode() - - abstract class CipherOperationSubtype extends TCipherOperationSubtype { - abstract string toString(); - } - - class EncryptionMode extends CipherOperationSubtype, TEncryptionMode { - override string toString() { result = "Encrypt" } - } - - class DecryptionMode extends CipherOperationSubtype, TDecryptionMode { - override string toString() { result = "Decrypt" } - } - - class WrapMode extends CipherOperationSubtype, TWrapMode { - override string toString() { result = "Wrap" } - } - - class UnwrapMode extends CipherOperationSubtype, TUnwrapMode { - override string toString() { result = "Unwrap" } - } - - class UnknownCipherOperationMode extends CipherOperationSubtype, TUnknownCipherOperationMode { - override string toString() { result = "Unknown" } - } - - /** - * An encryption operation that processes plaintext to generate a ciphertext. - * This operation takes an input message (plaintext) of arbitrary content and length - * and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). - */ - class CipherOperationImpl extends Operation, TCipherOperation { - CipherOperationInstance instance; - - CipherOperationImpl() { this = TCipherOperation(instance) } - - override string getInternalType() { result = "CipherOperation" } - - override Location getLocation() { result = instance.getLocation() } - - CipherOperationSubtype getCipherOperationMode() { - result = instance.getCipherOperationSubtype() - } - - final override CipherAlgorithm getAlgorithm() { result.getInstance() = instance.getAlgorithm() } - - override NodeBase getChild(string key) { - result = super.getChild(key) - or - // [KNOWN_OR_UNKNOWN] - key = "Nonce" and - if exists(this.getNonce()) then result = this.getNonce() else result = this - } - - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - // [ALWAYS_KNOWN] - Unknown is handled in getCipherOperationMode() - key = "Operation" and - value = this.getCipherOperationMode().toString() and - location = this.getLocation() - } - - /** - * Gets the initialization vector associated with this encryption operation. - * - * This predicate does not need to hold for all encryption operations, - * as the initialization vector is not always required. - */ - Nonce getNonce() { result = TNonce(instance.getNonce()) } - - DataFlowNode getInputData() { result = instance.getInputData() } - } - - final class CipherOperation = CipherOperationImpl; - - /** - * Block cipher modes of operation algorithms - */ - newtype TBlockCipherModeOperationType = - ECB() or // Not secure, widely used - CBC() or // Vulnerable to padding oracle attacks - GCM() or // Widely used AEAD mode (TLS 1.3, SSH, IPsec) - CTR() or // Fast stream-like encryption (SSH, disk encryption) - XTS() or // Standard for full-disk encryption (BitLocker, LUKS, FileVault) - CCM() or // Used in lightweight cryptography (IoT, WPA2) - SIV() or // Misuse-resistant encryption, used in secure storage - OCB() or // Efficient AEAD mode - OtherMode() - - abstract class ModeOfOperationAlgorithm extends Algorithm, TBlockCipherModeOfOperationAlgorithm { - override string getAlgorithmType() { result = "ModeOfOperation" } - - /** - * Gets the type of this mode of operation, e.g., "ECB" or "CBC". - * - * When modeling a new mode of operation, use this predicate to specify the type of the mode. - * - * If a type cannot be determined, the result is `OtherMode`. - */ - abstract TBlockCipherModeOperationType getModeType(); - - bindingset[type] - final private predicate modeToNameMapping(TBlockCipherModeOperationType type, string name) { - type instanceof ECB and name = "ECB" - or - type instanceof CBC and name = "CBC" - or - type instanceof GCM and name = "GCM" - or - type instanceof CTR and name = "CTR" - or - type instanceof XTS and name = "XTS" - or - type instanceof CCM and name = "CCM" - or - type instanceof SIV and name = "SIV" - or - type instanceof OCB and name = "OCB" - or - type instanceof OtherMode and name = this.getRawAlgorithmName() - } - - override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } - } - - newtype TPaddingType = - PKCS1_v1_5() or // RSA encryption/signing padding - PKCS7() or // Standard block cipher padding (PKCS5 for 8-byte blocks) - ANSI_X9_23() or // Zero-padding except last byte = padding length - NoPadding() or // Explicit no-padding - OAEP() or // RSA OAEP padding - OtherPadding() - - abstract class PaddingAlgorithm extends Algorithm, TPaddingAlgorithm { - override string getAlgorithmType() { result = "PaddingAlgorithm" } - - /** - * Gets the type of this padding algorithm, e.g., "PKCS7" or "OAEP". - * - * When modeling a new padding algorithm, use this predicate to specify the type of the padding. - * - * If a type cannot be determined, the result is `OtherPadding`. - */ - abstract TPaddingType getPaddingType(); - - bindingset[type] - final private predicate paddingToNameMapping(TPaddingType type, string name) { - type instanceof PKCS1_v1_5 and name = "PKCS1_v1_5" - or - type instanceof PKCS7 and name = "PKCS7" - or - type instanceof ANSI_X9_23 and name = "ANSI_X9_23" - or - type instanceof NoPadding and name = "NoPadding" - or - type instanceof OAEP and name = "OAEP" - or - type instanceof OtherPadding and name = this.getRawAlgorithmName() - } - - override string getAlgorithmName() { this.paddingToNameMapping(this.getPaddingType(), result) } - } - - /** - * A helper type for distinguishing between block and stream ciphers. - */ - newtype TCipherStructureType = - Block() or - Stream() or - Asymmetric() or - UnknownCipherStructureType() - - private string getCipherStructureTypeString(TCipherStructureType type) { - type instanceof Block and result = "Block" - or - type instanceof Stream and result = "Stream" - or - type instanceof Asymmetric and result = "Asymmetric" - or - type instanceof UnknownCipherStructureType and result instanceof UnknownPropertyValue - } - - /** - * Symmetric algorithms - */ - newtype TCipherType = - AES() or - Camellia() or - DES() or - TripleDES() or - IDEA() or - CAST5() or - ChaCha20() or - RC4() or - RC5() or - RSA() or - OtherCipherType() - - abstract class CipherAlgorithm extends Algorithm, TCipherAlgorithm { - final LocatableElement getInstance() { this = TCipherAlgorithm(result) } - - final TCipherStructureType getCipherStructure() { - this.cipherFamilyToNameAndStructure(this.getCipherFamily(), _, result) - } - - final override string getAlgorithmName() { - this.cipherFamilyToNameAndStructure(this.getCipherFamily(), result, _) - } - - override string getAlgorithmType() { result = "CipherAlgorithm" } - - /** - * Gets the key size of this cipher, e.g., "128" or "256". - */ - abstract string getKeySize(Location location); - - /** - * Gets the type of this cipher, e.g., "AES" or "ChaCha20". - */ - abstract TCipherType getCipherFamily(); - - /** - * Gets the mode of operation of this cipher, e.g., "GCM" or "CBC". - */ - abstract ModeOfOperationAlgorithm getModeOfOperation(); - - /** - * Gets the padding scheme of this cipher, e.g., "PKCS7" or "NoPadding". - */ - abstract PaddingAlgorithm getPadding(); - - bindingset[type] - final private predicate cipherFamilyToNameAndStructure( - TCipherType type, string name, TCipherStructureType s - ) { - type instanceof AES and name = "AES" and s = Block() - or - type instanceof Camellia and name = "Camellia" and s = Block() - or - type instanceof DES and name = "DES" and s = Block() - or - type instanceof TripleDES and name = "TripleDES" and s = Block() - or - type instanceof IDEA and name = "IDEA" and s = Block() - or - type instanceof CAST5 and name = "CAST5" and s = Block() - or - type instanceof ChaCha20 and name = "ChaCha20" and s = Stream() - or - type instanceof RC4 and name = "RC4" and s = Stream() - or - type instanceof RC5 and name = "RC5" and s = Block() - or - type instanceof RSA and name = "RSA" and s = Asymmetric() - or - type instanceof OtherCipherType and - name = this.getRawAlgorithmName() and - s = UnknownCipherStructureType() - } - - //mode, padding scheme, keysize, block/stream, auth'd - //nodes = mode, padding scheme - //properties = keysize, block/stream, auth'd - //leave authd to lang specific - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - // [KNOWN_OR_UNKNOWN] - edgeName = "Mode" and - if exists(this.getModeOfOperation()) - then result = this.getModeOfOperation() - else result = this - or - // [KNOWN_OR_UNKNOWN] - edgeName = "Padding" and - if exists(this.getPadding()) then result = this.getPadding() else result = this - } - - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - // [ALWAYS_KNOWN] - unknown case is handled in `getCipherStructureTypeString` - key = "Structure" and - getCipherStructureTypeString(this.getCipherStructure()) = value and - location instanceof UnknownLocation - or - ( - // [KNOWN_OR_UNKNOWN] - key = "KeySize" and - if exists(this.getKeySize(location)) - then value = this.getKeySize(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - } - } - - abstract class KEMAlgorithm extends TKeyEncapsulationAlgorithm, Algorithm { - final override string getAlgorithmType() { result = "KeyEncapsulationAlgorithm" } + abstract class KEMAlgorithm extends TKeyEncapsulationAlgorithm, AlgorithmNode { + final override string getInternalType() { result = "KeyEncapsulationAlgorithm" } } } From ef0614ad45d451e6f4982279230e5ba25fc050de Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 27 Feb 2025 05:45:27 +0100 Subject: [PATCH 031/122] Restore removed node location output --- shared/cryptography/codeql/cryptography/Model.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 5889e9eb5e28..2006804952a8 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -58,7 +58,7 @@ module CryptographyBase Input> { or // CodeQL's DGML output does not include a location key = "Location" and - value = "" // node.getLocation().toString() + value = node.getLocation().toString() or // Known unknown edges should be reported as properties rather than edges node = node.getChild(key) and From 0354afc365a8db1fa22b917f358d32bfa047bb24 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 27 Feb 2025 15:54:38 +0100 Subject: [PATCH 032/122] Make ArtifactConsumers instances of some Artifacts TODO: refactor the interfaces --- java/ql/lib/experimental/Quantum/JCA.qll | 50 ++++++++----------- java/ql/lib/experimental/Quantum/Language.qll | 39 ++++++++++++++- .../codeql/cryptography/Model.qll | 19 +++---- 3 files changed, 68 insertions(+), 40 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index f1e1ce9ccdc3..4370d34ca28a 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -346,7 +346,7 @@ module JCAModel { override Crypto::CipherOperationSubtype getCipherOperationSubtype() { result = mode } - override Crypto::ArtifactConsumer getNonceConsumer() { + override Crypto::NonceArtifactConsumer getNonceConsumer() { result = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg() } @@ -360,9 +360,10 @@ module JCAModel { /** * Initialization vectors and other nonce artifacts */ - abstract class NonceParameterInstantiation extends NonceArtifactInstance instanceof ClassInstanceExpr - { - override DataFlow::Node getOutputNode() { result.asExpr() = this } + abstract class NonceParameterInstantiation extends ClassInstanceExpr { + DataFlow::Node getOutputNode() { result.asExpr() = this } + + abstract DataFlow::Node getInputNode(); } class IvParameterSpecInstance extends NonceParameterInstantiation { @@ -396,32 +397,25 @@ module JCAModel { } } - module NonceArtifactToCipherInitCallConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { - exists(NonceParameterInstantiation n | - src = n.getOutputNode() and - not exists(IvParameterSpecGetIvCall m | n.getInputNode().asExpr() = m) - ) - } + predicate additionalFlowSteps(DataFlow::Node node1, DataFlow::Node node2) { + exists(IvParameterSpecGetIvCall m | + node1.asExpr() = m.getQualifier() and + node2.asExpr() = m + ) + or + exists(NonceParameterInstantiation n | + node1 = n.getInputNode() and + node2 = n.getOutputNode() + ) + } - predicate isSink(DataFlow::Node sink) { - exists(CipherInitCall c | c.getNonceArg() = sink.asExpr()) - } + class NonceAdditionalFlowInputStep extends AdditionalFlowInputStep { + DataFlow::Node output; - predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(IvParameterSpecGetIvCall m | - node1.asExpr() = m.getQualifier() and - node2.asExpr() = m - ) - or - exists(NonceParameterInstantiation n | - node1 = n.getInputNode() and - node2.asExpr() = n - ) - } - } + NonceAdditionalFlowInputStep() { additionalFlowSteps(this, output) } - module NonceArtifactToCipherInitCallFlow = DataFlow::Global; + override DataFlow::Node getOutput() { result = output } + } /** * A data-flow configuration to track flow from a mode field access to @@ -487,7 +481,7 @@ module JCAModel { } } - class CipherInitCallNonceArgConsumer extends Crypto::ArtifactConsumer instanceof Expr { + class CipherInitCallNonceArgConsumer extends Crypto::NonceArtifactConsumer instanceof Expr { CipherInitCallNonceArgConsumer() { this = any(CipherInitCall call).getNonceArg() } override DataFlow::Node getInputNode() { result.asExpr() = this } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 228f21bd69bf..a2945bb6dd38 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -65,6 +65,19 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { override string getAdditionalDescription() { result = this.toString() } } +class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { + override DataFlow::Node getOutputNode() { result.asExpr() = this } + + override predicate flowsTo(Crypto::FlowAwareElement other) { + other instanceof NonceArtifactInstance and + // limit to only nonces for now + // TODO: separate config to avoid blowing up data-flow analysis + GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + } + + override string getAdditionalDescription() { result = this.toString() } +} + /** * Random number generation, where each instance is modelled as the expression * tied to an output node (i.e., the result of the source of randomness) @@ -94,6 +107,12 @@ class InsecureRandomnessInstance extends RandomnessInstance { /** * Artifact output to node input configuration */ +abstract class AdditionalFlowInputStep extends DataFlow::Node { + abstract DataFlow::Node getOutput(); + + final DataFlow::Node getInput() { result = this } +} + module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source = any(Crypto::ArtifactElement artifact).getOutputNode() @@ -106,14 +125,28 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { predicate isBarrierIn(DataFlow::Node node) { node = any(Crypto::FlowAwareElement element).getOutputNode() } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node1.(AdditionalFlowInputStep).getOutput() = node2 + } } module ArtifactUniversalFlow = DataFlow::Global; -abstract class NonceArtifactInstance extends Crypto::NonceArtifactInstance { +class NonceArtifactInstance extends Crypto::NonceArtifactInstance { + NonceArtifactInstance() { this instanceof Crypto::NonceArtifactConsumer } + override predicate flowsTo(Crypto::FlowAwareElement other) { ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } + + override DataFlow::Node getOutputNode() { + result = this.(Crypto::NonceArtifactConsumer).getOutputNode() + } + + override DataFlow::Node getInputNode() { + result = this.(Crypto::NonceArtifactConsumer).getInputNode() + } } /** @@ -131,6 +164,10 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { predicate isBarrierIn(DataFlow::Node node) { node = any(Crypto::FlowAwareElement element).getOutputNode() } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node1.(AdditionalFlowInputStep).getOutput() = node2 + } } module GenericDataSourceUniversalFlow = DataFlow::Global; diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 2006804952a8..8799bbef572a 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -205,12 +205,16 @@ module CryptographyBase Input> { abstract AlgorithmElement getAKnownAlgorithmSource(); } - abstract class ArtifactConsumer extends ConsumerElement { + abstract class ArtifactConsumer extends ConsumerElement, ArtifactElement { final override KnownElement getAKnownSource() { result = this.getAKnownArtifactSource() } final ArtifactElement getAKnownArtifactSource() { result.flowsTo(this) } } + abstract class NonceArtifactConsumer extends ArtifactConsumer { + NonceArtifactInstance asNonce() { result = this } + } + abstract class CipherOperationInstance extends OperationElement { /** * Gets the subtype of this cipher operation, distinguishing encryption, decryption, key wrapping, and key unwrapping. @@ -220,7 +224,7 @@ module CryptographyBase Input> { /** * Gets the consumer of nonces/IVs associated with this cipher operation. */ - abstract ArtifactConsumer getNonceConsumer(); + abstract NonceArtifactConsumer getNonceConsumer(); /** * Gets the consumer of plaintext or ciphertext input associated with this cipher operation. @@ -586,13 +590,8 @@ module CryptographyBase Input> { result = instance.getCipherOperationSubtype() } - NodeBase getANonceOrUnknown() { - result = - this.asElement().(CipherOperationInstance).getNonceConsumer().getAKnownOrUnknownSourceNode() - } - NonceNode getANonce() { - result = this.asElement().(CipherOperationInstance).getNonceConsumer().getAKnownSourceNode() + result.asElement() = this.asElement().(CipherOperationInstance).getNonceConsumer().asNonce() } NodeBase getAMessageOrUnknown() { @@ -614,9 +613,7 @@ module CryptographyBase Input> { or // [KNOWN_OR_UNKNOWN] key = "Nonce" and - if exists(this.getANonceOrUnknown()) - then result = this.getANonceOrUnknown() - else result = this + if exists(this.getANonce()) then result = this.getANonce() else result = this or // [KNOWN_OR_UNKNOWN] key = "Message" and From cf33cf7653c0d8d902a0537bf098ad26eeb08458 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Fri, 28 Feb 2025 15:21:46 +0100 Subject: [PATCH 033/122] Add input and output nodes and fix cross product --- java/ql/lib/experimental/Quantum/JCA.qll | 42 +++-- java/ql/lib/experimental/Quantum/Language.qll | 68 +++++--- .../codeql/cryptography/Model.qll | 145 ++++++++++++++---- 3 files changed, 193 insertions(+), 62 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 4370d34ca28a..8d748772bffc 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -59,14 +59,22 @@ module JCAModel { Expr getProviderArg() { result = this.getArgument(1) } } - private class JCACipherOperationCall extends Call { - JCACipherOperationCall() { + private class CipherOperationCall extends MethodCall { + CipherOperationCall() { exists(string s | s in ["doFinal", "wrap", "unwrap"] | - this.getCallee().hasQualifiedName("javax.crypto", "Cipher", s) + this.getMethod().hasQualifiedName("javax.crypto", "Cipher", s) ) } - DataFlow::Node getMessageArg() { result.asExpr() = this.getArgument(0) } + Expr getInput() { result = this.getArgument(0) } + + Expr getOutput() { + result = this.getArgument(3) + or + this.getMethod().getReturnType().hasName("byte[]") and result = this + } + + DataFlow::Node getMessageArg() { result.asExpr() = this.getInput() } } /** @@ -304,7 +312,7 @@ module JCAModel { predicate isSink(DataFlow::Node sink, FlowState state) { none() } predicate isSink(DataFlow::Node sink) { - exists(JCACipherOperationCall c | c.getQualifier() = sink.asExpr()) + exists(CipherOperationCall c | c.getQualifier() = sink.asExpr()) } predicate isAdditionalFlowStep( @@ -330,7 +338,7 @@ module JCAModel { class CipherOperationInstance extends Crypto::CipherOperationInstance instanceof Call { Crypto::CipherOperationSubtype mode; CipherGetInstanceToCipherOperationFlow::PathNode sink; - JCACipherOperationCall doFinalize; + CipherOperationCall doFinalize; CipherGetInstanceAlgorithmArg consumer; CipherOperationInstance() { @@ -350,11 +358,15 @@ module JCAModel { result = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg() } - override Crypto::ArtifactConsumer getMessageConsumer() { + override Crypto::CipherInputConsumer getInputConsumer() { result = doFinalize.getMessageArg().asExpr() } override Crypto::AlgorithmConsumer getAlgorithmConsumer() { result = consumer } + + override Crypto::CipherOutputArtifactInstance getOutputArtifact() { + result = doFinalize.getOutput() + } } /** @@ -481,15 +493,27 @@ module JCAModel { } } - class CipherInitCallNonceArgConsumer extends Crypto::NonceArtifactConsumer instanceof Expr { + class CipherInitCallNonceArgConsumer extends NonceArtifactConsumer instanceof Expr { CipherInitCallNonceArgConsumer() { this = any(CipherInitCall call).getNonceArg() } override DataFlow::Node getInputNode() { result.asExpr() = this } } - class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer instanceof Expr { + class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer { CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() } override DataFlow::Node getInputNode() { result.asExpr() = this } } + + class CipherMessageInputConsumer extends Crypto::CipherInputConsumer { + CipherMessageInputConsumer() { this = any(CipherOperationCall call).getMessageArg().asExpr() } + + override DataFlow::Node getInputNode() { result.asExpr() = this } + } + + class CipherOperationCallOutput extends CipherOutputArtifact { + CipherOperationCallOutput() { this = any(CipherOperationCall call).getOutput() } + + override DataFlow::Node getOutputNode() { result.asExpr() = this } + } } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index a2945bb6dd38..03c351005380 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -69,8 +69,6 @@ class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource insta override DataFlow::Node getOutputNode() { result.asExpr() = this } override predicate flowsTo(Crypto::FlowAwareElement other) { - other instanceof NonceArtifactInstance and - // limit to only nonces for now // TODO: separate config to avoid blowing up data-flow analysis GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } @@ -113,40 +111,42 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node { final DataFlow::Node getInput() { result = this } } -module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - source = any(Crypto::ArtifactElement artifact).getOutputNode() - } +module ArtifactUniversalFlow = DataFlow::Global; - predicate isSink(DataFlow::Node sink) { - sink = any(Crypto::FlowAwareElement other).getInputNode() +class NonceArtifactConsumer extends Crypto::NonceArtifactInstance instanceof Crypto::NonceArtifactConsumer +{ + override predicate flowsTo(Crypto::FlowAwareElement other) { + ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } - predicate isBarrierIn(DataFlow::Node node) { - node = any(Crypto::FlowAwareElement element).getOutputNode() + override DataFlow::Node getOutputNode() { + result = this.(Crypto::NonceArtifactConsumer).getOutputNode() } - predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - node1.(AdditionalFlowInputStep).getOutput() = node2 + override DataFlow::Node getInputNode() { + result = this.(Crypto::NonceArtifactConsumer).getInputNode() } } -module ArtifactUniversalFlow = DataFlow::Global; - -class NonceArtifactInstance extends Crypto::NonceArtifactInstance { - NonceArtifactInstance() { this instanceof Crypto::NonceArtifactConsumer } - +class CipherInputConsumer extends Crypto::CipherInputArtifactInstance instanceof Crypto::CipherInputConsumer +{ override predicate flowsTo(Crypto::FlowAwareElement other) { ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } - override DataFlow::Node getOutputNode() { - result = this.(Crypto::NonceArtifactConsumer).getOutputNode() - } + override DataFlow::Node getOutputNode() { none() } override DataFlow::Node getInputNode() { - result = this.(Crypto::NonceArtifactConsumer).getInputNode() + result = this.(Crypto::CipherInputArtifactInstance).getInputNode() + } +} + +abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance { + override predicate flowsTo(Crypto::FlowAwareElement other) { + ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } + + override DataFlow::Node getInputNode() { none() } } /** @@ -161,6 +161,32 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { 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 ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::ArtifactElement 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() } diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 8799bbef572a..83111519a006 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -46,12 +46,7 @@ module CryptographyBase Input> { } predicate nodes_graph_impl(NodeBase node, string key, string value) { - not ( - // exclude certain Artifact nodes with no edges to or from them - node instanceof RandomNumberGenerationNode and - // TODO: performance? - not (edges_graph_impl(node, _, _, _) or edges_graph_impl(_, node, _, _)) - ) and + not node.isExcludedFromGraph() and ( key = "semmle.label" and value = node.toString() @@ -106,6 +101,9 @@ module CryptographyBase Input> { /** * An element that represents a _known_ cryptographic asset. + * + * CROSS PRODUCT WARNING: Do not model any *other* element that is a `FlowAwareElement` to the same + * instance in the database, as every other `KnownElement` will share that output artifact's flow. */ abstract class KnownElement extends LocatableElement { final ConsumerElement getAConsumer() { result.getAKnownSource() = this } @@ -167,25 +165,21 @@ module CryptographyBase Input> { * A consumer can consume multiple instances and types of assets at once, e.g., both a `PaddingAlgorithm` and `CipherAlgorithm`. */ abstract private class ConsumerElement extends FlowAwareElement { + abstract KnownElement getAKnownSource(); + override predicate flowsTo(FlowAwareElement other) { none() } override DataFlowNode getOutputNode() { none() } - GenericDataSourceInstance getAnUnknownSource() { result.flowsTo(this) } - - GenericSourceNode getAnUnknownSourceNode() { result.asElement() = this.getAnUnknownSource() } - - abstract KnownElement getAKnownSource(); - - final NodeBase getAKnownSourceNode() { result.asElement() = this.getAKnownSource() } + final GenericDataSourceInstance getAnUnknownSource() { + result.flowsTo(this) and not result = this.getAKnownSource() + } - final LocatableElement getAKnownOrUnknownSource() { - result = this.getAKnownSource() - or - result = this.getAnUnknownSource() + final GenericSourceNode getAnUnknownSourceNode() { + result.asElement() = this.getAnUnknownSource() } - NodeBase getAKnownOrUnknownSourceNode() { result.asElement() = this.getAKnownOrUnknownSource() } + final NodeBase getAKnownSourceNode() { result.asElement() = this.getAKnownSource() } } /** @@ -205,16 +199,47 @@ module CryptographyBase Input> { abstract AlgorithmElement getAKnownAlgorithmSource(); } - abstract class ArtifactConsumer extends ConsumerElement, ArtifactElement { + /** + * An `ArtifactConsumer` represents an element in code that consumes an artifact. + * + * The concept of "`ArtifactConsumer` = `ArtifactNode`" should be used for inputs, as a consumer can be directly tied + * to the artifact it receives, thereby becoming the definitive contextual source for that artifact. + * + * For example, consider a nonce artifact consumer: + * + * A `NonceArtifactConsumer` is always the `NonceArtifactInstance` itself, since data only becomes (i.e., is determined to be) + * a `NonceArtifactInstance` when it is consumed in a context that expects a nonce (e.g., an argument expecting nonce data). + * In this case, the artifact (nonce) is fully defined by the context in which it is consumed, and the consumer embodies + * that identity without the need for additional differentiation. Without the context a consumer provides, that data could + * otherwise be any other type of artifact or even simply random data. + * + * + * Architectural Implications: + * * By directly coupling a consumer with the node that receives an artifact, + * the data flow is fully transparent with the consumer itself serving only as a transparent node. + * * An artifact's properties (such as being a nonce) are not necessarily inherent; they are determined by the context in which the artifact is consumed. + * The consumer node is therefore essential in defining these properties for inputs. + * * This approach reduces ambiguity by avoiding separate notions of "artifact source" and "consumer", as the node itself encapsulates both roles. + * * Instances of nodes do not necessarily have to come from a consumer, allowing additional modelling of an artifact to occur outside of the consumer. + */ + abstract class ArtifactConsumer extends ConsumerElement { final override KnownElement getAKnownSource() { result = this.getAKnownArtifactSource() } final ArtifactElement getAKnownArtifactSource() { result.flowsTo(this) } } - abstract class NonceArtifactConsumer extends ArtifactConsumer { + abstract class NonceArtifactConsumer extends ArtifactConsumer, NonceArtifactInstance { NonceArtifactInstance asNonce() { result = this } } + abstract class CipherInputArtifactInstance extends ArtifactElement { } + + abstract class CipherInputConsumer extends ArtifactConsumer, CipherInputArtifactInstance { + CipherInputArtifactInstance asCipherInput() { result = this } + } + + abstract class CipherOutputArtifactInstance extends ArtifactElement { } + abstract class CipherOperationInstance extends OperationElement { /** * Gets the subtype of this cipher operation, distinguishing encryption, decryption, key wrapping, and key unwrapping. @@ -229,7 +254,16 @@ module CryptographyBase Input> { /** * Gets the consumer of plaintext or ciphertext input associated with this cipher operation. */ - abstract ArtifactConsumer getMessageConsumer(); + abstract CipherInputConsumer getInputConsumer(); + + /** + * Gets the output artifact of this cipher operation. + * + * Implementation guidelines: + * 1. Each unique output target should have an artifact. + * 1. Discarded outputs from intermittent calls should not be artifacts. + */ + abstract CipherOutputArtifactInstance getOutputArtifact(); } abstract class CipherAlgorithmInstance extends AlgorithmElement { @@ -325,6 +359,8 @@ module CryptographyBase Input> { TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or TNonce(NonceArtifactInstance e) or + TCipherInput(CipherInputArtifactInstance e) or + TCipherOutput(CipherOutputArtifactInstance e) or TRandomNumberGeneration(RandomNumberGenerationInstance e) { e.flowsTo(_) } or // Operations (e.g., hashing, encryption) THashOperation(HashOperationInstance e) or @@ -413,6 +449,11 @@ module CryptographyBase Input> { * Gets the element associated with this node. */ abstract LocatableElement asElement(); + + /** + * If this predicate holds, this node should be excluded from the graph. + */ + predicate isExcludedFromGraph() { none() } } /** @@ -435,6 +476,10 @@ module CryptographyBase Input> { value = instance.getAdditionalDescription() and location = this.getLocation() } + + override predicate isExcludedFromGraph() { + not exists(NodeBase other | not other = this and other.getChild(_) = this) + } } class AssetNode = NodeBase; @@ -444,9 +489,12 @@ module CryptographyBase Input> { */ abstract class ArtifactNode extends NodeBase { /** - * Gets the `Artifact` node that is the data source for this artifact. + * Gets the `ArtifactNode` or `GenericSourceNode` node that is the data source for this artifact. */ - final NodeBase getSourceNode() { result.asElement() = this.getSourceElement() } + final NodeBase getSourceNode() { + result.asElement() = this.getSourceElement() and + (result instanceof ArtifactNode or result instanceof GenericSourceNode) + } /** * Gets the `ArtifactLocatableElement` that is the data source for this artifact. @@ -486,6 +534,32 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } } + /** + * Output text from a cipher operation + */ + final class CipherOutputNode extends ArtifactNode, TCipherOutput { + CipherOutputArtifactInstance instance; + + CipherOutputNode() { this = TCipherOutput(instance) } + + final override string getInternalType() { result = "CipherOutput" } + + override LocatableElement asElement() { result = instance } + } + + /** + * Input text to a cipher operation + */ + final class CipherMessageNode extends ArtifactNode, TCipherInput { + CipherInputArtifactInstance instance; + + CipherMessageNode() { this = TCipherInput(instance) } + + final override string getInternalType() { result = "CipherMessage" } + + override LocatableElement asElement() { result = instance } + } + /** * A source of random number generation */ @@ -594,12 +668,13 @@ module CryptographyBase Input> { result.asElement() = this.asElement().(CipherOperationInstance).getNonceConsumer().asNonce() } - NodeBase getAMessageOrUnknown() { - result = - this.asElement() - .(CipherOperationInstance) - .getMessageConsumer() - .getAKnownOrUnknownSourceNode() + CipherMessageNode getAnInputArtifact() { + result.asElement() = + this.asElement().(CipherOperationInstance).getInputConsumer().asCipherInput() + } + + CipherOutputNode getAnOutputArtifact() { + result.asElement() = this.asElement().(CipherOperationInstance).getOutputArtifact() } override NodeBase getChild(string key) { @@ -616,9 +691,15 @@ module CryptographyBase Input> { if exists(this.getANonce()) then result = this.getANonce() else result = this or // [KNOWN_OR_UNKNOWN] - key = "Message" and - if exists(this.getAMessageOrUnknown()) - then result = this.getAMessageOrUnknown() + key = "InputText" and + if exists(this.getAnInputArtifact()) + then result = this.getAnInputArtifact() + else result = this + or + // [KNOWN_OR_UNKNOWN] + key = "OutputText" and + if exists(this.getAnOutputArtifact()) + then result = this.getAnOutputArtifact() else result = this } From 627790f98b1a0ebb0a724ee9f23b673276d1d04e Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Mon, 3 Mar 2025 19:06:53 +0100 Subject: [PATCH 034/122] Clean up consumer and instance interfaces --- java/ql/lib/experimental/Quantum/JCA.qll | 2 +- java/ql/lib/experimental/Quantum/Language.qll | 32 -------- .../codeql/cryptography/Model.qll | 82 +++++++++++++------ 3 files changed, 57 insertions(+), 59 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 8d748772bffc..5cb7bcf23dcb 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -493,7 +493,7 @@ module JCAModel { } } - class CipherInitCallNonceArgConsumer extends NonceArtifactConsumer instanceof Expr { + class CipherInitCallNonceArgConsumer extends Crypto::NonceArtifactConsumer instanceof Expr { CipherInitCallNonceArgConsumer() { this = any(CipherInitCall call).getNonceArg() } override DataFlow::Node getInputNode() { result.asExpr() = this } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 03c351005380..0e875295bd8a 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -83,8 +83,6 @@ class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource insta abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance { override DataFlow::Node getOutputNode() { result.asExpr() = this } - override DataFlow::Node getInputNode() { none() } // TODO: add seed - override predicate flowsTo(Crypto::FlowAwareElement other) { ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } @@ -113,40 +111,10 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node { module ArtifactUniversalFlow = DataFlow::Global; -class NonceArtifactConsumer extends Crypto::NonceArtifactInstance instanceof Crypto::NonceArtifactConsumer -{ - override predicate flowsTo(Crypto::FlowAwareElement other) { - ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - } - - override DataFlow::Node getOutputNode() { - result = this.(Crypto::NonceArtifactConsumer).getOutputNode() - } - - override DataFlow::Node getInputNode() { - result = this.(Crypto::NonceArtifactConsumer).getInputNode() - } -} - -class CipherInputConsumer extends Crypto::CipherInputArtifactInstance instanceof Crypto::CipherInputConsumer -{ - override predicate flowsTo(Crypto::FlowAwareElement other) { - ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - } - - override DataFlow::Node getOutputNode() { none() } - - override DataFlow::Node getInputNode() { - result = this.(Crypto::CipherInputArtifactInstance).getInputNode() - } -} - abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance { override predicate flowsTo(Crypto::FlowAwareElement other) { ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } - - override DataFlow::Node getInputNode() { none() } } /** diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 83111519a006..243d52cd2e67 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -45,15 +45,26 @@ module CryptographyBase Input> { ) } + NodeBase getPassthroughNodeChild(NodeBase node) { + result = node.(CipherInputNode).getChild(_) or + result = node.(NonceNode).getChild(_) + } + + predicate isPassthroughNode(NodeBase node) { + node instanceof CipherInputNode or + node instanceof NonceNode + } + predicate nodes_graph_impl(NodeBase node, string key, string value) { not node.isExcludedFromGraph() and + not isPassthroughNode(node) and ( key = "semmle.label" and value = node.toString() or // CodeQL's DGML output does not include a location key = "Location" and - value = node.getLocation().toString() + value = "" // node.getLocation().toString() or // Known unknown edges should be reported as properties rather than edges node = node.getChild(key) and @@ -66,7 +77,14 @@ module CryptographyBase Input> { predicate edges_graph_impl(NodeBase source, NodeBase target, string key, string value) { key = "semmle.label" and - target = source.getChild(value) and + exists(NodeBase directTarget | + directTarget = source.getChild(value) and + // [NodeA] ---Input--> [Passthrough] ---Source---> [NodeB] + // should get reported as [NodeA] ---Input--> [NodeB] + if isPassthroughNode(directTarget) + then target = getPassthroughNodeChild(directTarget) + else target = directTarget + ) and // Known unknowns are reported as properties rather than edges not source = target } @@ -223,22 +241,23 @@ module CryptographyBase Input> { * * Instances of nodes do not necessarily have to come from a consumer, allowing additional modelling of an artifact to occur outside of the consumer. */ abstract class ArtifactConsumer extends ConsumerElement { + /** + * Use `getAKnownArtifactSource() instead. The behaviour of these two predicates is equivalent. + */ final override KnownElement getAKnownSource() { result = this.getAKnownArtifactSource() } final ArtifactElement getAKnownArtifactSource() { result.flowsTo(this) } } - abstract class NonceArtifactConsumer extends ArtifactConsumer, NonceArtifactInstance { - NonceArtifactInstance asNonce() { result = this } - } - - abstract class CipherInputArtifactInstance extends ArtifactElement { } + abstract class ArtifactConsumerAndInstance extends ArtifactConsumer { + final override DataFlowNode getOutputNode() { none() } - abstract class CipherInputConsumer extends ArtifactConsumer, CipherInputArtifactInstance { - CipherInputArtifactInstance asCipherInput() { result = this } + final override predicate flowsTo(FlowAwareElement other) { none() } } - abstract class CipherOutputArtifactInstance extends ArtifactElement { } + abstract class CipherOutputArtifactInstance extends ArtifactElement { + final override DataFlowNode getInputNode() { none() } + } abstract class CipherOperationInstance extends OperationElement { /** @@ -345,21 +364,33 @@ module CryptographyBase Input> { abstract class KeyDerivationAlgorithmInstance extends KnownElement { } - // Artifacts - abstract class DigestArtifactInstance extends ArtifactElement { } + // Artifacts determined solely by the element that produces them + // Implementation guidance: these *do* need to be defined generically at the language-level + // in order for a flowsTo to be defined. At the per-modeling-instance level, extend that language-level class! + abstract class OutputArtifactElement extends ArtifactElement { + final override DataFlowNode getInputNode() { none() } + } + + abstract class DigestArtifactInstance extends OutputArtifactElement { } + + abstract class RandomNumberGenerationInstance extends OutputArtifactElement { } // TODO: is this an OutputArtifactElement if it takes a seed? - abstract class KeyArtifactInstance extends ArtifactElement { } + // Artifacts determined solely by the consumer that consumes them are defined as consumers + // Implementation guidance: these do not need to be defined generically at the language-level + // Only the sink node needs to be defined per-modeling-instance (e.g., in JCA.qll) + abstract class NonceArtifactConsumer extends ArtifactConsumerAndInstance { } - abstract class NonceArtifactInstance extends ArtifactElement { } + abstract class CipherInputConsumer extends ArtifactConsumerAndInstance { } - abstract class RandomNumberGenerationInstance extends ArtifactElement { } + // Other artifacts + abstract class KeyArtifactInstance extends ArtifactElement { } // TODO: implement and categorize newtype TNode = // Artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or - TNonce(NonceArtifactInstance e) or - TCipherInput(CipherInputArtifactInstance e) or + TNonce(NonceArtifactConsumer e) or + TCipherInput(CipherInputConsumer e) or TCipherOutput(CipherOutputArtifactInstance e) or TRandomNumberGeneration(RandomNumberGenerationInstance e) { e.flowsTo(_) } or // Operations (e.g., hashing, encryption) @@ -525,7 +556,7 @@ module CryptographyBase Input> { * A nonce or initialization vector */ final class NonceNode extends ArtifactNode, TNonce { - NonceArtifactInstance instance; + NonceArtifactConsumer instance; NonceNode() { this = TNonce(instance) } @@ -550,12 +581,12 @@ module CryptographyBase Input> { /** * Input text to a cipher operation */ - final class CipherMessageNode extends ArtifactNode, TCipherInput { - CipherInputArtifactInstance instance; + final class CipherInputNode extends ArtifactNode, TCipherInput { + CipherInputConsumer instance; - CipherMessageNode() { this = TCipherInput(instance) } + CipherInputNode() { this = TCipherInput(instance) } - final override string getInternalType() { result = "CipherMessage" } + final override string getInternalType() { result = "CipherInput" } override LocatableElement asElement() { result = instance } } @@ -665,12 +696,11 @@ module CryptographyBase Input> { } NonceNode getANonce() { - result.asElement() = this.asElement().(CipherOperationInstance).getNonceConsumer().asNonce() + result.asElement() = this.asElement().(CipherOperationInstance).getNonceConsumer() } - CipherMessageNode getAnInputArtifact() { - result.asElement() = - this.asElement().(CipherOperationInstance).getInputConsumer().asCipherInput() + CipherInputNode getAnInputArtifact() { + result.asElement() = this.asElement().(CipherOperationInstance).getInputConsumer() } CipherOutputNode getAnOutputArtifact() { From 076f53147dc7d1598ad6d9bea6bbfe9332e317fc Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 3 Mar 2025 13:53:16 -0500 Subject: [PATCH 035/122] Proof-of-concept query for InsecureOrUnknownNonceAtOperation --- .../InsecureOrUnknownNonceAtOperation.ql | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql diff --git a/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql b/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql new file mode 100644 index 000000000000..ba43413d281f --- /dev/null +++ b/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql @@ -0,0 +1,76 @@ +/** + * @name Insecure or unknown nonce source at a cipher operation + * @id java/insecure-or-unknown-nonce-at-operation + * @kind problem + */ + +import experimental.Quantum.Language + + +from Crypto::NonceNode n, Crypto::CipherOperationNode op, Crypto::FlowAwareElement src, string msg +where + op.getANonce() = n and + ( + // Known sources cases that are not secure + src = n.getSourceElement() and + not src instanceof SecureRandomnessInstance and + msg = "Operation uses insecure nonce source $@" + + or + // Totally unknown sources (unmodeled input sources) + not exists(n.getSourceElement()) and msg = "Operation uses unknown nonce source" and src = n.asElement() + ) +select n, msg, src, src.toString() + + +// variant using instances, does not yield the same results +// from Crypto::NonceArtifactConsumer n, Crypto::CipherOperationInstance op, Crypto::FlowAwareElement src, string msg +// where +// op.getNonceConsumer() = n and +// ( +// // Known sources cases that are not secure +// src = n.getAKnownArtifactSource()and +// not src instanceof SecureRandomnessInstance and +// msg = "Operation uses insecure nonce source $@" + +// or +// // Totally unknown sources (unmodeled input sources) +// // When this occurs set src to n, just to bind it, but the output message will not report any source +// not exists(n.getAKnownArtifactSource()) and msg = "Operation uses unknown nonce source" and src = n +// ) +// select n, msg, src, src.toString() + + + + + + + + + + + + + +// NOTE: this will find all unknowns too, constants, and allocations, without needing to model them +// which is kinda nice, but accidental, since getSourceElement is not modeled for everything +// If users want to find constants or unallocated, they need to model those sources, and output the +// getSourceElement +// QUESTION: why isn't the source element a node? +// NOTE: when not all sources are modeled, if one source is secure, even if others do exist, you +// will see the nonce and operation are secure, regardless of potentially insecure IV sources +// resulting in False Negatives +// NOTE: need to have a query where the op has no Nonce + +// // Ideal query +// from Crypto::NonceNode n, Crypto::CipherOperationNode op +// where +// n = op.getANonce() and +// // n = op.getAnUnknownNonce() +// not n.asElement() instanceof SecureRandomSource +// select op, "Operation uses insecure nonce source @", n, n.toString() +// from Crypto::Nonce n, Crypto::ArtifactLocatableElement nonceSrc +// where +// n.() = nonceSrc and +// not nonceSrc instanceof SecureRandomnessInstance +// select n, nonceSrc From 14cb2bb12f879e12a0b4b99f77e8d0a78808f3a7 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 3 Mar 2025 14:42:50 -0500 Subject: [PATCH 036/122] Updates to insecure or unknown nonce at operation. --- .../InsecureOrUnknownNonceAtOperation.ql | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql b/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql index ba43413d281f..cdeeebd5a7d7 100644 --- a/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql +++ b/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql @@ -6,52 +6,47 @@ import experimental.Quantum.Language - from Crypto::NonceNode n, Crypto::CipherOperationNode op, Crypto::FlowAwareElement src, string msg where op.getANonce() = n and + // Only encryption mode is relevant for insecure nonces, consder any 'unknown' subtype + // as possibly encryption. + ( + op.getCipherOperationSubtype() instanceof Crypto::EncryptionSubtype + or + op.getCipherOperationSubtype() instanceof Crypto::WrapSubtype + or + op.getCipherOperationSubtype() instanceof Crypto::UnwrapSubtype + ) and ( // Known sources cases that are not secure src = n.getSourceElement() and - not src instanceof SecureRandomnessInstance and - msg = "Operation uses insecure nonce source $@" - + not src instanceof SecureRandomnessInstance and + msg = "Operation uses insecure nonce source $@" or // Totally unknown sources (unmodeled input sources) - not exists(n.getSourceElement()) and msg = "Operation uses unknown nonce source" and src = n.asElement() + not exists(n.getSourceElement()) and + msg = "Operation uses unknown nonce source" and + src = n.asElement() ) select n, msg, src, src.toString() - // variant using instances, does not yield the same results // from Crypto::NonceArtifactConsumer n, Crypto::CipherOperationInstance op, Crypto::FlowAwareElement src, string msg // where // op.getNonceConsumer() = n and +// TODO: only perform the query on encryption // ( // // Known sources cases that are not secure // src = n.getAKnownArtifactSource()and // not src instanceof SecureRandomnessInstance and // msg = "Operation uses insecure nonce source $@" - // or // // Totally unknown sources (unmodeled input sources) // // When this occurs set src to n, just to bind it, but the output message will not report any source // not exists(n.getAKnownArtifactSource()) and msg = "Operation uses unknown nonce source" and src = n // ) // select n, msg, src, src.toString() - - - - - - - - - - - - - // NOTE: this will find all unknowns too, constants, and allocations, without needing to model them // which is kinda nice, but accidental, since getSourceElement is not modeled for everything // If users want to find constants or unallocated, they need to model those sources, and output the @@ -61,7 +56,6 @@ select n, msg, src, src.toString() // will see the nonce and operation are secure, regardless of potentially insecure IV sources // resulting in False Negatives // NOTE: need to have a query where the op has no Nonce - // // Ideal query // from Crypto::NonceNode n, Crypto::CipherOperationNode op // where From 2ee16811267268f610c0f47e5e4887fa97b60cad Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 3 Mar 2025 15:09:27 -0500 Subject: [PATCH 037/122] Adding a proof-of-concept PossibleReusedNonce query. --- .../Quantum/PossibleReusedNonce.ql | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 java/ql/src/experimental/Quantum/PossibleReusedNonce.ql diff --git a/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql b/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql new file mode 100644 index 000000000000..fde1ef121786 --- /dev/null +++ b/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql @@ -0,0 +1,35 @@ +/** + * @name Possible Nonce Reuse: Produces false positives if reuse occurs in a source that is a re-entry point. + * @id java/possible-nonce-reuse + */ + +import experimental.Quantum.Language +import semmle.code.java.dataflow.DataFlow + +from + Crypto::CipherOperationNode op1, Crypto::CipherOperationNode op2, Crypto::NonceNode nonce1, + Crypto::NonceNode nonce2, Crypto::FlowAwareElement src1, Crypto::FlowAwareElement src2 +where + // NOTE: not looking at value of the nonce, if we knew value, it would be insecure (hard coded) + // Instead trying to find nonce sources that trace to multiple operations. + // Only looking for encryption operations, presumably if reuse for decryption either wouldn't be observable + // (the encryption happened else where) or we are able to see the encryption and decryption operation and + // reuse for encryption is the concern) + ( + op1.getCipherOperationSubtype() instanceof Crypto::EncryptionSubtype or + op1.getCipherOperationSubtype() instanceof Crypto::WrapSubtype or + op1.getCipherOperationSubtype() instanceof Crypto::UnknownCipherOperationSubtype + ) and + ( + op2.getCipherOperationSubtype() instanceof Crypto::EncryptionSubtype or + op2.getCipherOperationSubtype() instanceof Crypto::WrapSubtype or + op2.getCipherOperationSubtype() instanceof Crypto::UnknownCipherOperationSubtype + ) and + nonce1 = op1.getANonce() and + nonce2 = op2.getANonce() and + op1 != op2 and + nonce1.getSourceElement() = src1 and + nonce2.getSourceElement() = src2 and + src1 = src2 +// TODO: need to clarify that a reuse in a non-finalize is ok, need to check if 'finalize' through a modeled predicate +select op1, "Operation has a possible reused nonce with source $@", src1, src1.toString() From 8865d89fe9c381d78be44e75b3fde1519d3f1868 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 3 Mar 2025 16:51:30 -0500 Subject: [PATCH 038/122] Removing old ReusedNonce query. --- .../src/experimental/Quantum/ReusedNonce.ql | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 java/ql/src/experimental/Quantum/ReusedNonce.ql diff --git a/java/ql/src/experimental/Quantum/ReusedNonce.ql b/java/ql/src/experimental/Quantum/ReusedNonce.ql deleted file mode 100644 index 7847781e0be5..000000000000 --- a/java/ql/src/experimental/Quantum/ReusedNonce.ql +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @name Unsafe nonce source or reuse - * @id java/unsafe-nonce-source-or-reuse - */ - -import experimental.Quantum.Language -import semmle.code.java.dataflow.DataFlow - -Crypto::NodeBase getNonceOrigin(Crypto::NonceNode nonce) { - // TODO: this check is currently ultra hacky just for demoing - result = nonce.getSourceNode() -} - -from Crypto::CipherOperationNode op, Crypto::NonceNode nonce1, Crypto::NonceNode nonce2 -where - op.asElement().(Expr).getEnclosingCallable().getName() = "encrypt" and - nonce1 = op.getANonce() and - nonce2 = op.getANonce() and - not nonce1 = nonce2 and - getNonceOrigin(nonce1) = getNonceOrigin(nonce2) -select op, nonce1, nonce2 From cce5f24b3860a451dd2869798cee012faf81ec5d Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 4 Mar 2025 15:52:57 -0500 Subject: [PATCH 039/122] Initial OpenSSL modeling work. --- cpp/ql/lib/experimental/Quantum/Language.qll | 123 +- cpp/ql/lib/experimental/Quantum/OpenSSL.qll | 244 -- .../Quantum/OpenSSL/AlgorithmSource.qll | 2848 +++++++++++++++++ .../experimental/Quantum/OpenSSL/CtxFlow.qll | 82 + .../Quantum/OpenSSL/EVPCipherConsumers.qll | 25 + .../Quantum/OpenSSL/EVPCipherInitializer.qll | 153 + .../Quantum/OpenSSL/EVPCipherOperation.qll | 87 + .../experimental/Quantum/OpenSSL/OpenSSL.qll | 357 +++ 8 files changed, 3673 insertions(+), 246 deletions(-) delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmSource.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherInitializer.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index 8f400858f07c..3f1d9aa9bdf3 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -1,12 +1,131 @@ private import codeql.cryptography.Model +import semmle.code.cpp.ir.IR +import semmle.code.cpp.security.FlowSources as FlowSources private import cpp as Lang + module CryptoInput implements InputSig { + class DataFlowNode = DataFlow::Node; class LocatableElement = Lang::Locatable; - class UnknownLocation = Lang::UnknownDefaultLocation; } module Crypto = CryptographyBase; -import OpenSSL +/** + * Artifact output to node input configuration + */ +abstract class AdditionalFlowInputStep extends DataFlow::Node { + abstract DataFlow::Node getOutput(); + + final DataFlow::Node getInput() { result = this } +} + + +/** + * Generic data source to node input configuration + */ +module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::GenericDataSourceInstance 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 + } +} + + + +// TODO: I think this will be inefficient, no? +class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { + override DataFlow::Node getOutputNode() { + result.asExpr() = this + } + + override predicate flowsTo(Crypto::FlowAwareElement other) { + // TODO: separate config to avoid blowing up data-flow analysis + GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + } + + override string getAdditionalDescription() { result = this.toString() } +} + +/** + * Definitions of various generic data sources + */ +// final class DefaultFlowSource = SourceNode; + +// final class DefaultRemoteFlowSource = RemoteFlowSource; + +// class GenericLocalDataSource extends Crypto::GenericLocalDataSource { +// GenericLocalDataSource() { +// any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this +// } + +// override DataFlow::Node getOutputNode() { result.asExpr() = this } + +// override predicate flowsTo(Crypto::FlowAwareElement other) { +// GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) +// } + +// override string getAdditionalDescription() { result = this.toString() } +// } + +// class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { +// GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this } + +// override DataFlow::Node getOutputNode() { result.asExpr() = this } + +// override predicate flowsTo(Crypto::FlowAwareElement other) { +// GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) +// } + +// override string getAdditionalDescription() { result = this.toString() } +// } + + +module GenericDataSourceUniversalFlow = DataFlow::Global; + +module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::ArtifactElement 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 ArtifactUniversalFlow = DataFlow::Global; +abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance { + override predicate flowsTo(Crypto::FlowAwareElement other) { + ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + } +} + + +import OpenSSL.OpenSSL diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll deleted file mode 100644 index 821fc0aec8ff..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll +++ /dev/null @@ -1,244 +0,0 @@ -import cpp -import semmle.code.cpp.dataflow.new.DataFlow - -module OpenSSLModel { - import Language - - /** - * Hash function references in OpenSSL. - */ - predicate hash_ref_type_mapping_known(string name, Crypto::THashType algo) { - // `ma` name has an LN_ or SN_ prefix, which we want to ignore - // capture any name after the _ prefix using regex matching - name = ["sha1", "sha160"] and algo instanceof Crypto::SHA1 - or - name = ["sha224", "sha256", "sha384", "sha512"] and algo instanceof Crypto::SHA2 - or - name = ["sha3-224", "sha3-256", "sha3-384", "sha3-512"] and algo instanceof Crypto::SHA3 - or - name = "md2" and algo instanceof Crypto::MD2 - or - name = "md4" and algo instanceof Crypto::MD4 - or - name = "md5" and algo instanceof Crypto::MD5 - or - name = "ripemd160" and algo instanceof Crypto::RIPEMD160 - or - name = "whirlpool" and algo instanceof Crypto::WHIRLPOOL - } - - predicate hash_ref_type_mapping(FunctionCallOrMacroAccess ref, string name, Crypto::THashType algo) { - name = ref.getTargetName().regexpCapture("(?:SN_|LN_|EVP_)([a-z0-9]+)", 1) and - hash_ref_type_mapping_known(name, algo) - } - - class FunctionCallOrMacroAccess extends Element { - FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess } - - string getTargetName() { - result = this.(FunctionCall).getTarget().getName() - or - result = this.(MacroAccess).getMacroName() - } - } - - class HashAlgorithmCallOrMacro extends Crypto::HashAlgorithmInstance instanceof FunctionCallOrMacroAccess - { - HashAlgorithmCallOrMacro() { hash_ref_type_mapping(this, _, _) } - - string getTargetName() { result = this.(FunctionCallOrMacroAccess).getTargetName() } - } - - class HashAlgorithm extends Crypto::HashAlgorithm { - HashAlgorithmCallOrMacro instance; - - HashAlgorithm() { this = Crypto::THashAlgorithm(instance) } - - override string getSHA2OrSHA3DigestSize(Location location) { - ( - this.getHashType() instanceof Crypto::SHA2 or - this.getHashType() instanceof Crypto::SHA3 - ) and - exists(string name | - hash_ref_type_mapping(instance, name, this.getHashType()) and - result = name.regexpFind("\\d{3}", 0, _) and - location = instance.getLocation() - ) - } - - override string getRawAlgorithmName() { result = instance.getTargetName() } - - override Crypto::THashType getHashType() { hash_ref_type_mapping(instance, _, result) } - - Element getInstance() { result = instance } - - override Location getLocation() { result = instance.getLocation() } - } - - /** - * Data-flow configuration for key derivation algorithm flow to EVP_KDF_derive. - */ - module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - source.asExpr() = any(KeyDerivationAlgorithm a).getInstance() - } - - predicate isSink(DataFlow::Node sink) { - exists(EVP_KDF_derive kdo | - sink.asExpr() = kdo.getCall().getAlgorithmArg() - or - sink.asExpr() = kdo.getCall().getContextArg() // via `EVP_KDF_CTX_set_params` - ) - } - - predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - none() // TODO - } - } - - module AlgorithmToEVPKeyDeriveFlow = DataFlow::Global; - - predicate algorithm_to_EVP_KDF_derive(KeyDerivationAlgorithm algo, EVP_KDF_derive derive) { - none() - } - - /** - * Key derivation operation (e.g., `EVP_KDF_derive`) - */ - class EVP_KDF_derive_FunctionCall extends Crypto::KeyDerivationOperationInstance instanceof FunctionCall - { - EVP_KDF_derive_FunctionCall() { this.getTarget().getName() = "EVP_KDF_derive" } - - Expr getAlgorithmArg() { result = super.getArgument(3) } - - Expr getContextArg() { result = super.getArgument(0) } - } - - class EVP_KDF_derive extends Crypto::KeyDerivationOperation { - EVP_KDF_derive_FunctionCall instance; - - EVP_KDF_derive() { this = Crypto::TKeyDerivationOperation(instance) } - - override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) } - - EVP_KDF_derive_FunctionCall getCall() { result = instance } - } - - /** - * Key derivation algorithm nodes - */ - abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm { - abstract Expr getInstance(); - } - - /** - * `EVP_KDF_fetch` returns a key derivation algorithm. - */ - class EVP_KDF_fetch_Call extends FunctionCall { - EVP_KDF_fetch_Call() { this.getTarget().getName() = "EVP_KDF_fetch" } - - Expr getAlgorithmArg() { result = this.getArgument(1) } - } - - class EVP_KDF_fetch_AlgorithmArg extends Crypto::KeyDerivationAlgorithmInstance instanceof Expr { - EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) } - } - - predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF", "PBKDF2"] } - - class KDFAlgorithmStringLiteral extends StringLiteral { - KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) } - } - - private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KDFAlgorithmStringLiteral } - - predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof EVP_KDF_fetch_AlgorithmArg } - } - - module AlgorithmStringToFetchFlow = DataFlow::Global; - - predicate algorithmStringToKDFFetchArgFlow( - string name, KDFAlgorithmStringLiteral origin, EVP_KDF_fetch_AlgorithmArg arg - ) { - origin.getValue().toUpperCase() = name and - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) - } - - /** - * HKDF key derivation algorithm. - */ - class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF { - KDFAlgorithmStringLiteral origin; - EVP_KDF_fetch_AlgorithmArg instance; - - HKDF() { - this = Crypto::TKeyDerivationAlgorithm(instance) and - algorithmStringToKDFFetchArgFlow("HKDF", origin, instance) - } - - override string getRawAlgorithmName() { result = origin.getValue() } - - override Crypto::HashAlgorithm getHashAlgorithm() { none() } - - override Crypto::LocatableElement getOrigin(string name) { - result = origin and name = origin.toString() - } - - override Expr getInstance() { result = origin } - } - - /** - * PBKDF2 key derivation algorithm. - */ - class PBKDF2 extends KeyDerivationAlgorithm, Crypto::PBKDF2 { - KDFAlgorithmStringLiteral origin; - EVP_KDF_fetch_AlgorithmArg instance; - - PBKDF2() { - this = Crypto::TKeyDerivationAlgorithm(instance) and - algorithmStringToKDFFetchArgFlow("PBKDF2", origin, instance) - } - - override string getRawAlgorithmName() { result = origin.getValue() } - - override string getIterationCount(Location location) { none() } // TODO - - override string getKeyLength(Location location) { none() } // TODO - - override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO - - override Crypto::LocatableElement getOrigin(string name) { - result = origin and name = origin.toString() - } - - override Expr getInstance() { result = instance } - } - - /** - * PKCS12KDF key derivation algorithm. - */ - class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF { - KDFAlgorithmStringLiteral origin; - EVP_KDF_fetch_AlgorithmArg instance; - - PKCS12KDF() { - this = Crypto::TKeyDerivationAlgorithm(instance) and - algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, instance) - } - - override string getRawAlgorithmName() { result = origin.getValue() } - - override string getIterationCount(Location location) { none() } // TODO - - override string getIDByte(Location location) { none() } // TODO - - override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO - - override Crypto::LocatableElement getOrigin(string name) { - result = origin and name = origin.toString() - } - - override Expr getInstance() { result = instance } - } -} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmSource.qll new file mode 100644 index 000000000000..f409578e98d2 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmSource.qll @@ -0,0 +1,2848 @@ +import cpp +import experimental.Quantum.Language +import EVPCipherConsumers + +/** + * Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable. + * This is because the mode will always be specified alongside the algorithm and never independently. + * Therefore, we can always assume that a determinable algorithm will have a determinable mode. + * + * In the case that only an algorithm is specified, e.g., "AES", the provider provides a default mode. + * + * TODO: Model the case of relying on a provider default, but alert on it as a bad practice. + */ +class CipherLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof ConstantDataSource +{ + Crypto::AlgorithmConsumer consumer; // TODO: I need to make this an open SSL algorithm consumer specifically + + CipherLiteralAlgorithmInstance() { + GenericDataSourceUniversalFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(consumer)) + } + + Crypto::AlgorithmConsumer getConsumer() { result = consumer } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { + none() // TODO: provider defaults + } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } + + override Crypto::TCipherType getCipherFamily() { none() } +} + +// override Crypto::TCipherType getCipherFamily() { +// if this.cipherNameMappingKnown(_, super.getAlgorithmName()) +// then this.cipherNameMappingKnown(result, super.getAlgorithmName()) +// else result instanceof Crypto::OtherCipherType +// } +// bindingset[name] +// private predicate cipherNameMappingKnown(Crypto::TCipherType type, string name) { +// name = "AES" and +// type instanceof Crypto::AES +// or +// name = "DES" and +// type instanceof Crypto::DES +// or +// name = "TripleDES" and +// type instanceof Crypto::TripleDES +// or +// name = "IDEA" and +// type instanceof Crypto::IDEA +// or +// name = "CAST5" and +// type instanceof Crypto::CAST5 +// or +// name = "ChaCha20" and +// type instanceof Crypto::ChaCha20 +// or +// name = "RC4" and +// type instanceof Crypto::RC4 +// or +// name = "RC5" and +// type instanceof Crypto::RC5 +// or +// name = "RSA" and +// type instanceof Crypto::RSA +// } +// private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { +// type instanceof Crypto::ECB and name = "ECB" +// or +// type instanceof Crypto::CBC and name = "CBC" +// or +// type instanceof Crypto::GCM and name = "GCM" +// or +// type instanceof Crypto::CTR and name = "CTR" +// or +// type instanceof Crypto::XTS and name = "XTS" +// or +// type instanceof Crypto::CCM and name = "CCM" +// or +// type instanceof Crypto::SIV and name = "SIV" +// or +// type instanceof Crypto::OCB and name = "OCB" +// } +// override Crypto::TBlockCipherModeOperationType getModeType() { +// if this.modeToNameMappingKnown(_, super.getMode()) +// then this.modeToNameMappingKnown(result, super.getMode()) +// else result instanceof Crypto::OtherMode +// } +// override string getRawModeAlgorithmName() { result = super.getMode() } +// override string getRawPaddingAlgorithmName() { result = super.getPadding() } +// bindingset[name] +// private predicate paddingToNameMappingKnown(Crypto::TPaddingType type, string name) { +// type instanceof Crypto::NoPadding and name = "NOPADDING" +// or +// type instanceof Crypto::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? +// or +// type instanceof Crypto::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% +// } +// } +/** + * Resolves literal `e` to a known algorithm name, nid, normalized name, and algType + * if `e` resolves to a known algorithm. + * If this predicate does not hold, then `e` can be interpreted as being of `UNKNOWN` type. + */ +predicate resolveAlgorithmFromLiteral(Literal e, string normalized, string algType) { + exists(int nid | + nid = getPossibleNidFromLiteral(e) and knownOpenSSLAlgorithm(_, nid, normalized, algType) + ) + or + exists(string name | + name = resolveAlgorithmAlias(e) and knownOpenSSLAlgorithm(name, _, normalized, algType) + ) +} + +string resolveAlgorithmAlias(StringLiteral name) { + exists(string lower | lower = name.getValue().toLowerCase() | + // The result is an alias algorithm name if known + result = getAlgorithmAlias(lower) + or + // or the name is itself a known algorithm + knownOpenSSLAlgorithm(lower, _, _, _) and result = lower + ) +} + +private int getPossibleNidFromLiteral(Literal e) { + result = e.getValue().toInt() and + not e instanceof CharLiteral and + not e instanceof StringLiteral and + // ASSUMPTION, no negative numbers are allowed + // RATIONALE: this is a performance improvement to avoid having to trace every number + not exists(UnaryMinusExpr u | u.getOperand() = e) and + // OPENSSL has a special macro for getting every line, ignore it + not exists(MacroInvocation mi | mi.getExpr() = e and mi.getMacroName() = "OPENSSL_LINE") and + // Filter out cases where an int is assigned into a pointer, e.g., char* x = NULL; + not exists(Assignment a | + a.getRValue() = e and a.getLValue().getType().getUnspecifiedType() instanceof PointerType + ) and + not exists(Initializer i | + i.getExpr() = e and + i.getDeclaration().getADeclarationEntry().getUnspecifiedType() instanceof PointerType + ) and + // Filter out cases where an int is returned into a pointer, e.g., return NULL; + not exists(ReturnStmt r | + r.getExpr() = e and + r.getEnclosingFunction().getType().getUnspecifiedType() instanceof PointerType + ) +} + +string getAlgorithmAlias(string alias) { + customAliases(result, alias) + or + defaultAliases(result, alias) +} + +/** + * Finds aliases of known alagorithms defined by users (through obj_name_add and various macros pointing to this function) + * + * The `target` and `alias` are converted to lowercase to be of a standard form. + */ +predicate customAliases(string target, string alias) { + exists(Call c | c.getTarget().getName().toLowerCase() = "obj_name_add" | + target = c.getArgument(2).getValue().toLowerCase() and + alias = c.getArgument(0).getValue().toLowerCase() + ) +} + +/** + * A hard-coded mapping of known algorithm aliases in OpenSSL. + * This was derived by applying the same kind of logic foun din `customAliases` to the + * OpenSSL code base directly. + * + * The `target` and `alias` are converted to lowercase to be of a standard form. + */ +predicate defaultAliases(string target, string alias) { + alias = "aes128" and target = "aes-128-cbc" + or + alias = "aes192" and target = "aes-192-cbc" + or + alias = "aes256" and target = "aes-256-cbc" + or + alias = "aes128-wrap" and target = "id-aes128-wrap" + or + alias = "aes192-wrap" and target = "id-aes192-wrap" + or + alias = "aes256-wrap" and target = "id-aes256-wrap" + or + alias = "aes128-wrap-pad" and target = "id-aes128-wrap-pad" + or + alias = "aes192-wrap-pad" and target = "id-aes192-wrap-pad" + or + alias = "aes256-wrap-pad" and target = "id-aes256-wrap-pad" + or + alias = "aes-128-wrap" and target = "id-aes128-wrap" + or + alias = "aes-192-wrap" and target = "id-aes192-wrap" + or + alias = "aes-256-wrap" and target = "id-aes256-wrap" + or + alias = "aria128" and target = "aria-128-cbc" + or + alias = "aria192" and target = "aria-192-cbc" + or + alias = "aria256" and target = "aria-256-cbc" + or + alias = "aes128" and target = "aes-128-cbc" + or + alias = "bf" and target = "bf-cbc" + or + alias = "blowfish" and target = "bf-cbc" + or + alias = "camellia128" and target = "camellia-128-cbc" + or + alias = "camellia192" and target = "camellia-192-cbc" + or + alias = "camellia256" and target = "camellia-256-cbc" + or + alias = "cast" and target = "cast5-cbc" + or + alias = "cast-cbc" and target = "cast5-cbc" + or + alias = "des" and target = "des-cbc" + or + alias = "des-ede-ecb" and target = "des-ede" + or + alias = "des-ede3-ecb" and target = "des-ede3" + or + alias = "des3" and target = "des-ede3-cbc" + or + alias = "des3-wrap" and target = "id-smime-alg-cms3deswrap" + or + alias = "desx" and target = "desx-cbc" + or + alias = "idea" and target = "idea-cbc" + or + alias = "rc2" and target = "rc2-cbc" + or + alias = "rc2-128" and target = "rc2-cbc" + or + alias = "rc2-40" and target = "rc2-40-cbc" + or + alias = "rc2-64" and target = "rc2-64-cbc" + or + alias = "ripemd" and target = "ripemd160" + or + alias = "rmd160" and target = "ripemd160" + or + alias = "rsa-sha1-2" and target = "rsa-sha1" + or + alias = "seed" and target = "seed-cbc" + or + alias = "sm4" and target = "sm4-cbc" + or + alias = "ssl3-md5" and target = "md5" + or + alias = "ssl3-sha1" and target = "sha1" +} + +/** + * Enumeration of all known crypto algorithms for openSSL + * `name` is all lower case (caller's must ensure they pass in lower case) + * `nid` is the numeric id of the algorithm, + * `normalized` is the normalized name of the algorithm (e.g., "AES128" for "aes-128-cbc") + * `algType` is the type of algorithm (e.g., "SYMMETRIC_ENCRYPTION") + */ +predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string algType) { + name = "rsa" and nid = 19 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "prime192v1" and nid = 409 and normalized = "PRIME192V1" and algType = "ELLIPTIC_CURVE" + or + name = "prime256v1" and nid = 415 and normalized = "PRIME256V1" and algType = "ELLIPTIC_CURVE" + or + name = "pbkdf2" and nid = 69 and normalized = "PBKDF2" and algType = "KEY_DERIVATION" + or + name = "dsa" and nid = 116 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "gost2001" and nid = 811 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost2012_256" and nid = 979 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost2012_512" and nid = 980 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "ed25519" and nid = 1087 and normalized = "ED25519" and algType = "ELLIPTIC_CURVE" + or + name = "ed448" and nid = 1088 and normalized = "ED448" and algType = "ELLIPTIC_CURVE" + or + name = "md2" and nid = 3 and normalized = "MD2" and algType = "HASH" + or + name = "sha" and nid = 41 and normalized = "SHA" and algType = "HASH" + or + name = "sha1" and nid = 64 and normalized = "SHA1" and algType = "HASH" + or + name = "scrypt" and nid = 973 and normalized = "SCRYPT" and algType = "KEY_DERIVATION" + or + name = "pkcs7" and nid = 20 and normalized = "PKCS7" and algType = "SYMMETRIC_PADDING" + or + name = "md4" and nid = 257 and normalized = "MD4" and algType = "HASH" + or + name = "md5" and nid = 4 and normalized = "MD5" and algType = "HASH" + or + name = "sha224" and nid = 675 and normalized = "SHA224" and algType = "HASH" + or + name = "sha256" and nid = 672 and normalized = "SHA256" and algType = "HASH" + or + name = "sha384" and nid = 673 and normalized = "SHA384" and algType = "HASH" + or + name = "sha512" and nid = 674 and normalized = "SHA512" and algType = "HASH" + or + name = "sha512-224" and nid = 1094 and normalized = "SHA512224" and algType = "HASH" + or + name = "sha512-256" and nid = 1095 and normalized = "SHA512256" and algType = "HASH" + or + name = "sha3-224" and nid = 1096 and normalized = "SHA3224" and algType = "HASH" + or + name = "sha3-256" and nid = 1097 and normalized = "SHA3256" and algType = "HASH" + or + name = "sha3-384" and nid = 1098 and normalized = "SHA3384" and algType = "HASH" + or + name = "sha3-512" and nid = 1099 and normalized = "SHA3512" and algType = "HASH" + or + name = "shake128" and nid = 1100 and normalized = "SHAKE128" and algType = "HASH" + or + name = "shake256" and nid = 1101 and normalized = "SHAKE256" and algType = "HASH" + or + name = "mdc2" and nid = 95 and normalized = "MDC2" and algType = "HASH" + or + name = "blake2b512" and nid = 1056 and normalized = "BLAKE2B" and algType = "HASH" + or + name = "blake2s256" and nid = 1057 and normalized = "BLAKE2S" and algType = "HASH" + or + name = "sm3" and nid = 1143 and normalized = "SM3" and algType = "HASH" + or + name = "aes-128-cbc" and nid = 419 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-cbc" and nid = 419 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aes-128-ecb" and nid = 418 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-ecb" and nid = 418 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "aes-192-cbc" and nid = 423 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-cbc" and nid = 423 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aes-192-ecb" and nid = 422 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-ecb" and nid = 422 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "aes-256-cbc" and nid = 427 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-cbc" and nid = 427 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aes-256-ecb" and nid = 426 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-ecb" and nid = 426 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "aria-128-cbc" and nid = 1066 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aria-128-cbc" and nid = 1066 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-128-cfb" and nid = 1067 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aria-128-cfb" and nid = 1067 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-128-ctr" and nid = 1069 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "aria-128-ctr" and nid = 1069 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-128-ecb" and nid = 1065 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "aria-128-ecb" and nid = 1065 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-128-ofb" and nid = 1068 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "aria-128-ofb" and nid = 1068 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-128-cfb1" and nid = 1080 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aria-128-cfb1" and nid = 1080 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-128-cfb8" and nid = 1083 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-128-cfb8" and nid = 1083 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "aria-192-cbc" and nid = 1071 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aria-192-cbc" and nid = 1071 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-192-cfb" and nid = 1072 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aria-192-cfb" and nid = 1072 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-192-ctr" and nid = 1074 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "aria-192-ctr" and nid = 1074 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-192-ecb" and nid = 1070 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "aria-192-ecb" and nid = 1070 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-192-ofb" and nid = 1073 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "aria-192-ofb" and nid = 1073 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-192-cfb1" and nid = 1081 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aria-192-cfb1" and nid = 1081 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-192-cfb8" and nid = 1084 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-192-cfb8" and nid = 1084 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "aria-256-cbc" and nid = 1076 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aria-256-cbc" and nid = 1076 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-256-cfb" and nid = 1077 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aria-256-cfb" and nid = 1077 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-256-ctr" and nid = 1079 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "aria-256-ctr" and nid = 1079 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-256-ecb" and nid = 1075 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "aria-256-ecb" and nid = 1075 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-256-ofb" and nid = 1078 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "aria-256-ofb" and nid = 1078 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-256-cfb1" and nid = 1082 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aria-256-cfb1" and nid = 1082 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-256-cfb8" and nid = 1085 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-256-cfb8" and nid = 1085 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "camellia-128-cbc" and + nid = 751 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-cbc" and nid = 751 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "camellia-128-ecb" and + nid = 754 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-ecb" and nid = 754 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "camellia-192-cbc" and + nid = 752 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-cbc" and nid = 752 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "camellia-192-ecb" and + nid = 755 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-ecb" and nid = 755 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "camellia-256-cbc" and + nid = 753 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-cbc" and nid = 753 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "camellia-256-ecb" and + nid = 756 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-ecb" and nid = 756 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "rc4" and nid = 5 and normalized = "RC4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rc4-40" and nid = 97 and normalized = "RC4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ecb" and nid = 29 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ecb" and nid = 29 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "des-ede" and nid = 32 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede3" and nid = 33 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede3" and nid = 33 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "des-cbc" and nid = 31 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-cbc" and nid = 31 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "des-ede-cbc" and nid = 43 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede-cbc" and nid = 43 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "des-ede-cbc" and nid = 43 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "des-ede3-cbc" and nid = 44 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede3-cbc" and nid = 44 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "des-cfb" and nid = 30 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-cfb" and nid = 30 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "des-ede-cfb" and nid = 60 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede-cfb" and nid = 60 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "des-ede3-cfb" and nid = 61 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede3-cfb" and nid = 61 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "des-ofb" and nid = 45 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ofb" and nid = 45 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "des-ede-ofb" and nid = 62 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede-ofb" and nid = 62 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "des-ede3-ofb" and nid = 63 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede3-ofb" and nid = 63 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "idea-cbc" and nid = 34 and normalized = "IDEA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "idea-cbc" and nid = 34 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "idea-ecb" and nid = 36 and normalized = "IDEA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "idea-ecb" and nid = 36 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "idea-cfb" and nid = 35 and normalized = "IDEA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "idea-cfb" and nid = 35 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "idea-ofb" and nid = 46 and normalized = "IDEA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "idea-ofb" and nid = 46 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "seed-cbc" and nid = 777 and normalized = "SEED" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "seed-cbc" and nid = 777 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "seed-ecb" and nid = 776 and normalized = "SEED" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "seed-ecb" and nid = 776 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "seed-cfb" and nid = 779 and normalized = "SEED" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "seed-cfb" and nid = 779 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "seed-ofb" and nid = 778 and normalized = "SEED" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "seed-ofb" and nid = 778 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "rc2-cbc" and nid = 37 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rc2-cbc" and nid = 37 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "rc2-ecb" and nid = 38 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rc2-ecb" and nid = 38 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "rc2-cfb" and nid = 39 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rc2-cfb" and nid = 39 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "rc2-ofb" and nid = 40 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rc2-ofb" and nid = 40 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "rc2-64-cbc" and nid = 166 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rc2-64-cbc" and nid = 166 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "rc2-40-cbc" and nid = 98 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rc2-40-cbc" and nid = 98 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "bf-cbc" and nid = 91 and normalized = "BF" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "bf-cbc" and nid = 91 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "bf-ecb" and nid = 92 and normalized = "BF" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "bf-ecb" and nid = 92 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "bf-cfb" and nid = 93 and normalized = "BF" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "bf-cfb" and nid = 93 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "bf-ofb" and nid = 94 and normalized = "BF" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "bf-ofb" and nid = 94 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "cast5-cbc" and nid = 108 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "cast5-cbc" and nid = 108 and normalized = "CAST5" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "cast5-ecb" and nid = 109 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "cast5-ecb" and nid = 109 and normalized = "CAST5" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "cast5-cfb" and nid = 110 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "cast5-cfb" and nid = 110 and normalized = "CAST5" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "cast5-ofb" and nid = 111 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "cast5-ofb" and nid = 111 and normalized = "CAST5" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-cbc" and nid = 1134 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-cbc" and nid = 1134 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "sm4-ecb" and nid = 1133 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-ecb" and nid = 1133 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "sm4-cfb" and nid = 1137 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-cfb" and nid = 1137 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "sm4-ofb" and nid = 1135 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-ofb" and nid = 1135 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "sm4-ctr" and nid = 1139 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-ctr" and nid = 1139 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "aes-128-gcm" and nid = 895 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-gcm" and nid = 895 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "secp160r1" and nid = 709 and normalized = "SECP160R1" and algType = "ELLIPTIC_CURVE" + or + name = "ripemd160" and nid = 117 and normalized = "RIPEMD160" and algType = "HASH" + or + name = "whirlpool" and nid = 804 and normalized = "WHIRLPOOL" and algType = "HASH" + or + name = "rc5-cbc" and nid = 120 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "rc5-cbc" and nid = 120 and normalized = "RC5" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pss" and nid = 435 and normalized = "PSS" and algType = "ASYMMETRIC_PADDING" + or + name = "id-aes128-wrap" and + nid = 788 and + normalized = "AES128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes192-wrap" and + nid = 789 and + normalized = "AES192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes256-wrap" and + nid = 790 and + normalized = "AES256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes128-wrap-pad" and + nid = 897 and + normalized = "AES128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes192-wrap-pad" and + nid = 900 and + normalized = "AES192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes256-wrap-pad" and + nid = 903 and + normalized = "AES256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "chacha20" and nid = 1019 and normalized = "CHACHA20" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "secp112r1" and nid = 704 and normalized = "SECP112R1" and algType = "ELLIPTIC_CURVE" + or + name = "secp112r2" and nid = 705 and normalized = "SECP112R2" and algType = "ELLIPTIC_CURVE" + or + name = "secp128r1" and nid = 706 and normalized = "SECP128R1" and algType = "ELLIPTIC_CURVE" + or + name = "secp128r2" and nid = 707 and normalized = "SECP128R2" and algType = "ELLIPTIC_CURVE" + or + name = "secp160k1" and nid = 708 and normalized = "SECP160K1" and algType = "ELLIPTIC_CURVE" + or + name = "secp160r2" and nid = 710 and normalized = "SECP160R2" and algType = "ELLIPTIC_CURVE" + or + name = "secp192k1" and nid = 711 and normalized = "SECP192K1" and algType = "ELLIPTIC_CURVE" + or + name = "secp224k1" and nid = 712 and normalized = "SECP224K1" and algType = "ELLIPTIC_CURVE" + or + name = "secp224r1" and nid = 713 and normalized = "SECP224R1" and algType = "ELLIPTIC_CURVE" + or + name = "secp256k1" and nid = 714 and normalized = "SECP256K1" and algType = "ELLIPTIC_CURVE" + or + name = "secp384r1" and nid = 715 and normalized = "SECP384R1" and algType = "ELLIPTIC_CURVE" + or + name = "secp521r1" and nid = 716 and normalized = "SECP521R1" and algType = "ELLIPTIC_CURVE" + or + name = "prime192v2" and nid = 410 and normalized = "PRIME192V2" and algType = "ELLIPTIC_CURVE" + or + name = "prime192v3" and nid = 411 and normalized = "PRIME192V3" and algType = "ELLIPTIC_CURVE" + or + name = "prime239v1" and nid = 412 and normalized = "PRIME239V1" and algType = "ELLIPTIC_CURVE" + or + name = "prime239v2" and nid = 413 and normalized = "PRIME239V2" and algType = "ELLIPTIC_CURVE" + or + name = "prime239v3" and nid = 414 and normalized = "PRIME239V3" and algType = "ELLIPTIC_CURVE" + or + name = "sect113r1" and nid = 717 and normalized = "SECT113R1" and algType = "ELLIPTIC_CURVE" + or + name = "sect113r2" and nid = 718 and normalized = "SECT113R2" and algType = "ELLIPTIC_CURVE" + or + name = "sect131r1" and nid = 719 and normalized = "SECT131R1" and algType = "ELLIPTIC_CURVE" + or + name = "sect131r2" and nid = 720 and normalized = "SECT131R2" and algType = "ELLIPTIC_CURVE" + or + name = "sect163k1" and nid = 721 and normalized = "SECT163K1" and algType = "ELLIPTIC_CURVE" + or + name = "sect163r1" and nid = 722 and normalized = "SECT163R1" and algType = "ELLIPTIC_CURVE" + or + name = "sect163r2" and nid = 723 and normalized = "SECT163R2" and algType = "ELLIPTIC_CURVE" + or + name = "sect193r1" and nid = 724 and normalized = "SECT193R1" and algType = "ELLIPTIC_CURVE" + or + name = "sect193r2" and nid = 725 and normalized = "SECT193R2" and algType = "ELLIPTIC_CURVE" + or + name = "sect233k1" and nid = 726 and normalized = "SECT233K1" and algType = "ELLIPTIC_CURVE" + or + name = "sect233r1" and nid = 727 and normalized = "SECT233R1" and algType = "ELLIPTIC_CURVE" + or + name = "sect239k1" and nid = 728 and normalized = "SECT239K1" and algType = "ELLIPTIC_CURVE" + or + name = "sect283k1" and nid = 729 and normalized = "SECT283K1" and algType = "ELLIPTIC_CURVE" + or + name = "sect283r1" and nid = 730 and normalized = "SECT283R1" and algType = "ELLIPTIC_CURVE" + or + name = "sect409k1" and nid = 731 and normalized = "SECT409K1" and algType = "ELLIPTIC_CURVE" + or + name = "sect409r1" and nid = 732 and normalized = "SECT409R1" and algType = "ELLIPTIC_CURVE" + or + name = "sect571k1" and nid = 733 and normalized = "SECT571K1" and algType = "ELLIPTIC_CURVE" + or + name = "sect571r1" and nid = 734 and normalized = "SECT571R1" and algType = "ELLIPTIC_CURVE" + or + name = "c2pnb163v1" and nid = 684 and normalized = "C2PNB163V1" and algType = "ELLIPTIC_CURVE" + or + name = "c2pnb163v2" and nid = 685 and normalized = "C2PNB163V2" and algType = "ELLIPTIC_CURVE" + or + name = "c2pnb163v3" and nid = 686 and normalized = "C2PNB163V3" and algType = "ELLIPTIC_CURVE" + or + name = "c2pnb176v1" and nid = 687 and normalized = "C2PNB176V1" and algType = "ELLIPTIC_CURVE" + or + name = "c2tnb191v1" and nid = 688 and normalized = "C2TNB191V1" and algType = "ELLIPTIC_CURVE" + or + name = "c2tnb191v2" and nid = 689 and normalized = "C2TNB191V2" and algType = "ELLIPTIC_CURVE" + or + name = "c2tnb191v3" and nid = 690 and normalized = "C2TNB191V3" and algType = "ELLIPTIC_CURVE" + or + name = "c2pnb208w1" and nid = 693 and normalized = "C2PNB208W1" and algType = "ELLIPTIC_CURVE" + or + name = "c2tnb239v1" and nid = 694 and normalized = "C2TNB239V1" and algType = "ELLIPTIC_CURVE" + or + name = "c2tnb239v2" and nid = 695 and normalized = "C2TNB239V2" and algType = "ELLIPTIC_CURVE" + or + name = "c2tnb239v3" and nid = 696 and normalized = "C2TNB239V3" and algType = "ELLIPTIC_CURVE" + or + name = "c2pnb272w1" and nid = 699 and normalized = "C2PNB272W1" and algType = "ELLIPTIC_CURVE" + or + name = "c2pnb304w1" and nid = 700 and normalized = "C2PNB304W1" and algType = "ELLIPTIC_CURVE" + or + name = "c2tnb359v1" and nid = 701 and normalized = "C2TNB359V1" and algType = "ELLIPTIC_CURVE" + or + name = "c2pnb368w1" and nid = 702 and normalized = "C2PNB368W1" and algType = "ELLIPTIC_CURVE" + or + name = "c2tnb431r1" and nid = 703 and normalized = "C2TNB431R1" and algType = "ELLIPTIC_CURVE" + or + name = "pkcs5" and nid = 187 and normalized = "PKCS5" and algType = "KEY_DERIVATION" + or + name = "aes-256-gcm" and nid = 901 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-gcm" and nid = 901 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "chacha20-poly1305" and nid = 1018 and normalized = "POLY1305" and algType = "HASH" + or + name = "chacha20-poly1305" and + nid = 1018 and + normalized = "CHACHA20POLY1305" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "rsadsi" and nid = 1 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "pkcs7-data" and nid = 21 and normalized = "PKCS7" and algType = "SYMMETRIC_PADDING" + or + name = "desx-cbc" and nid = 80 and normalized = "DESX" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "desx-cbc" and nid = 80 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "md5-sha1" and nid = 114 and normalized = "SHA1" and algType = "HASH" + or + name = "rc5-ecb" and nid = 121 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "rc5-ecb" and nid = 121 and normalized = "RC5" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rc5-cfb" and nid = 122 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "rc5-cfb" and nid = 122 and normalized = "RC5" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rc5-ofb" and nid = 123 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "rc5-ofb" and nid = 123 and normalized = "RC5" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-alg-des40" and nid = 323 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-alg-dh-sig-hmac-sha1" and nid = 325 and normalized = "SHA1" and algType = "HASH" + or + name = "aes-128-ofb" and nid = 420 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-ofb" and nid = 420 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "aes-128-cfb" and nid = 421 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-cfb" and nid = 421 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aes-192-ofb" and nid = 424 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-ofb" and nid = 424 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "aes-192-cfb" and nid = 425 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-cfb" and nid = 425 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aes-256-ofb" and nid = 428 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-ofb" and nid = 428 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "aes-256-cfb" and nid = 429 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-cfb" and nid = 429 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "des-cdmf" and nid = 643 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-cfb1" and nid = 650 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-cfb1" and nid = 650 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aes-192-cfb1" and nid = 651 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-cfb1" and nid = 651 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aes-256-cfb1" and nid = 652 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-cfb1" and nid = 652 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "aes-128-cfb8" and nid = 653 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-cfb8" and nid = 653 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "aes-192-cfb8" and nid = 654 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-cfb8" and nid = 654 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "aes-256-cfb8" and nid = 655 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-cfb8" and nid = 655 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "des-cfb1" and nid = 656 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-cfb1" and nid = 656 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "des-cfb8" and nid = 657 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-cfb8" and nid = 657 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "des-ede3-cfb1" and nid = 658 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede3-cfb1" and nid = 658 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "des-ede3-cfb8" and nid = 659 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "des-ede3-cfb8" and nid = 659 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "c2onb191v4" and nid = 691 and normalized = "C2ONB191V4" and algType = "ELLIPTIC_CURVE" + or + name = "c2onb191v5" and nid = 692 and normalized = "C2ONB191V5" and algType = "ELLIPTIC_CURVE" + or + name = "c2onb239v4" and nid = 697 and normalized = "C2ONB239V4" and algType = "ELLIPTIC_CURVE" + or + name = "c2onb239v5" and nid = 698 and normalized = "C2ONB239V5" and algType = "ELLIPTIC_CURVE" + or + name = "camellia-128-cfb" and + nid = 757 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-cfb" and nid = 757 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "camellia-192-cfb" and + nid = 758 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-cfb" and nid = 758 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "camellia-256-cfb" and + nid = 759 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-cfb" and nid = 759 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "camellia-128-cfb1" and + nid = 760 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-cfb1" and nid = 760 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "camellia-192-cfb1" and + nid = 761 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-cfb1" and nid = 761 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "camellia-256-cfb1" and + nid = 762 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-cfb1" and nid = 762 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "camellia-128-cfb8" and + nid = 763 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-cfb8" and nid = 763 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "camellia-192-cfb8" and + nid = 764 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-cfb8" and nid = 764 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "camellia-256-cfb8" and + nid = 765 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-cfb8" and nid = 765 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "camellia-128-ofb" and + nid = 766 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-ofb" and nid = 766 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "camellia-192-ofb" and + nid = 767 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-ofb" and nid = 767 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "camellia-256-ofb" and + nid = 768 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-ofb" and nid = 768 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "hmac-md5" and nid = 780 and normalized = "MD5" and algType = "HASH" + or + name = "hmac-sha1" and nid = 781 and normalized = "SHA1" and algType = "HASH" + or + name = "md_gost94" and nid = 809 and normalized = "GOST94" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost94" and nid = 812 and normalized = "GOST94" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost89" and nid = 813 and normalized = "GOST89" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost89-cnt" and nid = 814 and normalized = "GOST89" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost-mac" and nid = 815 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "prf-gostr3411-94" and + nid = 816 and + normalized = "GOSTR341194" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost94cc" and nid = 850 and normalized = "GOST94" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost2001cc" and nid = 851 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-ccm" and nid = 896 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-ccm" and nid = 896 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "aes-192-gcm" and nid = 898 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-gcm" and nid = 898 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "aes-192-ccm" and nid = 899 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-ccm" and nid = 899 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "aes-256-ccm" and nid = 902 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-ccm" and nid = 902 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "aes-128-ctr" and nid = 904 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-ctr" and nid = 904 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "aes-192-ctr" and nid = 905 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-ctr" and nid = 905 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "aes-256-ctr" and nid = 906 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-ctr" and nid = 906 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "id-camellia128-wrap" and + nid = 907 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-camellia192-wrap" and + nid = 908 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-camellia256-wrap" and + nid = 909 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "mgf1" and nid = 911 and normalized = "MGF1" and algType = "HASH" + or + name = "aes-128-xts" and nid = 913 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-xts" and nid = 913 and normalized = "XTS" and algType = "BLOCK_MODE" + or + name = "aes-256-xts" and nid = 914 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-xts" and nid = 914 and normalized = "XTS" and algType = "BLOCK_MODE" + or + name = "rc4-hmac-md5" and nid = 915 and normalized = "MD5" and algType = "HASH" + or + name = "rc4-hmac-md5" and nid = 915 and normalized = "RC4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-cbc-hmac-sha1" and nid = 916 and normalized = "SHA1" and algType = "HASH" + or + name = "aes-128-cbc-hmac-sha1" and + nid = 916 and + normalized = "AES128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-cbc-hmac-sha1" and nid = 916 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aes-192-cbc-hmac-sha1" and nid = 917 and normalized = "SHA1" and algType = "HASH" + or + name = "aes-192-cbc-hmac-sha1" and + nid = 917 and + normalized = "AES192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-cbc-hmac-sha1" and nid = 917 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aes-256-cbc-hmac-sha1" and + nid = 918 and + normalized = "AES256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-cbc-hmac-sha1" and nid = 918 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aes-128-cbc-hmac-sha256" and nid = 948 and normalized = "SHA256" and algType = "HASH" + or + name = "aes-128-cbc-hmac-sha256" and + nid = 948 and + normalized = "AES128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-cbc-hmac-sha256" and nid = 948 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aes-192-cbc-hmac-sha256" and nid = 949 and normalized = "SHA256" and algType = "HASH" + or + name = "aes-192-cbc-hmac-sha256" and + nid = 949 and + normalized = "AES192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-cbc-hmac-sha256" and nid = 949 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aes-256-cbc-hmac-sha256" and nid = 950 and normalized = "SHA256" and algType = "HASH" + or + name = "aes-256-cbc-hmac-sha256" and + nid = 950 and + normalized = "AES256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-cbc-hmac-sha256" and nid = 950 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "aes-128-ocb" and nid = 958 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-ocb" and nid = 959 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-ocb" and nid = 960 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-gcm" and + nid = 961 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-gcm" and nid = 961 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "camellia-128-ccm" and + nid = 962 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-ccm" and nid = 962 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "camellia-128-ctr" and + nid = 963 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-ctr" and nid = 963 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "camellia-128-cmac" and + nid = 964 and + normalized = "CAMELLIA128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-gcm" and + nid = 965 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-gcm" and nid = 965 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "camellia-192-ccm" and + nid = 966 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-ccm" and nid = 966 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "camellia-192-ctr" and + nid = 967 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-ctr" and nid = 967 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "camellia-192-cmac" and + nid = 968 and + normalized = "CAMELLIA192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-gcm" and + nid = 969 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-gcm" and nid = 969 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "camellia-256-ccm" and + nid = 970 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-ccm" and nid = 970 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "camellia-256-ctr" and + nid = 971 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-ctr" and nid = 971 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "camellia-256-cmac" and + nid = 972 and + normalized = "CAMELLIA256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-scrypt" and nid = 973 and normalized = "SCRYPT" and algType = "KEY_DERIVATION" + or + name = "gost89-cnt-12" and + nid = 975 and + normalized = "GOST89" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost-mac-12" and nid = 976 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "md_gost12_256" and nid = 982 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "md_gost12_512" and nid = 983 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-signwithdigest-gost3410-2012-256" and + nid = 985 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-signwithdigest-gost3410-2012-512" and + nid = 986 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-hmac-gost-3411-2012-256" and + nid = 988 and + normalized = "GOST34112012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-hmac-gost-3411-2012-512" and + nid = 989 and + normalized = "GOST34112012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-agreement-gost-3410-2012-256" and + nid = 992 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-agreement-gost-3410-2012-512" and + nid = 993 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-3410-2012-512-constants" and + nid = 996 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-28147-constants" and + nid = 1002 and + normalized = "GOST28147" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost89-cbc" and nid = 1009 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "gost89-cbc" and nid = 1009 and normalized = "GOST89" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost89-ecb" and nid = 1010 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "gost89-ecb" and nid = 1010 and normalized = "GOST89" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost89-ctr" and nid = 1011 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "gost89-ctr" and nid = 1011 and normalized = "GOST89" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "kuznyechik-ecb" and nid = 1012 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "kuznyechik-ecb" and + nid = 1012 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "kuznyechik-ctr" and nid = 1013 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "kuznyechik-ctr" and + nid = 1013 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "kuznyechik-ofb" and nid = 1014 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "kuznyechik-ofb" and + nid = 1014 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "kuznyechik-cbc" and nid = 1015 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "kuznyechik-cbc" and + nid = 1015 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "kuznyechik-cfb" and nid = 1016 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "kuznyechik-cfb" and + nid = 1016 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "kuznyechik-mac" and + nid = 1017 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "hkdf" and nid = 1036 and normalized = "HKDF" and algType = "KEY_DERIVATION" + or + name = "kx-rsa" and nid = 1037 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "kx-ecdhe" and nid = 1038 and normalized = "ECDH" and algType = "KEY_EXCHANGE" + or + name = "kx-ecdhe-psk" and nid = 1040 and normalized = "ECDH" and algType = "KEY_EXCHANGE" + or + name = "kx-rsa-psk" and nid = 1042 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "kx-gost" and nid = 1045 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "auth-rsa" and nid = 1046 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "auth-ecdsa" and nid = 1047 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "auth-gost01" and nid = 1050 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "auth-gost12" and nid = 1051 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "poly1305" and nid = 1061 and normalized = "POLY1305" and algType = "HASH" + or + name = "hmac-sha3-224" and nid = 1102 and normalized = "SHA3224" and algType = "HASH" + or + name = "hmac-sha3-256" and nid = 1103 and normalized = "SHA3256" and algType = "HASH" + or + name = "hmac-sha3-384" and nid = 1104 and normalized = "SHA3384" and algType = "HASH" + or + name = "hmac-sha3-512" and nid = 1105 and normalized = "SHA3512" and algType = "HASH" + or + name = "id-dsa-with-sha384" and nid = 1106 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "id-dsa-with-sha384" and nid = 1106 and normalized = "SHA384" and algType = "HASH" + or + name = "id-dsa-with-sha512" and nid = 1107 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "id-dsa-with-sha512" and nid = 1107 and normalized = "SHA512" and algType = "HASH" + or + name = "id-dsa-with-sha3-224" and nid = 1108 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "id-dsa-with-sha3-224" and nid = 1108 and normalized = "SHA3224" and algType = "HASH" + or + name = "id-dsa-with-sha3-256" and nid = 1109 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "id-dsa-with-sha3-256" and nid = 1109 and normalized = "SHA3256" and algType = "HASH" + or + name = "id-dsa-with-sha3-384" and nid = 1110 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "id-dsa-with-sha3-384" and nid = 1110 and normalized = "SHA3384" and algType = "HASH" + or + name = "id-dsa-with-sha3-512" and nid = 1111 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "id-dsa-with-sha3-512" and nid = 1111 and normalized = "SHA3512" and algType = "HASH" + or + name = "id-ecdsa-with-sha3-224" and nid = 1112 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "id-ecdsa-with-sha3-224" and nid = 1112 and normalized = "SHA3224" and algType = "HASH" + or + name = "id-ecdsa-with-sha3-256" and nid = 1113 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "id-ecdsa-with-sha3-256" and nid = 1113 and normalized = "SHA3256" and algType = "HASH" + or + name = "id-ecdsa-with-sha3-384" and nid = 1114 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "id-ecdsa-with-sha3-384" and nid = 1114 and normalized = "SHA3384" and algType = "HASH" + or + name = "id-ecdsa-with-sha3-512" and nid = 1115 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "id-ecdsa-with-sha3-512" and nid = 1115 and normalized = "SHA3512" and algType = "HASH" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-224" and + nid = 1116 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-224" and + nid = 1116 and + normalized = "PKCS1V15" and + algType = "ASYMMETRIC_PADDING" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-224" and + nid = 1116 and + normalized = "SHA3224" and + algType = "HASH" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-256" and + nid = 1117 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-256" and + nid = 1117 and + normalized = "PKCS1V15" and + algType = "ASYMMETRIC_PADDING" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-256" and + nid = 1117 and + normalized = "SHA3256" and + algType = "HASH" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-384" and + nid = 1118 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-384" and + nid = 1118 and + normalized = "PKCS1V15" and + algType = "ASYMMETRIC_PADDING" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-384" and + nid = 1118 and + normalized = "SHA3384" and + algType = "HASH" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-512" and + nid = 1119 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-512" and + nid = 1119 and + normalized = "PKCS1V15" and + algType = "ASYMMETRIC_PADDING" + or + name = "id-rsassa-pkcs1-v1_5-with-sha3-512" and + nid = 1119 and + normalized = "SHA3512" and + algType = "HASH" + or + name = "aria-128-ccm" and nid = 1120 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "aria-128-ccm" and nid = 1120 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-192-ccm" and nid = 1121 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "aria-192-ccm" and nid = 1121 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-256-ccm" and nid = 1122 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "aria-256-ccm" and nid = 1122 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-128-gcm" and nid = 1123 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "aria-128-gcm" and nid = 1123 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-192-gcm" and nid = 1124 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "aria-192-gcm" and nid = 1124 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aria-256-gcm" and nid = 1125 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "aria-256-gcm" and nid = 1125 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-cfb1" and nid = 1136 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-cfb1" and nid = 1136 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "sm4-cfb8" and nid = 1138 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-cfb8" and nid = 1138 and normalized = "CFB8" and algType = "BLOCK_MODE" + or + name = "id-tc26-gost-3410-2012-256-constants" and + nid = 1147 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "dstu28147-ofb" and nid = 1153 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "dstu28147-cfb" and nid = 1154 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "id-tc26-cipher-gostr3412-2015-magma" and + nid = 1173 and + normalized = "MAGMA" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "magma-ctr-acpkm" and nid = 1174 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "magma-ctr-acpkm" and + nid = 1174 and + normalized = "MAGMA" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "magma-ctr-acpkm-omac" and nid = 1175 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "magma-ctr-acpkm-omac" and + nid = 1175 and + normalized = "MAGMA" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-cipher-gostr3412-2015-kuznyechik" and + nid = 1176 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "kuznyechik-ctr-acpkm" and nid = 1177 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "kuznyechik-ctr-acpkm" and + nid = 1177 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "kuznyechik-ctr-acpkm-omac" and + nid = 1178 and + normalized = "CTR" and + algType = "BLOCK_MODE" + or + name = "kuznyechik-ctr-acpkm-omac" and + nid = 1178 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-wrap-gostr3412-2015-magma" and + nid = 1180 and + normalized = "MAGMA" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "magma-kexp15" and nid = 1181 and normalized = "MAGMA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-wrap-gostr3412-2015-kuznyechik" and + nid = 1182 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "kuznyechik-kexp15" and + nid = 1183 and + normalized = "KUZNYECHIK" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "magma-ecb" and nid = 1187 and normalized = "ECB" and algType = "BLOCK_MODE" + or + name = "magma-ecb" and nid = 1187 and normalized = "MAGMA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "magma-ctr" and nid = 1188 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "magma-ctr" and nid = 1188 and normalized = "MAGMA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "magma-ofb" and nid = 1189 and normalized = "OFB" and algType = "BLOCK_MODE" + or + name = "magma-ofb" and nid = 1189 and normalized = "MAGMA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "magma-cbc" and nid = 1190 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "magma-cbc" and nid = 1190 and normalized = "MAGMA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "magma-cfb" and nid = 1191 and normalized = "CFB" and algType = "BLOCK_MODE" + or + name = "magma-cfb" and nid = 1191 and normalized = "MAGMA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "magma-mac" and nid = 1192 and normalized = "MAGMA" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-siv" and nid = 1198 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-128-siv" and nid = 1198 and normalized = "SIV" and algType = "BLOCK_MODE" + or + name = "aes-192-siv" and nid = 1199 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-192-siv" and nid = 1199 and normalized = "SIV" and algType = "BLOCK_MODE" + or + name = "aes-256-siv" and nid = 1200 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "aes-256-siv" and nid = 1200 and normalized = "SIV" and algType = "BLOCK_MODE" + or + name = "blake2bmac" and nid = 1201 and normalized = "BLAKE2B" and algType = "HASH" + or + name = "blake2smac" and nid = 1202 and normalized = "BLAKE2S" and algType = "HASH" + or + name = "sshkdf" and nid = 1203 and normalized = "HKDF" and algType = "KEY_DERIVATION" + or + name = "x963kdf" and nid = 1206 and normalized = "X963KDF" and algType = "KEY_DERIVATION" + or + name = "kx-gost18" and nid = 1218 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-gcm" and nid = 1248 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-gcm" and nid = 1248 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "sm4-ccm" and nid = 1249 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-ccm" and nid = 1249 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "sm4-xts" and nid = 1290 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "sm4-xts" and nid = 1290 and normalized = "XTS" and algType = "BLOCK_MODE" + or + name = "x448" and nid = 1035 and normalized = "X448" and algType = "ELLIPTIC_CURVE" + or + name = "x25519" and nid = 1034 and normalized = "X25519" and algType = "ELLIPTIC_CURVE" + or + name = "authecdsa" and nid = 1047 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "authgost01" and nid = 1050 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "authgost12" and nid = 1051 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "authrsa" and nid = 1046 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "brainpoolp160r1" and + nid = 921 and + normalized = "BRAINPOOLP160R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp160t1" and + nid = 922 and + normalized = "BRAINPOOLP160T1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp192r1" and + nid = 923 and + normalized = "BRAINPOOLP192R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp192t1" and + nid = 924 and + normalized = "BRAINPOOLP192T1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp224r1" and + nid = 925 and + normalized = "BRAINPOOLP224R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp224t1" and + nid = 926 and + normalized = "BRAINPOOLP224T1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp256r1" and + nid = 927 and + normalized = "BRAINPOOLP256R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp256r1tls13" and + nid = 1285 and + normalized = "BRAINPOOLP256R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp256t1" and + nid = 928 and + normalized = "BRAINPOOLP256T1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp320r1" and + nid = 929 and + normalized = "BRAINPOOLP320R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp320t1" and + nid = 930 and + normalized = "BRAINPOOLP320T1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp384r1" and + nid = 931 and + normalized = "BRAINPOOLP384R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp384r1tls13" and + nid = 1286 and + normalized = "BRAINPOOLP384R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp384t1" and + nid = 932 and + normalized = "BRAINPOOLP384T1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp512r1" and + nid = 933 and + normalized = "BRAINPOOLP512R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp512r1tls13" and + nid = 1287 and + normalized = "BRAINPOOLP512R1" and + algType = "ELLIPTIC_CURVE" + or + name = "brainpoolp512t1" and + nid = 934 and + normalized = "BRAINPOOLP512T1" and + algType = "ELLIPTIC_CURVE" + or + name = "dhsinglepass-cofactordh-sha1kdf-scheme" and + nid = 941 and + normalized = "SHA1" and + algType = "HASH" + or + name = "dhsinglepass-cofactordh-sha224kdf-scheme" and + nid = 942 and + normalized = "SHA224" and + algType = "HASH" + or + name = "dhsinglepass-cofactordh-sha256kdf-scheme" and + nid = 943 and + normalized = "SHA256" and + algType = "HASH" + or + name = "dhsinglepass-cofactordh-sha384kdf-scheme" and + nid = 944 and + normalized = "SHA384" and + algType = "HASH" + or + name = "dhsinglepass-cofactordh-sha512kdf-scheme" and + nid = 945 and + normalized = "SHA512" and + algType = "HASH" + or + name = "dhsinglepass-stddh-sha1kdf-scheme" and + nid = 936 and + normalized = "SHA1" and + algType = "HASH" + or + name = "dhsinglepass-stddh-sha224kdf-scheme" and + nid = 937 and + normalized = "SHA224" and + algType = "HASH" + or + name = "dhsinglepass-stddh-sha256kdf-scheme" and + nid = 938 and + normalized = "SHA256" and + algType = "HASH" + or + name = "dhsinglepass-stddh-sha384kdf-scheme" and + nid = 939 and + normalized = "SHA384" and + algType = "HASH" + or + name = "dhsinglepass-stddh-sha512kdf-scheme" and + nid = 940 and + normalized = "SHA512" and + algType = "HASH" + or + name = "dsa-old" and nid = 67 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa-sha" and nid = 66 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa-sha" and nid = 66 and normalized = "SHA" and algType = "HASH" + or + name = "dsa-sha1" and nid = 113 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa-sha1" and nid = 113 and normalized = "SHA1" and algType = "HASH" + or + name = "dsa-sha1-old" and nid = 70 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa-sha1-old" and nid = 70 and normalized = "SHA1" and algType = "HASH" + or + name = "dsa_with_sha224" and nid = 802 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa_with_sha224" and nid = 802 and normalized = "SHA224" and algType = "HASH" + or + name = "dsa_with_sha256" and nid = 803 and normalized = "SHA256" and algType = "HASH" + or + name = "dsa_with_sha256" and nid = 803 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa_with_sha3-224" and nid = 1108 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa_with_sha3-224" and nid = 1108 and normalized = "SHA3224" and algType = "HASH" + or + name = "dsa_with_sha3-256" and nid = 1109 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa_with_sha3-256" and nid = 1109 and normalized = "SHA3256" and algType = "HASH" + or + name = "dsa_with_sha3-384" and nid = 1110 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa_with_sha3-384" and nid = 1110 and normalized = "SHA3384" and algType = "HASH" + or + name = "dsa_with_sha3-512" and nid = 1111 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa_with_sha3-512" and nid = 1111 and normalized = "SHA3512" and algType = "HASH" + or + name = "dsa_with_sha384" and nid = 1106 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa_with_sha384" and nid = 1106 and normalized = "SHA384" and algType = "HASH" + or + name = "dsa_with_sha512" and nid = 1107 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsa_with_sha512" and nid = 1107 and normalized = "SHA512" and algType = "HASH" + or + name = "dsaencryption" and nid = 116 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsaencryption-old" and nid = 67 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsaquality" and nid = 495 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsawithsha" and nid = 66 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsawithsha" and nid = 66 and normalized = "SHA" and algType = "HASH" + or + name = "dsawithsha1" and nid = 113 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsawithsha1" and nid = 113 and normalized = "SHA1" and algType = "HASH" + or + name = "dsawithsha1-old" and nid = 70 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "dsawithsha1-old" and nid = 70 and normalized = "SHA1" and algType = "HASH" + or + name = "dstu gost 28147-2009 cfb mode" and + nid = 1154 and + normalized = "CFB" and + algType = "BLOCK_MODE" + or + name = "dstu gost 28147-2009 cfb mode" and + nid = 1154 and + normalized = "GOST28147" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "dstu gost 28147-2009 ofb mode" and + nid = 1153 and + normalized = "OFB" and + algType = "BLOCK_MODE" + or + name = "dstu gost 28147-2009 ofb mode" and + nid = 1153 and + normalized = "GOST28147" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "ecdsa-with-recommended" and nid = 791 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa-with-sha1" and nid = 416 and normalized = "SHA1" and algType = "HASH" + or + name = "ecdsa-with-sha1" and nid = 416 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa-with-sha224" and nid = 793 and normalized = "SHA224" and algType = "HASH" + or + name = "ecdsa-with-sha224" and nid = 793 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa-with-sha256" and nid = 794 and normalized = "SHA256" and algType = "HASH" + or + name = "ecdsa-with-sha256" and nid = 794 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa-with-sha384" and nid = 795 and normalized = "SHA384" and algType = "HASH" + or + name = "ecdsa-with-sha384" and nid = 795 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa-with-sha512" and nid = 796 and normalized = "SHA512" and algType = "HASH" + or + name = "ecdsa-with-sha512" and nid = 796 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa-with-specified" and nid = 792 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa_with_sha3-224" and nid = 1112 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa_with_sha3-224" and nid = 1112 and normalized = "SHA3224" and algType = "HASH" + or + name = "ecdsa_with_sha3-256" and nid = 1113 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa_with_sha3-256" and nid = 1113 and normalized = "SHA3256" and algType = "HASH" + or + name = "ecdsa_with_sha3-384" and nid = 1114 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa_with_sha3-384" and nid = 1114 and normalized = "SHA3384" and algType = "HASH" + or + name = "ecdsa_with_sha3-512" and nid = 1115 and normalized = "ECDSA" and algType = "SIGNATURE" + or + name = "ecdsa_with_sha3-512" and nid = 1115 and normalized = "SHA3512" and algType = "HASH" + or + name = "gost 28147-89" and + nid = 813 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost 28147-89 cryptocom paramset" and + nid = 849 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost 28147-89 mac" and + nid = 815 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost 28147-89 tc26 parameter set" and + nid = 1003 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost 34.10-2001 cryptocom" and + nid = 851 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost 34.10-94 cryptocom" and + nid = 850 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2001" and + nid = 811 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2001 dh" and + nid = 817 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 (256 bit) paramset a" and + nid = 1148 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 (256 bit) paramset b" and + nid = 1184 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 (256 bit) paramset c" and + nid = 1185 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 (256 bit) paramset d" and + nid = 1186 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 (512 bit) paramset a" and + nid = 998 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 (512 bit) paramset b" and + nid = 999 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 (512 bit) paramset c" and + nid = 1149 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 (512 bit) testing parameter set" and + nid = 997 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 with 256 bit modulus" and + nid = 979 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 with 512 bit modulus" and + nid = 980 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 with gost r 34.11-2012 (256 bit)" and + nid = 985 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-2012 with gost r 34.11-2012 (512 bit)" and + nid = 986 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-94" and + nid = 812 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.10-94 dh" and + nid = 818 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.11-2012 with 256 bit hash" and + nid = 982 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.11-2012 with 512 bit hash" and + nid = 983 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.11-94" and + nid = 809 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.11-94 prf" and + nid = 816 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.11-94 with gost r 34.10-2001" and + nid = 807 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.11-94 with gost r 34.10-2001 cryptocom" and + nid = 853 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.11-94 with gost r 34.10-94" and + nid = 808 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 34.11-94 with gost r 34.10-94 cryptocom" and + nid = 852 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "gost r 3410-2001 parameter set cryptocom" and + nid = 854 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "hmac gost 34.11-2012 256 bit" and + nid = 988 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "hmac gost 34.11-2012 512 bit" and + nid = 989 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "hmac gost 34.11-94" and + nid = 810 and + normalized = "GOST" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "hmacwithmd5" and nid = 797 and normalized = "MD5" and algType = "HASH" + or + name = "hmacwithsha1" and nid = 163 and normalized = "SHA1" and algType = "HASH" + or + name = "hmacwithsha224" and nid = 798 and normalized = "SHA224" and algType = "HASH" + or + name = "hmacwithsha256" and nid = 799 and normalized = "SHA256" and algType = "HASH" + or + name = "hmacwithsha384" and nid = 800 and normalized = "SHA384" and algType = "HASH" + or + name = "hmacwithsha512" and nid = 801 and normalized = "SHA512" and algType = "HASH" + or + name = "hmacwithsha512-224" and nid = 1193 and normalized = "SHA512224" and algType = "HASH" + or + name = "hmacwithsha512-256" and nid = 1194 and normalized = "SHA512256" and algType = "HASH" + or + name = "hmacwithsm3" and nid = 1281 and normalized = "SM3" and algType = "HASH" + or + name = "id-aes128-ccm" and + nid = 896 and + normalized = "AES128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes128-ccm" and nid = 896 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "id-aes128-gcm" and + nid = 895 and + normalized = "AES128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes128-gcm" and nid = 895 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "id-aes192-ccm" and + nid = 899 and + normalized = "AES192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes192-ccm" and nid = 899 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "id-aes192-gcm" and + nid = 898 and + normalized = "AES192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes192-gcm" and nid = 898 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "id-aes256-ccm" and + nid = 902 and + normalized = "AES256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes256-ccm" and nid = 902 and normalized = "CCM" and algType = "BLOCK_MODE" + or + name = "id-aes256-gcm" and + nid = 901 and + normalized = "AES256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes256-gcm" and nid = 901 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "id-gost28147-89-cc" and + nid = 849 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-cryptopro-a-paramset" and + nid = 824 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-cryptopro-b-paramset" and + nid = 825 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-cryptopro-c-paramset" and + nid = 826 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-cryptopro-d-paramset" and + nid = 827 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-cryptopro-keymeshing" and + nid = 819 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-cryptopro-oscar-1-0-paramset" and + nid = 829 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-cryptopro-oscar-1-1-paramset" and + nid = 828 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-cryptopro-ric-1-paramset" and + nid = 830 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-none-keymeshing" and + nid = 820 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gost28147-89-testparamset" and + nid = 823 and + normalized = "GOST2814789" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-2001-cryptopro-a-paramset" and + nid = 840 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-2001-cryptopro-b-paramset" and + nid = 841 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-2001-cryptopro-c-paramset" and + nid = 842 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-2001-cryptopro-xcha-paramset" and + nid = 843 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-2001-cryptopro-xchb-paramset" and + nid = 844 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-2001-paramset-cc" and + nid = 854 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-2001-testparamset" and + nid = 839 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-2001dh" and + nid = 817 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-a" and + nid = 845 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-abis" and + nid = 846 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-b" and + nid = 847 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-bbis" and + nid = 848 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-cryptopro-a-paramset" and + nid = 832 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-cryptopro-b-paramset" and + nid = 833 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-cryptopro-c-paramset" and + nid = 834 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-cryptopro-d-paramset" and + nid = 835 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-cryptopro-xcha-paramset" and + nid = 836 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-cryptopro-xchb-paramset" and + nid = 837 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-cryptopro-xchc-paramset" and + nid = 838 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94-testparamset" and + nid = 831 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3410-94dh" and + nid = 818 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3411-94-cryptoproparamset" and + nid = 822 and + normalized = "GOSTR341194" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3411-94-testparamset" and + nid = 821 and + normalized = "GOSTR341194" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3411-94-with-gostr3410-2001" and + nid = 807 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3411-94-with-gostr3410-2001-cc" and + nid = 853 and + normalized = "GOSTR34102001" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3411-94-with-gostr3410-94" and + nid = 808 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3411-94-with-gostr3410-94" and + nid = 808 and + normalized = "GOSTR341194" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3411-94-with-gostr3410-94-cc" and + nid = 852 and + normalized = "GOSTR341094" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-gostr3411-94-with-gostr3410-94-cc" and + nid = 852 and + normalized = "GOSTR341194" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-hmacgostr3411-94" and + nid = 810 and + normalized = "GOSTR341194" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-hmacwithsha3-224" and nid = 1102 and normalized = "SHA3224" and algType = "HASH" + or + name = "id-hmacwithsha3-256" and nid = 1103 and normalized = "SHA3256" and algType = "HASH" + or + name = "id-hmacwithsha3-384" and nid = 1104 and normalized = "SHA3384" and algType = "HASH" + or + name = "id-hmacwithsha3-512" and nid = 1105 and normalized = "SHA3512" and algType = "HASH" + or + name = "id-regctrl" and nid = 313 and normalized = "CTR" and algType = "BLOCK_MODE" + or + name = "id-smime-alg-3deswrap" and + nid = 243 and + normalized = "3DES" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-smime-alg-cms3deswrap" and nid = 246 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "id-smime-alg-cms3deswrap" and + nid = 246 and + normalized = "3DES" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-smime-alg-cmsrc2wrap" and + nid = 247 and + normalized = "RC2" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-smime-alg-cmsrc2wrap" and nid = 247 and normalized = "GCM" and algType = "BLOCK_MODE" + or + name = "id-smime-alg-esdhwith3des" and + nid = 241 and + normalized = "3DES" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-smime-alg-esdhwithrc2" and + nid = 242 and + normalized = "RC2" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-smime-alg-rc2wrap" and + nid = 244 and + normalized = "RC2" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-28147-param-z" and + nid = 1003 and + normalized = "GOST28147" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-3410-2012-256-paramseta" and + nid = 1148 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-3410-2012-256-paramsetb" and + nid = 1184 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-3410-2012-256-paramsetc" and + nid = 1185 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-3410-2012-256-paramsetd" and + nid = 1186 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-3410-2012-512-paramseta" and + nid = 998 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-3410-2012-512-paramsetb" and + nid = 999 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-3410-2012-512-paramsetc" and + nid = 1149 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-tc26-gost-3410-2012-512-paramsettest" and + nid = 997 and + normalized = "GOST34102012" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "kxecdhe" and nid = 1038 and normalized = "ECDH" and algType = "KEY_EXCHANGE" + or + name = "kxecdhe-psk" and nid = 1040 and normalized = "ECDH" and algType = "KEY_EXCHANGE" + or + name = "kxgost" and nid = 1045 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "kxgost18" and nid = 1218 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "kxrsa" and nid = 1037 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "kxrsa_psk" and nid = 1042 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "md2withrsaencryption" and + nid = 7 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "md2withrsaencryption" and nid = 7 and normalized = "MD2" and algType = "HASH" + or + name = "md4withrsaencryption" and + nid = 396 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "md4withrsaencryption" and nid = 396 and normalized = "MD4" and algType = "HASH" + or + name = "md5withrsa" and nid = 104 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "md5withrsa" and nid = 104 and normalized = "MD5" and algType = "HASH" + or + name = "md5withrsaencryption" and + nid = 8 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "md5withrsaencryption" and nid = 8 and normalized = "MD5" and algType = "HASH" + or + name = "mdc2withrsa" and nid = 96 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "mdc2withrsa" and nid = 96 and normalized = "MDC2" and algType = "HASH" + or + name = "pbe-md2-des" and nid = 9 and normalized = "MD2" and algType = "HASH" + or + name = "pbe-md2-des" and nid = 9 and normalized = "2DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-md2-rc2-64" and nid = 168 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-md2-rc2-64" and nid = 168 and normalized = "MD2" and algType = "HASH" + or + name = "pbe-md5-des" and nid = 10 and normalized = "MD5" and algType = "HASH" + or + name = "pbe-md5-des" and nid = 10 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-md5-rc2-64" and nid = 169 and normalized = "MD5" and algType = "HASH" + or + name = "pbe-md5-rc2-64" and nid = 169 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-sha1-2des" and nid = 147 and normalized = "SHA1" and algType = "HASH" + or + name = "pbe-sha1-2des" and nid = 147 and normalized = "2DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-sha1-3des" and nid = 146 and normalized = "SHA1" and algType = "HASH" + or + name = "pbe-sha1-3des" and nid = 146 and normalized = "3DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-sha1-des" and nid = 170 and normalized = "SHA1" and algType = "HASH" + or + name = "pbe-sha1-des" and nid = 170 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-sha1-rc2-128" and nid = 148 and normalized = "SHA1" and algType = "HASH" + or + name = "pbe-sha1-rc2-128" and + nid = 148 and + normalized = "RC2" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-sha1-rc2-40" and nid = 149 and normalized = "SHA1" and algType = "HASH" + or + name = "pbe-sha1-rc2-40" and nid = 149 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-sha1-rc2-64" and nid = 68 and normalized = "SHA1" and algType = "HASH" + or + name = "pbe-sha1-rc2-64" and nid = 68 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-sha1-rc4-128" and nid = 144 and normalized = "SHA1" and algType = "HASH" + or + name = "pbe-sha1-rc4-128" and + nid = 144 and + normalized = "RC4" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbe-sha1-rc4-40" and nid = 145 and normalized = "SHA1" and algType = "HASH" + or + name = "pbe-sha1-rc4-40" and nid = 145 and normalized = "RC4" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithmd2anddes-cbc" and + nid = 9 and + normalized = "DES" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithmd2anddes-cbc" and nid = 9 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "pbewithmd2anddes-cbc" and nid = 9 and normalized = "MD2" and algType = "HASH" + or + name = "pbewithmd2andrc2-cbc" and + nid = 168 and + normalized = "RC2" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithmd2andrc2-cbc" and nid = 168 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "pbewithmd2andrc2-cbc" and nid = 168 and normalized = "MD2" and algType = "HASH" + or + name = "pbewithmd5andcast5cbc" and nid = 112 and normalized = "MD5" and algType = "HASH" + or + name = "pbewithmd5andcast5cbc" and nid = 112 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "pbewithmd5andcast5cbc" and + nid = 112 and + normalized = "CAST5" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithmd5anddes-cbc" and nid = 10 and normalized = "MD5" and algType = "HASH" + or + name = "pbewithmd5anddes-cbc" and + nid = 10 and + normalized = "DES" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithmd5anddes-cbc" and nid = 10 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "pbewithmd5andrc2-cbc" and nid = 169 and normalized = "MD5" and algType = "HASH" + or + name = "pbewithmd5andrc2-cbc" and + nid = 169 and + normalized = "RC2" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithmd5andrc2-cbc" and nid = 169 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "pbewithsha1and128bitrc2-cbc" and nid = 148 and normalized = "SHA1" and algType = "HASH" + or + name = "pbewithsha1and128bitrc2-cbc" and + nid = 148 and + normalized = "RC2" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithsha1and128bitrc2-cbc" and + nid = 148 and + normalized = "CBC" and + algType = "BLOCK_MODE" + or + name = "pbewithsha1and128bitrc4" and nid = 144 and normalized = "SHA1" and algType = "HASH" + or + name = "pbewithsha1and128bitrc4" and + nid = 144 and + normalized = "RC4" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithsha1and2-keytripledes-cbc" and + nid = 147 and + normalized = "SHA1" and + algType = "HASH" + or + name = "pbewithsha1and2-keytripledes-cbc" and + nid = 147 and + normalized = "CBC" and + algType = "BLOCK_MODE" + or + name = "pbewithsha1and2-keytripledes-cbc" and + nid = 147 and + normalized = "TRIPLEDES" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithsha1and3-keytripledes-cbc" and + nid = 146 and + normalized = "SHA1" and + algType = "HASH" + or + name = "pbewithsha1and3-keytripledes-cbc" and + nid = 146 and + normalized = "CBC" and + algType = "BLOCK_MODE" + or + name = "pbewithsha1and3-keytripledes-cbc" and + nid = 146 and + normalized = "TRIPLEDES" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithsha1and40bitrc2-cbc" and nid = 149 and normalized = "SHA1" and algType = "HASH" + or + name = "pbewithsha1and40bitrc2-cbc" and + nid = 149 and + normalized = "RC2" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithsha1and40bitrc2-cbc" and + nid = 149 and + normalized = "CBC" and + algType = "BLOCK_MODE" + or + name = "pbewithsha1and40bitrc4" and nid = 145 and normalized = "SHA1" and algType = "HASH" + or + name = "pbewithsha1and40bitrc4" and + nid = 145 and + normalized = "RC4" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithsha1anddes-cbc" and nid = 170 and normalized = "SHA1" and algType = "HASH" + or + name = "pbewithsha1anddes-cbc" and + nid = 170 and + normalized = "DES" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithsha1anddes-cbc" and nid = 170 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "pbewithsha1andrc2-cbc" and nid = 68 and normalized = "SHA1" and algType = "HASH" + or + name = "pbewithsha1andrc2-cbc" and + nid = 68 and + normalized = "RC2" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "pbewithsha1andrc2-cbc" and nid = 68 and normalized = "CBC" and algType = "BLOCK_MODE" + or + name = "pilotdsa" and nid = 456 and normalized = "DSA" and algType = "SIGNATURE" + or + name = "pkcs7-digestdata" and nid = 25 and normalized = "PKCS7" and algType = "SYMMETRIC_PADDING" + or + name = "pkcs7-encrypteddata" and + nid = 26 and + normalized = "PKCS7" and + algType = "SYMMETRIC_PADDING" + or + name = "pkcs7-envelopeddata" and + nid = 23 and + normalized = "PKCS7" and + algType = "SYMMETRIC_PADDING" + or + name = "pkcs7-signedandenvelopeddata" and + nid = 24 and + normalized = "PKCS7" and + algType = "SYMMETRIC_PADDING" + or + name = "pkcs7-signeddata" and nid = 22 and normalized = "PKCS7" and algType = "SYMMETRIC_PADDING" + or + name = "ripemd160withrsa" and + nid = 119 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "ripemd160withrsa" and nid = 119 and normalized = "RIPEMD160" and algType = "HASH" + or + name = "rsa-md2" and nid = 7 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-md2" and nid = 7 and normalized = "MD2" and algType = "HASH" + or + name = "rsa-md4" and nid = 396 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-md4" and nid = 396 and normalized = "MD4" and algType = "HASH" + or + name = "rsa-md5" and nid = 8 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-md5" and nid = 8 and normalized = "MD5" and algType = "HASH" + or + name = "rsa-mdc2" and nid = 96 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-mdc2" and nid = 96 and normalized = "MDC2" and algType = "HASH" + or + name = "rsa-np-md5" and nid = 104 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-np-md5" and nid = 104 and normalized = "MD5" and algType = "HASH" + or + name = "rsa-ripemd160" and nid = 119 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-ripemd160" and nid = 119 and normalized = "RIPEMD160" and algType = "HASH" + or + name = "rsa-sha" and nid = 42 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha" and nid = 42 and normalized = "SHA" and algType = "HASH" + or + name = "rsa-sha1" and nid = 65 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha1" and nid = 65 and normalized = "SHA1" and algType = "HASH" + or + name = "rsa-sha1-2" and nid = 115 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha1-2" and nid = 115 and normalized = "SHA1" and algType = "HASH" + or + name = "rsa-sha224" and nid = 671 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha224" and nid = 671 and normalized = "SHA224" and algType = "HASH" + or + name = "rsa-sha256" and nid = 668 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha256" and nid = 668 and normalized = "SHA256" and algType = "HASH" + or + name = "rsa-sha3-224" and nid = 1116 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha3-224" and nid = 1116 and normalized = "SHA3224" and algType = "HASH" + or + name = "rsa-sha3-256" and nid = 1117 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha3-256" and nid = 1117 and normalized = "SHA3256" and algType = "HASH" + or + name = "rsa-sha3-384" and nid = 1118 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha3-384" and nid = 1118 and normalized = "SHA3384" and algType = "HASH" + or + name = "rsa-sha3-512" and nid = 1119 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha3-512" and nid = 1119 and normalized = "SHA3512" and algType = "HASH" + or + name = "rsa-sha384" and nid = 669 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha384" and nid = 669 and normalized = "SHA384" and algType = "HASH" + or + name = "rsa-sha512" and nid = 670 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha512" and nid = 670 and normalized = "SHA512" and algType = "HASH" + or + name = "rsa-sha512/224" and + nid = 1145 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha512/224" and nid = 1145 and normalized = "SHA512224" and algType = "HASH" + or + name = "rsa-sha512/256" and + nid = 1146 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sha512/256" and nid = 1146 and normalized = "SHA512256" and algType = "HASH" + or + name = "rsa-sm3" and nid = 1144 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsa-sm3" and nid = 1144 and normalized = "SM3" and algType = "HASH" + or + name = "rsaencryption" and nid = 6 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsaes-oaep" and nid = 919 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsaes-oaep" and nid = 919 and normalized = "AES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rsaes-oaep" and nid = 919 and normalized = "OAEP" and algType = "ASYMMETRIC_PADDING" + or + name = "rsaesoaep" and nid = 919 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsaesoaep" and nid = 919 and normalized = "AES" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "rsaesoaep" and nid = 919 and normalized = "OAEP" and algType = "ASYMMETRIC_PADDING" + or + name = "rsaoaepencryptionset" and + nid = 644 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsaoaepencryptionset" and + nid = 644 and + normalized = "OAEP" and + algType = "ASYMMETRIC_PADDING" + or + name = "rsasignature" and nid = 377 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsassa-pss" and nid = 912 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsassa-pss" and nid = 912 and normalized = "PSS" and algType = "ASYMMETRIC_PADDING" + or + name = "rsassapss" and nid = 912 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "rsassapss" and nid = 912 and normalized = "PSS" and algType = "ASYMMETRIC_PADDING" + or + name = "sha1withrsa" and nid = 115 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sha1withrsa" and nid = 115 and normalized = "SHA1" and algType = "HASH" + or + name = "sha1withrsaencryption" and + nid = 65 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sha1withrsaencryption" and nid = 65 and normalized = "SHA1" and algType = "HASH" + or + name = "sha224withrsaencryption" and + nid = 671 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sha224withrsaencryption" and nid = 671 and normalized = "SHA224" and algType = "HASH" + or + name = "sha256withrsaencryption" and + nid = 668 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sha256withrsaencryption" and nid = 668 and normalized = "SHA256" and algType = "HASH" + or + name = "sha384withrsaencryption" and + nid = 669 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sha384withrsaencryption" and nid = 669 and normalized = "SHA384" and algType = "HASH" + or + name = "sha512-224withrsaencryption" and + nid = 1145 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sha512-224withrsaencryption" and + nid = 1145 and + normalized = "SHA512224" and + algType = "HASH" + or + name = "sha512-256withrsaencryption" and + nid = 1146 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sha512-256withrsaencryption" and + nid = 1146 and + normalized = "SHA512256" and + algType = "HASH" + or + name = "sha512withrsaencryption" and + nid = 670 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sha512withrsaencryption" and nid = 670 and normalized = "SHA512" and algType = "HASH" + or + name = "shawithrsaencryption" and + nid = 42 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "shawithrsaencryption" and nid = 42 and normalized = "SHA" and algType = "HASH" + or + name = "sm2" and nid = 1172 and normalized = "SM2" and algType = "ELLIPTIC_CURVE" + or + name = "sm2-sm3" and nid = 1204 and normalized = "SM3" and algType = "HASH" + or + name = "sm2-sm3" and nid = 1204 and normalized = "SM2" and algType = "ELLIPTIC_CURVE" + or + name = "sm2-with-sm3" and nid = 1204 and normalized = "SM3" and algType = "HASH" + or + name = "sm2-with-sm3" and nid = 1204 and normalized = "SM2" and algType = "ELLIPTIC_CURVE" + or + name = "sm3withrsaencryption" and + nid = 1144 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sm3withrsaencryption" and nid = 1144 and normalized = "SM3" and algType = "HASH" +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll new file mode 100644 index 000000000000..54be40729b6f --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll @@ -0,0 +1,82 @@ +import semmle.code.cpp.dataflow.new.DataFlow + +class CTXType extends Type { + CTXType() { + // TODO: should we limit this to an openssl path? + this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st") + } +} + +class CTXPointerExpr extends Expr { + CTXPointerExpr() { + this.getType() instanceof CTXType and + this.getType() instanceof PointerType + } +} + +class CTXPointerArgument extends CTXPointerExpr { + CTXPointerArgument() { + + exists(Call c | c.getAnArgument() = this) + } + + Call getCall() { result.getAnArgument() = this } +} + +class CTXClearCall extends Call { + CTXClearCall() { + this.getTarget().getName().toLowerCase().matches(["%free%", "%reset%"]) and + this.getAnArgument() instanceof CTXPointerArgument + } +} + +class CTXCopyOutArgCall extends Call { + CTXCopyOutArgCall() { + this.getTarget().getName().toLowerCase().matches(["%copy%"]) and + this.getAnArgument() instanceof CTXPointerArgument + } +} + +class CTXCopyReturnCall extends Call { + CTXCopyReturnCall() { + this.getTarget().getName().toLowerCase().matches(["%dup%"]) and + this.getAnArgument() instanceof CTXPointerArgument and + this instanceof CTXPointerExpr + } +} + +module OpenSSLCTXArgumentFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CTXPointerArgument } + + predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof CTXPointerArgument } + + predicate isBarrier(DataFlow::Node node) { + exists(CTXClearCall c | c.getAnArgument() = node.asExpr()) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(CTXCopyOutArgCall c | + c.getAnArgument() = node1.asExpr() and + c.getAnArgument() = node2.asExpr() and + node1.asExpr() != node2.asExpr() and + node2.asExpr().getType() instanceof CTXType + ) + or + exists(CTXCopyReturnCall c | + c.getAnArgument() = node1.asExpr() and + c = node2.asExpr() and + node1.asExpr() != node2.asExpr() and + node2.asExpr().getType() instanceof CTXType + ) + } +} + +module OpenSSLCTXArgumentFlow = DataFlow::Global; + +predicate ctxFlowsTo(CTXPointerArgument source, CTXPointerArgument sink) { + exists(DataFlow::Node a, DataFlow::Node b | + OpenSSLCTXArgumentFlow::flow(a, b) and + a.asExpr() = source and + b.asExpr() = sink + ) +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll new file mode 100644 index 000000000000..ceabae77374c --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll @@ -0,0 +1,25 @@ +import EVPCipherInitializer +import EVPCipherOperation +import AlgorithmSource + + +class EVP_Cipher_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVPCipherInitializerAlgorithmArgument +{ + override DataFlow::Node getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmElement getAKnownAlgorithmSource() { + result.(CipherLiteralAlgorithmInstance).getConsumer() = this + } +} +// //TODO: need a key consumer +// class EVP_Initializer_Key_Consumer extends Crypto::KeyConsumer instanceof EVP_Cipher_Inititalizer isntanceof InitializerKeyArgument{ +// } +class EVP_Cipher_Initializer_IV_Consumer extends Crypto::NonceArtifactConsumer instanceof EVPCipherInitializerIVArgument{ + override DataFlow::Node getInputNode() { result.asExpr() = this } +} + +class EVP_Cipher_Input_Consumer extends Crypto::CipherInputConsumer instanceof EVPCipherInputArgument{ + + override DataFlow::Node getInputNode() { result.asExpr() = this } +} + diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherInitializer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherInitializer.qll new file mode 100644 index 000000000000..12cbe4da650a --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherInitializer.qll @@ -0,0 +1,153 @@ +import experimental.Quantum.Language +import CtxFlow as CTXFlow + +module EncValToInitEncArgConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] } + + predicate isSink(DataFlow::Node sink) { + exists(EVP_Cipher_Inititalizer initCall | sink.asExpr() = initCall.getOperataionSubtypeArg()) + } +} + +module EncValToInitEncArgFlow = DataFlow::Global; + +int getEncConfigValue(Expr e) { + exists(EVP_Cipher_Inititalizer initCall | e = initCall.getOperataionSubtypeArg()) and + exists(DataFlow::Node a, DataFlow::Node b | + EncValToInitEncArgFlow::flow(a, b) and b.asExpr() = e and result = a.asExpr().getValue().toInt() + ) +} + +bindingset[i] +Crypto::CipherOperationSubtype intToCipherOperationSubtype(int i) { + if i = 0 + then result instanceof Crypto::EncryptionSubtype + else + if i = 1 + then result instanceof Crypto::DecryptionSubtype + else result instanceof Crypto::UnknownCipherOperationSubtype +} + +// TODO: need to add key consumer +abstract class EVP_Cipher_Inititalizer extends Call { + Expr getContextArg() { result = this.(Call).getArgument(0) } + + Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } + + abstract Expr getKeyArg(); + + abstract Expr getIVArg(); + +// abstract Crypto::CipherOperationSubtype getCipherOperationSubtype(); + + abstract Expr getOperataionSubtypeArg(); + +Crypto::CipherOperationSubtype getCipherOperationSubtype() { + if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") + then result instanceof Crypto::EncryptionSubtype + else + if this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") + then result instanceof Crypto::DecryptionSubtype + else + if exists(getEncConfigValue(this.getOperataionSubtypeArg())) + then result = intToCipherOperationSubtype(getEncConfigValue(this.getOperataionSubtypeArg())) + else result instanceof Crypto::UnknownCipherOperationSubtype + } +} + +abstract class EVP_EX_Initializer extends EVP_Cipher_Inititalizer { + override Expr getKeyArg() { result = this.(Call).getArgument(3) } + + override Expr getIVArg() { result = this.(Call).getArgument(4) } +} + +abstract class EVP_EX2_Initializer extends EVP_Cipher_Inititalizer { + override Expr getKeyArg() { result = this.(Call).getArgument(2) } + + override Expr getIVArg() { result = this.(Call).getArgument(3) } +} + +class EVP_Cipher_EX_Init_Call extends EVP_EX_Initializer { + EVP_Cipher_EX_Init_Call() { + this.(Call).getTarget().getName() in [ + "EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex" + ] + } + + override Expr getOperataionSubtypeArg() { + this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and + result = this.(Call).getArgument(5) + } +} + +// abstract class EVP_CipherInit extends EVP_Cipher_Inititalizer{ +// abstract Expr getOperataionSubtypeArg(); +// } +// class EVP_CipherInit_ex_Call extends EVP_EX_Initializer, EVP_CipherInit { +// EVP_CipherInit_ex_Call() { this.(Call).getTarget().getName() = "EVP_CipherInit_ex" } +// override Crypto::CipherOperationSubtype getCipherOperationSubtype() { +// result instanceof Crypto::EncryptionSubtype +// } +// override Expr getOperataionSubtypeArg(){ +// result = this.(Call).getArgument(5) +// } +// } +// class EVP_CipherInit_ex2_Call extends EVP_EX_Initializer, EVP_CipherInit { +// EVP_CipherInit_ex2_Call() { this.(Call).getTarget().getName() = "EVP_CipherInit_ex2" } +// override Crypto::CipherOperationSubtype getCipherOperationSubtype() { +// result instanceof Crypto::EncryptionSubtype +// } +// override Expr getOperataionSubtypeArg(){ +// result = this.(Call).getArgument(4) +// } +// } +class EVP_Cipher_EX2_or_Simple_Init_Call extends EVP_EX2_Initializer { + EVP_Cipher_EX2_or_Simple_Init_Call() { + this.(Call).getTarget().getName() in [ + "EVP_EncryptInit_ex2", "EVP_DecryptInit_ex2", "EVP_CipherInit_ex2", + "EVP_EncryptInit", "EVP_DecryptInit", "EVP_CipherInit" + ] + } + + + override Expr getOperataionSubtypeArg() { + this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and + result = this.(Call).getArgument(4) + } +} + +class EVP_CipherInit_SKEY_Call extends EVP_EX2_Initializer { + EVP_CipherInit_SKEY_Call() { + this.(Call).getTarget().getName() in [ + "EVP_CipherInit_SKEY" + ] + } + + override Expr getOperataionSubtypeArg() { + result = this.(Call).getArgument(5) + } +} + + +// class EVP_CipherInit extends EVP_Cipher_Inititalizer { +// EVP_CipherInit() { this.(Call).getTarget().getName() = "EVP_CipherInit" } +// override Expr getKeyArg() { result = this.(Call).getArgument(2) } +// override Expr getIVArg() { result = this.(Call).getArgument(3) } +// override Crypto::CipherOperationSubtype getCipherOperationSubtype() { +// result instanceof Crypto::EncryptionSubtype +// } +// } +class EVPCipherInitializerAlgorithmArgument extends Expr { + EVPCipherInitializerAlgorithmArgument() { + exists(EVP_Cipher_Inititalizer initCall | this = initCall.getAlgorithmArg()) + } +} +class EVPCipherInitializerKeyArgument extends Expr { + EVPCipherInitializerKeyArgument() { + exists(EVP_Cipher_Inititalizer initCall | this = initCall.getKeyArg()) + } +} +class EVPCipherInitializerIVArgument extends Expr { + EVPCipherInitializerIVArgument() { exists(EVP_Cipher_Inititalizer initCall | this = initCall.getIVArg()) } +} + diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll new file mode 100644 index 000000000000..4913d2763e96 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll @@ -0,0 +1,87 @@ +import experimental.Quantum.Language +import CtxFlow as CTXFlow +import EVPCipherInitializer +import EVPCipherConsumers + +//https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis +abstract class EVP_Cipher_Operation extends Crypto::CipherOperationInstance instanceof Call { + Expr getContextArg() { result = this.(Call).getArgument(0) } + + abstract Expr getInputArg(); + + Expr getOutputArg() { result = this.(Call).getArgument(1) } + + override Crypto::CipherOperationSubtype getCipherOperationSubtype() { + result instanceof Crypto::EncryptionSubtype and + this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") + or + result instanceof Crypto::DecryptionSubtype and + this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") + or + result = this.getInitCall().getCipherOperationSubtype() and + this.(Call).getTarget().getName().toLowerCase().matches("%cipher%") + } + + EVP_Cipher_Inititalizer getInitCall() { + CTXFlow::ctxFlowsTo(result.getContextArg(), this.getContextArg()) + } + + override Crypto::NonceArtifactConsumer getNonceConsumer() { + this.getInitCall().getIVArg() = result + } + + override Crypto::CipherInputConsumer getInputConsumer() { this.getInputArg() = result } + + override Crypto::CipherOutputArtifactInstance getOutputArtifact() { this.getOutputArg() = result } + + override Crypto::AlgorithmConsumer getAlgorithmConsumer() { + this.getInitCall().getAlgorithmArg() = result + } +} + +abstract class EVP_Update_Call extends EVP_Cipher_Operation { } + +abstract class EVP_Final_Call extends EVP_Cipher_Operation { + override Expr getInputArg() { none() } +} + +class EVP_Cipher_Call extends EVP_Cipher_Operation { + // TODO/QUESTION: what is the better way to do this? + EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } + + override Expr getInputArg() { result = this.(Call).getArgument(2) } +} + +class EVP_Encrypt_Decrypt_or_Cipher_Update_Call extends EVP_Update_Call { + // TODO/QUESTION: what is the better way to do this? + EVP_Encrypt_Decrypt_or_Cipher_Update_Call() { + this.(Call).getTarget().getName() in [ + "EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate" + ] + } + + override Expr getInputArg() { result = this.(Call).getArgument(3) } +} + +class EVP_Encrypt_Decrypt_or_Cipher_Final_Call extends EVP_Final_Call { + // TODO/QUESTION: what is the better way to do this? + EVP_Encrypt_Decrypt_or_Cipher_Final_Call() { + this.(Call).getTarget().getName() in + [ + "EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal", + "EVP_DecryptFinal", "EVP_CipherFinal" + ] + } +} + +class EVPCipherOutput extends CipherOutputArtifact { + EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) } + + override DataFlow::Node getOutputNode(){ + result.asExpr() = this + } +} + +class EVPCipherInputArgument extends Expr { + EVPCipherInputArgument() { exists(EVP_Cipher_Operation op | op.getInputArg() = this) } +} \ No newline at end of file diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll new file mode 100644 index 000000000000..f942f4687da5 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll @@ -0,0 +1,357 @@ +import cpp +import semmle.code.cpp.dataflow.new.DataFlow + +module OpenSSLModel { + import experimental.Quantum.Language + import experimental.Quantum.OpenSSL.EVPCipherOperation + +// // TODO: trace CTX from init variants to the context arg of EVP update calls +// //https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis +// abstract class EVP_Cipher_Init_Call extends Call { +// Expr getContextArg() { result = this.getArgument(0) } +// abstract Expr getKeyArg(); + +// abstract Expr getIVArg(); + +// abstract Crypto::CipherOperationSubtype getCipherOperationSubtype(); +// } + +// abstract class EVP_Cipher_EX_Init_Call extends EVP_Cipher_Init_Call { +// override Expr getKeyArg() { result = this.getArgument(3) } + +// override Expr getIVArg() { result = this.getArgument(4) } +// } + +// abstract class EVP_Cipher_EX2_Init_Call extends EVP_Cipher_Init_Call { +// override Expr getKeyArg() { result = this.getArgument(2) } + +// override Expr getIVArg() { result = this.getArgument(3) } +// } + +// abstract class EVP_Cipher_Operation_Call extends Crypto::CipherOperationInstance instanceof Call { +// Expr getContextArg() { result = this.(Call).getArgument(0) } +// abstract Expr getInputArg(); +// Expr getOutputArg() { result = this.(Call).getArgument(1) } +// abstract Expr getInitCall(); +// } + +// abstract class EVP_Update_Call extends EVP_Cipher_Operation_Call { +// override Expr getInputArg() { result = this.(Call).getArgument(3) } + +// } + +// abstract class EVP_Final_Call extends EVP_Cipher_Operation_Call{ +// override Expr getInputArg() { none() } + +// } + +// class EVP_Cipher_Call extends EVP_Cipher_Operation_Call{ +// // TODO/QUESTION: what is the better way to do this? +// EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } + +// override Expr getInputArg() { result = this.(Call).getArgument(2) } + +// override Expr getOutputArg() { result = this.(Call).getArgument(1) } + +// override Crypto::CipherOperationSubtype getCipherOperationSubtype(){ +// result instanceof Crypto::EncryptionSubtype +// } + +// override Expr getInitCall(){ +// //TODO: +// none() +// } + +// override Crypto::NonceArtifactConsumer getNonceConsumer(){ +// none() +// } + +// override Crypto::CipherInputConsumer getInputConsumer(){ +// none() +// } + +// override Crypto::CipherOutputArtifactInstance getOutputArtifact(){ +// none() +// } + +// override Crypto::AlgorithmConsumer getAlgorithmConsumer(){ +// none() +// } +// } + + + //TODO: what about EVP_CIpher + + + // class EVP_EncryptUpdateCall extends Crypto::CipherOperationInstance instanceof Call { + // // NICK QUESTION: is there a better way to tie this to openssl? + // EVP_EncryptUpdateCall() { this.getTarget().getName() = "EVP_EncryptUpdate" } + + // Expr getContextArg() { result = super.getArgument(0) } + + // Expr getInputArg() { result = super.getArgument(3) } + + // Expr getOutputArg() { result = super.getArgument(1) } + + // override Crypto::CipherOperationSubtype getCipherOperationSubtype(){ + // result instanceof Crypto::EncryptionSubtype + // } + + // override Crypto::NonceArtifactConsumer getNonceConsumer(){ + // none() + // } + + // override Crypto::CipherInputConsumer getInputConsumer(){ + // none() + // } + + // override Crypto::CipherOutputArtifactInstance getOutputArtifact(){ + // none() + // } + + // override Crypto::AlgorithmConsumer getAlgorithmConsumer(){ + // none() + // } + + // } + + //EVP_EncryptUpdate + + // /** + // * Hash function references in OpenSSL. + // */ + // predicate hash_ref_type_mapping_known(string name, Crypto::THashType algo) { + // // `ma` name has an LN_ or SN_ prefix, which we want to ignore + // // capture any name after the _ prefix using regex matching + // name = ["sha1", "sha160"] and algo instanceof Crypto::SHA1 + // or + // name = ["sha224", "sha256", "sha384", "sha512"] and algo instanceof Crypto::SHA2 + // or + // name = ["sha3-224", "sha3-256", "sha3-384", "sha3-512"] and algo instanceof Crypto::SHA3 + // or + // name = "md2" and algo instanceof Crypto::MD2 + // or + // name = "md4" and algo instanceof Crypto::MD4 + // or + // name = "md5" and algo instanceof Crypto::MD5 + // or + // name = "ripemd160" and algo instanceof Crypto::RIPEMD160 + // or + // name = "whirlpool" and algo instanceof Crypto::WHIRLPOOL + // } + + // predicate hash_ref_type_mapping(FunctionCallOrMacroAccess ref, string name, Crypto::THashType algo) { + // name = ref.getTargetName().regexpCapture("(?:SN_|LN_|EVP_)([a-z0-9]+)", 1) and + // hash_ref_type_mapping_known(name, algo) + // } + + // class FunctionCallOrMacroAccess extends Element { + // FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess } + + // string getTargetName() { + // result = this.(FunctionCall).getTarget().getName() + // or + // result = this.(MacroAccess).getMacroName() + // } + // } + + // class HashAlgorithmCallOrMacro extends Crypto::HashAlgorithmInstance instanceof FunctionCallOrMacroAccess + // { + // HashAlgorithmCallOrMacro() { hash_ref_type_mapping(this, _, _) } + + // string getTargetName() { result = this.(FunctionCallOrMacroAccess).getTargetName() } + // } + + // class HashAlgorithm extends Crypto::HashAlgorithm { + // HashAlgorithmCallOrMacro instance; + + // HashAlgorithm() { this = Crypto::THashAlgorithm(instance) } + + // override string getSHA2OrSHA3DigestSize(Location location) { + // ( + // this.getHashType() instanceof Crypto::SHA2 or + // this.getHashType() instanceof Crypto::SHA3 + // ) and + // exists(string name | + // hash_ref_type_mapping(instance, name, this.getHashType()) and + // result = name.regexpFind("\\d{3}", 0, _) and + // location = instance.getLocation() + // ) + // } + + // override string getRawAlgorithmName() { result = instance.getTargetName() } + + // override Crypto::THashType getHashType() { hash_ref_type_mapping(instance, _, result) } + + // Element getInstance() { result = instance } + + // override Location getLocation() { result = instance.getLocation() } + // } + + // /** + // * Data-flow configuration for key derivation algorithm flow to EVP_KDF_derive. + // */ + // module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig { + // predicate isSource(DataFlow::Node source) { + // source.asExpr() = any(KeyDerivationAlgorithm a).getInstance() + // } + + // predicate isSink(DataFlow::Node sink) { + // exists(EVP_KDF_derive kdo | + // sink.asExpr() = kdo.getCall().getAlgorithmArg() + // or + // sink.asExpr() = kdo.getCall().getContextArg() // via `EVP_KDF_CTX_set_params` + // ) + // } + + // predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + // none() // TODO + // } + // } + + // module AlgorithmToEVPKeyDeriveFlow = DataFlow::Global; + + // predicate algorithm_to_EVP_KDF_derive(KeyDerivationAlgorithm algo, EVP_KDF_derive derive) { + // none() + // } + + // /** + // * Key derivation operation (e.g., `EVP_KDF_derive`) + // */ + // class EVP_KDF_derive_FunctionCall extends Crypto::KeyDerivationOperationInstance instanceof FunctionCall + // { + // EVP_KDF_derive_FunctionCall() { this.getTarget().getName() = "EVP_KDF_derive" } + + // Expr getAlgorithmArg() { result = super.getArgument(3) } + + // Expr getContextArg() { result = super.getArgument(0) } + // } + + // class EVP_KDF_derive extends Crypto::KeyDerivationOperation { + // EVP_KDF_derive_FunctionCall instance; + + // EVP_KDF_derive() { this = Crypto::TKeyDerivationOperation(instance) } + + // override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) } + + // EVP_KDF_derive_FunctionCall getCall() { result = instance } + // } + + // /** + // * Key derivation algorithm nodes + // */ + // abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm { + // abstract Expr getInstance(); + // } + + // /** + // * `EVP_KDF_fetch` returns a key derivation algorithm. + // */ + // class EVP_KDF_fetch_Call extends FunctionCall { + // EVP_KDF_fetch_Call() { this.getTarget().getName() = "EVP_KDF_fetch" } + + // Expr getAlgorithmArg() { result = this.getArgument(1) } + // } + + // class EVP_KDF_fetch_AlgorithmArg extends Crypto::KeyDerivationAlgorithmInstance instanceof Expr { + // EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) } + // } + + // predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF", "PBKDF2"] } + + // class KDFAlgorithmStringLiteral extends StringLiteral { + // KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) } + // } + + // private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + // predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KDFAlgorithmStringLiteral } + + // predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof EVP_KDF_fetch_AlgorithmArg } + // } + + // module AlgorithmStringToFetchFlow = DataFlow::Global; + + // predicate algorithmStringToKDFFetchArgFlow( + // string name, KDFAlgorithmStringLiteral origin, EVP_KDF_fetch_AlgorithmArg arg + // ) { + // origin.getValue().toUpperCase() = name and + // AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) + // } + + // /** + // * HKDF key derivation algorithm. + // */ + // class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF { + // KDFAlgorithmStringLiteral origin; + // EVP_KDF_fetch_AlgorithmArg instance; + + // HKDF() { + // this = Crypto::TKeyDerivationAlgorithm(instance) and + // algorithmStringToKDFFetchArgFlow("HKDF", origin, instance) + // } + + // override string getRawAlgorithmName() { result = origin.getValue() } + + // override Crypto::HashAlgorithm getHashAlgorithm() { none() } + + // override Crypto::LocatableElement getOrigin(string name) { + // result = origin and name = origin.toString() + // } + + // override Expr getInstance() { result = origin } + // } + + // /** + // * PBKDF2 key derivation algorithm. + // */ + // class PBKDF2 extends KeyDerivationAlgorithm, Crypto::PBKDF2 { + // KDFAlgorithmStringLiteral origin; + // EVP_KDF_fetch_AlgorithmArg instance; + + // PBKDF2() { + // this = Crypto::TKeyDerivationAlgorithm(instance) and + // algorithmStringToKDFFetchArgFlow("PBKDF2", origin, instance) + // } + + // override string getRawAlgorithmName() { result = origin.getValue() } + + // override string getIterationCount(Location location) { none() } // TODO + + // override string getKeyLength(Location location) { none() } // TODO + + // override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO + + // override Crypto::LocatableElement getOrigin(string name) { + // result = origin and name = origin.toString() + // } + + // override Expr getInstance() { result = instance } + // } + + // /** + // * PKCS12KDF key derivation algorithm. + // */ + // class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF { + // KDFAlgorithmStringLiteral origin; + // EVP_KDF_fetch_AlgorithmArg instance; + + // PKCS12KDF() { + // this = Crypto::TKeyDerivationAlgorithm(instance) and + // algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, instance) + // } + + // override string getRawAlgorithmName() { result = origin.getValue() } + + // override string getIterationCount(Location location) { none() } // TODO + + // override string getIDByte(Location location) { none() } // TODO + + // override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO + + // override Crypto::LocatableElement getOrigin(string name) { + // result = origin and name = origin.toString() + // } + + // override Expr getInstance() { result = instance } + // } +} From de3ff45cba87043af0709026cdc9eccda4404710 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Wed, 5 Mar 2025 15:17:52 -0500 Subject: [PATCH 040/122] Misc updates for OpenSSL modeling to trace algorithm literals to known alg getters, and converting the literal to a TCipherType. --- cpp/ql/lib/experimental/Quantum/Language.qll | 22 +- .../experimental/Quantum/OpenSSL/CtxFlow.qll | 15 +- .../OpenSSL/EVPCipherAlgorithmSource.qll | 148 ++++++ .../Quantum/OpenSSL/EVPCipherConsumers.qll | 4 +- .../Quantum/OpenSSL/LibraryDetector.qll | 10 + .../OpenSSL/OpenSSLAlgorithmGetter.qll | 436 ++++++++++++++++++ ...qll => OpenSSLKnownAlgorithmConstants.qll} | 103 +---- .../codeql/cryptography/Model.qll | 62 +-- 8 files changed, 651 insertions(+), 149 deletions(-) create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/LibraryDetector.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll rename cpp/ql/lib/experimental/Quantum/OpenSSL/{AlgorithmSource.qll => OpenSSLKnownAlgorithmConstants.qll} (96%) diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index 3f1d9aa9bdf3..5d4ff4bc6863 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -49,19 +49,19 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { -// TODO: I think this will be inefficient, no? -class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { - override DataFlow::Node getOutputNode() { - result.asExpr() = this - } +// // TODO: I think this will be inefficient, no? +// class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { +// override DataFlow::Node getOutputNode() { +// result.asExpr() = this +// } - override predicate flowsTo(Crypto::FlowAwareElement other) { - // TODO: separate config to avoid blowing up data-flow analysis - GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - } +// override predicate flowsTo(Crypto::FlowAwareElement other) { +// // TODO: separate config to avoid blowing up data-flow analysis +// GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) +// } - override string getAdditionalDescription() { result = this.toString() } -} +// override string getAdditionalDescription() { result = this.toString() } +// } /** * Definitions of various generic data sources diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll index 54be40729b6f..e1b08c9652d8 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll @@ -8,17 +8,14 @@ class CTXType extends Type { } class CTXPointerExpr extends Expr { - CTXPointerExpr() { - this.getType() instanceof CTXType and - this.getType() instanceof PointerType + CTXPointerExpr() { + this.getType() instanceof CTXType and + this.getType() instanceof PointerType } } class CTXPointerArgument extends CTXPointerExpr { - CTXPointerArgument() { - - exists(Call c | c.getAnArgument() = this) - } + CTXPointerArgument() { exists(Call c | c.getAnArgument() = this) } Call getCall() { result.getAnArgument() = this } } @@ -31,14 +28,14 @@ class CTXClearCall extends Call { } class CTXCopyOutArgCall extends Call { - CTXCopyOutArgCall() { + CTXCopyOutArgCall() { this.getTarget().getName().toLowerCase().matches(["%copy%"]) and this.getAnArgument() instanceof CTXPointerArgument } } class CTXCopyReturnCall extends Call { - CTXCopyReturnCall() { + CTXCopyReturnCall() { this.getTarget().getName().toLowerCase().matches(["%dup%"]) and this.getAnArgument() instanceof CTXPointerArgument and this instanceof CTXPointerExpr diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll new file mode 100644 index 000000000000..046abf3f43a9 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll @@ -0,0 +1,148 @@ +import cpp +import experimental.Quantum.Language +import EVPCipherConsumers +import OpenSSLAlgorithmGetter + +predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { + exists(string name, string algType | algType.toLowerCase().matches("%encryption") | + resolveAlgorithmFromLiteral(e, name, algType) and + ( + name.matches("AES%") and type instanceof Crypto::AES + or + name.matches("ARIA") and type instanceof Crypto::ARIA + or + name.matches("BLOWFISH") and type instanceof Crypto::BLOWFISH + or + name.matches("BF") and type instanceof Crypto::BLOWFISH + or + name.matches("CAMELLIA%") and type instanceof Crypto::CAMELLIA + or + name.matches("CHACHA20") and type instanceof Crypto::CHACHA20 + or + name.matches("CAST5") and type instanceof Crypto::CAST5 + or + name.matches("2DES") and type instanceof Crypto::DOUBLEDES + or + name.matches(["3DES", "TRIPLEDES"]) and type instanceof Crypto::TRIPLEDES + or + name.matches("DES") and type instanceof Crypto::DES + or + name.matches("DESX") and type instanceof Crypto::DESX + or + name.matches("GOST%") and type instanceof Crypto::GOST + or + name.matches("IDEA") and type instanceof Crypto::IDEA + or + name.matches("KUZNYECHIK") and type instanceof Crypto::KUZNYECHIK + or + name.matches("MAGMA") and type instanceof Crypto::MAGMA + or + name.matches("RC2") and type instanceof Crypto::RC2 + or + name.matches("RC4") and type instanceof Crypto::RC4 + or + name.matches("RC5") and type instanceof Crypto::RC5 + or + name.matches("RSA") and type instanceof Crypto::RSA + or + name.matches("SEED") and type instanceof Crypto::SEED + or + name.matches("SM4") and type instanceof Crypto::SM4 + ) + ) +} + +class CipherKnownAlgorithmLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof Literal +{ + CipherKnownAlgorithmLiteralAlgorithmInstance() { + exists(EVPCipherGetterCall c, DataFlow::Node src, DataFlow::Node sink | + sink = c.getValueArgNode() and + src.asExpr() = this and + KnownAlgorithmLiteralToAlgorithmGetterFlow::flow(src, sink) and + // Not just any known value, but specifically a known cipher operation + exists(string algType | + resolveAlgorithmFromLiteral(src.asExpr(), _, algType) and + algType.toLowerCase().matches("%encryption") + ) + ) + } + + Crypto::AlgorithmConsumer getConsumer() { none() } //result = consumer } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { + none() // TODO: provider defaults + } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } + + override Crypto::TCipherType getCipherFamily() { literalToCipherFamilyType(this, result) } +} +// override Crypto::TCipherType getCipherFamily() { +// if this.cipherNameMappingKnown(_, super.getAlgorithmName()) +// then this.cipherNameMappingKnown(result, super.getAlgorithmName()) +// else result instanceof Crypto::OtherCipherType +// } +// bindingset[name] +// private predicate cipherNameMappingKnown(Crypto::TCipherType type, string name) { +// name = "AES" and +// type instanceof Crypto::AES +// or +// name = "DES" and +// type instanceof Crypto::DES +// or +// name = "TripleDES" and +// type instanceof Crypto::TripleDES +// or +// name = "IDEA" and +// type instanceof Crypto::IDEA +// or +// name = "CAST5" and +// type instanceof Crypto::CAST5 +// or +// name = "ChaCha20" and +// type instanceof Crypto::ChaCha20 +// or +// name = "RC4" and +// type instanceof Crypto::RC4 +// or +// name = "RC5" and +// type instanceof Crypto::RC5 +// or +// name = "RSA" and +// type instanceof Crypto::RSA +// } +// private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { +// type instanceof Crypto::ECB and name = "ECB" +// or +// type instanceof Crypto::CBC and name = "CBC" +// or +// type instanceof Crypto::GCM and name = "GCM" +// or +// type instanceof Crypto::CTR and name = "CTR" +// or +// type instanceof Crypto::XTS and name = "XTS" +// or +// type instanceof Crypto::CCM and name = "CCM" +// or +// type instanceof Crypto::SIV and name = "SIV" +// or +// type instanceof Crypto::OCB and name = "OCB" +// } +// override Crypto::TBlockCipherModeOperationType getModeType() { +// if this.modeToNameMappingKnown(_, super.getMode()) +// then this.modeToNameMappingKnown(result, super.getMode()) +// else result instanceof Crypto::OtherMode +// } +// override string getRawModeAlgorithmName() { result = super.getMode() } +// override string getRawPaddingAlgorithmName() { result = super.getPadding() } +// bindingset[name] +// private predicate paddingToNameMappingKnown(Crypto::TPaddingType type, string name) { +// type instanceof Crypto::NoPadding and name = "NOPADDING" +// or +// type instanceof Crypto::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? +// or +// type instanceof Crypto::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% +// } +// } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll index ceabae77374c..8717a857385a 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll @@ -1,6 +1,6 @@ import EVPCipherInitializer import EVPCipherOperation -import AlgorithmSource +import EVPCipherAlgorithmSource class EVP_Cipher_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVPCipherInitializerAlgorithmArgument @@ -8,7 +8,7 @@ class EVP_Cipher_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsume override DataFlow::Node getInputNode() { result.asExpr() = this } override Crypto::AlgorithmElement getAKnownAlgorithmSource() { - result.(CipherLiteralAlgorithmInstance).getConsumer() = this + result.(CipherKnownAlgorithmLiteralAlgorithmInstance).getConsumer() = this } } // //TODO: need a key consumer diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/LibraryDetector.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/LibraryDetector.qll new file mode 100644 index 000000000000..3cd359ce58b3 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/LibraryDetector.qll @@ -0,0 +1,10 @@ +import cpp + +predicate isPossibleOpenSSLFunction(Function f) { + isPossibleOpenSSLLocation(f.getADeclarationLocation()) + } + +predicate isPossibleOpenSSLLocation(Location l){ + l.toString().toLowerCase().matches("%openssl%") +} + \ No newline at end of file diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll new file mode 100644 index 000000000000..79491ca2a09d --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll @@ -0,0 +1,436 @@ +import cpp +import semmle.code.cpp.dataflow.new.DataFlow +import LibraryDetector +import OpenSSLKnownAlgorithmConstants + +abstract class AlgorithmPassthroughCall extends Call { + abstract DataFlow::Node getInNode(); + + abstract DataFlow::Node getOutNode(); +} + +class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall { + DataFlow::Node inNode; + DataFlow::Node outNode; + + CopyAndDupAlgorithmPassthroughCall() { + // Flow out through any return or other argument of the same type + // Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed + // to be involved + // NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup + this.getTarget().getName().toLowerCase().matches(["%_dup", "%_copy"]) and + exists(Expr inArg, Type t | + inArg = this.getAnArgument() and t = inArg.getUnspecifiedType().stripType() + | + inNode.asIndirectExpr() = inArg and + ( + // Case 1: flow through another argument as an out arg of the same type + exists(Expr outArg | + outArg = this.getAnArgument() and + outArg != inArg and + outArg.getUnspecifiedType().stripType() = t + | + outNode.asDefiningArgument() = outArg + ) + or + // Case 2: flow through the return value if the result is the same as the intput type + exists(Expr outArg | outArg = this and outArg.getUnspecifiedType().stripType() = t | + outNode.asIndirectExpr() = outArg + ) + ) + ) + } + + override DataFlow::Node getInNode() { result = inNode } + + override DataFlow::Node getOutNode() { result = outNode } +} + +class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall { + DataFlow::Node inNode; + DataFlow::Node outNode; + + NIDToPointerPassthroughCall() { + this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and + inNode.asExpr() = this.getArgument(0) and + outNode.asExpr() = this + //outNode.asIndirectExpr() = this + } + + override DataFlow::Node getInNode() { result = inNode } + + override DataFlow::Node getOutNode() { result = outNode } +} + +class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall { + DataFlow::Node inNode; + DataFlow::Node outNode; + + PointerToPointerPassthroughCall() { + this.getTarget().getName() = "OBJ_txt2obj" and + inNode.asIndirectExpr() = this.getArgument(0) and + outNode.asIndirectExpr() = this + or + //outNode.asExpr() = this + this.getTarget().getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and + inNode.asIndirectExpr() = this.getArgument(2) and + outNode.asDefiningArgument() = this.getArgument(0) + } + + override DataFlow::Node getInNode() { result = inNode } + + override DataFlow::Node getOutNode() { result = outNode } +} + +class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall { + DataFlow::Node inNode; + DataFlow::Node outNode; + + PointerToNIDPassthroughCall() { + this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and + ( + inNode.asIndirectExpr() = this.getArgument(0) + or + inNode.asExpr() = this.getArgument(0) + ) and + outNode.asExpr() = this + } + + override DataFlow::Node getInNode() { result = inNode } + + override DataFlow::Node getOutNode() { result = outNode } +} + +predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(AlgorithmPassthroughCall c | c.getInNode() = node1 and c.getOutNode() = node2) +} + +abstract class OpenSSLAlgorithmGetterCall extends Call { + abstract DataFlow::Node getValueArgNode(); + + abstract DataFlow::Node getResultNode(); + + abstract Expr getValueArgExpr(); + + abstract Expr getResultExpr(); +} + +module KnownAlgorithmLiteralToAlgorithmGetterConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + ( + source.asExpr() instanceof Literal and + // 0 sources, for nid are unknown, and 0 otherwise represents a null assignment (ignore as unknown) + exists(source.asExpr().(Literal).getValue().toInt()) implies source.asExpr().(Literal).getValue().toInt() != 0 + //resolveAlgorithmFromLiteral(source.asExpr(),_,_) + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(OpenSSLAlgorithmGetterCall c | c.getValueArgNode() = sink) + } + + predicate isBarrier(DataFlow::Node node) { + // False positive reducer, don't flow out through argv + exists(VariableAccess va, Variable v | + v.getAnAccess() = va and va = node.asExpr() + or + va = node.asIndirectExpr() + | + v.getName().matches("%argv") + ) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + knownPassThroughStep(node1, node2) + } +} + +module KnownAlgorithmLiteralToAlgorithmGetterFlow = + DataFlow::Global; + +// https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html +class EVPCipherGetterCall extends OpenSSLAlgorithmGetterCall { + DataFlow::Node valueArgNode; + DataFlow::Node resultNode; + Expr valueArgExpr; + Expr resultExpr; + + EVPCipherGetterCall() { + // Flow out through the return pointer itself (trace the pointer, not what it is pointing to) + resultExpr = this and + resultNode.asExpr() = this and + ( + this.getTarget().getName() in ["EVP_get_cipherbyname", "EVP_get_cipherbyobj"] and + valueArgExpr = this.getArgument(0) and + valueArgNode.asExpr() = valueArgExpr + or + this.getTarget().getName() = "EVP_CIPHER_fetch" and + valueArgExpr = this.getArgument(1) and + valueArgNode.asExpr() = valueArgExpr + or + this.getTarget().getName() = "EVP_get_cipherbynid" and + valueArgExpr = this.getArgument(0) and + valueArgNode.asExpr() = valueArgExpr + ) + } + + override DataFlow::Node getValueArgNode() { result = valueArgNode } + + override DataFlow::Node getResultNode() { result = resultNode } + + override Expr getValueArgExpr() { result = valueArgExpr } + + override Expr getResultExpr() { result = resultExpr } +} +// /** +// * Predicates/classes for identifying algorithm sinks. +// * An Algorithm Sink is a function that takes an algorithm as an argument. +// * In particular, any function that takes in an algorithm that until the call +// * the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm) +// */ +// //TODO: enforce a hierarchy of AlgorithmSinkArgument, e.g., so I can get all Asymmetric SinkArguments that includes all the strictly RSA etc. +// import cpp +// // import experimental.cryptography.utils.OpenSSL.LibraryFunction +// // import experimental.cryptography.CryptoAlgorithmNames +// predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType) { arg.algType() = algType } +// abstract class AlgorithmSinkArgument extends Expr { +// AlgorithmSinkArgument() { +// exists(Call c | c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget())) +// } +// /** +// * Gets the function call in which the argument exists +// */ +// Call getSinkCall() { result.getAnArgument() = this } +// abstract string algType(); +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html +// predicate cipherAlgorithmSink(string funcName, int argInd) { +// funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0 +// or +// funcName = "EVP_CIPHER_fetch" and argInd = 1 +// } +// class CipherAlgorithmSink extends AlgorithmSinkArgument { +// CipherAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// cipherAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = getSymmetricEncryptionType() } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch +// predicate macAlgorithmSink(string funcName, int argInd) { +// (funcName = "EVP_MAC_fetch" and argInd = 1) +// } +// class MACAlgorithmSink extends AlgorithmSinkArgument { +// MACAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// macAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = "TBD" } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch +// predicate messageDigestAlgorithmSink(string funcName, int argInd) { +// funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0 +// or +// funcName = "EVP_MD_fetch" and argInd = 1 +// } +// class MessageDigestAlgorithmSink extends AlgorithmSinkArgument { +// MessageDigestAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// messageDigestAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = getHashType() } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch +// // https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch +// predicate keyExchangeAlgorithmSink(string funcName, int argInd) { +// funcName = "EVP_KEYEXCH_fetch" and argInd = 1 +// or +// funcName = "EVP_KEM_fetch" and argInd = 1 +// } +// class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument { +// KeyExchangeAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// keyExchangeAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = getKeyExchangeType() } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch +// predicate keyManagementAlgorithmSink(string funcName, int argInd) { +// funcName = "EVP_KEYMGMT_fetch" and argInd = 1 +// } +// class KeyManagementAlgorithmSink extends AlgorithmSinkArgument { +// KeyManagementAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// keyManagementAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = "TBD" } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_KDF +// predicate keyDerivationAlgorithmSink(string funcName, int argInd) { +// funcName = "EVP_KDF_fetch" and argInd = 1 +// } +// class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument { +// KeyDerivationAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// keyDerivationAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = getKeyDerivationType() } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch +// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id +// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html +// predicate asymmetricCipherAlgorithmSink(string funcName, int argInd) { +// funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1 +// or +// funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3 +// // NOTE: other cases are handled by AsymmetricAlgorithmSink +// } +// class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument { +// AsymmetricCipherAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// asymmetricCipherAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = "ASYMMETRIC_ENCRYPTION" } +// } +// class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { +// AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() { +// exists(Call c, string funcName | +// funcName = c.getTarget().getName() and +// this = c.getArgument(3) +// | +// funcName = "EVP_PKEY_Q_keygen" and +// c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType +// ) +// } +// override string algType() { result = "ASYMMETRIC_ENCRYPTION" } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch +// predicate randomAlgorithmSink(string funcName, int argInd) { +// funcName = "EVP_RAND_fetch" and argInd = 1 +// } +// class RandomAlgorithmSink extends AlgorithmSinkArgument { +// RandomAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// randomAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = "TBD" } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch +// predicate signatureAlgorithmSink(string funcName, int argInd) { +// funcName = "EVP_SIGNATURE_fetch" and argInd = 1 +// } +// class SignatureAlgorithmSink extends AlgorithmSinkArgument { +// SignatureAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// signatureAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = getSignatureType() } +// } +// // https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html +// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html +// predicate ellipticCurveAlgorithmSink(string funcName, int argInd) { +// funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0 +// or +// funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2 +// or +// funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1 +// } +// class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument { +// EllipticCurveAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// ellipticCurveAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = getEllipticCurveType() } +// } +// /** +// * Special cased to address the fact that arg index 3 (zero offset based) is the curve name. +// * ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve +// */ +// class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { +// EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() { +// exists(Call c, string funcName | +// funcName = c.getTarget().getName() and +// this = c.getArgument(3) +// | +// funcName = "EVP_PKEY_Q_keygen" and +// c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and +// c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType +// ) +// } +// override string algType() { result = getEllipticCurveType() } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html +// // https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html +// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html +// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html +// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html +// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html +// predicate asymmetricAlgorithmSink(string funcName, int argInd) { +// funcName = "EVP_PKEY_CTX_new_id" and argInd = 0 +// or +// funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1 +// or +// funcName in [ +// "EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key" +// ] and +// argInd = 0 +// or +// funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1 +// or +// // special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs +// // funcName = "EVP_PKEY_Q_keygen" and argInd = 2 +// funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1 +// // TODO consider void cases EVP_PKEY_new +// } +// class AsymmetricAlgorithmSink extends AlgorithmSinkArgument { +// AsymmetricAlgorithmSink() { +// exists(Call c, string funcName, int argInd | +// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// | +// asymmetricAlgorithmSink(funcName, argInd) +// ) +// } +// override string algType() { result = getAsymmetricType() } +// } +// class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { +// AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() { +// exists(Call c, string funcName | +// funcName = c.getTarget().getName() and +// this = c.getArgument(2) +// | +// funcName = "EVP_PKEY_Q_keygen" and +// not exists(c.getArgument(3)) +// ) +// } +// override string algType() { result = getAsymmetricType() } +// } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll similarity index 96% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmSource.qll rename to cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll index f409578e98d2..dcf24d01ace8 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll @@ -1,104 +1,5 @@ import cpp -import experimental.Quantum.Language -import EVPCipherConsumers -/** - * Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable. - * This is because the mode will always be specified alongside the algorithm and never independently. - * Therefore, we can always assume that a determinable algorithm will have a determinable mode. - * - * In the case that only an algorithm is specified, e.g., "AES", the provider provides a default mode. - * - * TODO: Model the case of relying on a provider default, but alert on it as a bad practice. - */ -class CipherLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof ConstantDataSource -{ - Crypto::AlgorithmConsumer consumer; // TODO: I need to make this an open SSL algorithm consumer specifically - - CipherLiteralAlgorithmInstance() { - GenericDataSourceUniversalFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(consumer)) - } - - Crypto::AlgorithmConsumer getConsumer() { result = consumer } - - override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { - none() // TODO: provider defaults - } - - override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } - - override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } - - override Crypto::TCipherType getCipherFamily() { none() } -} - -// override Crypto::TCipherType getCipherFamily() { -// if this.cipherNameMappingKnown(_, super.getAlgorithmName()) -// then this.cipherNameMappingKnown(result, super.getAlgorithmName()) -// else result instanceof Crypto::OtherCipherType -// } -// bindingset[name] -// private predicate cipherNameMappingKnown(Crypto::TCipherType type, string name) { -// name = "AES" and -// type instanceof Crypto::AES -// or -// name = "DES" and -// type instanceof Crypto::DES -// or -// name = "TripleDES" and -// type instanceof Crypto::TripleDES -// or -// name = "IDEA" and -// type instanceof Crypto::IDEA -// or -// name = "CAST5" and -// type instanceof Crypto::CAST5 -// or -// name = "ChaCha20" and -// type instanceof Crypto::ChaCha20 -// or -// name = "RC4" and -// type instanceof Crypto::RC4 -// or -// name = "RC5" and -// type instanceof Crypto::RC5 -// or -// name = "RSA" and -// type instanceof Crypto::RSA -// } -// private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { -// type instanceof Crypto::ECB and name = "ECB" -// or -// type instanceof Crypto::CBC and name = "CBC" -// or -// type instanceof Crypto::GCM and name = "GCM" -// or -// type instanceof Crypto::CTR and name = "CTR" -// or -// type instanceof Crypto::XTS and name = "XTS" -// or -// type instanceof Crypto::CCM and name = "CCM" -// or -// type instanceof Crypto::SIV and name = "SIV" -// or -// type instanceof Crypto::OCB and name = "OCB" -// } -// override Crypto::TBlockCipherModeOperationType getModeType() { -// if this.modeToNameMappingKnown(_, super.getMode()) -// then this.modeToNameMappingKnown(result, super.getMode()) -// else result instanceof Crypto::OtherMode -// } -// override string getRawModeAlgorithmName() { result = super.getMode() } -// override string getRawPaddingAlgorithmName() { result = super.getPadding() } -// bindingset[name] -// private predicate paddingToNameMappingKnown(Crypto::TPaddingType type, string name) { -// type instanceof Crypto::NoPadding and name = "NOPADDING" -// or -// type instanceof Crypto::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? -// or -// type instanceof Crypto::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% -// } -// } /** * Resolves literal `e` to a known algorithm name, nid, normalized name, and algType * if `e` resolves to a known algorithm. @@ -992,7 +893,7 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "hmac-sha1" and nid = 781 and normalized = "SHA1" and algType = "HASH" or - name = "md_gost94" and nid = 809 and normalized = "GOST94" and algType = "SYMMETRIC_ENCRYPTION" + name = "md_gost94" and nid = 809 and normalized = "GOST94" and algType = "HASH" or name = "gost94" and nid = 812 and normalized = "GOST94" and algType = "SYMMETRIC_ENCRYPTION" or @@ -2432,7 +2333,7 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "pbe-md2-des" and nid = 9 and normalized = "MD2" and algType = "HASH" or - name = "pbe-md2-des" and nid = 9 and normalized = "2DES" and algType = "SYMMETRIC_ENCRYPTION" + name = "pbe-md2-des" and nid = 9 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" or name = "pbe-md2-rc2-64" and nid = 168 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" or diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 243d52cd2e67..522ff3b27848 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -864,16 +864,26 @@ module CryptographyBase Input> { */ newtype TCipherType = AES() or - Camellia() or + ARIA() or + BLOWFISH() or + CAMELLIA() or + CAST5() or + CHACHA20() or DES() or - TripleDES() or + DESX() or + GOST() or IDEA() or - CAST5() or - ChaCha20() or + KUZNYECHIK() or + MAGMA() or + TRIPLEDES() or + DOUBLEDES() or + RC2() or RC4() or RC5() or RSA() or - OtherCipherType() + SEED() or + SM4() or + OTHERCIPHERTYPE() final class CipherAlgorithmNode extends AlgorithmNode, TCipherAlgorithm { CipherAlgorithmInstance instance; @@ -922,27 +932,27 @@ module CryptographyBase Input> { final private predicate cipherFamilyToNameAndStructure( TCipherType type, string name, TCipherStructureType s ) { - type instanceof AES and name = "AES" and s = Block() - or - type instanceof Camellia and name = "Camellia" and s = Block() - or - type instanceof DES and name = "DES" and s = Block() - or - type instanceof TripleDES and name = "TripleDES" and s = Block() - or - type instanceof IDEA and name = "IDEA" and s = Block() - or - type instanceof CAST5 and name = "CAST5" and s = Block() - or - type instanceof ChaCha20 and name = "ChaCha20" and s = Stream() - or - type instanceof RC4 and name = "RC4" and s = Stream() - or - type instanceof RC5 and name = "RC5" and s = Block() - or - type instanceof RSA and name = "RSA" and s = Asymmetric() - or - type instanceof OtherCipherType and + type instanceof AES and name = "AES" and s = Block() or + type instanceof ARIA and name = "ARIA" and s = Block() or + type instanceof BLOWFISH and name = "Blowfish" and s = Block() or + type instanceof CAMELLIA and name = "Camellia" and s = Block() or + type instanceof CAST5 and name = "CAST5" and s = Block() or + type instanceof CHACHA20 and name = "ChaCha20" and s = Stream() or + type instanceof DES and name = "DES" and s = Block() or + type instanceof DESX and name = "DESX" and s = Block() or + type instanceof GOST and name = "GOST" and s = Block() or + type instanceof IDEA and name = "IDEA" and s = Block() or + type instanceof KUZNYECHIK and name = "Kuznyechik" and s = Block() or + type instanceof MAGMA and name = "Magma" and s = Block() or + type instanceof TRIPLEDES and name = "TripleDES" and s = Block() or + type instanceof DOUBLEDES and name = "DoubleDES" and s = Block() or + type instanceof RC2 and name = "RC2" and s = Block() or + type instanceof RC4 and name = "RC4" and s = Stream() or + type instanceof RC5 and name = "RC5" and s = Block() or + type instanceof RSA and name = "RSA" and s = Asymmetric() or + type instanceof SEED and name = "SEED" and s = Block() or + type instanceof SM4 and name = "SM4" and s = Block() or + type instanceof OTHERCIPHERTYPE and name = this.getRawAlgorithmName() and s = UnknownCipherStructureType() } From 6083df2b7f22955bf123982f7a66760e3f756aa2 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Wed, 5 Mar 2025 15:48:08 -0500 Subject: [PATCH 041/122] Completed tying algorithm instances to consumers. Now the model should have known literals for cipher instances, and it traces those instances to consumers (inits) and those inits are traced to cipher operations. --- .../OpenSSL/EVPCipherAlgorithmSource.qll | 10 ++++++--- .../OpenSSL/OpenSSLAlgorithmGetter.qll | 22 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll index 046abf3f43a9..c6810c0ab396 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll @@ -52,11 +52,13 @@ predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { ) } + class CipherKnownAlgorithmLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof Literal { + OpenSSLAlgorithmGetterCall cipherGetterCall; CipherKnownAlgorithmLiteralAlgorithmInstance() { - exists(EVPCipherGetterCall c, DataFlow::Node src, DataFlow::Node sink | - sink = c.getValueArgNode() and + exists(DataFlow::Node src, DataFlow::Node sink | + sink = cipherGetterCall.getValueArgNode() and src.asExpr() = this and KnownAlgorithmLiteralToAlgorithmGetterFlow::flow(src, sink) and // Not just any known value, but specifically a known cipher operation @@ -67,7 +69,9 @@ class CipherKnownAlgorithmLiteralAlgorithmInstance extends Crypto::CipherAlgorit ) } - Crypto::AlgorithmConsumer getConsumer() { none() } //result = consumer } + Crypto::AlgorithmConsumer getConsumer() { + AlgGetterToAlgConsumerFlow::flow(cipherGetterCall.getResultNode(), DataFlow::exprNode(result)) + } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() // TODO: provider defaults diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll index 79491ca2a09d..0b1f5546c4ba 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll @@ -2,6 +2,19 @@ import cpp import semmle.code.cpp.dataflow.new.DataFlow import LibraryDetector import OpenSSLKnownAlgorithmConstants +import experimental.Quantum.Language + +module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(OpenSSLAlgorithmGetterCall c | c.getResultNode() = source) + } + + predicate isSink(DataFlow::Node sink) { + exists(Crypto::AlgorithmConsumer c | c.getInputNode() = sink) + } +} + +module AlgGetterToAlgConsumerFlow = DataFlow::Global; abstract class AlgorithmPassthroughCall extends Call { abstract DataFlow::Node getInNode(); @@ -116,14 +129,7 @@ abstract class OpenSSLAlgorithmGetterCall extends Call { } module KnownAlgorithmLiteralToAlgorithmGetterConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - ( - source.asExpr() instanceof Literal and - // 0 sources, for nid are unknown, and 0 otherwise represents a null assignment (ignore as unknown) - exists(source.asExpr().(Literal).getValue().toInt()) implies source.asExpr().(Literal).getValue().toInt() != 0 - //resolveAlgorithmFromLiteral(source.asExpr(),_,_) - ) - } + predicate isSource(DataFlow::Node source) { resolveAlgorithmFromLiteral(source.asExpr(), _, _) } predicate isSink(DataFlow::Node sink) { exists(OpenSSLAlgorithmGetterCall c | c.getValueArgNode() = sink) From b9bd199432e19a8d3a8235aa479ee4eea33aebc7 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Thu, 6 Mar 2025 13:39:23 -0500 Subject: [PATCH 042/122] Regression fixes for JCA --- java/ql/lib/experimental/Quantum/JCA.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 5cb7bcf23dcb..5d5184fdda22 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -123,7 +123,7 @@ module JCAModel { override Crypto::TCipherType getCipherFamily() { if this.cipherNameMappingKnown(_, super.getAlgorithmName()) then this.cipherNameMappingKnown(result, super.getAlgorithmName()) - else result instanceof Crypto::OtherCipherType + else result instanceof Crypto::OTHERCIPHERTYPE } bindingset[name] @@ -135,7 +135,7 @@ module JCAModel { type instanceof Crypto::DES or name = "TripleDES" and - type instanceof Crypto::TripleDES + type instanceof Crypto::TRIPLEDES or name = "IDEA" and type instanceof Crypto::IDEA @@ -144,7 +144,7 @@ module JCAModel { type instanceof Crypto::CAST5 or name = "ChaCha20" and - type instanceof Crypto::ChaCha20 + type instanceof Crypto::CHACHA20 or name = "RC4" and type instanceof Crypto::RC4 From 32d29ffde3c1f0392c0b4e7fc72bc95d8a493806 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 7 Mar 2025 10:02:36 -0500 Subject: [PATCH 043/122] Changed casing on TCipherType, Added some initial fixes for hash support, started developing openssl hashing modeling. --- .../OpenSSL/EVPCipherAlgorithmSource.qll | 11 +- .../OpenSSL/EVPDigestAlgorithmSource.qll | 65 +++++++++++ .../Quantum/OpenSSL/EVPHashOperation.qll | 5 + .../OpenSSL/OpenSSLAlgorithmGetter.qll | 34 +++++- java/ql/lib/experimental/Quantum/JCA.qll | 4 +- .../codeql/cryptography/Model.qll | 108 ++++++++++++------ 6 files changed, 185 insertions(+), 42 deletions(-) create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll index c6810c0ab396..8aa4f88a81b8 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll @@ -3,7 +3,7 @@ import experimental.Quantum.Language import EVPCipherConsumers import OpenSSLAlgorithmGetter -predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { +predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { exists(string name, string algType | algType.toLowerCase().matches("%encryption") | resolveAlgorithmFromLiteral(e, name, algType) and ( @@ -21,9 +21,9 @@ predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { or name.matches("CAST5") and type instanceof Crypto::CAST5 or - name.matches("2DES") and type instanceof Crypto::DOUBLEDES + name.matches("2DES") and type instanceof Crypto::DoubleDES or - name.matches(["3DES", "TRIPLEDES"]) and type instanceof Crypto::TRIPLEDES + name.matches(["3DES", "TRIPLEDES"]) and type instanceof Crypto::TripleDES or name.matches("DES") and type instanceof Crypto::DES or @@ -52,7 +52,6 @@ predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { ) } - class CipherKnownAlgorithmLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof Literal { OpenSSLAlgorithmGetterCall cipherGetterCall; @@ -81,7 +80,9 @@ class CipherKnownAlgorithmLiteralAlgorithmInstance extends Crypto::CipherAlgorit override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } - override Crypto::TCipherType getCipherFamily() { literalToCipherFamilyType(this, result) } + override Crypto::TCipherType getCipherFamily() { + literalToCipherFamilyType(this, result) + } } // override Crypto::TCipherType getCipherFamily() { // if this.cipherNameMappingKnown(_, super.getAlgorithmName()) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll new file mode 100644 index 000000000000..aa9c4008dd11 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll @@ -0,0 +1,65 @@ +import cpp +import experimental.Quantum.Language +import EVPCipherConsumers +import OpenSSLAlgorithmGetter + +predicate literalToHashFamilyType(Literal e, Crypto::THashType type) { + exists(string name, string algType | algType.toLowerCase().matches("%hash") | + resolveAlgorithmFromLiteral(e, name, algType) and + ( + name.matches("BLAKE2B") and type instanceof Crypto::BLAKE2B + or + name.matches("BLAKE2S") and type instanceof Crypto::BLAKE2S + or + name.matches("RIPEMD160") and type instanceof Crypto::RIPEMD160 + or + name.matches("MD2") and type instanceof Crypto::MD2 + or + name.matches("MD4") and type instanceof Crypto::MD4 + or + name.matches("MD5") and type instanceof Crypto::MD5 + or + name.matches("POLY1305") and type instanceof Crypto::POLY1305 + or + name.matches(["SHA1", "SHA"]) and type instanceof Crypto::SHA1 + or + name.matches("SHA2") and type instanceof Crypto::SHA2 + or + name.matches("SHA3") and type instanceof Crypto::SHA3 + or + name.matches("SHAKE") and type instanceof Crypto::SHAKE + or + name.matches("SM3") and type instanceof Crypto::SM3 + or + name.matches("WHIRLPOOL") and type instanceof Crypto::WHIRLPOOL + // TODO: what about MD_GOST? + ) + ) +} + +class HashKnownAlgorithmLiteralAlgorithmInstance extends Crypto::HashAlgorithmInstance instanceof Literal +{ + OpenSSLAlgorithmGetterCall cipherGetterCall; + + HashKnownAlgorithmLiteralAlgorithmInstance() { + exists(DataFlow::Node src, DataFlow::Node sink | + sink = cipherGetterCall.getValueArgNode() and + src.asExpr() = this and + KnownAlgorithmLiteralToAlgorithmGetterFlow::flow(src, sink) and + // Not just any known value, but specifically a known cipher operation + exists(string algType | + resolveAlgorithmFromLiteral(src.asExpr(), _, algType) and + algType.toLowerCase().matches("hash") + ) + ) + } + + // TODO: should this not be part of the abstract algorithm definition? + Crypto::AlgorithmConsumer getConsumer() { + AlgGetterToAlgConsumerFlow::flow(cipherGetterCall.getResultNode(), DataFlow::exprNode(result)) + } + + override Crypto::THashType getHashFamily() { literalToHashFamilyType(this, result) } + + override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll new file mode 100644 index 000000000000..91b3f2ea62b9 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll @@ -0,0 +1,5 @@ +import experimental.Quantum.Language +import CtxFlow as CTXFlow + + +//https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis \ No newline at end of file diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll index 0b1f5546c4ba..30ffd1e1f3e8 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll @@ -162,9 +162,9 @@ class EVPCipherGetterCall extends OpenSSLAlgorithmGetterCall { Expr resultExpr; EVPCipherGetterCall() { - // Flow out through the return pointer itself (trace the pointer, not what it is pointing to) resultExpr = this and resultNode.asExpr() = this and + isPossibleOpenSSLFunction(this.getTarget()) and ( this.getTarget().getName() in ["EVP_get_cipherbyname", "EVP_get_cipherbyobj"] and valueArgExpr = this.getArgument(0) and @@ -188,6 +188,38 @@ class EVPCipherGetterCall extends OpenSSLAlgorithmGetterCall { override Expr getResultExpr() { result = resultExpr } } + +class EVPDigestGetterCall extends OpenSSLAlgorithmGetterCall { + DataFlow::Node valueArgNode; + DataFlow::Node resultNode; + Expr valueArgExpr; + Expr resultExpr; + + EVPDigestGetterCall() { + resultExpr = this and + resultNode.asExpr() = this and + isPossibleOpenSSLFunction(this.getTarget()) and + ( + this.getTarget().getName() in [ + "EVP_get_digestbyname", "EVP_get_digestbyobj", "EVP_get_digestbynid" + ] and + valueArgExpr = this.getArgument(0) and + valueArgNode.asExpr() = valueArgExpr + or + this.getTarget().getName() = "EVP_MD_fetch" and + valueArgExpr = this.getArgument(1) and + valueArgNode.asExpr() = valueArgExpr + ) + } + + override DataFlow::Node getValueArgNode() { result = valueArgNode } + + override DataFlow::Node getResultNode() { result = resultNode } + + override Expr getValueArgExpr() { result = valueArgExpr } + + override Expr getResultExpr() { result = resultExpr } +} // /** // * Predicates/classes for identifying algorithm sinks. // * An Algorithm Sink is a function that takes an algorithm as an argument. diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 5d5184fdda22..b2698cabee24 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -123,7 +123,7 @@ module JCAModel { override Crypto::TCipherType getCipherFamily() { if this.cipherNameMappingKnown(_, super.getAlgorithmName()) then this.cipherNameMappingKnown(result, super.getAlgorithmName()) - else result instanceof Crypto::OTHERCIPHERTYPE + else result instanceof Crypto::OtherCipherType } bindingset[name] @@ -135,7 +135,7 @@ module JCAModel { type instanceof Crypto::DES or name = "TripleDES" and - type instanceof Crypto::TRIPLEDES + type instanceof Crypto::TripleDES or name = "IDEA" and type instanceof Crypto::IDEA diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 522ff3b27848..e3a1a6123ec6 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -140,7 +140,13 @@ module CryptographyBase Input> { /** * An element that represents a _known_ cryptographic algorithm. */ - abstract class AlgorithmElement extends KnownElement { } + abstract class AlgorithmElement extends KnownElement { + /** + * Gets the raw name as it appears in source, e.g., "AES/CBC/PKCS7Padding". + * This name is not parsed or formatted. + */ + abstract string getRawAlgorithmName(); + } /** * An element that represents a _known_ cryptographic artifact. @@ -286,12 +292,6 @@ module CryptographyBase Input> { } abstract class CipherAlgorithmInstance extends AlgorithmElement { - /** - * Gets the raw name as it appears in source, e.g., "AES/CBC/PKCS7Padding". - * This name is not parsed or formatted. - */ - abstract string getRawAlgorithmName(); - /** * Gets the type of this cipher, e.g., "AES" or "ChaCha20". */ @@ -358,7 +358,12 @@ module CryptographyBase Input> { abstract class HashOperationInstance extends KnownElement { } - abstract class HashAlgorithmInstance extends KnownElement { } + abstract class HashAlgorithmInstance extends AlgorithmElement { + /** + * Gets the type of this digest algorithm, e.g., "SHA1", "SHA2", "MD5" etc. + */ + abstract THashType getHashFamily(); + } abstract class KeyDerivationOperationInstance extends KnownElement { } @@ -875,15 +880,15 @@ module CryptographyBase Input> { IDEA() or KUZNYECHIK() or MAGMA() or - TRIPLEDES() or - DOUBLEDES() or + TripleDES() or + DoubleDES() or RC2() or RC4() or RC5() or RSA() or SEED() or SM4() or - OTHERCIPHERTYPE() + OtherCipherType() final class CipherAlgorithmNode extends AlgorithmNode, TCipherAlgorithm { CipherAlgorithmInstance instance; @@ -932,27 +937,47 @@ module CryptographyBase Input> { final private predicate cipherFamilyToNameAndStructure( TCipherType type, string name, TCipherStructureType s ) { - type instanceof AES and name = "AES" and s = Block() or - type instanceof ARIA and name = "ARIA" and s = Block() or - type instanceof BLOWFISH and name = "Blowfish" and s = Block() or - type instanceof CAMELLIA and name = "Camellia" and s = Block() or - type instanceof CAST5 and name = "CAST5" and s = Block() or - type instanceof CHACHA20 and name = "ChaCha20" and s = Stream() or - type instanceof DES and name = "DES" and s = Block() or - type instanceof DESX and name = "DESX" and s = Block() or - type instanceof GOST and name = "GOST" and s = Block() or - type instanceof IDEA and name = "IDEA" and s = Block() or - type instanceof KUZNYECHIK and name = "Kuznyechik" and s = Block() or - type instanceof MAGMA and name = "Magma" and s = Block() or - type instanceof TRIPLEDES and name = "TripleDES" and s = Block() or - type instanceof DOUBLEDES and name = "DoubleDES" and s = Block() or - type instanceof RC2 and name = "RC2" and s = Block() or - type instanceof RC4 and name = "RC4" and s = Stream() or - type instanceof RC5 and name = "RC5" and s = Block() or - type instanceof RSA and name = "RSA" and s = Asymmetric() or - type instanceof SEED and name = "SEED" and s = Block() or - type instanceof SM4 and name = "SM4" and s = Block() or - type instanceof OTHERCIPHERTYPE and + type instanceof AES and name = "AES" and s = Block() + or + type instanceof ARIA and name = "ARIA" and s = Block() + or + type instanceof BLOWFISH and name = "Blowfish" and s = Block() + or + type instanceof CAMELLIA and name = "Camellia" and s = Block() + or + type instanceof CAST5 and name = "CAST5" and s = Block() + or + type instanceof CHACHA20 and name = "ChaCha20" and s = Stream() + or + type instanceof DES and name = "DES" and s = Block() + or + type instanceof DESX and name = "DESX" and s = Block() + or + type instanceof GOST and name = "GOST" and s = Block() + or + type instanceof IDEA and name = "IDEA" and s = Block() + or + type instanceof KUZNYECHIK and name = "Kuznyechik" and s = Block() + or + type instanceof MAGMA and name = "Magma" and s = Block() + or + type instanceof TripleDES and name = "TripleDES" and s = Block() + or + type instanceof DoubleDES and name = "DoubleDES" and s = Block() + or + type instanceof RC2 and name = "RC2" and s = Block() + or + type instanceof RC4 and name = "RC4" and s = Stream() + or + type instanceof RC5 and name = "RC5" and s = Block() + or + type instanceof RSA and name = "RSA" and s = Asymmetric() + or + type instanceof SEED and name = "SEED" and s = Block() + or + type instanceof SM4 and name = "SM4" and s = Block() + or + type instanceof OtherCipherType and name = this.getRawAlgorithmName() and s = UnknownCipherStructureType() } @@ -1004,13 +1029,18 @@ module CryptographyBase Input> { } newtype THashType = + BLAKE2B() or + BLAKE2S() or + RIPEMD160() or MD2() or MD4() or MD5() or + POLY1305() or SHA1() or SHA2() or SHA3() or - RIPEMD160() or + SHAKE() or + SM3() or WHIRLPOOL() or OtherHashType() @@ -1021,19 +1051,29 @@ module CryptographyBase Input> { override string getInternalType() { result = "HashAlgorithm" } final predicate hashTypeToNameMapping(THashType type, string name) { + type instanceof BLAKE2B and name = "BLAKE2B" + or + type instanceof BLAKE2S and name = "BLAKE2S" + or + type instanceof RIPEMD160 and name = "RIPEMD160" + or type instanceof MD2 and name = "MD2" or type instanceof MD4 and name = "MD4" or type instanceof MD5 and name = "MD5" or + type instanceof POLY1305 and name = "POLY1305" + or type instanceof SHA1 and name = "SHA1" or type instanceof SHA2 and name = "SHA2" or type instanceof SHA3 and name = "SHA3" or - type instanceof RIPEMD160 and name = "RIPEMD160" + type instanceof SHAKE and name = "SHAKE" + or + type instanceof SM3 and name = "SM3" or type instanceof WHIRLPOOL and name = "WHIRLPOOL" or From cf72fde911b1d73047454009aa5bdb268f0dbabb Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 7 Mar 2025 12:11:12 -0500 Subject: [PATCH 044/122] Fixing cross product in getPropertyAsGraphString (parameter `root` not bound in all cases, fixing using a bindingetset) --- shared/cryptography/codeql/cryptography/Model.qll | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index e3a1a6123ec6..5a6cd3288fe7 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -32,13 +32,16 @@ module CryptographyBase Input> { UnknownPropertyValue() { this = "" } } + bindingset[root] private string getPropertyAsGraphString(NodeBase node, string key, Location root) { result = strictconcat(any(string value, Location location, string parsed | node.properties(key, value, location) and - if location = root or location instanceof UnknownLocation - then parsed = value - else parsed = "(" + value + "," + location.toString() + ")" + ( + if location = root or location instanceof UnknownLocation + then parsed = value + else parsed = "(" + value + "," + location.toString() + ")" + ) | parsed ), "," From 47affa0fed674e875699b0587f84a15bef683bbc Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Fri, 7 Mar 2025 18:30:58 +0100 Subject: [PATCH 045/122] Restore location output for nodes --- shared/cryptography/codeql/cryptography/Model.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 5a6cd3288fe7..433c97a83875 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -67,7 +67,7 @@ module CryptographyBase Input> { or // CodeQL's DGML output does not include a location key = "Location" and - value = "" // node.getLocation().toString() + value = node.getLocation().toString() or // Known unknown edges should be reported as properties rather than edges node = node.getChild(key) and From bac0a635f943abb9a1bf50f001364e3094f74d04 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 7 Mar 2025 14:24:01 -0500 Subject: [PATCH 046/122] Initial hash models for openssl. --- cpp/ql/lib/experimental/Quantum/Language.qll | 110 +++++++++--------- .../OpenSSL/EVPDigestAlgorithmSource.qll | 8 +- .../OpenSSL/EVPHashAlgorithmSource.qll | 70 +++++++++++ .../Quantum/OpenSSL/EVPHashConsumers.qll | 27 +++++ .../Quantum/OpenSSL/EVPHashInitializer.qll | 25 ++++ .../Quantum/OpenSSL/EVPHashOperation.qll | 80 ++++++++++++- .../OpenSSLKnownAlgorithmConstants.qll | 72 ++++++------ .../codeql/cryptography/Model.qll | 8 +- 8 files changed, 303 insertions(+), 97 deletions(-) create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashInitializer.qll diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index 5d4ff4bc6863..f0775df1b2e6 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -22,82 +22,82 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node { } -/** - * Generic data source to node input configuration - */ -module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - source = any(Crypto::GenericDataSourceInstance i).getOutputNode() - } +// /** +// * Generic data source to node input configuration +// */ +// module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { +// predicate isSource(DataFlow::Node source) { +// source = any(Crypto::GenericDataSourceInstance i).getOutputNode() +// } - predicate isSink(DataFlow::Node sink) { - sink = any(Crypto::FlowAwareElement other).getInputNode() - } +// predicate isSink(DataFlow::Node sink) { +// sink = any(Crypto::FlowAwareElement other).getInputNode() +// } - predicate isBarrierOut(DataFlow::Node node) { - node = any(Crypto::FlowAwareElement element).getInputNode() - } +// predicate isBarrierOut(DataFlow::Node node) { +// node = any(Crypto::FlowAwareElement element).getInputNode() +// } - predicate isBarrierIn(DataFlow::Node node) { - node = any(Crypto::FlowAwareElement element).getOutputNode() - } +// predicate isBarrierIn(DataFlow::Node node) { +// node = any(Crypto::FlowAwareElement element).getOutputNode() +// } - predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - node1.(AdditionalFlowInputStep).getOutput() = node2 - } -} +// predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { +// node1.(AdditionalFlowInputStep).getOutput() = node2 +// } +// } -// // TODO: I think this will be inefficient, no? -// class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { -// override DataFlow::Node getOutputNode() { -// result.asExpr() = this -// } +// // // TODO: I think this will be inefficient, no? +// // class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { +// // override DataFlow::Node getOutputNode() { +// // result.asExpr() = this +// // } -// override predicate flowsTo(Crypto::FlowAwareElement other) { -// // TODO: separate config to avoid blowing up data-flow analysis -// GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) -// } +// // override predicate flowsTo(Crypto::FlowAwareElement other) { +// // // TODO: separate config to avoid blowing up data-flow analysis +// // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) +// // } -// override string getAdditionalDescription() { result = this.toString() } -// } +// // override string getAdditionalDescription() { result = this.toString() } +// // } -/** - * Definitions of various generic data sources - */ -// final class DefaultFlowSource = SourceNode; +// /** +// * Definitions of various generic data sources +// */ +// // final class DefaultFlowSource = SourceNode; -// final class DefaultRemoteFlowSource = RemoteFlowSource; +// // final class DefaultRemoteFlowSource = RemoteFlowSource; -// class GenericLocalDataSource extends Crypto::GenericLocalDataSource { -// GenericLocalDataSource() { -// any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this -// } +// // class GenericLocalDataSource extends Crypto::GenericLocalDataSource { +// // GenericLocalDataSource() { +// // any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this +// // } -// override DataFlow::Node getOutputNode() { result.asExpr() = this } +// // override DataFlow::Node getOutputNode() { result.asExpr() = this } -// override predicate flowsTo(Crypto::FlowAwareElement other) { -// GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) -// } +// // override predicate flowsTo(Crypto::FlowAwareElement other) { +// // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) +// // } -// override string getAdditionalDescription() { result = this.toString() } -// } +// // override string getAdditionalDescription() { result = this.toString() } +// // } -// class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { -// GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this } +// // class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { +// // GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this } -// override DataFlow::Node getOutputNode() { result.asExpr() = this } +// // override DataFlow::Node getOutputNode() { result.asExpr() = this } -// override predicate flowsTo(Crypto::FlowAwareElement other) { -// GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) -// } +// // override predicate flowsTo(Crypto::FlowAwareElement other) { +// // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) +// // } -// override string getAdditionalDescription() { result = this.toString() } -// } +// // override string getAdditionalDescription() { result = this.toString() } +// // } -module GenericDataSourceUniversalFlow = DataFlow::Global; +// module GenericDataSourceUniversalFlow = DataFlow::Global; module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll index aa9c4008dd11..7d132b22a3ac 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll @@ -39,11 +39,11 @@ predicate literalToHashFamilyType(Literal e, Crypto::THashType type) { class HashKnownAlgorithmLiteralAlgorithmInstance extends Crypto::HashAlgorithmInstance instanceof Literal { - OpenSSLAlgorithmGetterCall cipherGetterCall; + OpenSSLAlgorithmGetterCall getterCall; HashKnownAlgorithmLiteralAlgorithmInstance() { exists(DataFlow::Node src, DataFlow::Node sink | - sink = cipherGetterCall.getValueArgNode() and + sink = getterCall.getValueArgNode() and src.asExpr() = this and KnownAlgorithmLiteralToAlgorithmGetterFlow::flow(src, sink) and // Not just any known value, but specifically a known cipher operation @@ -56,10 +56,12 @@ class HashKnownAlgorithmLiteralAlgorithmInstance extends Crypto::HashAlgorithmIn // TODO: should this not be part of the abstract algorithm definition? Crypto::AlgorithmConsumer getConsumer() { - AlgGetterToAlgConsumerFlow::flow(cipherGetterCall.getResultNode(), DataFlow::exprNode(result)) + AlgGetterToAlgConsumerFlow::flow(getterCall.getResultNode(), DataFlow::exprNode(result)) } override Crypto::THashType getHashFamily() { literalToHashFamilyType(this, result) } + override int getHashSize() { none() } //TODO + override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll new file mode 100644 index 000000000000..ad2af7e05bed --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll @@ -0,0 +1,70 @@ +import cpp +import experimental.Quantum.Language +import EVPHashConsumers +import OpenSSLAlgorithmGetter + +predicate literalToHashFamilyType(Literal e, Crypto::THashType type) { + exists(string name, string algType | algType.toLowerCase().matches("hash") | + resolveAlgorithmFromLiteral(e, name, algType) and + ( + name.matches("BLAKE2B") and type instanceof Crypto::BLAKE2B + or + name.matches("BLAKE2S") and type instanceof Crypto::BLAKE2S + or + name.matches("MD2") and type instanceof Crypto::MD2 + or + name.matches("MD4") and type instanceof Crypto::MD4 + or + name.matches("MD5") and type instanceof Crypto::MD5 + or + name.matches("POLY1305") and type instanceof Crypto::POLY1305 + or + name.matches(["SHA", "SHA1"]) and type instanceof Crypto::SHA1 + or + name.matches("SHA+%") and not name.matches(["SHA1", "SHA3-"]) and type instanceof Crypto::SHA2 + or + name.matches("SHA3-%") and type instanceof Crypto::SHA3 + or + name.matches(["SHAKE"]) and type instanceof Crypto::SHAKE + or + name.matches("SM3") and type instanceof Crypto::SM3 + or + name.matches("RIPEMD160") and type instanceof Crypto::RIPEMD160 + or + //or + //TODO: need to handle MACs differently, including md_GOST94 + // name.matches("%GOST%") and type instanceof Crypto::GOST + name.matches("WHIRLPOOL") and type instanceof Crypto::WHIRLPOOL + ) + ) +} + +class HashKnownAlgorithmLiteralAlgorithmInstance extends Crypto::HashAlgorithmInstance instanceof Literal +{ + OpenSSLAlgorithmGetterCall cipherGetterCall; + + HashKnownAlgorithmLiteralAlgorithmInstance() { + exists(DataFlow::Node src, DataFlow::Node sink | + sink = cipherGetterCall.getValueArgNode() and + src.asExpr() = this and + KnownAlgorithmLiteralToAlgorithmGetterFlow::flow(src, sink) and + // Not just any known value, but specifically a known cipher operation + exists(string algType | + resolveAlgorithmFromLiteral(src.asExpr(), _, algType) and + algType.toLowerCase().matches("hash") + ) + ) + } + + Crypto::AlgorithmConsumer getConsumer() { + AlgGetterToAlgConsumerFlow::flow(cipherGetterCall.getResultNode(), DataFlow::exprNode(result)) + } + + override Crypto::THashType getHashFamily() { + literalToHashFamilyType(this, result) + } + + override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } + + override int getHashSize() {none() }//TODO +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll new file mode 100644 index 000000000000..31bf00ef94a5 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll @@ -0,0 +1,27 @@ +import EVPHashInitializer +import EVPHashOperation +import EVPHashAlgorithmSource + +class EVP_Digest_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVPDigestInitializerAlgorithmArgument{ + override DataFlow::Node getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmElement getAKnownAlgorithmSource() { + result.(HashKnownAlgorithmLiteralAlgorithmInstance).getConsumer() = this + } +} + +class EVP_Q_Digest_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVP_Q_Digest_Algorithm_Argument{ + override DataFlow::Node getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmElement getAKnownAlgorithmSource() { + result.(HashKnownAlgorithmLiteralAlgorithmInstance).getConsumer() = this + } +} + +class EVP_Digest_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVP_Digest_Algorithm_Argument{ + override DataFlow::Node getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmElement getAKnownAlgorithmSource() { + result.(HashKnownAlgorithmLiteralAlgorithmInstance).getConsumer() = this + } +} \ No newline at end of file diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashInitializer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashInitializer.qll new file mode 100644 index 000000000000..16a9234d68ca --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashInitializer.qll @@ -0,0 +1,25 @@ +import cpp + +abstract class EVP_Hash_Inititalizer extends Call { + Expr getContextArg() { result = this.(Call).getArgument(0) } + + abstract Expr getAlgorithmArg(); +} + +class EVP_DigestInit_Variant_Calls extends EVP_Hash_Inititalizer { + EVP_DigestInit_Variant_Calls() { + this.(Call).getTarget().getName() in [ + "EVP_DigestInit", "EVP_DigestInit_ex", "EVP_DigestInit_ex2" + ] + } + + override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } + +} + + +class EVPDigestInitializerAlgorithmArgument extends Expr { + EVPDigestInitializerAlgorithmArgument() { + exists(EVP_Hash_Inititalizer initCall | this = initCall.getAlgorithmArg()) + } +} \ No newline at end of file diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll index 91b3f2ea62b9..7db90c4eccc0 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll @@ -1,5 +1,83 @@ import experimental.Quantum.Language import CtxFlow as CTXFlow +import LibraryDetector +import EVPHashInitializer +import EVPHashConsumers +abstract class EVP_Hash_Operation extends Crypto::HashOperationInstance instanceof Call { + Expr getContextArg() { result = this.(Call).getArgument(0) } -//https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis \ No newline at end of file + EVP_Hash_Inititalizer getInitCall() { + CTXFlow::ctxFlowsTo(result.getContextArg(), this.getContextArg()) + } +} + +//https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis +class EVP_Q_Digest_Operation extends EVP_Hash_Operation { + EVP_Q_Digest_Operation() { + this.(Call).getTarget().getName() = "EVP_Q_digest" and + isPossibleOpenSSLFunction(this.(Call).getTarget()) + } + + override Crypto::AlgorithmConsumer getAlgorithmConsumer() { this.(Call).getArgument(1) = result } + + override EVP_Hash_Inititalizer getInitCall() { + // This variant of digest does not use an init + // and even if it were used, the init would be ignored/undefined + none() + } +} + +class EVP_Q_Digest_Algorithm_Argument extends Expr { + EVP_Q_Digest_Algorithm_Argument() { + exists(EVP_Q_Digest_Operation op | this = op.(Call).getArgument(1)) + } +} + +class EVP_Digest_Operation extends EVP_Hash_Operation { + EVP_Digest_Operation() { + this.(Call).getTarget().getName() = "EVP_Digest" and + isPossibleOpenSSLFunction(this.(Call).getTarget()) + } + + // There is no context argument for this function + override Expr getContextArg() { none() } + + override Crypto::AlgorithmConsumer getAlgorithmConsumer() { this.(Call).getArgument(4) = result } + + override EVP_Hash_Inititalizer getInitCall() { + // This variant of digest does not use an init + // and even if it were used, the init would be ignored/undefined + none() + } +} + +class EVP_Digest_Algorithm_Argument extends Expr { + EVP_Digest_Algorithm_Argument() { + exists(EVP_Digest_Operation op | this = op.(Call).getArgument(4)) + } +} + +class EVP_DigestUpdate_Operation extends EVP_Hash_Operation { + EVP_DigestUpdate_Operation() { + this.(Call).getTarget().getName() = "EVP_DigestUpdate" and + isPossibleOpenSSLFunction(this.(Call).getTarget()) + } + + override Crypto::AlgorithmConsumer getAlgorithmConsumer() { + this.getInitCall().getAlgorithmArg() = result + } +} + +class EVP_DigestFinal_Variants_Operation extends EVP_Hash_Operation { + EVP_DigestFinal_Variants_Operation() { + this.(Call).getTarget().getName() in [ + "EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF" + ] and + isPossibleOpenSSLFunction(this.(Call).getTarget()) + } + + override Crypto::AlgorithmConsumer getAlgorithmConsumer() { + this.getInitCall().getAlgorithmArg() = result + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll index dcf24d01ace8..76c6c172bf6e 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll @@ -212,13 +212,13 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "sha512-256" and nid = 1095 and normalized = "SHA512256" and algType = "HASH" or - name = "sha3-224" and nid = 1096 and normalized = "SHA3224" and algType = "HASH" + name = "sha3-224" and nid = 1096 and normalized = "SHA3-224" and algType = "HASH" or - name = "sha3-256" and nid = 1097 and normalized = "SHA3256" and algType = "HASH" + name = "sha3-256" and nid = 1097 and normalized = "SHA3-256" and algType = "HASH" or - name = "sha3-384" and nid = 1098 and normalized = "SHA3384" and algType = "HASH" + name = "sha3-384" and nid = 1098 and normalized = "SHA3-384" and algType = "HASH" or - name = "sha3-512" and nid = 1099 and normalized = "SHA3512" and algType = "HASH" + name = "sha3-512" and nid = 1099 and normalized = "SHA3-512" and algType = "HASH" or name = "shake128" and nid = 1100 and normalized = "SHAKE128" and algType = "HASH" or @@ -1232,13 +1232,13 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "poly1305" and nid = 1061 and normalized = "POLY1305" and algType = "HASH" or - name = "hmac-sha3-224" and nid = 1102 and normalized = "SHA3224" and algType = "HASH" + name = "hmac-sha3-224" and nid = 1102 and normalized = "SHA3-224" and algType = "HASH" or - name = "hmac-sha3-256" and nid = 1103 and normalized = "SHA3256" and algType = "HASH" + name = "hmac-sha3-256" and nid = 1103 and normalized = "SHA3-256" and algType = "HASH" or - name = "hmac-sha3-384" and nid = 1104 and normalized = "SHA3384" and algType = "HASH" + name = "hmac-sha3-384" and nid = 1104 and normalized = "SHA3-384" and algType = "HASH" or - name = "hmac-sha3-512" and nid = 1105 and normalized = "SHA3512" and algType = "HASH" + name = "hmac-sha3-512" and nid = 1105 and normalized = "SHA3-512" and algType = "HASH" or name = "id-dsa-with-sha384" and nid = 1106 and normalized = "DSA" and algType = "SIGNATURE" or @@ -1250,35 +1250,35 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "id-dsa-with-sha3-224" and nid = 1108 and normalized = "DSA" and algType = "SIGNATURE" or - name = "id-dsa-with-sha3-224" and nid = 1108 and normalized = "SHA3224" and algType = "HASH" + name = "id-dsa-with-sha3-224" and nid = 1108 and normalized = "SHA3-224" and algType = "HASH" or name = "id-dsa-with-sha3-256" and nid = 1109 and normalized = "DSA" and algType = "SIGNATURE" or - name = "id-dsa-with-sha3-256" and nid = 1109 and normalized = "SHA3256" and algType = "HASH" + name = "id-dsa-with-sha3-256" and nid = 1109 and normalized = "SHA3-256" and algType = "HASH" or name = "id-dsa-with-sha3-384" and nid = 1110 and normalized = "DSA" and algType = "SIGNATURE" or - name = "id-dsa-with-sha3-384" and nid = 1110 and normalized = "SHA3384" and algType = "HASH" + name = "id-dsa-with-sha3-384" and nid = 1110 and normalized = "SHA3-384" and algType = "HASH" or name = "id-dsa-with-sha3-512" and nid = 1111 and normalized = "DSA" and algType = "SIGNATURE" or - name = "id-dsa-with-sha3-512" and nid = 1111 and normalized = "SHA3512" and algType = "HASH" + name = "id-dsa-with-sha3-512" and nid = 1111 and normalized = "SHA3-512" and algType = "HASH" or name = "id-ecdsa-with-sha3-224" and nid = 1112 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "id-ecdsa-with-sha3-224" and nid = 1112 and normalized = "SHA3224" and algType = "HASH" + name = "id-ecdsa-with-sha3-224" and nid = 1112 and normalized = "SHA3-224" and algType = "HASH" or name = "id-ecdsa-with-sha3-256" and nid = 1113 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "id-ecdsa-with-sha3-256" and nid = 1113 and normalized = "SHA3256" and algType = "HASH" + name = "id-ecdsa-with-sha3-256" and nid = 1113 and normalized = "SHA3-256" and algType = "HASH" or name = "id-ecdsa-with-sha3-384" and nid = 1114 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "id-ecdsa-with-sha3-384" and nid = 1114 and normalized = "SHA3384" and algType = "HASH" + name = "id-ecdsa-with-sha3-384" and nid = 1114 and normalized = "SHA3-384" and algType = "HASH" or name = "id-ecdsa-with-sha3-512" and nid = 1115 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "id-ecdsa-with-sha3-512" and nid = 1115 and normalized = "SHA3512" and algType = "HASH" + name = "id-ecdsa-with-sha3-512" and nid = 1115 and normalized = "SHA3-512" and algType = "HASH" or name = "id-rsassa-pkcs1-v1_5-with-sha3-224" and nid = 1116 and @@ -1292,7 +1292,7 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "id-rsassa-pkcs1-v1_5-with-sha3-224" and nid = 1116 and - normalized = "SHA3224" and + normalized = "SHA3-224" and algType = "HASH" or name = "id-rsassa-pkcs1-v1_5-with-sha3-256" and @@ -1307,7 +1307,7 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "id-rsassa-pkcs1-v1_5-with-sha3-256" and nid = 1117 and - normalized = "SHA3256" and + normalized = "SHA3-256" and algType = "HASH" or name = "id-rsassa-pkcs1-v1_5-with-sha3-384" and @@ -1322,7 +1322,7 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "id-rsassa-pkcs1-v1_5-with-sha3-384" and nid = 1118 and - normalized = "SHA3384" and + normalized = "SHA3-384" and algType = "HASH" or name = "id-rsassa-pkcs1-v1_5-with-sha3-512" and @@ -1337,7 +1337,7 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "id-rsassa-pkcs1-v1_5-with-sha3-512" and nid = 1119 and - normalized = "SHA3512" and + normalized = "SHA3-512" and algType = "HASH" or name = "aria-128-ccm" and nid = 1120 and normalized = "CCM" and algType = "BLOCK_MODE" @@ -1666,19 +1666,19 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "dsa_with_sha3-224" and nid = 1108 and normalized = "DSA" and algType = "SIGNATURE" or - name = "dsa_with_sha3-224" and nid = 1108 and normalized = "SHA3224" and algType = "HASH" + name = "dsa_with_sha3-224" and nid = 1108 and normalized = "SHA3-224" and algType = "HASH" or name = "dsa_with_sha3-256" and nid = 1109 and normalized = "DSA" and algType = "SIGNATURE" or - name = "dsa_with_sha3-256" and nid = 1109 and normalized = "SHA3256" and algType = "HASH" + name = "dsa_with_sha3-256" and nid = 1109 and normalized = "SHA3-256" and algType = "HASH" or name = "dsa_with_sha3-384" and nid = 1110 and normalized = "DSA" and algType = "SIGNATURE" or - name = "dsa_with_sha3-384" and nid = 1110 and normalized = "SHA3384" and algType = "HASH" + name = "dsa_with_sha3-384" and nid = 1110 and normalized = "SHA3-384" and algType = "HASH" or name = "dsa_with_sha3-512" and nid = 1111 and normalized = "DSA" and algType = "SIGNATURE" or - name = "dsa_with_sha3-512" and nid = 1111 and normalized = "SHA3512" and algType = "HASH" + name = "dsa_with_sha3-512" and nid = 1111 and normalized = "SHA3-512" and algType = "HASH" or name = "dsa_with_sha384" and nid = 1106 and normalized = "DSA" and algType = "SIGNATURE" or @@ -1752,19 +1752,19 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "ecdsa_with_sha3-224" and nid = 1112 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "ecdsa_with_sha3-224" and nid = 1112 and normalized = "SHA3224" and algType = "HASH" + name = "ecdsa_with_sha3-224" and nid = 1112 and normalized = "SHA3-224" and algType = "HASH" or name = "ecdsa_with_sha3-256" and nid = 1113 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "ecdsa_with_sha3-256" and nid = 1113 and normalized = "SHA3256" and algType = "HASH" + name = "ecdsa_with_sha3-256" and nid = 1113 and normalized = "SHA3-256" and algType = "HASH" or name = "ecdsa_with_sha3-384" and nid = 1114 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "ecdsa_with_sha3-384" and nid = 1114 and normalized = "SHA3384" and algType = "HASH" + name = "ecdsa_with_sha3-384" and nid = 1114 and normalized = "SHA3-384" and algType = "HASH" or name = "ecdsa_with_sha3-512" and nid = 1115 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "ecdsa_with_sha3-512" and nid = 1115 and normalized = "SHA3512" and algType = "HASH" + name = "ecdsa_with_sha3-512" and nid = 1115 and normalized = "SHA3-512" and algType = "HASH" or name = "gost 28147-89" and nid = 813 and @@ -2201,13 +2201,13 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string normalized = "GOSTR341194" and algType = "SYMMETRIC_ENCRYPTION" or - name = "id-hmacwithsha3-224" and nid = 1102 and normalized = "SHA3224" and algType = "HASH" + name = "id-hmacwithsha3-224" and nid = 1102 and normalized = "SHA3-224" and algType = "HASH" or - name = "id-hmacwithsha3-256" and nid = 1103 and normalized = "SHA3256" and algType = "HASH" + name = "id-hmacwithsha3-256" and nid = 1103 and normalized = "SHA3-256" and algType = "HASH" or - name = "id-hmacwithsha3-384" and nid = 1104 and normalized = "SHA3384" and algType = "HASH" + name = "id-hmacwithsha3-384" and nid = 1104 and normalized = "SHA3-384" and algType = "HASH" or - name = "id-hmacwithsha3-512" and nid = 1105 and normalized = "SHA3512" and algType = "HASH" + name = "id-hmacwithsha3-512" and nid = 1105 and normalized = "SHA3-512" and algType = "HASH" or name = "id-regctrl" and nid = 313 and normalized = "CTR" and algType = "BLOCK_MODE" or @@ -2590,19 +2590,19 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "rsa-sha3-224" and nid = 1116 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha3-224" and nid = 1116 and normalized = "SHA3224" and algType = "HASH" + name = "rsa-sha3-224" and nid = 1116 and normalized = "SHA3-224" and algType = "HASH" or name = "rsa-sha3-256" and nid = 1117 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha3-256" and nid = 1117 and normalized = "SHA3256" and algType = "HASH" + name = "rsa-sha3-256" and nid = 1117 and normalized = "SHA3-256" and algType = "HASH" or name = "rsa-sha3-384" and nid = 1118 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha3-384" and nid = 1118 and normalized = "SHA3384" and algType = "HASH" + name = "rsa-sha3-384" and nid = 1118 and normalized = "SHA3-384" and algType = "HASH" or name = "rsa-sha3-512" and nid = 1119 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha3-512" and nid = 1119 and normalized = "SHA3512" and algType = "HASH" + name = "rsa-sha3-512" and nid = 1119 and normalized = "SHA3-512" and algType = "HASH" or name = "rsa-sha384" and nid = 669 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 5a6cd3288fe7..bc7631755899 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -359,13 +359,17 @@ module CryptographyBase Input> { abstract class EllipticCurveAlgorithmInstance extends LocatableElement { } - abstract class HashOperationInstance extends KnownElement { } + abstract class HashOperationInstance extends OperationElement { + // TODO: need input and outputs, but this should be universal to all Operations + } abstract class HashAlgorithmInstance extends AlgorithmElement { /** * Gets the type of this digest algorithm, e.g., "SHA1", "SHA2", "MD5" etc. */ abstract THashType getHashFamily(); + + abstract int getHashSize(); } abstract class KeyDerivationOperationInstance extends KnownElement { } @@ -1034,7 +1038,6 @@ module CryptographyBase Input> { newtype THashType = BLAKE2B() or BLAKE2S() or - RIPEMD160() or MD2() or MD4() or MD5() or @@ -1044,6 +1047,7 @@ module CryptographyBase Input> { SHA3() or SHAKE() or SM3() or + RIPEMD160() or WHIRLPOOL() or OtherHashType() From d99812a10d6a50371b70fd419038874acceb28ea Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 09:59:28 -0400 Subject: [PATCH 047/122] Adding GOSTHash to THashType. --- shared/cryptography/codeql/cryptography/Model.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 25165cf5bf3a..bbe817e2110b 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -1038,6 +1038,7 @@ module CryptographyBase Input> { newtype THashType = BLAKE2B() or BLAKE2S() or + GOSTHash() or MD2() or MD4() or MD5() or From 3316d6135d29f0c64acabae614cdad553af18f45 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 11:32:14 -0400 Subject: [PATCH 048/122] Ctx flow comments. --- .../experimental/Quantum/OpenSSL/CtxFlow.qll | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll index e1b08c9652d8..c07902022d89 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll @@ -1,3 +1,21 @@ +/** + * In OpenSSL, flow between 'context' parameters is often used to + * store state/config of how an operation will eventually be performed. + * Tracing algorithms and configurations to operations therefore + * requires tracing context parameters for many OpenSSL apis. + * + * This library provides a dataflow analysis to track context parameters + * between any two functions accepting openssl context parameters. + * The dataflow takes into consideration flowing through duplication and copy calls + * as well as flow through flow killers (free/reset calls). + * + * TODO: we may need to revisit 'free' as a dataflow killer, depending on how + * we want to model use after frees. + * + * This library also provides classes to represent context Types and relevant + * arguments/expressions. + */ + import semmle.code.cpp.dataflow.new.DataFlow class CTXType extends Type { From 6a4659fc7eaa067e79f07f517abdd32bada665da Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 11:33:46 -0400 Subject: [PATCH 049/122] Updating known constants for OpenSSL to handle direct algorithm getters from older versions of openssl (e.g., EVP_md5()) --- .../OpenSSLKnownAlgorithmConstants.qll | 62 ++++++++++++++++--- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll index 76c6c172bf6e..063a7084d3db 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll @@ -1,4 +1,42 @@ import cpp +import LibraryDetector + +class KnownOpenSSLAlgorithmConstant extends Expr { + string normalizedName; + string algType; + + KnownOpenSSLAlgorithmConstant() { + resolveAlgorithmFromCall(this, normalizedName, algType) + or + resolveAlgorithmFromLiteral(this, normalizedName, algType) + } + + string getNormalizedName() { result = normalizedName } + + string getAlgType() { result = algType } +} + +/** + * Resolves a call to a 'direct algorithm getter', e.g., EVP_MD5() + * This approach to fetching algorithms was used in OpenSSL 1.0.2. + * The strategy for resolving these calls is to parse the target name + * and resolve the name as though it were a known literal. + * There are a few exceptions where the name doesn't directly match the + * known literal set. If that occurs, users must add the name to the + * set of aliases. E.g., EVP_dss() and EVP_dss1() needed such mappings + * alias = "dss" and target = "dsa" + * or + * alias = "dss1" and target = "dsaWithSHA1" + */ +predicate resolveAlgorithmFromCall(Call c, string normalized, string algType) { + isPossibleOpenSSLFunction(c.getTarget()) and + exists(string name, string parsedTargetName | + parsedTargetName = + c.getTarget().getName().replaceAll("EVP_", "").toLowerCase().replaceAll("_", "-") and + name = resolveAlgorithmAlias(parsedTargetName) and + knownOpenSSLAlgorithmLiteral(name, _, normalized, algType) + ) +} /** * Resolves literal `e` to a known algorithm name, nid, normalized name, and algType @@ -7,21 +45,23 @@ import cpp */ predicate resolveAlgorithmFromLiteral(Literal e, string normalized, string algType) { exists(int nid | - nid = getPossibleNidFromLiteral(e) and knownOpenSSLAlgorithm(_, nid, normalized, algType) + nid = getPossibleNidFromLiteral(e) and knownOpenSSLAlgorithmLiteral(_, nid, normalized, algType) ) or exists(string name | - name = resolveAlgorithmAlias(e) and knownOpenSSLAlgorithm(name, _, normalized, algType) + name = resolveAlgorithmAlias(e.getValue()) and + knownOpenSSLAlgorithmLiteral(name, _, normalized, algType) ) } -string resolveAlgorithmAlias(StringLiteral name) { - exists(string lower | lower = name.getValue().toLowerCase() | +bindingset[name] +string resolveAlgorithmAlias(string name) { + exists(string lower | lower = name.toLowerCase() | // The result is an alias algorithm name if known result = getAlgorithmAlias(lower) or // or the name is itself a known algorithm - knownOpenSSLAlgorithm(lower, _, _, _) and result = lower + knownOpenSSLAlgorithmLiteral(lower, _, _, _) and result = lower ) } @@ -133,6 +173,10 @@ predicate defaultAliases(string target, string alias) { or alias = "desx" and target = "desx-cbc" or + alias = "dss" and target = "dsa" + or + alias = "dss1" and target = "dsaWithSHA1" + or alias = "idea" and target = "idea-cbc" or alias = "rc2" and target = "rc2-cbc" @@ -165,7 +209,7 @@ predicate defaultAliases(string target, string alias) { * `normalized` is the normalized name of the algorithm (e.g., "AES128" for "aes-128-cbc") * `algType` is the type of algorithm (e.g., "SYMMETRIC_ENCRYPTION") */ -predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string algType) { +predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, string algType) { name = "rsa" and nid = 19 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or name = "prime192v1" and nid = 409 and normalized = "PRIME192V1" and algType = "ELLIPTIC_CURVE" @@ -895,7 +939,7 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "md_gost94" and nid = 809 and normalized = "GOST94" and algType = "HASH" or - name = "gost94" and nid = 812 and normalized = "GOST94" and algType = "SYMMETRIC_ENCRYPTION" + name = "gost94" and nid = 812 and normalized = "GOST94" and algType = "HASH" or name = "gost89" and nid = 813 and normalized = "GOST89" and algType = "SYMMETRIC_ENCRYPTION" or @@ -1114,9 +1158,9 @@ predicate knownOpenSSLAlgorithm(string name, int nid, string normalized, string or name = "gost-mac-12" and nid = 976 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" or - name = "md_gost12_256" and nid = 982 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + name = "md_gost12_256" and nid = 982 and normalized = "GOST" and algType = "HASH" or - name = "md_gost12_512" and nid = 983 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + name = "md_gost12_512" and nid = 983 and normalized = "GOST" and algType = "HASH" or name = "id-tc26-signwithdigest-gost3410-2012-256" and nid = 985 and From bd07b8a4c7946107db067d2b79a748e1105227b8 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 11:34:26 -0400 Subject: [PATCH 050/122] Making getter flow through 'copy' more general (copy can appear in any part of the call name now. --- .../lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll index 30ffd1e1f3e8..cee5937d5516 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll @@ -31,7 +31,7 @@ class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall { // Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed // to be involved // NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup - this.getTarget().getName().toLowerCase().matches(["%_dup", "%_copy"]) and + this.getTarget().getName().toLowerCase().matches(["%_dup%", "%_copy%"]) and exists(Expr inArg, Type t | inArg = this.getAnArgument() and t = inArg.getUnspecifiedType().stripType() | From 451808616e8746bc31dfd8f5b294fe4c58f77079 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 11:35:16 -0400 Subject: [PATCH 051/122] Getting rid of commented out code. --- .../OpenSSL/EVPCipherAlgorithmSource.qll | 71 +------------------ 1 file changed, 1 insertion(+), 70 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll index 8aa4f88a81b8..4523bd860358 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll @@ -80,74 +80,5 @@ class CipherKnownAlgorithmLiteralAlgorithmInstance extends Crypto::CipherAlgorit override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } - override Crypto::TCipherType getCipherFamily() { - literalToCipherFamilyType(this, result) - } + override Crypto::TCipherType getCipherFamily() { literalToCipherFamilyType(this, result) } } -// override Crypto::TCipherType getCipherFamily() { -// if this.cipherNameMappingKnown(_, super.getAlgorithmName()) -// then this.cipherNameMappingKnown(result, super.getAlgorithmName()) -// else result instanceof Crypto::OtherCipherType -// } -// bindingset[name] -// private predicate cipherNameMappingKnown(Crypto::TCipherType type, string name) { -// name = "AES" and -// type instanceof Crypto::AES -// or -// name = "DES" and -// type instanceof Crypto::DES -// or -// name = "TripleDES" and -// type instanceof Crypto::TripleDES -// or -// name = "IDEA" and -// type instanceof Crypto::IDEA -// or -// name = "CAST5" and -// type instanceof Crypto::CAST5 -// or -// name = "ChaCha20" and -// type instanceof Crypto::ChaCha20 -// or -// name = "RC4" and -// type instanceof Crypto::RC4 -// or -// name = "RC5" and -// type instanceof Crypto::RC5 -// or -// name = "RSA" and -// type instanceof Crypto::RSA -// } -// private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { -// type instanceof Crypto::ECB and name = "ECB" -// or -// type instanceof Crypto::CBC and name = "CBC" -// or -// type instanceof Crypto::GCM and name = "GCM" -// or -// type instanceof Crypto::CTR and name = "CTR" -// or -// type instanceof Crypto::XTS and name = "XTS" -// or -// type instanceof Crypto::CCM and name = "CCM" -// or -// type instanceof Crypto::SIV and name = "SIV" -// or -// type instanceof Crypto::OCB and name = "OCB" -// } -// override Crypto::TBlockCipherModeOperationType getModeType() { -// if this.modeToNameMappingKnown(_, super.getMode()) -// then this.modeToNameMappingKnown(result, super.getMode()) -// else result instanceof Crypto::OtherMode -// } -// override string getRawModeAlgorithmName() { result = super.getMode() } -// override string getRawPaddingAlgorithmName() { result = super.getPadding() } -// bindingset[name] -// private predicate paddingToNameMappingKnown(Crypto::TPaddingType type, string name) { -// type instanceof Crypto::NoPadding and name = "NOPADDING" -// or -// type instanceof Crypto::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? -// or -// type instanceof Crypto::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% -// } -// } From 06720278220acccc4c230196a1932a869d2c89b8 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 11:46:26 -0400 Subject: [PATCH 052/122] Tracing new notion of known getters, which now includes direct getters for cipher and hash. Removed a redundant hash qll, and fixed misplacement of has type in model. --- .../OpenSSL/EVPCipherAlgorithmSource.qll | 47 ++++++++----- .../Quantum/OpenSSL/EVPCipherConsumers.qll | 24 +++---- .../OpenSSL/EVPDigestAlgorithmSource.qll | 67 ------------------- .../OpenSSL/EVPHashAlgorithmSource.qll | 48 +++++++------ .../Quantum/OpenSSL/EVPHashConsumers.qll | 6 +- .../OpenSSL/OpenSSLAlgorithmGetter.qll | 35 ++++++++-- .../codeql/cryptography/Model.qll | 1 + 7 files changed, 107 insertions(+), 121 deletions(-) delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll index 4523bd860358..2c1930698b12 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll @@ -3,7 +3,13 @@ import experimental.Quantum.Language import EVPCipherConsumers import OpenSSLAlgorithmGetter -predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { +/** + * Given a literal `e`, converts this to a cipher family type. + * The literal must be a known literal representing a cipher algorithm. + * If the literal does not represent any known cipher algorithm, + * this predicate will not hold (i.e., it will not bind an unknown to an unknown cipher type) + */ +predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { exists(string name, string algType | algType.toLowerCase().matches("%encryption") | resolveAlgorithmFromLiteral(e, name, algType) and ( @@ -52,25 +58,36 @@ predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { ) } -class CipherKnownAlgorithmLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof Literal +class KnownOpenSSLCipherConstantAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof KnownOpenSSLAlgorithmConstant { - OpenSSLAlgorithmGetterCall cipherGetterCall; - CipherKnownAlgorithmLiteralAlgorithmInstance() { - exists(DataFlow::Node src, DataFlow::Node sink | - sink = cipherGetterCall.getValueArgNode() and - src.asExpr() = this and - KnownAlgorithmLiteralToAlgorithmGetterFlow::flow(src, sink) and - // Not just any known value, but specifically a known cipher operation - exists(string algType | - resolveAlgorithmFromLiteral(src.asExpr(), _, algType) and - algType.toLowerCase().matches("%encryption") + OpenSSLAlgorithmGetterCall getterCall; + + KnownOpenSSLCipherConstantAlgorithmInstance() { + // Not just any known value, but specifically a known cipher operation + this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("%encryption") and + ( + // Two possibilities: + // 1) The source is a literal and flows to a getter, then we know we have an instance + // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that + // Possibility 1: + this instanceof Literal and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a CipherGetterCall + sink = getterCall.(OpenSSLAlgorithmGetterCall).getValueArgNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a getter + KnownOpenSSLAlgorithmToAlgorithmGetterFlow::flow(src, sink) ) + or + // Possibility 2: + this instanceof DirectGetterCall and getterCall = this ) } - Crypto::AlgorithmConsumer getConsumer() { - AlgGetterToAlgConsumerFlow::flow(cipherGetterCall.getResultNode(), DataFlow::exprNode(result)) - } + Crypto::AlgorithmConsumer getConsumer() { + AlgGetterToAlgConsumerFlow::flow(getterCall.getResultNode(), DataFlow::exprNode(result)) + } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() // TODO: provider defaults diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll index 8717a857385a..4f8a5ccd354c 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll @@ -2,24 +2,24 @@ import EVPCipherInitializer import EVPCipherOperation import EVPCipherAlgorithmSource - class EVP_Cipher_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVPCipherInitializerAlgorithmArgument -{ - override DataFlow::Node getInputNode() { result.asExpr() = this } +{ + override DataFlow::Node getInputNode() { result.asExpr() = this } - override Crypto::AlgorithmElement getAKnownAlgorithmSource() { - result.(CipherKnownAlgorithmLiteralAlgorithmInstance).getConsumer() = this - } + override Crypto::AlgorithmElement getAKnownAlgorithmSource() { + result.(KnownOpenSSLCipherConstantAlgorithmInstance).getConsumer() = this + } } + // //TODO: need a key consumer // class EVP_Initializer_Key_Consumer extends Crypto::KeyConsumer instanceof EVP_Cipher_Inititalizer isntanceof InitializerKeyArgument{ // } -class EVP_Cipher_Initializer_IV_Consumer extends Crypto::NonceArtifactConsumer instanceof EVPCipherInitializerIVArgument{ - override DataFlow::Node getInputNode() { result.asExpr() = this } +class EVP_Cipher_Initializer_IV_Consumer extends Crypto::NonceArtifactConsumer instanceof EVPCipherInitializerIVArgument +{ + override DataFlow::Node getInputNode() { result.asExpr() = this } } -class EVP_Cipher_Input_Consumer extends Crypto::CipherInputConsumer instanceof EVPCipherInputArgument{ - - override DataFlow::Node getInputNode() { result.asExpr() = this } +class EVP_Cipher_Input_Consumer extends Crypto::CipherInputConsumer instanceof EVPCipherInputArgument +{ + override DataFlow::Node getInputNode() { result.asExpr() = this } } - diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll deleted file mode 100644 index 7d132b22a3ac..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPDigestAlgorithmSource.qll +++ /dev/null @@ -1,67 +0,0 @@ -import cpp -import experimental.Quantum.Language -import EVPCipherConsumers -import OpenSSLAlgorithmGetter - -predicate literalToHashFamilyType(Literal e, Crypto::THashType type) { - exists(string name, string algType | algType.toLowerCase().matches("%hash") | - resolveAlgorithmFromLiteral(e, name, algType) and - ( - name.matches("BLAKE2B") and type instanceof Crypto::BLAKE2B - or - name.matches("BLAKE2S") and type instanceof Crypto::BLAKE2S - or - name.matches("RIPEMD160") and type instanceof Crypto::RIPEMD160 - or - name.matches("MD2") and type instanceof Crypto::MD2 - or - name.matches("MD4") and type instanceof Crypto::MD4 - or - name.matches("MD5") and type instanceof Crypto::MD5 - or - name.matches("POLY1305") and type instanceof Crypto::POLY1305 - or - name.matches(["SHA1", "SHA"]) and type instanceof Crypto::SHA1 - or - name.matches("SHA2") and type instanceof Crypto::SHA2 - or - name.matches("SHA3") and type instanceof Crypto::SHA3 - or - name.matches("SHAKE") and type instanceof Crypto::SHAKE - or - name.matches("SM3") and type instanceof Crypto::SM3 - or - name.matches("WHIRLPOOL") and type instanceof Crypto::WHIRLPOOL - // TODO: what about MD_GOST? - ) - ) -} - -class HashKnownAlgorithmLiteralAlgorithmInstance extends Crypto::HashAlgorithmInstance instanceof Literal -{ - OpenSSLAlgorithmGetterCall getterCall; - - HashKnownAlgorithmLiteralAlgorithmInstance() { - exists(DataFlow::Node src, DataFlow::Node sink | - sink = getterCall.getValueArgNode() and - src.asExpr() = this and - KnownAlgorithmLiteralToAlgorithmGetterFlow::flow(src, sink) and - // Not just any known value, but specifically a known cipher operation - exists(string algType | - resolveAlgorithmFromLiteral(src.asExpr(), _, algType) and - algType.toLowerCase().matches("hash") - ) - ) - } - - // TODO: should this not be part of the abstract algorithm definition? - Crypto::AlgorithmConsumer getConsumer() { - AlgGetterToAlgConsumerFlow::flow(getterCall.getResultNode(), DataFlow::exprNode(result)) - } - - override Crypto::THashType getHashFamily() { literalToHashFamilyType(this, result) } - - override int getHashSize() { none() } //TODO - - override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } -} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll index ad2af7e05bed..5fc61c7b6c57 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll @@ -1,6 +1,5 @@ import cpp import experimental.Quantum.Language -import EVPHashConsumers import OpenSSLAlgorithmGetter predicate literalToHashFamilyType(Literal e, Crypto::THashType type) { @@ -11,12 +10,16 @@ predicate literalToHashFamilyType(Literal e, Crypto::THashType type) { or name.matches("BLAKE2S") and type instanceof Crypto::BLAKE2S or + name.matches("GOST%") and type instanceof Crypto::GOSTHash + or name.matches("MD2") and type instanceof Crypto::MD2 or name.matches("MD4") and type instanceof Crypto::MD4 or name.matches("MD5") and type instanceof Crypto::MD5 or + name.matches("MDC2") and type instanceof Crypto::MDC2 + or name.matches("POLY1305") and type instanceof Crypto::POLY1305 or name.matches(["SHA", "SHA1"]) and type instanceof Crypto::SHA1 @@ -31,40 +34,45 @@ predicate literalToHashFamilyType(Literal e, Crypto::THashType type) { or name.matches("RIPEMD160") and type instanceof Crypto::RIPEMD160 or - //or - //TODO: need to handle MACs differently, including md_GOST94 - // name.matches("%GOST%") and type instanceof Crypto::GOST name.matches("WHIRLPOOL") and type instanceof Crypto::WHIRLPOOL ) ) } -class HashKnownAlgorithmLiteralAlgorithmInstance extends Crypto::HashAlgorithmInstance instanceof Literal +class KnownOpenSSLHashConstantAlgorithmInstance extends Crypto::HashAlgorithmInstance instanceof KnownOpenSSLAlgorithmConstant { - OpenSSLAlgorithmGetterCall cipherGetterCall; + OpenSSLAlgorithmGetterCall getterCall; - HashKnownAlgorithmLiteralAlgorithmInstance() { - exists(DataFlow::Node src, DataFlow::Node sink | - sink = cipherGetterCall.getValueArgNode() and - src.asExpr() = this and - KnownAlgorithmLiteralToAlgorithmGetterFlow::flow(src, sink) and - // Not just any known value, but specifically a known cipher operation - exists(string algType | - resolveAlgorithmFromLiteral(src.asExpr(), _, algType) and - algType.toLowerCase().matches("hash") + KnownOpenSSLHashConstantAlgorithmInstance() { + // Not just any known value, but specifically a known hash + this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("hash") and + ( + // Two possibilities: + // 1) The source is a literal and flows to a getter, then we know we have an instance + // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that + // Possibility 1: + this instanceof Literal and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a CipherGetterCall + sink = getterCall.(OpenSSLAlgorithmGetterCall).getValueArgNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a getter + KnownOpenSSLAlgorithmToAlgorithmGetterFlow::flow(src, sink) ) + or + // Possibility 2: + this instanceof DirectGetterCall and getterCall = this ) } Crypto::AlgorithmConsumer getConsumer() { - AlgGetterToAlgConsumerFlow::flow(cipherGetterCall.getResultNode(), DataFlow::exprNode(result)) + AlgGetterToAlgConsumerFlow::flow(getterCall.getResultNode(), DataFlow::exprNode(result)) } - override Crypto::THashType getHashFamily() { - literalToHashFamilyType(this, result) - } + override Crypto::THashType getHashFamily() { literalToHashFamilyType(this, result) } override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } - override int getHashSize() {none() }//TODO + override int getHashSize() { none() } //TODO } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll index 31bf00ef94a5..2edca9d5763b 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll @@ -6,7 +6,7 @@ class EVP_Digest_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsume override DataFlow::Node getInputNode() { result.asExpr() = this } override Crypto::AlgorithmElement getAKnownAlgorithmSource() { - result.(HashKnownAlgorithmLiteralAlgorithmInstance).getConsumer() = this + result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this } } @@ -14,7 +14,7 @@ class EVP_Q_Digest_Algorithm_Consumer extends Crypto::AlgorithmConsumer instance override DataFlow::Node getInputNode() { result.asExpr() = this } override Crypto::AlgorithmElement getAKnownAlgorithmSource() { - result.(HashKnownAlgorithmLiteralAlgorithmInstance).getConsumer() = this + result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this } } @@ -22,6 +22,6 @@ class EVP_Digest_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof override DataFlow::Node getInputNode() { result.asExpr() = this } override Crypto::AlgorithmElement getAKnownAlgorithmSource() { - result.(HashKnownAlgorithmLiteralAlgorithmInstance).getConsumer() = this + result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this } } \ No newline at end of file diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll index cee5937d5516..689f022b56dc 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll @@ -128,8 +128,10 @@ abstract class OpenSSLAlgorithmGetterCall extends Call { abstract Expr getResultExpr(); } -module KnownAlgorithmLiteralToAlgorithmGetterConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { resolveAlgorithmFromLiteral(source.asExpr(), _, _) } +module KnownOpenSSLAlgorithmToAlgorithmGetterConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof KnownOpenSSLAlgorithmConstant + } predicate isSink(DataFlow::Node sink) { exists(OpenSSLAlgorithmGetterCall c | c.getValueArgNode() = sink) @@ -151,8 +153,33 @@ module KnownAlgorithmLiteralToAlgorithmGetterConfig implements DataFlow::ConfigS } } -module KnownAlgorithmLiteralToAlgorithmGetterFlow = - DataFlow::Global; +module KnownOpenSSLAlgorithmToAlgorithmGetterFlow = + DataFlow::Global; + +/** + * Cases like EVP_MD5(), + * there is no input, rather it directly gets an algorithm + * and returns it. + */ +class DirectGetterCall extends OpenSSLAlgorithmGetterCall { + DataFlow::Node resultNode; + Expr resultExpr; + + DirectGetterCall() { + this instanceof KnownOpenSSLAlgorithmConstant and + this instanceof Call and + resultExpr = this and + resultNode.asExpr() = resultExpr + } + + override DataFlow::Node getValueArgNode() { none() } + + override DataFlow::Node getResultNode() { result = resultNode } + + override Expr getValueArgExpr() { none() } + + override Expr getResultExpr() { result = resultExpr } +} // https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html class EVPCipherGetterCall extends OpenSSLAlgorithmGetterCall { diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index bbe817e2110b..298b270e6b62 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -1042,6 +1042,7 @@ module CryptographyBase Input> { MD2() or MD4() or MD5() or + MDC2() or POLY1305() or SHA1() or SHA2() or From 73368ea59abd9be8bc4df7df8dfd853dfcbde40c Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 13:27:39 -0400 Subject: [PATCH 053/122] Adding hashes to openssl library import --- cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll index f942f4687da5..6c30bec16cdd 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll @@ -4,6 +4,10 @@ import semmle.code.cpp.dataflow.new.DataFlow module OpenSSLModel { import experimental.Quantum.Language import experimental.Quantum.OpenSSL.EVPCipherOperation + import experimental.Quantum.OpenSSL.EVPHashOperation + import experimental.Quantum.OpenSSL.EVPCipherAlgorithmSource + import experimental.Quantum.OpenSSL.EVPHashAlgorithmSource + // // TODO: trace CTX from init variants to the context arg of EVP update calls // //https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis From fe52351aed103e958312521da6a386858606dfd0 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 14:10:55 -0400 Subject: [PATCH 054/122] Stubbing out hash operation node, borrowing from cipher operaiton node --- .../codeql/cryptography/Model.qll | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 298b270e6b62..0e8a39596756 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -1031,8 +1031,29 @@ module CryptographyBase Input> { * This operation takes an input message of arbitrary content and length and produces a fixed-size * hash value as the output using a specified hashing algorithm. */ - abstract class HashOperationNode extends OperationNode, THashOperation { - abstract HashAlgorithmNode getAlgorithm(); + class HashOperationNode extends OperationNode, THashOperation { + HashAlgorithmInstance instance; + + HashOperationNode() { this = THashOperation(instance) } + + override string getInternalType() { result = "HashOperation" } + + override LocatableElement asElement() { result = instance } + + /** + * Gets the algorithm or unknown source nodes consumed as an algorithm associated with this operation. + */ + NodeBase getACipherAlgorithmOrUnknown() { + result = this.getAKnownCipherAlgorithm() or + result = this.asElement().(OperationElement).getAlgorithmConsumer().getAnUnknownSourceNode() + } + + /** + * Gets a known algorithm associated with this operation + */ + HashAlgorithmNode getAKnownCipherAlgorithm() { + result = this.asElement().(OperationElement).getAlgorithmConsumer().getAKnownSourceNode() + } } newtype THashType = From 036035b6a224d70e1105a96fe2c29270bf3b4fb3 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 15:04:19 -0400 Subject: [PATCH 055/122] Adding modeling for OpenSSL random number generation. --- .../experimental/Quantum/OpenSSL/OpenSSL.qll | 1 + .../experimental/Quantum/OpenSSL/Random.qll | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll index 6c30bec16cdd..9e48f8cbb934 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll @@ -7,6 +7,7 @@ module OpenSSLModel { import experimental.Quantum.OpenSSL.EVPHashOperation import experimental.Quantum.OpenSSL.EVPCipherAlgorithmSource import experimental.Quantum.OpenSSL.EVPHashAlgorithmSource + import experimental.Quantum.OpenSSL.Random // // TODO: trace CTX from init variants to the context arg of EVP update calls diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll new file mode 100644 index 000000000000..eceff8748743 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll @@ -0,0 +1,21 @@ +import cpp +private import experimental.Quantum.Language +private import codeql.cryptography.Model +private import LibraryDetector +private import semmle.code.cpp.dataflow.new.DataFlow + +class OpenSSLRandomNumberGeneratorInstance extends Crypto::RandomNumberGenerationInstance instanceof Call +{ + OpenSSLRandomNumberGeneratorInstance() { + this.(Call).getTarget().getName() in ["RAND_bytes", "RAND_pseudo_bytes"] and + isPossibleOpenSSLFunction(this.(Call).getTarget()) + } + + override Crypto::DataFlowNode getOutputNode() { + result.asDefiningArgument() = this.(Call).getArgument(0) + } + + override predicate flowsTo(Crypto::FlowAwareElement other) { + ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + } +} From c83cb533ce4dac9fcd5082bc82c562ccc8afc0d6 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 15:56:01 -0400 Subject: [PATCH 056/122] Adding an instantiation of the additional flow step class to automatically apply to generic dataflow. Flow step passthrough comes from the algorithm to getter flow passthroughs. --- cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll | 2 ++ .../Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll index 9e48f8cbb934..16b9e6485c96 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll @@ -8,6 +8,8 @@ module OpenSSLModel { import experimental.Quantum.OpenSSL.EVPCipherAlgorithmSource import experimental.Quantum.OpenSSL.EVPHashAlgorithmSource import experimental.Quantum.OpenSSL.Random + // Imports the additional algorithm flow step for OpenSSL + import experimental.Quantum.OpenSSL.OpenSSLAlgorithmGetter // // TODO: trace CTX from init variants to the context arg of EVP update calls diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll index 689f022b56dc..02d0d8339183 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll @@ -4,6 +4,14 @@ import LibraryDetector import OpenSSLKnownAlgorithmConstants import experimental.Quantum.Language +class OpenSSLAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep { + OpenSSLAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) } + + override DataFlow::Node getOutput() { + exists(AlgorithmPassthroughCall c | c.getInNode() = this and c.getOutNode() = result) + } +} + module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { exists(OpenSSLAlgorithmGetterCall c | c.getResultNode() = source) From f72efa638adc9a7c9c5c69acf90bd59daee88baf Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 10 Mar 2025 16:12:53 -0400 Subject: [PATCH 057/122] Uncommenting out generic dataflow --- cpp/ql/lib/experimental/Quantum/Language.qll | 48 ++++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index f0775df1b2e6..5ec4da8d19d6 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -22,30 +22,30 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node { } -// /** -// * Generic data source to node input configuration -// */ -// module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { -// predicate isSource(DataFlow::Node source) { -// source = any(Crypto::GenericDataSourceInstance 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 -// } -// } +/** + * Generic data source to node input configuration + */ +module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::GenericDataSourceInstance 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 + } +} From ae574f7cf2b67a981e7052851d23a85a601a421a Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 13:35:02 -0400 Subject: [PATCH 058/122] Resolving hash and cipher types for openssl not using literals but KnownOpenSSLAlgorithmConstant. --- .../Quantum/OpenSSL/EVPCipherAlgorithmSource.qll | 14 +++++++++----- .../Quantum/OpenSSL/EVPHashAlgorithmSource.qll | 11 +++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll index 2c1930698b12..8718fb5b44db 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll @@ -9,9 +9,9 @@ import OpenSSLAlgorithmGetter * If the literal does not represent any known cipher algorithm, * this predicate will not hold (i.e., it will not bind an unknown to an unknown cipher type) */ -predicate literalToCipherFamilyType(Literal e, Crypto::TCipherType type) { - exists(string name, string algType | algType.toLowerCase().matches("%encryption") | - resolveAlgorithmFromLiteral(e, name, algType) and +predicate knownOpenSSLConstantToCipherFamilyType(KnownOpenSSLAlgorithmConstant e, Crypto::TCipherType type) { + exists(string name | e.getAlgType().toLowerCase().matches("%encryption") | + name = e.getNormalizedName() and ( name.matches("AES%") and type instanceof Crypto::AES or @@ -97,5 +97,9 @@ class KnownOpenSSLCipherConstantAlgorithmInstance extends Crypto::CipherAlgorith override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } - override Crypto::TCipherType getCipherFamily() { literalToCipherFamilyType(this, result) } -} + override Crypto::TCipherType getCipherFamily() { + knownOpenSSLConstantToCipherFamilyType(this, result) + or + not knownOpenSSLConstantToCipherFamilyType(this, _) and result = Crypto::OtherCipherType() + } +} \ No newline at end of file diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll index 5fc61c7b6c57..d89eee2590d1 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll @@ -2,9 +2,9 @@ import cpp import experimental.Quantum.Language import OpenSSLAlgorithmGetter -predicate literalToHashFamilyType(Literal e, Crypto::THashType type) { - exists(string name, string algType | algType.toLowerCase().matches("hash") | - resolveAlgorithmFromLiteral(e, name, algType) and +predicate knownOpenSSLConstantToHashFamilyType(KnownOpenSSLAlgorithmConstant e, Crypto::THashType type) { + exists(string name | e.getAlgType().toLowerCase().matches("hash") | + name = e.getNormalizedName() and ( name.matches("BLAKE2B") and type instanceof Crypto::BLAKE2B or @@ -70,7 +70,10 @@ class KnownOpenSSLHashConstantAlgorithmInstance extends Crypto::HashAlgorithmIns AlgGetterToAlgConsumerFlow::flow(getterCall.getResultNode(), DataFlow::exprNode(result)) } - override Crypto::THashType getHashFamily() { literalToHashFamilyType(this, result) } + override Crypto::THashType getHashFamily() { + knownOpenSSLConstantToHashFamilyType(this, result) or + not knownOpenSSLConstantToHashFamilyType(this, _) and result = Crypto::OtherHashType() + } override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } From f69b0578936314eed92339e1d43ba753b029cb45 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 13:35:44 -0400 Subject: [PATCH 059/122] Updating OpenSSLKnownAlgorithmConstants.qll (a few bugs) and also enforcing that known key sizes are on the normalized name (need to re-visit for GOST). --- .../OpenSSLKnownAlgorithmConstants.qll | 64 +++++++++---------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll index 063a7084d3db..0d328e287f6a 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll @@ -302,85 +302,85 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aria-128-cbc" and nid = 1066 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aria-128-cbc" and nid = 1066 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-cbc" and nid = 1066 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-cfb" and nid = 1067 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aria-128-cfb" and nid = 1067 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-cfb" and nid = 1067 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-ctr" and nid = 1069 and normalized = "CTR" and algType = "BLOCK_MODE" or - name = "aria-128-ctr" and nid = 1069 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-ctr" and nid = 1069 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-ecb" and nid = 1065 and normalized = "ECB" and algType = "BLOCK_MODE" or - name = "aria-128-ecb" and nid = 1065 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-ecb" and nid = 1065 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-ofb" and nid = 1068 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "aria-128-ofb" and nid = 1068 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-ofb" and nid = 1068 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aria-128-cfb1" and nid = 1080 and normalized = "CFB" and algType = "BLOCK_MODE" + name = "aria-128-cfb1" and nid = 1080 and normalized = "CFB1" and algType = "BLOCK_MODE" or - name = "aria-128-cfb1" and nid = 1080 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-cfb1" and nid = 1080 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aria-128-cfb8" and nid = 1083 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-cfb8" and nid = 1083 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-cfb8" and nid = 1083 and normalized = "CFB8" and algType = "BLOCK_MODE" or name = "aria-192-cbc" and nid = 1071 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aria-192-cbc" and nid = 1071 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-cbc" and nid = 1071 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-cfb" and nid = 1072 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aria-192-cfb" and nid = 1072 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-cfb" and nid = 1072 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-ctr" and nid = 1074 and normalized = "CTR" and algType = "BLOCK_MODE" or - name = "aria-192-ctr" and nid = 1074 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-ctr" and nid = 1074 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-ecb" and nid = 1070 and normalized = "ECB" and algType = "BLOCK_MODE" or - name = "aria-192-ecb" and nid = 1070 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-ecb" and nid = 1070 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-ofb" and nid = 1073 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "aria-192-ofb" and nid = 1073 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-ofb" and nid = 1073 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aria-192-cfb1" and nid = 1081 and normalized = "CFB" and algType = "BLOCK_MODE" + name = "aria-192-cfb1" and nid = 1081 and normalized = "CFB1" and algType = "BLOCK_MODE" or - name = "aria-192-cfb1" and nid = 1081 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-cfb1" and nid = 1081 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aria-192-cfb8" and nid = 1084 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-cfb8" and nid = 1084 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-cfb8" and nid = 1084 and normalized = "CFB8" and algType = "BLOCK_MODE" or name = "aria-256-cbc" and nid = 1076 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aria-256-cbc" and nid = 1076 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-cbc" and nid = 1076 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-cfb" and nid = 1077 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aria-256-cfb" and nid = 1077 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-cfb" and nid = 1077 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-ctr" and nid = 1079 and normalized = "CTR" and algType = "BLOCK_MODE" or - name = "aria-256-ctr" and nid = 1079 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-ctr" and nid = 1079 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-ecb" and nid = 1075 and normalized = "ECB" and algType = "BLOCK_MODE" or - name = "aria-256-ecb" and nid = 1075 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-ecb" and nid = 1075 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-ofb" and nid = 1078 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "aria-256-ofb" and nid = 1078 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-ofb" and nid = 1078 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aria-256-cfb1" and nid = 1082 and normalized = "CFB" and algType = "BLOCK_MODE" + name = "aria-256-cfb1" and nid = 1082 and normalized = "CFB1" and algType = "BLOCK_MODE" or - name = "aria-256-cfb1" and nid = 1082 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-cfb1" and nid = 1082 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aria-256-cfb8" and nid = 1085 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-cfb8" and nid = 1085 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-cfb8" and nid = 1085 and normalized = "CFB8" and algType = "BLOCK_MODE" or @@ -1386,27 +1386,27 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aria-128-ccm" and nid = 1120 and normalized = "CCM" and algType = "BLOCK_MODE" or - name = "aria-128-ccm" and nid = 1120 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-ccm" and nid = 1120 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-ccm" and nid = 1121 and normalized = "CCM" and algType = "BLOCK_MODE" or - name = "aria-192-ccm" and nid = 1121 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-ccm" and nid = 1121 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-ccm" and nid = 1122 and normalized = "CCM" and algType = "BLOCK_MODE" or - name = "aria-256-ccm" and nid = 1122 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-ccm" and nid = 1122 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-gcm" and nid = 1123 and normalized = "GCM" and algType = "BLOCK_MODE" or - name = "aria-128-gcm" and nid = 1123 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-gcm" and nid = 1123 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-gcm" and nid = 1124 and normalized = "GCM" and algType = "BLOCK_MODE" or - name = "aria-192-gcm" and nid = 1124 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-gcm" and nid = 1124 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-gcm" and nid = 1125 and normalized = "GCM" and algType = "BLOCK_MODE" or - name = "aria-256-gcm" and nid = 1125 and normalized = "ARIA" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-gcm" and nid = 1125 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" or name = "sm4-cfb1" and nid = 1136 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" or @@ -2678,14 +2678,10 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "rsaes-oaep" and nid = 919 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsaes-oaep" and nid = 919 and normalized = "AES" and algType = "SYMMETRIC_ENCRYPTION" - or name = "rsaes-oaep" and nid = 919 and normalized = "OAEP" and algType = "ASYMMETRIC_PADDING" or name = "rsaesoaep" and nid = 919 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsaesoaep" and nid = 919 and normalized = "AES" and algType = "SYMMETRIC_ENCRYPTION" - or name = "rsaesoaep" and nid = 919 and normalized = "OAEP" and algType = "ASYMMETRIC_PADDING" or name = "rsaoaepencryptionset" and From 085e8d40fd0c2d2ccfd801be695b66ed627bfd68 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 14:06:36 -0400 Subject: [PATCH 060/122] Hash nodes have instances. --- shared/cryptography/codeql/cryptography/Model.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 0e8a39596756..4ad83b12210f 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -1078,6 +1078,8 @@ module CryptographyBase Input> { * A hashing algorithm that transforms variable-length input into a fixed-size hash value. */ abstract class HashAlgorithmNode extends AlgorithmNode, THashAlgorithm { + HashAlgorithmInstance instance; + HashAlgorithmNode() { this = THashAlgorithm(instance) } override string getInternalType() { result = "HashAlgorithm" } final predicate hashTypeToNameMapping(THashType type, string name) { From 44b1e921d6fd0609e65fc7874b21a09c3fdec2f3 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 14:10:04 -0400 Subject: [PATCH 061/122] commenting out hash size for now, TODO --- .../lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll | 2 +- shared/cryptography/codeql/cryptography/Model.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll index d89eee2590d1..d269658c8aab 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll @@ -77,5 +77,5 @@ class KnownOpenSSLHashConstantAlgorithmInstance extends Crypto::HashAlgorithmIns override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } - override int getHashSize() { none() } //TODO + // override int getHashSize() { none() } //TODO } diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 4ad83b12210f..74a9b5d02a76 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -369,7 +369,7 @@ module CryptographyBase Input> { */ abstract THashType getHashFamily(); - abstract int getHashSize(); + // abstract int getHashSize(); } abstract class KeyDerivationOperationInstance extends KnownElement { } From 7757279908a1e9049d20e67b4651d7018d7ed285 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 14:46:36 -0400 Subject: [PATCH 062/122] Adding a KDF algorithm getter. --- .../OpenSSL/OpenSSLAlgorithmGetter.qll | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll index 02d0d8339183..364bb3ab0b82 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll @@ -255,6 +255,30 @@ class EVPDigestGetterCall extends OpenSSLAlgorithmGetterCall { override Expr getResultExpr() { result = resultExpr } } + +class EVPKDFFetch extends OpenSSLAlgorithmGetterCall { + DataFlow::Node valueArgNode; + DataFlow::Node resultNode; + Expr valueArgExpr; + Expr resultExpr; + + EVPKDFFetch() { + resultExpr = this and + resultNode.asExpr() = this and + isPossibleOpenSSLFunction(this.getTarget()) and + this.getTarget().getName() in ["EVP_KDF_fetch"] and + valueArgExpr = this.getArgument(1) and + valueArgNode.asExpr() = valueArgExpr + } + + override DataFlow::Node getValueArgNode() { result = valueArgNode } + + override DataFlow::Node getResultNode() { result = resultNode } + + override Expr getValueArgExpr() { result = valueArgExpr } + + override Expr getResultExpr() { result = resultExpr } +} // /** // * Predicates/classes for identifying algorithm sinks. // * An Algorithm Sink is a function that takes an algorithm as an argument. From d988afd4a4b3cd177dab2dd48f933c14c04061ee Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 14:47:10 -0400 Subject: [PATCH 063/122] Adding an EVP_AASYM_CIPHER_fetch getter. --- .../OpenSSL/OpenSSLAlgorithmGetter.qll | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll index 364bb3ab0b82..3e061eeff88f 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll @@ -224,6 +224,30 @@ class EVPCipherGetterCall extends OpenSSLAlgorithmGetterCall { override Expr getResultExpr() { result = resultExpr } } +class EVPAsymCipherGetterCall extends OpenSSLAlgorithmGetterCall { + DataFlow::Node valueArgNode; + DataFlow::Node resultNode; + Expr valueArgExpr; + Expr resultExpr; + + EVPAsymCipherGetterCall() { + isPossibleOpenSSLFunction(this.getTarget()) and + resultExpr = this and + resultNode.asExpr() = this and + this.getTarget().getName() = "EVP_ASYM_CIPHER_fetch" and + valueArgExpr = this.getArgument(1) and + valueArgNode.asExpr() = valueArgExpr + } + + override DataFlow::Node getValueArgNode() { result = valueArgNode } + + override DataFlow::Node getResultNode() { result = resultNode } + + override Expr getValueArgExpr() { result = valueArgExpr } + + override Expr getResultExpr() { result = resultExpr } +} + class EVPDigestGetterCall extends OpenSSLAlgorithmGetterCall { DataFlow::Node valueArgNode; DataFlow::Node resultNode; From a9458ba762875bacfc985242883d90d894095eed Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 14:48:38 -0400 Subject: [PATCH 064/122] Formatting, removing dead comments, --- .../Quantum/OpenSSL/EVPCipherOperation.qll | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll index 4913d2763e96..fd95bb718056 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll @@ -46,14 +46,12 @@ abstract class EVP_Final_Call extends EVP_Cipher_Operation { } class EVP_Cipher_Call extends EVP_Cipher_Operation { - // TODO/QUESTION: what is the better way to do this? EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } override Expr getInputArg() { result = this.(Call).getArgument(2) } } class EVP_Encrypt_Decrypt_or_Cipher_Update_Call extends EVP_Update_Call { - // TODO/QUESTION: what is the better way to do this? EVP_Encrypt_Decrypt_or_Cipher_Update_Call() { this.(Call).getTarget().getName() in [ "EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate" @@ -64,10 +62,8 @@ class EVP_Encrypt_Decrypt_or_Cipher_Update_Call extends EVP_Update_Call { } class EVP_Encrypt_Decrypt_or_Cipher_Final_Call extends EVP_Final_Call { - // TODO/QUESTION: what is the better way to do this? EVP_Encrypt_Decrypt_or_Cipher_Final_Call() { - this.(Call).getTarget().getName() in - [ + this.(Call).getTarget().getName() in [ "EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal", "EVP_DecryptFinal", "EVP_CipherFinal" ] @@ -77,11 +73,9 @@ class EVP_Encrypt_Decrypt_or_Cipher_Final_Call extends EVP_Final_Call { class EVPCipherOutput extends CipherOutputArtifact { EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) } - override DataFlow::Node getOutputNode(){ - result.asExpr() = this - } + override DataFlow::Node getOutputNode() { result.asExpr() = this } } class EVPCipherInputArgument extends Expr { EVPCipherInputArgument() { exists(EVP_Cipher_Operation op | op.getInputArg() = this) } -} \ No newline at end of file +} From c98e6d7c56011e429c2f945930a41fa94b89192e Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 14:49:07 -0400 Subject: [PATCH 065/122] Adding a stub EVP_CIpher_Operation for EVP_PKEY, this probably should be made into it's own class, hence it is a stub with comments. --- .../Quantum/OpenSSL/EVPCipherOperation.qll | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll index fd95bb718056..f426eb3f92dd 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll @@ -70,6 +70,17 @@ class EVP_Encrypt_Decrypt_or_Cipher_Final_Call extends EVP_Final_Call { } } +class EVP_PKEY_Operation extends EVP_Cipher_Operation { + EVP_PKEY_Operation() { + this.(Call).getTarget().getName() in ["EVP_PKEY_decrypt", "EVP_PKEY_encrypt"] + } + + override Expr getInputArg() { result = this.(Call).getArgument(3) } + // TODO: how PKEY is initialized is different that symmetric cipher + // Consider making an entirely new class for this and specializing + // the get init call +} + class EVPCipherOutput extends CipherOutputArtifact { EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) } From 71eae39feb73e2d7e680b04d2c6536831864ae02 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 15:19:42 -0400 Subject: [PATCH 066/122] Adding missing block modes. --- shared/cryptography/codeql/cryptography/Model.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 74a9b5d02a76..f5f57d245ba1 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -761,12 +761,14 @@ module CryptographyBase Input> { newtype TBlockCipherModeOperationType = ECB() or // Not secure, widely used CBC() or // Vulnerable to padding oracle attacks + CFB() or GCM() or // Widely used AEAD mode (TLS 1.3, SSH, IPsec) CTR() or // Fast stream-like encryption (SSH, disk encryption) XTS() or // Standard for full-disk encryption (BitLocker, LUKS, FileVault) CCM() or // Used in lightweight cryptography (IoT, WPA2) SIV() or // Misuse-resistant encryption, used in secure storage OCB() or // Efficient AEAD mode + OFB() or OtherMode() class ModeOfOperationAlgorithmNode extends AlgorithmNode, TModeOfOperationAlgorithm { @@ -807,6 +809,10 @@ module CryptographyBase Input> { or type instanceof OCB and name = "OCB" or + type instanceof CFB and name = "CFB" + or + type instanceof OFB and name = "OFB" + or type instanceof OtherMode and name = this.getRawAlgorithmName() } From 8a7671dc2a5bdc4bdfa87a0b4a24e617e85ddb25 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 11 Mar 2025 15:20:05 -0400 Subject: [PATCH 067/122] Adding block mode models to openssl. --- .../OpenSSL/EVPCipherAlgorithmSource.qll | 94 +++++++++++++++++-- 1 file changed, 85 insertions(+), 9 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll index 8718fb5b44db..65231e32cd03 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll @@ -4,12 +4,12 @@ import EVPCipherConsumers import OpenSSLAlgorithmGetter /** - * Given a literal `e`, converts this to a cipher family type. - * The literal must be a known literal representing a cipher algorithm. - * If the literal does not represent any known cipher algorithm, - * this predicate will not hold (i.e., it will not bind an unknown to an unknown cipher type) + * Given a `KnownOpenSSLAlgorithmConstant`, converts this to a cipher family type. + * Does not bind if there is know mapping (no mapping to 'unknown' or 'other'). */ -predicate knownOpenSSLConstantToCipherFamilyType(KnownOpenSSLAlgorithmConstant e, Crypto::TCipherType type) { +predicate knownOpenSSLConstantToCipherFamilyType( + KnownOpenSSLAlgorithmConstant e, Crypto::TCipherType type +) { exists(string name | e.getAlgType().toLowerCase().matches("%encryption") | name = e.getNormalizedName() and ( @@ -90,16 +90,92 @@ class KnownOpenSSLCipherConstantAlgorithmInstance extends Crypto::CipherAlgorith } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { - none() // TODO: provider defaults + // if there is a block mode associated with the same element, then that's the block mode + // note, if none are associated, we may need to parse if the cipher is a block cipher + // to determine if this is an unknown vs not relevant. + result = this } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } - override Crypto::TCipherType getCipherFamily() { - knownOpenSSLConstantToCipherFamilyType(this, result) + override Crypto::TCipherType getCipherFamily() { + knownOpenSSLConstantToCipherFamilyType(this, result) or not knownOpenSSLConstantToCipherFamilyType(this, _) and result = Crypto::OtherCipherType() } -} \ No newline at end of file +} + +/** + * Given a `KnownOpenSSLAlgorithmConstant`, converts this to a cipher family type. + * Does not bind if there is know mapping (no mapping to 'unknown' or 'other'). + */ +predicate knownOpenSSLConstantToBlockModeFamilyType( + KnownOpenSSLAlgorithmConstant e, Crypto::TBlockCipherModeOperationType type +) { + exists(string name | e.getAlgType().toLowerCase().matches("block_mode") | + name = e.getNormalizedName() and + ( + name.matches("CBC") and type instanceof Crypto::CBC + or + name.matches("CFB%") and type instanceof Crypto::CFB + or + name.matches("CTR") and type instanceof Crypto::CTR + or + name.matches("GCM") and type instanceof Crypto::GCM + or + name.matches("OFB") and type instanceof Crypto::OFB + or + name.matches("XTS") and type instanceof Crypto::XTS + or + name.matches("CCM") and type instanceof Crypto::CCM + or + name.matches("GCM") and type instanceof Crypto::GCM + or + name.matches("CCM") and type instanceof Crypto::CCM + or + name.matches("ECB") and type instanceof Crypto::ECB + ) + ) +} + +class KnownOpenSSLBlockModeConstantAlgorithmInstance extends Crypto::ModeOfOperationAlgorithmInstance instanceof KnownOpenSSLAlgorithmConstant +{ + OpenSSLAlgorithmGetterCall getterCall; + + KnownOpenSSLBlockModeConstantAlgorithmInstance() { + // Not just any known value, but specifically a known cipher operation + this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("block_mode") and + ( + // Two possibilities: + // 1) The source is a literal and flows to a getter, then we know we have an instance + // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that + // Possibility 1: + this instanceof Literal and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a CipherGetterCall + sink = getterCall.(OpenSSLAlgorithmGetterCall).getValueArgNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a getter + KnownOpenSSLAlgorithmToAlgorithmGetterFlow::flow(src, sink) + ) + or + // Possibility 2: + this instanceof DirectGetterCall and getterCall = this + ) + } + + override Crypto::TBlockCipherModeOperationType getModeType() { + knownOpenSSLConstantToBlockModeFamilyType(this, result) + or + not knownOpenSSLConstantToBlockModeFamilyType(this, _) and result = Crypto::OtherMode() + } + + // NOTE: I'm not going to attempt to parse out the mode specific part, so returning + // the same as the raw name for now. + override string getRawModeAlgorithmName() { result = this.(Literal).getValue().toString() } + + override string getRawAlgorithmName() { result = this.getRawModeAlgorithmName() } +} From 95607c5f31c9ee72e9b556bce21af8d92c5281de Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Tue, 18 Mar 2025 22:05:00 +0100 Subject: [PATCH 068/122] Refactor instances and consumers + add JCA hashes --- cpp/ql/lib/experimental/Quantum/Language.qll | 30 +- .../Quantum/OpenSSL/EVPCipherConsumers.qll | 2 +- .../Quantum/OpenSSL/EVPHashConsumers.qll | 35 +- java/ql/lib/experimental/Quantum/JCA.qll | 121 +++++- java/ql/lib/experimental/Quantum/Language.qll | 20 +- .../InsecureOrUnknownNonceAtOperation.ql | 9 +- .../Quantum/PossibleReusedNonce.ql | 6 +- .../ql/src/experimental/Quantum/TestCipher.ql | 3 +- java/ql/src/experimental/Quantum/TestHash.ql | 9 + .../codeql/cryptography/Model.qll | 391 +++++++++++++----- 10 files changed, 467 insertions(+), 159 deletions(-) create mode 100644 java/ql/src/experimental/Quantum/TestHash.ql diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index 5ec4da8d19d6..dbcb2004b83b 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -3,10 +3,11 @@ import semmle.code.cpp.ir.IR import semmle.code.cpp.security.FlowSources as FlowSources private import cpp as Lang - module CryptoInput implements InputSig { class DataFlowNode = DataFlow::Node; + class LocatableElement = Lang::Locatable; + class UnknownLocation = Lang::UnknownDefaultLocation; } @@ -21,7 +22,6 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node { final DataFlow::Node getInput() { result = this } } - /** * Generic data source to node input configuration */ @@ -47,61 +47,44 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { } } - - // // // TODO: I think this will be inefficient, no? // // class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { -// // override DataFlow::Node getOutputNode() { -// // result.asExpr() = this +// // override DataFlow::Node getOutputNode() { +// // result.asExpr() = this // // } - // // override predicate flowsTo(Crypto::FlowAwareElement other) { // // // TODO: separate config to avoid blowing up data-flow analysis // // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) // // } - // // override string getAdditionalDescription() { result = this.toString() } // // } - // /** // * Definitions of various generic data sources // */ // // final class DefaultFlowSource = SourceNode; - // // final class DefaultRemoteFlowSource = RemoteFlowSource; - // // class GenericLocalDataSource extends Crypto::GenericLocalDataSource { // // GenericLocalDataSource() { // // any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this // // } - // // override DataFlow::Node getOutputNode() { result.asExpr() = this } - // // override predicate flowsTo(Crypto::FlowAwareElement other) { // // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) // // } - // // override string getAdditionalDescription() { result = this.toString() } // // } - // // class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { // // GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this } - // // override DataFlow::Node getOutputNode() { result.asExpr() = this } - // // override predicate flowsTo(Crypto::FlowAwareElement other) { // // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) // // } - // // override string getAdditionalDescription() { result = this.toString() } // // } - - // module GenericDataSourceUniversalFlow = DataFlow::Global; - module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { - source = any(Crypto::ArtifactElement artifact).getOutputNode() + source = any(Crypto::ArtifactInstance artifact).getOutputNode() } predicate isSink(DataFlow::Node sink) { @@ -120,12 +103,13 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { node1.(AdditionalFlowInputStep).getOutput() = node2 } } + module ArtifactUniversalFlow = DataFlow::Global; + abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance { override predicate flowsTo(Crypto::FlowAwareElement other) { ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } } - import OpenSSL.OpenSSL diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll index 4f8a5ccd354c..e2f5bc0e8380 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll @@ -6,7 +6,7 @@ class EVP_Cipher_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsume { override DataFlow::Node getInputNode() { result.asExpr() = this } - override Crypto::AlgorithmElement getAKnownAlgorithmSource() { + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result.(KnownOpenSSLCipherConstantAlgorithmInstance).getConsumer() = this } } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll index 2edca9d5763b..82bf331546f7 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll @@ -2,26 +2,29 @@ import EVPHashInitializer import EVPHashOperation import EVPHashAlgorithmSource -class EVP_Digest_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVPDigestInitializerAlgorithmArgument{ - override DataFlow::Node getInputNode() { result.asExpr() = this } +class EVP_Digest_Initializer_Algorithm_Consumer extends Crypto::AlgorithmValueConsumer instanceof EVPDigestInitializerAlgorithmArgument +{ + override DataFlow::Node getInputNode() { result.asExpr() = this } - override Crypto::AlgorithmElement getAKnownAlgorithmSource() { - result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this - } + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this + } } -class EVP_Q_Digest_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVP_Q_Digest_Algorithm_Argument{ - override DataFlow::Node getInputNode() { result.asExpr() = this } +class EVP_Q_Digest_Algorithm_Consumer extends Crypto::AlgorithmValueConsumer instanceof EVP_Q_Digest_Algorithm_Argument +{ + override DataFlow::Node getInputNode() { result.asExpr() = this } - override Crypto::AlgorithmElement getAKnownAlgorithmSource() { - result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this - } + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this + } } -class EVP_Digest_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVP_Digest_Algorithm_Argument{ - override DataFlow::Node getInputNode() { result.asExpr() = this } +class EVP_Digest_Algorithm_Consumer extends Crypto::AlgorithmValueConsumer instanceof EVP_Digest_Algorithm_Argument +{ + override DataFlow::Node getInputNode() { result.asExpr() = this } - override Crypto::AlgorithmElement getAKnownAlgorithmSource() { - result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this - } -} \ No newline at end of file + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this + } +} diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index b2698cabee24..336f7fe5cc55 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -205,7 +205,7 @@ module JCAModel { * * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`. */ - class CipherGetInstanceAlgorithmArg extends Crypto::AlgorithmConsumer instanceof Expr { + class CipherGetInstanceAlgorithmArg extends Crypto::AlgorithmValueConsumer instanceof Expr { CipherGetInstanceCall call; CipherGetInstanceAlgorithmArg() { this = call.getAlgorithmArg() } @@ -218,7 +218,7 @@ module JCAModel { value = result.getValue() } - override Crypto::AlgorithmElement getAKnownAlgorithmSource() { + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this } } @@ -354,15 +354,17 @@ module JCAModel { override Crypto::CipherOperationSubtype getCipherOperationSubtype() { result = mode } - override Crypto::NonceArtifactConsumer getNonceConsumer() { - result = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg() + override DataFlow::Node getNonceConsumer() { + result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg() } - override Crypto::CipherInputConsumer getInputConsumer() { - result = doFinalize.getMessageArg().asExpr() + override DataFlow::Node getInputConsumer() { result = doFinalize.getMessageArg() } + + override DataFlow::Node getKeyConsumer() { + result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getKeyArg() } - override Crypto::AlgorithmConsumer getAlgorithmConsumer() { result = consumer } + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = consumer } override Crypto::CipherOutputArtifactInstance getOutputArtifact() { result = doFinalize.getOutput() @@ -493,27 +495,114 @@ module JCAModel { } } - class CipherInitCallNonceArgConsumer extends Crypto::NonceArtifactConsumer instanceof Expr { - CipherInitCallNonceArgConsumer() { this = any(CipherInitCall call).getNonceArg() } + class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer { + CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() } override DataFlow::Node getInputNode() { result.asExpr() = this } } - class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer { - CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() } + class CipherOperationCallOutput extends CipherOutputArtifact { + CipherOperationCallOutput() { this = any(CipherOperationCall call).getOutput() } - override DataFlow::Node getInputNode() { result.asExpr() = this } + override DataFlow::Node getOutputNode() { result.asExpr() = this } + } + + bindingset[hash] + predicate hash_names(string hash) { + hash.toUpperCase() + .matches([ + "SHA-1", "SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256", "SHA3-384", + "SHA3-512", "BLAKE2b", "BLAKE2s" + ].toUpperCase()) + } + + // flow config from a known hash algorithm literal to MessageDigest.getInstance + module KnownHashAlgorithmLiteralToMessageDigestConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { hash_names(src.asExpr().(StringLiteral).getValue()) } + + predicate isSink(DataFlow::Node sink) { + exists(MessageDigestGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) + } + } + + module KnownHashAlgorithmLiteralToMessageDigestFlow = + DataFlow::Global; + + class KnownHashAlgorithm extends Crypto::HashAlgorithmInstance instanceof StringLiteral { + MessageDigestAlgorithmValueConsumer consumer; + + KnownHashAlgorithm() { + hash_names(this.getValue()) and + KnownHashAlgorithmLiteralToMessageDigestFlow::flow(DataFlow::exprNode(this), + consumer.getInputNode()) + } + + MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer } + + override string getRawAlgorithmName() { result = this.(StringLiteral).getValue() } + + override Crypto::THashType getHashFamily() { + result = Crypto::OtherHashType() // TODO + } } - class CipherMessageInputConsumer extends Crypto::CipherInputConsumer { - CipherMessageInputConsumer() { this = any(CipherOperationCall call).getMessageArg().asExpr() } + class MessageDigestAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + MessageDigestGetInstanceCall call; + + MessageDigestAlgorithmValueConsumer() { this = call.getAlgorithmArg() } override DataFlow::Node getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(KnownHashAlgorithm l | l.getConsumer() = this and result = l) + } } - class CipherOperationCallOutput extends CipherOutputArtifact { - CipherOperationCallOutput() { this = any(CipherOperationCall call).getOutput() } + class MessageDigestGetInstanceCall extends MethodCall { + MessageDigestGetInstanceCall() { + this.getCallee().hasQualifiedName("java.security", "MessageDigest", "getInstance") + } + + Expr getAlgorithmArg() { result = this.getArgument(0) } + + DigestHashOperation getDigestCall() { + DigestGetInstanceToDigestFlow::flow(DataFlow::exprNode(this), + DataFlow::exprNode(result.(DigestCall).getQualifier())) + } + } + + class DigestCall extends MethodCall { + DigestCall() { this.getCallee().hasQualifiedName("java.security", "MessageDigest", "digest") } + + Expr getDigestArtifactOutput() { result = this } + } + + // flow config from MessageDigest.getInstance to MessageDigest.digest + module DigestGetInstanceToDigestConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MessageDigestGetInstanceCall } + + predicate isSink(DataFlow::Node sink) { + exists(DigestCall c | c.getQualifier() = sink.asExpr()) + } + } + + module DigestGetInstanceToDigestFlow = DataFlow::Global; + + class DigestArtifact extends DigestArtifactInstance { + DigestArtifact() { this = any(DigestCall call).getDigestArtifactOutput() } override DataFlow::Node getOutputNode() { result.asExpr() = this } } + + class DigestHashOperation extends Crypto::HashOperationInstance instanceof DigestCall { + override Crypto::DigestArtifactInstance getDigestArtifact() { + result = this.(DigestCall).getDigestArtifactOutput() + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + exists(MessageDigestGetInstanceCall call | + call.getDigestCall() = this and result = call.getAlgorithmArg() + ) + } + } } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 0e875295bd8a..5666a0c42bb5 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -25,6 +25,11 @@ module CryptoInput implements InputSig { class LocatableElement = Language::Element; class UnknownLocation = UnknownDefaultLocation; + + LocatableElement dfn_to_element(DataFlow::Node node) { + result = node.asExpr() or + result = node.asParameter() + } } /** @@ -100,6 +105,17 @@ class InsecureRandomnessInstance extends RandomnessInstance { InsecureRandomnessInstance() { exists(InsecureRandomnessSource node | this = node.asExpr()) } } +/** + * Output artifact flow logic + */ +abstract class DigestArtifactInstance extends Crypto::DigestArtifactInstance { + override predicate flowsTo(Crypto::FlowAwareElement other) { + ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + } + + override predicate isConsumerArtifact() { none() } +} + /** * Artifact output to node input configuration */ @@ -115,6 +131,8 @@ abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance override predicate flowsTo(Crypto::FlowAwareElement other) { ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } + + override predicate isConsumerArtifact() { none() } } /** @@ -144,7 +162,7 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { - source = any(Crypto::ArtifactElement artifact).getOutputNode() + source = any(Crypto::ArtifactInstance artifact).getOutputNode() } predicate isSink(DataFlow::Node sink) { diff --git a/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql b/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql index cdeeebd5a7d7..7fea3515b7dd 100644 --- a/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql +++ b/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql @@ -6,17 +6,19 @@ import experimental.Quantum.Language -from Crypto::NonceNode n, Crypto::CipherOperationNode op, Crypto::FlowAwareElement src, string msg +from + Crypto::NonceArtifactNode n, Crypto::CipherOperationNode op, Crypto::FlowAwareElement src, + string msg where op.getANonce() = n and // Only encryption mode is relevant for insecure nonces, consder any 'unknown' subtype - // as possibly encryption. + // as possibly encryption. ( op.getCipherOperationSubtype() instanceof Crypto::EncryptionSubtype or op.getCipherOperationSubtype() instanceof Crypto::WrapSubtype or - op.getCipherOperationSubtype() instanceof Crypto::UnwrapSubtype + op.getCipherOperationSubtype() instanceof Crypto::UnwrapSubtype ) and ( // Known sources cases that are not secure @@ -30,7 +32,6 @@ where src = n.asElement() ) select n, msg, src, src.toString() - // variant using instances, does not yield the same results // from Crypto::NonceArtifactConsumer n, Crypto::CipherOperationInstance op, Crypto::FlowAwareElement src, string msg // where diff --git a/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql b/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql index fde1ef121786..f9fc7a873db1 100644 --- a/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql +++ b/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql @@ -1,14 +1,16 @@ /** * @name Possible Nonce Reuse: Produces false positives if reuse occurs in a source that is a re-entry point. * @id java/possible-nonce-reuse + * @kind problem */ import experimental.Quantum.Language import semmle.code.java.dataflow.DataFlow from - Crypto::CipherOperationNode op1, Crypto::CipherOperationNode op2, Crypto::NonceNode nonce1, - Crypto::NonceNode nonce2, Crypto::FlowAwareElement src1, Crypto::FlowAwareElement src2 + Crypto::CipherOperationNode op1, Crypto::CipherOperationNode op2, + Crypto::NonceArtifactNode nonce1, Crypto::NonceArtifactNode nonce2, Crypto::FlowAwareElement src1, + Crypto::FlowAwareElement src2 where // NOTE: not looking at value of the nonce, if we knew value, it would be insecure (hard coded) // Instead trying to find nonce sources that trace to multiple operations. diff --git a/java/ql/src/experimental/Quantum/TestCipher.ql b/java/ql/src/experimental/Quantum/TestCipher.ql index fa04770d8549..b98347a57eca 100644 --- a/java/ql/src/experimental/Quantum/TestCipher.ql +++ b/java/ql/src/experimental/Quantum/TestCipher.ql @@ -6,7 +6,8 @@ import experimental.Quantum.Language from Crypto::CipherOperationNode op, Crypto::CipherAlgorithmNode a, - Crypto::ModeOfOperationAlgorithmNode m, Crypto::PaddingAlgorithmNode p, Crypto::NonceNode nonce + Crypto::ModeOfOperationAlgorithmNode m, Crypto::PaddingAlgorithmNode p, + Crypto::NonceArtifactNode nonce where a = op.getAKnownCipherAlgorithm() and m = a.getModeOfOperation() and diff --git a/java/ql/src/experimental/Quantum/TestHash.ql b/java/ql/src/experimental/Quantum/TestHash.ql new file mode 100644 index 000000000000..96f99193cba4 --- /dev/null +++ b/java/ql/src/experimental/Quantum/TestHash.ql @@ -0,0 +1,9 @@ +/** + * @name TestHashOperations + */ + +import experimental.Quantum.Language + +from Crypto::HashOperationNode op, Crypto::HashAlgorithmNode alg +where alg = op.getAKnownHashAlgorithm() +select op, op.getDigest(), alg, alg.getRawAlgorithmName() diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index f5f57d245ba1..cebbac2ff6bd 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -4,6 +4,7 @@ import codeql.util.Location import codeql.util.Option +import codeql.util.Either signature module InputSig { class LocatableElement { @@ -19,6 +20,8 @@ signature module InputSig { } class UnknownLocation instanceof Location; + + LocatableElement dfn_to_element(DataFlowNode node); } module CryptographyBase Input> { @@ -50,12 +53,12 @@ module CryptographyBase Input> { NodeBase getPassthroughNodeChild(NodeBase node) { result = node.(CipherInputNode).getChild(_) or - result = node.(NonceNode).getChild(_) + result = node.(NonceArtifactNode).getChild(_) } predicate isPassthroughNode(NodeBase node) { node instanceof CipherInputNode or - node instanceof NonceNode + node instanceof NonceArtifactNode } predicate nodes_graph_impl(NodeBase node, string key, string value) { @@ -121,7 +124,7 @@ module CryptographyBase Input> { } /** - * An element that represents a _known_ cryptographic asset. + * An element that represents a _known_ cryptographic asset with a determinable value OR an artifact. * * CROSS PRODUCT WARNING: Do not model any *other* element that is a `FlowAwareElement` to the same * instance in the database, as every other `KnownElement` will share that output artifact's flow. @@ -133,17 +136,17 @@ module CryptographyBase Input> { /** * An element that represents a _known_ cryptographic operation. */ - abstract class OperationElement extends KnownElement { + abstract class OperationInstance extends KnownElement { /** - * Gets the consumer of algorithms associated with this operation. + * Gets the consumers of algorithm values associated with this operation. */ - abstract AlgorithmConsumer getAlgorithmConsumer(); + abstract AlgorithmValueConsumer getAnAlgorithmValueConsumer(); } /** * An element that represents a _known_ cryptographic algorithm. */ - abstract class AlgorithmElement extends KnownElement { + abstract class AlgorithmInstance extends KnownElement { /** * Gets the raw name as it appears in source, e.g., "AES/CBC/PKCS7Padding". * This name is not parsed or formatted. @@ -151,11 +154,6 @@ module CryptographyBase Input> { abstract string getRawAlgorithmName(); } - /** - * An element that represents a _known_ cryptographic artifact. - */ - abstract class ArtifactElement extends KnownElement, FlowAwareElement { } - /** * An element that represents an _unknown_ data-source with a non-statically determinable value. */ @@ -198,6 +196,7 @@ module CryptographyBase Input> { override DataFlowNode getOutputNode() { none() } + // for internal use only final GenericDataSourceInstance getAnUnknownSource() { result.flowsTo(this) and not result = this.getAKnownSource() } @@ -207,23 +206,31 @@ module CryptographyBase Input> { } final NodeBase getAKnownSourceNode() { result.asElement() = this.getAKnownSource() } - } - - /** - * An element that consumes _known_ and _unknown_ values. - * - * A value consumer can consume multiple values and multiple value sources at once. - */ - abstract class ValueConsumer extends ConsumerElement { - final override KnownElement getAKnownSource() { none() } - abstract string getAKnownValue(Location location); + final LocatableElement getASource() { + result = this.getAnUnknownSource() or + result = this.getAKnownSource() + } } - abstract class AlgorithmConsumer extends ConsumerElement { + abstract class AlgorithmValueConsumer extends ConsumerElement { + /** + * DO NOT USE. + * Model `getAKnownAlgorithmSource()` instead, which is equivalent but correctly typed. + */ final override KnownElement getAKnownSource() { result = this.getAKnownAlgorithmSource() } - abstract AlgorithmElement getAKnownAlgorithmSource(); + /** + * Gets a known algorithm value that is equivalent to or consumed by this element. + */ + abstract AlgorithmInstance getAKnownAlgorithmSource(); + } + + /** + * An element that represents a _known_ cryptographic artifact. + */ + abstract class ArtifactInstance extends KnownElement, FlowAwareElement { + abstract predicate isConsumerArtifact(); // whether this is an input artifact defined by its consumer } /** @@ -232,15 +239,6 @@ module CryptographyBase Input> { * The concept of "`ArtifactConsumer` = `ArtifactNode`" should be used for inputs, as a consumer can be directly tied * to the artifact it receives, thereby becoming the definitive contextual source for that artifact. * - * For example, consider a nonce artifact consumer: - * - * A `NonceArtifactConsumer` is always the `NonceArtifactInstance` itself, since data only becomes (i.e., is determined to be) - * a `NonceArtifactInstance` when it is consumed in a context that expects a nonce (e.g., an argument expecting nonce data). - * In this case, the artifact (nonce) is fully defined by the context in which it is consumed, and the consumer embodies - * that identity without the need for additional differentiation. Without the context a consumer provides, that data could - * otherwise be any other type of artifact or even simply random data. - * - * * Architectural Implications: * * By directly coupling a consumer with the node that receives an artifact, * the data flow is fully transparent with the consumer itself serving only as a transparent node. @@ -251,24 +249,114 @@ module CryptographyBase Input> { */ abstract class ArtifactConsumer extends ConsumerElement { /** + * DO NOT USE: * Use `getAKnownArtifactSource() instead. The behaviour of these two predicates is equivalent. */ final override KnownElement getAKnownSource() { result = this.getAKnownArtifactSource() } - final ArtifactElement getAKnownArtifactSource() { result.flowsTo(this) } + final ArtifactInstance getAKnownArtifactSource() { result.flowsTo(this) } } - abstract class ArtifactConsumerAndInstance extends ArtifactConsumer { - final override DataFlowNode getOutputNode() { none() } + /** + * An `ArtifactConsumer` that is also an `ArtifactInstance`. + * + * For example: + * A `NonceArtifactConsumer` is always the `NonceArtifactInstance` itself, since data only becomes (i.e., is determined to be) + * a `NonceArtifactInstance` when it is consumed in a context that expects a nonce (e.g., an argument expecting nonce data). + * In this case, the artifact (nonce) is fully defined by the context in which it is consumed, and the consumer embodies + * that identity without the need for additional differentiation. Without the context a consumer provides, that data could + * otherwise be any other type of artifact or even simply random data. + * + * TODO: what if a Nonce from hypothetical func `generateNonce()` flows to this instance which is also a Nonce? + * TODO: potential solution is creating another artifact type called NonceData or treating it as a generic source. + * + * TODO: An alternative is simply having a predicate DataFlowNode getNonceInputNode() on (for example) operations. + * Under the hood, in Model.qll, we would create the instance for the modeller, thus avoiding the need for the modeller + * to create a separate consumer class / instance themselves using this class. + */ + abstract private class ArtifactConsumerAndInstance extends ArtifactConsumer, ArtifactInstance { + override predicate isConsumerArtifact() { any() } + } + + final private class NonceArtifactConsumer extends ArtifactConsumerAndInstance { + DataFlowNode inputNode; + + NonceArtifactConsumer() { + exists(CipherOperationInstance op | inputNode = op.getNonceConsumer()) and + this = Input::dfn_to_element(inputNode) + } + + final override DataFlowNode getInputNode() { result = inputNode } + } + + final private class CipherInputArtifactConsumer extends ArtifactConsumerAndInstance { + DataFlowNode inputNode; - final override predicate flowsTo(FlowAwareElement other) { none() } + CipherInputArtifactConsumer() { + exists(CipherOperationInstance op | inputNode = op.getInputConsumer()) and + this = Input::dfn_to_element(inputNode) + } + + final override DataFlowNode getInputNode() { result = inputNode } + } + + // Output artifacts are determined solely by the element that produces them. + // Implementation guidance: these *do* need to be defined generically at the language-level + // in order for a flowsTo to be defined. At the per-modeling-instance level, extend that language-level class! + abstract class OutputArtifactInstance extends ArtifactInstance { + override predicate isConsumerArtifact() { none() } + } + + abstract class DigestArtifactInstance extends OutputArtifactInstance { + final override DataFlowNode getInputNode() { none() } + } + + abstract class RandomNumberGenerationInstance extends OutputArtifactInstance { + // TODO: input seed? + final override DataFlowNode getInputNode() { none() } } - abstract class CipherOutputArtifactInstance extends ArtifactElement { + abstract class CipherOutputArtifactInstance extends ArtifactInstance { final override DataFlowNode getInputNode() { none() } } - abstract class CipherOperationInstance extends OperationElement { + // Artifacts that may be outputs or inputs + newtype TKeyArtifactType = + TSymmetricKeyType() or + TAsymmetricKeyType() or + TUnknownKeyType() + + class KeyArtifactType extends TKeyArtifactType { + string toString() { + this = TSymmetricKeyType() and result = "Symmetric" + or + this = TAsymmetricKeyType() and result = "Asymmetric" + or + this = TUnknownKeyType() and result = "Unknown" + } + } + + abstract class KeyArtifactInstance extends ArtifactInstance { + abstract KeyArtifactType getKeyType(); + } + + final class KeyArtifactConsumer extends ArtifactConsumerAndInstance, KeyArtifactInstance { + DataFlowNode inputNode; + + KeyArtifactConsumer() { + exists(CipherOperationInstance op | inputNode = op.getKeyConsumer()) and + this = Input::dfn_to_element(inputNode) + } + + override KeyArtifactType getKeyType() { result instanceof TUnknownKeyType } + + final override DataFlowNode getInputNode() { result = inputNode } + } + + /** + * A cipher operation instance, such as encryption or decryption. + */ + abstract class CipherOperationInstance extends OperationInstance { /** * Gets the subtype of this cipher operation, distinguishing encryption, decryption, key wrapping, and key unwrapping. */ @@ -277,12 +365,17 @@ module CryptographyBase Input> { /** * Gets the consumer of nonces/IVs associated with this cipher operation. */ - abstract NonceArtifactConsumer getNonceConsumer(); + abstract DataFlowNode getNonceConsumer(); /** * Gets the consumer of plaintext or ciphertext input associated with this cipher operation. */ - abstract CipherInputConsumer getInputConsumer(); + abstract DataFlowNode getInputConsumer(); + + /** + * Gets the consumer of a key. + */ + abstract DataFlowNode getKeyConsumer(); /** * Gets the output artifact of this cipher operation. @@ -294,7 +387,7 @@ module CryptographyBase Input> { abstract CipherOutputArtifactInstance getOutputArtifact(); } - abstract class CipherAlgorithmInstance extends AlgorithmElement { + abstract class CipherAlgorithmInstance extends AlgorithmInstance { /** * Gets the type of this cipher, e.g., "AES" or "ChaCha20". */ @@ -303,7 +396,7 @@ module CryptographyBase Input> { /** * Gets the mode of operation of this cipher, e.g., "GCM" or "CBC". * - * IMPLEMENTATION NOTE: as a trade-off, this is not a consumer but always either an instance or unknown. + * IMPLEMENTATION NOTE: as a tradeoff, this is not a consumer but always either an instance or unknown. * A mode of operation is therefore assumed to always be part of the cipher algorithm itself. */ abstract ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm(); @@ -311,13 +404,13 @@ module CryptographyBase Input> { /** * Gets the padding scheme of this cipher, e.g., "PKCS7" or "NoPadding". * - * IMPLEMENTATION NOTE: as a trade-off, this is not a consumer but always either an instance or unknown. + * IMPLEMENTATION NOTE: as a tradeoff, this is not a consumer but always either an instance or unknown. * A padding algorithm is therefore assumed to always be defined as part of the cipher algorithm itself. */ abstract PaddingAlgorithmInstance getPaddingAlgorithm(); } - abstract class ModeOfOperationAlgorithmInstance extends AlgorithmElement { + abstract class ModeOfOperationAlgorithmInstance extends AlgorithmInstance { /** * Gets the type of this mode of operation, e.g., "ECB" or "CBC". * @@ -335,7 +428,7 @@ module CryptographyBase Input> { abstract string getRawModeAlgorithmName(); } - abstract class PaddingAlgorithmInstance extends AlgorithmElement { + abstract class PaddingAlgorithmInstance extends AlgorithmInstance { /** * Gets the isolated name as it appears in source, e.g., "PKCS7Padding" in "AES/CBC/PKCS7Padding". * @@ -353,56 +446,70 @@ module CryptographyBase Input> { abstract TPaddingType getPaddingType(); } - abstract class KeyEncapsulationOperationInstance extends LocatableElement { } + abstract class OAEPPaddingAlgorithmInstance extends PaddingAlgorithmInstance { + OAEPPaddingAlgorithmInstance() { this.getPaddingType() = OAEP() } + + /** + * Gets the hash algorithm used in this padding scheme. + */ + abstract HashAlgorithmInstance getHashAlgorithm(); + + /** + * Gets the mask generation function used in this padding scheme. + */ + abstract HashAlgorithmInstance getMaskGenerationFunction(); + } + + abstract class KeyEncapsulationOperationInstance extends OperationInstance { } - abstract class KeyEncapsulationAlgorithmInstance extends LocatableElement { } + abstract class KeyEncapsulationAlgorithmInstance extends AlgorithmInstance { } - abstract class EllipticCurveAlgorithmInstance extends LocatableElement { } + abstract class EllipticCurveAlgorithmInstance extends AlgorithmInstance { } - abstract class HashOperationInstance extends OperationElement { - // TODO: need input and outputs, but this should be universal to all Operations + abstract class HashOperationInstance extends OperationInstance { + abstract DigestArtifactInstance getDigestArtifact(); } - abstract class HashAlgorithmInstance extends AlgorithmElement { + abstract class HashAlgorithmInstance extends AlgorithmInstance { /** * Gets the type of this digest algorithm, e.g., "SHA1", "SHA2", "MD5" etc. */ abstract THashType getHashFamily(); - - // abstract int getHashSize(); } - abstract class KeyDerivationOperationInstance extends KnownElement { } + abstract class KeyDerivationOperationInstance extends OperationInstance { } - abstract class KeyDerivationAlgorithmInstance extends KnownElement { } + abstract class KeyDerivationAlgorithmInstance extends AlgorithmInstance { } - // Artifacts determined solely by the element that produces them - // Implementation guidance: these *do* need to be defined generically at the language-level - // in order for a flowsTo to be defined. At the per-modeling-instance level, extend that language-level class! - abstract class OutputArtifactElement extends ArtifactElement { - final override DataFlowNode getInputNode() { none() } - } + private signature class AlgorithmInstanceType instanceof AlgorithmInstance; - abstract class DigestArtifactInstance extends OutputArtifactElement { } + module AlgorithmInstanceOrValueConsumer { + class Union extends LocatableElement { + Union() { + this instanceof Alg + or + this instanceof AlgorithmValueConsumer and + not exists(this.(AlgorithmValueConsumer).getASource()) + } - abstract class RandomNumberGenerationInstance extends OutputArtifactElement { } // TODO: is this an OutputArtifactElement if it takes a seed? + Alg asAlg() { result = this } - // Artifacts determined solely by the consumer that consumes them are defined as consumers - // Implementation guidance: these do not need to be defined generically at the language-level - // Only the sink node needs to be defined per-modeling-instance (e.g., in JCA.qll) - abstract class NonceArtifactConsumer extends ArtifactConsumerAndInstance { } + AlgorithmValueConsumer asAVC() { result = this } + } + } - abstract class CipherInputConsumer extends ArtifactConsumerAndInstance { } + class CipherAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; - // Other artifacts - abstract class KeyArtifactInstance extends ArtifactElement { } // TODO: implement and categorize + class HashAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; newtype TNode = // Artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or TNonce(NonceArtifactConsumer e) or - TCipherInput(CipherInputConsumer e) or + TCipherInput(CipherInputArtifactConsumer e) or TCipherOutput(CipherOutputArtifactInstance e) or TRandomNumberGeneration(RandomNumberGenerationInstance e) { e.flowsTo(_) } or // Operations (e.g., hashing, encryption) @@ -411,9 +518,9 @@ module CryptographyBase Input> { TCipherOperation(CipherOperationInstance e) or TKeyEncapsulationOperation(KeyEncapsulationOperationInstance e) or // Algorithms (e.g., SHA-256, AES) - TCipherAlgorithm(CipherAlgorithmInstance e) or + TCipherAlgorithm(CipherAlgorithmInstanceOrValueConsumer e) or TEllipticCurveAlgorithm(EllipticCurveAlgorithmInstance e) or - THashAlgorithm(HashAlgorithmInstance e) or + THashAlgorithm(HashAlgorithmInstanceOrValueConsumer e) or TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstance e) or TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or // Non-standalone Algorithms (e.g., Mode, Padding) @@ -567,10 +674,10 @@ module CryptographyBase Input> { /** * A nonce or initialization vector */ - final class NonceNode extends ArtifactNode, TNonce { + final class NonceArtifactNode extends ArtifactNode, TNonce { NonceArtifactConsumer instance; - NonceNode() { this = TNonce(instance) } + NonceArtifactNode() { this = TNonce(instance) } final override string getInternalType() { result = "Nonce" } @@ -594,7 +701,7 @@ module CryptographyBase Input> { * Input text to a cipher operation */ final class CipherInputNode extends ArtifactNode, TCipherInput { - CipherInputConsumer instance; + CipherInputArtifactConsumer instance; CipherInputNode() { this = TCipherInput(instance) } @@ -616,6 +723,41 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } } + /** + * A cryptographic key, such as a symmetric key or asymmetric key pair. + */ + final class KeyArtifactNode extends ArtifactNode, TKey { + KeyArtifactInstance instance; + + KeyArtifactNode() { this = TKey(instance) } + + final override string getInternalType() { result = "Key" } + + override LocatableElement asElement() { result = instance } + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [ONLY_KNOWN] + key = "KeyType" and + value = instance.getKeyType().toString() and + location = this.getLocation() + } + } + + /** + * A digest produced by a hash operation. + */ + final class DigestArtifactNode extends ArtifactNode, TDigest { + DigestArtifactInstance instance; + + DigestArtifactNode() { this = TDigest(instance) } + + final override string getInternalType() { result = "Digest" } + + override LocatableElement asElement() { result = instance } + } + /** * A cryptographic operation, such as hashing or encryption. */ @@ -693,26 +835,30 @@ module CryptographyBase Input> { */ NodeBase getACipherAlgorithmOrUnknown() { result = this.getAKnownCipherAlgorithm() or - result = this.asElement().(OperationElement).getAlgorithmConsumer().getAnUnknownSourceNode() + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAnUnknownSourceNode() } /** * Gets a known algorithm associated with this operation */ CipherAlgorithmNode getAKnownCipherAlgorithm() { - result = this.asElement().(OperationElement).getAlgorithmConsumer().getAKnownSourceNode() + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() } CipherOperationSubtype getCipherOperationSubtype() { result = instance.getCipherOperationSubtype() } - NonceNode getANonce() { - result.asElement() = this.asElement().(CipherOperationInstance).getNonceConsumer() + NonceArtifactNode getANonce() { + result.asElement() = + Input::dfn_to_element(this.asElement().(CipherOperationInstance).getNonceConsumer()) } CipherInputNode getAnInputArtifact() { - result.asElement() = this.asElement().(CipherOperationInstance).getInputConsumer() + result.asElement() = + Input::dfn_to_element(this.asElement().(CipherOperationInstance).getInputConsumer()) } CipherOutputNode getAnOutputArtifact() { @@ -858,6 +1004,32 @@ module CryptographyBase Input> { override string getRawAlgorithmName() { result = instance.getRawPaddingAlgorithmName() } } + class OAEPPaddingAlgorithmNode extends PaddingAlgorithmNode { + override OAEPPaddingAlgorithmInstance instance; + + OAEPPaddingAlgorithmNode() { this = TPaddingAlgorithm(instance) } + + HashAlgorithmNode getHashAlgorithm() { result.asElement() = instance.getHashAlgorithm() } + + HashAlgorithmNode getMaskGenerationFunction() { + result.asElement() = instance.getMaskGenerationFunction() + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] + edgeName = "MD" and + if exists(this.getHashAlgorithm()) then result = this.getHashAlgorithm() else result = this + or + // [KNOWN_OR_UNKNOWN] + edgeName = "MGF" and + if exists(this.getMaskGenerationFunction()) + then result = this.getMaskGenerationFunction() + else result = this + } + } + /** * A helper type for distinguishing between block and stream ciphers. */ @@ -904,7 +1076,7 @@ module CryptographyBase Input> { OtherCipherType() final class CipherAlgorithmNode extends AlgorithmNode, TCipherAlgorithm { - CipherAlgorithmInstance instance; + CipherAlgorithmInstanceOrValueConsumer instance; CipherAlgorithmNode() { this = TCipherAlgorithm(instance) } @@ -920,7 +1092,7 @@ module CryptographyBase Input> { this.cipherFamilyToNameAndStructure(this.getCipherFamily(), result, _) } - final override string getRawAlgorithmName() { result = instance.getRawAlgorithmName() } + final override string getRawAlgorithmName() { result = instance.asAlg().getRawAlgorithmName() } /** * Gets the key size of this cipher, e.g., "128" or "256". @@ -930,20 +1102,20 @@ module CryptographyBase Input> { /** * Gets the type of this cipher, e.g., "AES" or "ChaCha20". */ - TCipherType getCipherFamily() { result = instance.getCipherFamily() } + TCipherType getCipherFamily() { result = instance.asAlg().getCipherFamily() } /** * Gets the mode of operation of this cipher, e.g., "GCM" or "CBC". */ ModeOfOperationAlgorithmNode getModeOfOperation() { - result.asElement() = instance.getModeOfOperationAlgorithm() + result.asElement() = instance.asAlg().getModeOfOperationAlgorithm() } /** * Gets the padding scheme of this cipher, e.g., "PKCS7" or "NoPadding". */ PaddingAlgorithmNode getPaddingAlgorithm() { - result.asElement() = instance.getPaddingAlgorithm() + result.asElement() = instance.asAlg().getPaddingAlgorithm() } bindingset[type] @@ -1038,7 +1210,7 @@ module CryptographyBase Input> { * hash value as the output using a specified hashing algorithm. */ class HashOperationNode extends OperationNode, THashOperation { - HashAlgorithmInstance instance; + HashOperationInstance instance; HashOperationNode() { this = THashOperation(instance) } @@ -1049,16 +1221,39 @@ module CryptographyBase Input> { /** * Gets the algorithm or unknown source nodes consumed as an algorithm associated with this operation. */ - NodeBase getACipherAlgorithmOrUnknown() { - result = this.getAKnownCipherAlgorithm() or - result = this.asElement().(OperationElement).getAlgorithmConsumer().getAnUnknownSourceNode() + NodeBase getAHashAlgorithmOrUnknown() { + result = this.getAKnownHashAlgorithm() or + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAnUnknownSourceNode() } /** * Gets a known algorithm associated with this operation */ - HashAlgorithmNode getAKnownCipherAlgorithm() { - result = this.asElement().(OperationElement).getAlgorithmConsumer().getAKnownSourceNode() + HashAlgorithmNode getAKnownHashAlgorithm() { + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + } + + /** + * Gets the output digest node + */ + DigestArtifactNode getDigest() { + result.asElement() = this.asElement().(HashOperationInstance).getDigestArtifact() + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] + edgeName = "Algorithm" and + if exists(this.getAHashAlgorithmOrUnknown()) + then result = this.getAHashAlgorithmOrUnknown() + else result = this + or + // [KNOWN_OR_UNKNOWN] + edgeName = "Digest" and + if exists(this.getDigest()) then result = this.getDigest() else result = this } } @@ -1083,11 +1278,17 @@ module CryptographyBase Input> { /** * A hashing algorithm that transforms variable-length input into a fixed-size hash value. */ - abstract class HashAlgorithmNode extends AlgorithmNode, THashAlgorithm { - HashAlgorithmInstance instance; + final class HashAlgorithmNode extends AlgorithmNode, THashAlgorithm { + HashAlgorithmInstanceOrValueConsumer instance; + HashAlgorithmNode() { this = THashAlgorithm(instance) } + override string getInternalType() { result = "HashAlgorithm" } + override LocatableElement asElement() { result = instance } + + override string getRawAlgorithmName() { result = instance.asAlg().getRawAlgorithmName() } + final predicate hashTypeToNameMapping(THashType type, string name) { type instanceof BLAKE2B and name = "BLAKE2B" or From 63aaebbea6bd4a5249393265d980c27a5f1724c7 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Wed, 19 Mar 2025 18:30:06 -0400 Subject: [PATCH 069/122] . --- cpp/ql/lib/experimental/Quantum/Language.qll | 6 + .../OpenSSL/EVPCipherAlgorithmSource.qll | 181 ---- .../Quantum/OpenSSL/EVPCipherInitializer.qll | 153 --- .../Quantum/OpenSSL/EVPCipherOperation.qll | 92 -- .../experimental/Quantum/OpenSSL/OpenSSL.qll | 216 ++-- .../OpenSSL/OpenSSLAlgorithmGetter.qll | 976 ++++++++---------- .../codeql/cryptography/Model.qll | 5 + 7 files changed, 531 insertions(+), 1098 deletions(-) delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherInitializer.qll delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index dbcb2004b83b..2dc17f5e267a 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -9,6 +9,12 @@ module CryptoInput implements InputSig { class LocatableElement = Lang::Locatable; class UnknownLocation = Lang::UnknownDefaultLocation; + + LocatableElement dfn_to_element(DataFlow::Node node) { + result = node.asExpr() or + result = node.asParameter() or + result = node.asVariable() + } } module Crypto = CryptographyBase; diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll deleted file mode 100644 index 65231e32cd03..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherAlgorithmSource.qll +++ /dev/null @@ -1,181 +0,0 @@ -import cpp -import experimental.Quantum.Language -import EVPCipherConsumers -import OpenSSLAlgorithmGetter - -/** - * Given a `KnownOpenSSLAlgorithmConstant`, converts this to a cipher family type. - * Does not bind if there is know mapping (no mapping to 'unknown' or 'other'). - */ -predicate knownOpenSSLConstantToCipherFamilyType( - KnownOpenSSLAlgorithmConstant e, Crypto::TCipherType type -) { - exists(string name | e.getAlgType().toLowerCase().matches("%encryption") | - name = e.getNormalizedName() and - ( - name.matches("AES%") and type instanceof Crypto::AES - or - name.matches("ARIA") and type instanceof Crypto::ARIA - or - name.matches("BLOWFISH") and type instanceof Crypto::BLOWFISH - or - name.matches("BF") and type instanceof Crypto::BLOWFISH - or - name.matches("CAMELLIA%") and type instanceof Crypto::CAMELLIA - or - name.matches("CHACHA20") and type instanceof Crypto::CHACHA20 - or - name.matches("CAST5") and type instanceof Crypto::CAST5 - or - name.matches("2DES") and type instanceof Crypto::DoubleDES - or - name.matches(["3DES", "TRIPLEDES"]) and type instanceof Crypto::TripleDES - or - name.matches("DES") and type instanceof Crypto::DES - or - name.matches("DESX") and type instanceof Crypto::DESX - or - name.matches("GOST%") and type instanceof Crypto::GOST - or - name.matches("IDEA") and type instanceof Crypto::IDEA - or - name.matches("KUZNYECHIK") and type instanceof Crypto::KUZNYECHIK - or - name.matches("MAGMA") and type instanceof Crypto::MAGMA - or - name.matches("RC2") and type instanceof Crypto::RC2 - or - name.matches("RC4") and type instanceof Crypto::RC4 - or - name.matches("RC5") and type instanceof Crypto::RC5 - or - name.matches("RSA") and type instanceof Crypto::RSA - or - name.matches("SEED") and type instanceof Crypto::SEED - or - name.matches("SM4") and type instanceof Crypto::SM4 - ) - ) -} - -class KnownOpenSSLCipherConstantAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof KnownOpenSSLAlgorithmConstant -{ - OpenSSLAlgorithmGetterCall getterCall; - - KnownOpenSSLCipherConstantAlgorithmInstance() { - // Not just any known value, but specifically a known cipher operation - this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("%encryption") and - ( - // Two possibilities: - // 1) The source is a literal and flows to a getter, then we know we have an instance - // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that - // Possibility 1: - this instanceof Literal and - exists(DataFlow::Node src, DataFlow::Node sink | - // Sink is an argument to a CipherGetterCall - sink = getterCall.(OpenSSLAlgorithmGetterCall).getValueArgNode() and - // Source is `this` - src.asExpr() = this and - // This traces to a getter - KnownOpenSSLAlgorithmToAlgorithmGetterFlow::flow(src, sink) - ) - or - // Possibility 2: - this instanceof DirectGetterCall and getterCall = this - ) - } - - Crypto::AlgorithmConsumer getConsumer() { - AlgGetterToAlgConsumerFlow::flow(getterCall.getResultNode(), DataFlow::exprNode(result)) - } - - override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { - // if there is a block mode associated with the same element, then that's the block mode - // note, if none are associated, we may need to parse if the cipher is a block cipher - // to determine if this is an unknown vs not relevant. - result = this - } - - override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } - - override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } - - override Crypto::TCipherType getCipherFamily() { - knownOpenSSLConstantToCipherFamilyType(this, result) - or - not knownOpenSSLConstantToCipherFamilyType(this, _) and result = Crypto::OtherCipherType() - } -} - -/** - * Given a `KnownOpenSSLAlgorithmConstant`, converts this to a cipher family type. - * Does not bind if there is know mapping (no mapping to 'unknown' or 'other'). - */ -predicate knownOpenSSLConstantToBlockModeFamilyType( - KnownOpenSSLAlgorithmConstant e, Crypto::TBlockCipherModeOperationType type -) { - exists(string name | e.getAlgType().toLowerCase().matches("block_mode") | - name = e.getNormalizedName() and - ( - name.matches("CBC") and type instanceof Crypto::CBC - or - name.matches("CFB%") and type instanceof Crypto::CFB - or - name.matches("CTR") and type instanceof Crypto::CTR - or - name.matches("GCM") and type instanceof Crypto::GCM - or - name.matches("OFB") and type instanceof Crypto::OFB - or - name.matches("XTS") and type instanceof Crypto::XTS - or - name.matches("CCM") and type instanceof Crypto::CCM - or - name.matches("GCM") and type instanceof Crypto::GCM - or - name.matches("CCM") and type instanceof Crypto::CCM - or - name.matches("ECB") and type instanceof Crypto::ECB - ) - ) -} - -class KnownOpenSSLBlockModeConstantAlgorithmInstance extends Crypto::ModeOfOperationAlgorithmInstance instanceof KnownOpenSSLAlgorithmConstant -{ - OpenSSLAlgorithmGetterCall getterCall; - - KnownOpenSSLBlockModeConstantAlgorithmInstance() { - // Not just any known value, but specifically a known cipher operation - this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("block_mode") and - ( - // Two possibilities: - // 1) The source is a literal and flows to a getter, then we know we have an instance - // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that - // Possibility 1: - this instanceof Literal and - exists(DataFlow::Node src, DataFlow::Node sink | - // Sink is an argument to a CipherGetterCall - sink = getterCall.(OpenSSLAlgorithmGetterCall).getValueArgNode() and - // Source is `this` - src.asExpr() = this and - // This traces to a getter - KnownOpenSSLAlgorithmToAlgorithmGetterFlow::flow(src, sink) - ) - or - // Possibility 2: - this instanceof DirectGetterCall and getterCall = this - ) - } - - override Crypto::TBlockCipherModeOperationType getModeType() { - knownOpenSSLConstantToBlockModeFamilyType(this, result) - or - not knownOpenSSLConstantToBlockModeFamilyType(this, _) and result = Crypto::OtherMode() - } - - // NOTE: I'm not going to attempt to parse out the mode specific part, so returning - // the same as the raw name for now. - override string getRawModeAlgorithmName() { result = this.(Literal).getValue().toString() } - - override string getRawAlgorithmName() { result = this.getRawModeAlgorithmName() } -} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherInitializer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherInitializer.qll deleted file mode 100644 index 12cbe4da650a..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherInitializer.qll +++ /dev/null @@ -1,153 +0,0 @@ -import experimental.Quantum.Language -import CtxFlow as CTXFlow - -module EncValToInitEncArgConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] } - - predicate isSink(DataFlow::Node sink) { - exists(EVP_Cipher_Inititalizer initCall | sink.asExpr() = initCall.getOperataionSubtypeArg()) - } -} - -module EncValToInitEncArgFlow = DataFlow::Global; - -int getEncConfigValue(Expr e) { - exists(EVP_Cipher_Inititalizer initCall | e = initCall.getOperataionSubtypeArg()) and - exists(DataFlow::Node a, DataFlow::Node b | - EncValToInitEncArgFlow::flow(a, b) and b.asExpr() = e and result = a.asExpr().getValue().toInt() - ) -} - -bindingset[i] -Crypto::CipherOperationSubtype intToCipherOperationSubtype(int i) { - if i = 0 - then result instanceof Crypto::EncryptionSubtype - else - if i = 1 - then result instanceof Crypto::DecryptionSubtype - else result instanceof Crypto::UnknownCipherOperationSubtype -} - -// TODO: need to add key consumer -abstract class EVP_Cipher_Inititalizer extends Call { - Expr getContextArg() { result = this.(Call).getArgument(0) } - - Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } - - abstract Expr getKeyArg(); - - abstract Expr getIVArg(); - -// abstract Crypto::CipherOperationSubtype getCipherOperationSubtype(); - - abstract Expr getOperataionSubtypeArg(); - -Crypto::CipherOperationSubtype getCipherOperationSubtype() { - if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") - then result instanceof Crypto::EncryptionSubtype - else - if this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") - then result instanceof Crypto::DecryptionSubtype - else - if exists(getEncConfigValue(this.getOperataionSubtypeArg())) - then result = intToCipherOperationSubtype(getEncConfigValue(this.getOperataionSubtypeArg())) - else result instanceof Crypto::UnknownCipherOperationSubtype - } -} - -abstract class EVP_EX_Initializer extends EVP_Cipher_Inititalizer { - override Expr getKeyArg() { result = this.(Call).getArgument(3) } - - override Expr getIVArg() { result = this.(Call).getArgument(4) } -} - -abstract class EVP_EX2_Initializer extends EVP_Cipher_Inititalizer { - override Expr getKeyArg() { result = this.(Call).getArgument(2) } - - override Expr getIVArg() { result = this.(Call).getArgument(3) } -} - -class EVP_Cipher_EX_Init_Call extends EVP_EX_Initializer { - EVP_Cipher_EX_Init_Call() { - this.(Call).getTarget().getName() in [ - "EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex" - ] - } - - override Expr getOperataionSubtypeArg() { - this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and - result = this.(Call).getArgument(5) - } -} - -// abstract class EVP_CipherInit extends EVP_Cipher_Inititalizer{ -// abstract Expr getOperataionSubtypeArg(); -// } -// class EVP_CipherInit_ex_Call extends EVP_EX_Initializer, EVP_CipherInit { -// EVP_CipherInit_ex_Call() { this.(Call).getTarget().getName() = "EVP_CipherInit_ex" } -// override Crypto::CipherOperationSubtype getCipherOperationSubtype() { -// result instanceof Crypto::EncryptionSubtype -// } -// override Expr getOperataionSubtypeArg(){ -// result = this.(Call).getArgument(5) -// } -// } -// class EVP_CipherInit_ex2_Call extends EVP_EX_Initializer, EVP_CipherInit { -// EVP_CipherInit_ex2_Call() { this.(Call).getTarget().getName() = "EVP_CipherInit_ex2" } -// override Crypto::CipherOperationSubtype getCipherOperationSubtype() { -// result instanceof Crypto::EncryptionSubtype -// } -// override Expr getOperataionSubtypeArg(){ -// result = this.(Call).getArgument(4) -// } -// } -class EVP_Cipher_EX2_or_Simple_Init_Call extends EVP_EX2_Initializer { - EVP_Cipher_EX2_or_Simple_Init_Call() { - this.(Call).getTarget().getName() in [ - "EVP_EncryptInit_ex2", "EVP_DecryptInit_ex2", "EVP_CipherInit_ex2", - "EVP_EncryptInit", "EVP_DecryptInit", "EVP_CipherInit" - ] - } - - - override Expr getOperataionSubtypeArg() { - this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and - result = this.(Call).getArgument(4) - } -} - -class EVP_CipherInit_SKEY_Call extends EVP_EX2_Initializer { - EVP_CipherInit_SKEY_Call() { - this.(Call).getTarget().getName() in [ - "EVP_CipherInit_SKEY" - ] - } - - override Expr getOperataionSubtypeArg() { - result = this.(Call).getArgument(5) - } -} - - -// class EVP_CipherInit extends EVP_Cipher_Inititalizer { -// EVP_CipherInit() { this.(Call).getTarget().getName() = "EVP_CipherInit" } -// override Expr getKeyArg() { result = this.(Call).getArgument(2) } -// override Expr getIVArg() { result = this.(Call).getArgument(3) } -// override Crypto::CipherOperationSubtype getCipherOperationSubtype() { -// result instanceof Crypto::EncryptionSubtype -// } -// } -class EVPCipherInitializerAlgorithmArgument extends Expr { - EVPCipherInitializerAlgorithmArgument() { - exists(EVP_Cipher_Inititalizer initCall | this = initCall.getAlgorithmArg()) - } -} -class EVPCipherInitializerKeyArgument extends Expr { - EVPCipherInitializerKeyArgument() { - exists(EVP_Cipher_Inititalizer initCall | this = initCall.getKeyArg()) - } -} -class EVPCipherInitializerIVArgument extends Expr { - EVPCipherInitializerIVArgument() { exists(EVP_Cipher_Inititalizer initCall | this = initCall.getIVArg()) } -} - diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll deleted file mode 100644 index f426eb3f92dd..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherOperation.qll +++ /dev/null @@ -1,92 +0,0 @@ -import experimental.Quantum.Language -import CtxFlow as CTXFlow -import EVPCipherInitializer -import EVPCipherConsumers - -//https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis -abstract class EVP_Cipher_Operation extends Crypto::CipherOperationInstance instanceof Call { - Expr getContextArg() { result = this.(Call).getArgument(0) } - - abstract Expr getInputArg(); - - Expr getOutputArg() { result = this.(Call).getArgument(1) } - - override Crypto::CipherOperationSubtype getCipherOperationSubtype() { - result instanceof Crypto::EncryptionSubtype and - this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") - or - result instanceof Crypto::DecryptionSubtype and - this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") - or - result = this.getInitCall().getCipherOperationSubtype() and - this.(Call).getTarget().getName().toLowerCase().matches("%cipher%") - } - - EVP_Cipher_Inititalizer getInitCall() { - CTXFlow::ctxFlowsTo(result.getContextArg(), this.getContextArg()) - } - - override Crypto::NonceArtifactConsumer getNonceConsumer() { - this.getInitCall().getIVArg() = result - } - - override Crypto::CipherInputConsumer getInputConsumer() { this.getInputArg() = result } - - override Crypto::CipherOutputArtifactInstance getOutputArtifact() { this.getOutputArg() = result } - - override Crypto::AlgorithmConsumer getAlgorithmConsumer() { - this.getInitCall().getAlgorithmArg() = result - } -} - -abstract class EVP_Update_Call extends EVP_Cipher_Operation { } - -abstract class EVP_Final_Call extends EVP_Cipher_Operation { - override Expr getInputArg() { none() } -} - -class EVP_Cipher_Call extends EVP_Cipher_Operation { - EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } - - override Expr getInputArg() { result = this.(Call).getArgument(2) } -} - -class EVP_Encrypt_Decrypt_or_Cipher_Update_Call extends EVP_Update_Call { - EVP_Encrypt_Decrypt_or_Cipher_Update_Call() { - this.(Call).getTarget().getName() in [ - "EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate" - ] - } - - override Expr getInputArg() { result = this.(Call).getArgument(3) } -} - -class EVP_Encrypt_Decrypt_or_Cipher_Final_Call extends EVP_Final_Call { - EVP_Encrypt_Decrypt_or_Cipher_Final_Call() { - this.(Call).getTarget().getName() in [ - "EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal", - "EVP_DecryptFinal", "EVP_CipherFinal" - ] - } -} - -class EVP_PKEY_Operation extends EVP_Cipher_Operation { - EVP_PKEY_Operation() { - this.(Call).getTarget().getName() in ["EVP_PKEY_decrypt", "EVP_PKEY_encrypt"] - } - - override Expr getInputArg() { result = this.(Call).getArgument(3) } - // TODO: how PKEY is initialized is different that symmetric cipher - // Consider making an entirely new class for this and specializing - // the get init call -} - -class EVPCipherOutput extends CipherOutputArtifact { - EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) } - - override DataFlow::Node getOutputNode() { result.asExpr() = this } -} - -class EVPCipherInputArgument extends Expr { - EVPCipherInputArgument() { exists(EVP_Cipher_Operation op | op.getInputArg() = this) } -} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll index 16b9e6485c96..1f9fb9a54bae 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll @@ -3,127 +3,93 @@ import semmle.code.cpp.dataflow.new.DataFlow module OpenSSLModel { import experimental.Quantum.Language - import experimental.Quantum.OpenSSL.EVPCipherOperation - import experimental.Quantum.OpenSSL.EVPHashOperation - import experimental.Quantum.OpenSSL.EVPCipherAlgorithmSource - import experimental.Quantum.OpenSSL.EVPHashAlgorithmSource - import experimental.Quantum.OpenSSL.Random - // Imports the additional algorithm flow step for OpenSSL - import experimental.Quantum.OpenSSL.OpenSSLAlgorithmGetter - - -// // TODO: trace CTX from init variants to the context arg of EVP update calls -// //https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis -// abstract class EVP_Cipher_Init_Call extends Call { -// Expr getContextArg() { result = this.getArgument(0) } -// abstract Expr getKeyArg(); - -// abstract Expr getIVArg(); - -// abstract Crypto::CipherOperationSubtype getCipherOperationSubtype(); -// } - -// abstract class EVP_Cipher_EX_Init_Call extends EVP_Cipher_Init_Call { -// override Expr getKeyArg() { result = this.getArgument(3) } - -// override Expr getIVArg() { result = this.getArgument(4) } -// } - -// abstract class EVP_Cipher_EX2_Init_Call extends EVP_Cipher_Init_Call { -// override Expr getKeyArg() { result = this.getArgument(2) } - -// override Expr getIVArg() { result = this.getArgument(3) } -// } - -// abstract class EVP_Cipher_Operation_Call extends Crypto::CipherOperationInstance instanceof Call { -// Expr getContextArg() { result = this.(Call).getArgument(0) } -// abstract Expr getInputArg(); -// Expr getOutputArg() { result = this.(Call).getArgument(1) } -// abstract Expr getInitCall(); -// } - -// abstract class EVP_Update_Call extends EVP_Cipher_Operation_Call { -// override Expr getInputArg() { result = this.(Call).getArgument(3) } - -// } - -// abstract class EVP_Final_Call extends EVP_Cipher_Operation_Call{ -// override Expr getInputArg() { none() } - -// } - -// class EVP_Cipher_Call extends EVP_Cipher_Operation_Call{ -// // TODO/QUESTION: what is the better way to do this? -// EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } - -// override Expr getInputArg() { result = this.(Call).getArgument(2) } - -// override Expr getOutputArg() { result = this.(Call).getArgument(1) } - -// override Crypto::CipherOperationSubtype getCipherOperationSubtype(){ -// result instanceof Crypto::EncryptionSubtype -// } - -// override Expr getInitCall(){ -// //TODO: -// none() -// } - -// override Crypto::NonceArtifactConsumer getNonceConsumer(){ -// none() -// } - -// override Crypto::CipherInputConsumer getInputConsumer(){ -// none() -// } - -// override Crypto::CipherOutputArtifactInstance getOutputArtifact(){ -// none() -// } - -// override Crypto::AlgorithmConsumer getAlgorithmConsumer(){ -// none() -// } -// } - - + import experimental.Quantum.OpenSSL.AlgorithmInstances.Instances + import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers + import experimental.Quantum.OpenSSL.Operations.Operations + // import experimental.Quantum.OpenSSL.EVPCipherOperation + // import experimental.Quantum.OpenSSL.EVPHashOperation + // import experimental.Quantum.OpenSSL.EVPCipherAlgorithmSource + // import experimental.Quantum.OpenSSL.EVPHashAlgorithmSource + // import experimental.Quantum.OpenSSL.Random + // // Imports the additional algorithm flow step for OpenSSL + // import experimental.Quantum.OpenSSL.OpenSSLAlgorithmGetter + // // TODO: trace CTX from init variants to the context arg of EVP update calls + // //https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis + // abstract class EVP_Cipher_Init_Call extends Call { + // Expr getContextArg() { result = this.getArgument(0) } + // abstract Expr getKeyArg(); + // abstract Expr getIVArg(); + // abstract Crypto::CipherOperationSubtype getCipherOperationSubtype(); + // } + // abstract class EVP_Cipher_EX_Init_Call extends EVP_Cipher_Init_Call { + // override Expr getKeyArg() { result = this.getArgument(3) } + // override Expr getIVArg() { result = this.getArgument(4) } + // } + // abstract class EVP_Cipher_EX2_Init_Call extends EVP_Cipher_Init_Call { + // override Expr getKeyArg() { result = this.getArgument(2) } + // override Expr getIVArg() { result = this.getArgument(3) } + // } + // abstract class EVP_Cipher_Operation_Call extends Crypto::CipherOperationInstance instanceof Call { + // Expr getContextArg() { result = this.(Call).getArgument(0) } + // abstract Expr getInputArg(); + // Expr getOutputArg() { result = this.(Call).getArgument(1) } + // abstract Expr getInitCall(); + // } + // abstract class EVP_Update_Call extends EVP_Cipher_Operation_Call { + // override Expr getInputArg() { result = this.(Call).getArgument(3) } + // } + // abstract class EVP_Final_Call extends EVP_Cipher_Operation_Call{ + // override Expr getInputArg() { none() } + // } + // class EVP_Cipher_Call extends EVP_Cipher_Operation_Call{ + // // TODO/QUESTION: what is the better way to do this? + // EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } + // override Expr getInputArg() { result = this.(Call).getArgument(2) } + // override Expr getOutputArg() { result = this.(Call).getArgument(1) } + // override Crypto::CipherOperationSubtype getCipherOperationSubtype(){ + // result instanceof Crypto::EncryptionSubtype + // } + // override Expr getInitCall(){ + // //TODO: + // none() + // } + // override Crypto::NonceArtifactConsumer getNonceConsumer(){ + // none() + // } + // override Crypto::CipherInputConsumer getInputConsumer(){ + // none() + // } + // override Crypto::CipherOutputArtifactInstance getOutputArtifact(){ + // none() + // } + // override Crypto::AlgorithmConsumer getAlgorithmConsumer(){ + // none() + // } + // } //TODO: what about EVP_CIpher - - // class EVP_EncryptUpdateCall extends Crypto::CipherOperationInstance instanceof Call { // // NICK QUESTION: is there a better way to tie this to openssl? // EVP_EncryptUpdateCall() { this.getTarget().getName() = "EVP_EncryptUpdate" } - // Expr getContextArg() { result = super.getArgument(0) } - // Expr getInputArg() { result = super.getArgument(3) } - // Expr getOutputArg() { result = super.getArgument(1) } - // override Crypto::CipherOperationSubtype getCipherOperationSubtype(){ // result instanceof Crypto::EncryptionSubtype // } - // override Crypto::NonceArtifactConsumer getNonceConsumer(){ // none() // } - // override Crypto::CipherInputConsumer getInputConsumer(){ // none() // } - // override Crypto::CipherOutputArtifactInstance getOutputArtifact(){ // none() // } - // override Crypto::AlgorithmConsumer getAlgorithmConsumer(){ // none() // } - // } - //EVP_EncryptUpdate - // /** // * Hash function references in OpenSSL. // */ @@ -146,34 +112,26 @@ module OpenSSLModel { // or // name = "whirlpool" and algo instanceof Crypto::WHIRLPOOL // } - // predicate hash_ref_type_mapping(FunctionCallOrMacroAccess ref, string name, Crypto::THashType algo) { // name = ref.getTargetName().regexpCapture("(?:SN_|LN_|EVP_)([a-z0-9]+)", 1) and // hash_ref_type_mapping_known(name, algo) // } - // class FunctionCallOrMacroAccess extends Element { // FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess } - // string getTargetName() { // result = this.(FunctionCall).getTarget().getName() // or // result = this.(MacroAccess).getMacroName() // } // } - // class HashAlgorithmCallOrMacro extends Crypto::HashAlgorithmInstance instanceof FunctionCallOrMacroAccess // { // HashAlgorithmCallOrMacro() { hash_ref_type_mapping(this, _, _) } - // string getTargetName() { result = this.(FunctionCallOrMacroAccess).getTargetName() } // } - // class HashAlgorithm extends Crypto::HashAlgorithm { // HashAlgorithmCallOrMacro instance; - // HashAlgorithm() { this = Crypto::THashAlgorithm(instance) } - // override string getSHA2OrSHA3DigestSize(Location location) { // ( // this.getHashType() instanceof Crypto::SHA2 or @@ -185,16 +143,11 @@ module OpenSSLModel { // location = instance.getLocation() // ) // } - // override string getRawAlgorithmName() { result = instance.getTargetName() } - // override Crypto::THashType getHashType() { hash_ref_type_mapping(instance, _, result) } - // Element getInstance() { result = instance } - // override Location getLocation() { result = instance.getLocation() } // } - // /** // * Data-flow configuration for key derivation algorithm flow to EVP_KDF_derive. // */ @@ -202,7 +155,6 @@ module OpenSSLModel { // predicate isSource(DataFlow::Node source) { // source.asExpr() = any(KeyDerivationAlgorithm a).getInstance() // } - // predicate isSink(DataFlow::Node sink) { // exists(EVP_KDF_derive kdo | // sink.asExpr() = kdo.getCall().getAlgorithmArg() @@ -210,155 +162,113 @@ module OpenSSLModel { // sink.asExpr() = kdo.getCall().getContextArg() // via `EVP_KDF_CTX_set_params` // ) // } - // predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { // none() // TODO // } // } - // module AlgorithmToEVPKeyDeriveFlow = DataFlow::Global; - // predicate algorithm_to_EVP_KDF_derive(KeyDerivationAlgorithm algo, EVP_KDF_derive derive) { // none() // } - // /** // * Key derivation operation (e.g., `EVP_KDF_derive`) // */ // class EVP_KDF_derive_FunctionCall extends Crypto::KeyDerivationOperationInstance instanceof FunctionCall // { // EVP_KDF_derive_FunctionCall() { this.getTarget().getName() = "EVP_KDF_derive" } - // Expr getAlgorithmArg() { result = super.getArgument(3) } - // Expr getContextArg() { result = super.getArgument(0) } // } - // class EVP_KDF_derive extends Crypto::KeyDerivationOperation { // EVP_KDF_derive_FunctionCall instance; - // EVP_KDF_derive() { this = Crypto::TKeyDerivationOperation(instance) } - // override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) } - // EVP_KDF_derive_FunctionCall getCall() { result = instance } // } - // /** // * Key derivation algorithm nodes // */ // abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm { // abstract Expr getInstance(); // } - // /** // * `EVP_KDF_fetch` returns a key derivation algorithm. // */ // class EVP_KDF_fetch_Call extends FunctionCall { // EVP_KDF_fetch_Call() { this.getTarget().getName() = "EVP_KDF_fetch" } - // Expr getAlgorithmArg() { result = this.getArgument(1) } // } - // class EVP_KDF_fetch_AlgorithmArg extends Crypto::KeyDerivationAlgorithmInstance instanceof Expr { // EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) } // } - // predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF", "PBKDF2"] } - // class KDFAlgorithmStringLiteral extends StringLiteral { // KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) } // } - // private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { // predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KDFAlgorithmStringLiteral } - // predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof EVP_KDF_fetch_AlgorithmArg } // } - // module AlgorithmStringToFetchFlow = DataFlow::Global; - // predicate algorithmStringToKDFFetchArgFlow( // string name, KDFAlgorithmStringLiteral origin, EVP_KDF_fetch_AlgorithmArg arg // ) { // origin.getValue().toUpperCase() = name and // AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) // } - // /** // * HKDF key derivation algorithm. // */ // class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF { // KDFAlgorithmStringLiteral origin; // EVP_KDF_fetch_AlgorithmArg instance; - // HKDF() { // this = Crypto::TKeyDerivationAlgorithm(instance) and // algorithmStringToKDFFetchArgFlow("HKDF", origin, instance) // } - // override string getRawAlgorithmName() { result = origin.getValue() } - // override Crypto::HashAlgorithm getHashAlgorithm() { none() } - // override Crypto::LocatableElement getOrigin(string name) { // result = origin and name = origin.toString() // } - // override Expr getInstance() { result = origin } // } - // /** // * PBKDF2 key derivation algorithm. // */ // class PBKDF2 extends KeyDerivationAlgorithm, Crypto::PBKDF2 { // KDFAlgorithmStringLiteral origin; // EVP_KDF_fetch_AlgorithmArg instance; - // PBKDF2() { // this = Crypto::TKeyDerivationAlgorithm(instance) and // algorithmStringToKDFFetchArgFlow("PBKDF2", origin, instance) // } - // override string getRawAlgorithmName() { result = origin.getValue() } - // override string getIterationCount(Location location) { none() } // TODO - // override string getKeyLength(Location location) { none() } // TODO - // override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO - // override Crypto::LocatableElement getOrigin(string name) { // result = origin and name = origin.toString() // } - // override Expr getInstance() { result = instance } // } - // /** // * PKCS12KDF key derivation algorithm. // */ // class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF { // KDFAlgorithmStringLiteral origin; // EVP_KDF_fetch_AlgorithmArg instance; - // PKCS12KDF() { // this = Crypto::TKeyDerivationAlgorithm(instance) and // algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, instance) // } - // override string getRawAlgorithmName() { result = origin.getValue() } - // override string getIterationCount(Location location) { none() } // TODO - // override string getIDByte(Location location) { none() } // TODO - // override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO - // override Crypto::LocatableElement getOrigin(string name) { // result = origin and name = origin.toString() // } - // override Expr getInstance() { result = instance } // } } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll index 3e061eeff88f..b436f3b412a8 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll @@ -1,557 +1,495 @@ -import cpp -import semmle.code.cpp.dataflow.new.DataFlow -import LibraryDetector -import OpenSSLKnownAlgorithmConstants -import experimental.Quantum.Language -class OpenSSLAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep { - OpenSSLAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) } - - override DataFlow::Node getOutput() { - exists(AlgorithmPassthroughCall c | c.getInNode() = this and c.getOutNode() = result) - } -} - -module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - exists(OpenSSLAlgorithmGetterCall c | c.getResultNode() = source) - } - - predicate isSink(DataFlow::Node sink) { - exists(Crypto::AlgorithmConsumer c | c.getInputNode() = sink) - } -} - -module AlgGetterToAlgConsumerFlow = DataFlow::Global; - -abstract class AlgorithmPassthroughCall extends Call { - abstract DataFlow::Node getInNode(); - - abstract DataFlow::Node getOutNode(); -} - -class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall { - DataFlow::Node inNode; - DataFlow::Node outNode; - - CopyAndDupAlgorithmPassthroughCall() { - // Flow out through any return or other argument of the same type - // Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed - // to be involved - // NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup - this.getTarget().getName().toLowerCase().matches(["%_dup%", "%_copy%"]) and - exists(Expr inArg, Type t | - inArg = this.getAnArgument() and t = inArg.getUnspecifiedType().stripType() - | - inNode.asIndirectExpr() = inArg and - ( - // Case 1: flow through another argument as an out arg of the same type - exists(Expr outArg | - outArg = this.getAnArgument() and - outArg != inArg and - outArg.getUnspecifiedType().stripType() = t - | - outNode.asDefiningArgument() = outArg - ) - or - // Case 2: flow through the return value if the result is the same as the intput type - exists(Expr outArg | outArg = this and outArg.getUnspecifiedType().stripType() = t | - outNode.asIndirectExpr() = outArg - ) - ) - ) - } - - override DataFlow::Node getInNode() { result = inNode } - - override DataFlow::Node getOutNode() { result = outNode } -} - -class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall { - DataFlow::Node inNode; - DataFlow::Node outNode; - - NIDToPointerPassthroughCall() { - this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and - inNode.asExpr() = this.getArgument(0) and - outNode.asExpr() = this - //outNode.asIndirectExpr() = this - } - - override DataFlow::Node getInNode() { result = inNode } - - override DataFlow::Node getOutNode() { result = outNode } -} - -class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall { - DataFlow::Node inNode; - DataFlow::Node outNode; - - PointerToPointerPassthroughCall() { - this.getTarget().getName() = "OBJ_txt2obj" and - inNode.asIndirectExpr() = this.getArgument(0) and - outNode.asIndirectExpr() = this - or - //outNode.asExpr() = this - this.getTarget().getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and - inNode.asIndirectExpr() = this.getArgument(2) and - outNode.asDefiningArgument() = this.getArgument(0) - } - - override DataFlow::Node getInNode() { result = inNode } - - override DataFlow::Node getOutNode() { result = outNode } -} - -class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall { - DataFlow::Node inNode; - DataFlow::Node outNode; - - PointerToNIDPassthroughCall() { - this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and - ( - inNode.asIndirectExpr() = this.getArgument(0) - or - inNode.asExpr() = this.getArgument(0) - ) and - outNode.asExpr() = this - } - - override DataFlow::Node getInNode() { result = inNode } - - override DataFlow::Node getOutNode() { result = outNode } -} - -predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(AlgorithmPassthroughCall c | c.getInNode() = node1 and c.getOutNode() = node2) -} - -abstract class OpenSSLAlgorithmGetterCall extends Call { - abstract DataFlow::Node getValueArgNode(); - - abstract DataFlow::Node getResultNode(); - - abstract Expr getValueArgExpr(); - - abstract Expr getResultExpr(); -} - -module KnownOpenSSLAlgorithmToAlgorithmGetterConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - source.asExpr() instanceof KnownOpenSSLAlgorithmConstant - } - - predicate isSink(DataFlow::Node sink) { - exists(OpenSSLAlgorithmGetterCall c | c.getValueArgNode() = sink) - } - - predicate isBarrier(DataFlow::Node node) { - // False positive reducer, don't flow out through argv - exists(VariableAccess va, Variable v | - v.getAnAccess() = va and va = node.asExpr() - or - va = node.asIndirectExpr() - | - v.getName().matches("%argv") - ) - } - - predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - knownPassThroughStep(node1, node2) - } -} - -module KnownOpenSSLAlgorithmToAlgorithmGetterFlow = - DataFlow::Global; - -/** - * Cases like EVP_MD5(), - * there is no input, rather it directly gets an algorithm - * and returns it. - */ -class DirectGetterCall extends OpenSSLAlgorithmGetterCall { - DataFlow::Node resultNode; - Expr resultExpr; - - DirectGetterCall() { - this instanceof KnownOpenSSLAlgorithmConstant and - this instanceof Call and - resultExpr = this and - resultNode.asExpr() = resultExpr - } - - override DataFlow::Node getValueArgNode() { none() } - - override DataFlow::Node getResultNode() { result = resultNode } - - override Expr getValueArgExpr() { none() } - - override Expr getResultExpr() { result = resultExpr } -} - -// https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html -class EVPCipherGetterCall extends OpenSSLAlgorithmGetterCall { - DataFlow::Node valueArgNode; - DataFlow::Node resultNode; - Expr valueArgExpr; - Expr resultExpr; - - EVPCipherGetterCall() { - resultExpr = this and - resultNode.asExpr() = this and - isPossibleOpenSSLFunction(this.getTarget()) and - ( - this.getTarget().getName() in ["EVP_get_cipherbyname", "EVP_get_cipherbyobj"] and - valueArgExpr = this.getArgument(0) and - valueArgNode.asExpr() = valueArgExpr - or - this.getTarget().getName() = "EVP_CIPHER_fetch" and - valueArgExpr = this.getArgument(1) and - valueArgNode.asExpr() = valueArgExpr - or - this.getTarget().getName() = "EVP_get_cipherbynid" and - valueArgExpr = this.getArgument(0) and - valueArgNode.asExpr() = valueArgExpr - ) - } - - override DataFlow::Node getValueArgNode() { result = valueArgNode } - - override DataFlow::Node getResultNode() { result = resultNode } - - override Expr getValueArgExpr() { result = valueArgExpr } - - override Expr getResultExpr() { result = resultExpr } -} - -class EVPAsymCipherGetterCall extends OpenSSLAlgorithmGetterCall { - DataFlow::Node valueArgNode; - DataFlow::Node resultNode; - Expr valueArgExpr; - Expr resultExpr; - - EVPAsymCipherGetterCall() { - isPossibleOpenSSLFunction(this.getTarget()) and - resultExpr = this and - resultNode.asExpr() = this and - this.getTarget().getName() = "EVP_ASYM_CIPHER_fetch" and - valueArgExpr = this.getArgument(1) and - valueArgNode.asExpr() = valueArgExpr - } - - override DataFlow::Node getValueArgNode() { result = valueArgNode } - - override DataFlow::Node getResultNode() { result = resultNode } - - override Expr getValueArgExpr() { result = valueArgExpr } - - override Expr getResultExpr() { result = resultExpr } -} - -class EVPDigestGetterCall extends OpenSSLAlgorithmGetterCall { - DataFlow::Node valueArgNode; - DataFlow::Node resultNode; - Expr valueArgExpr; - Expr resultExpr; - - EVPDigestGetterCall() { - resultExpr = this and - resultNode.asExpr() = this and - isPossibleOpenSSLFunction(this.getTarget()) and - ( - this.getTarget().getName() in [ - "EVP_get_digestbyname", "EVP_get_digestbyobj", "EVP_get_digestbynid" - ] and - valueArgExpr = this.getArgument(0) and - valueArgNode.asExpr() = valueArgExpr - or - this.getTarget().getName() = "EVP_MD_fetch" and - valueArgExpr = this.getArgument(1) and - valueArgNode.asExpr() = valueArgExpr - ) - } - - override DataFlow::Node getValueArgNode() { result = valueArgNode } - - override DataFlow::Node getResultNode() { result = resultNode } - - override Expr getValueArgExpr() { result = valueArgExpr } - - override Expr getResultExpr() { result = resultExpr } -} - -class EVPKDFFetch extends OpenSSLAlgorithmGetterCall { - DataFlow::Node valueArgNode; - DataFlow::Node resultNode; - Expr valueArgExpr; - Expr resultExpr; - - EVPKDFFetch() { - resultExpr = this and - resultNode.asExpr() = this and - isPossibleOpenSSLFunction(this.getTarget()) and - this.getTarget().getName() in ["EVP_KDF_fetch"] and - valueArgExpr = this.getArgument(1) and - valueArgNode.asExpr() = valueArgExpr - } - - override DataFlow::Node getValueArgNode() { result = valueArgNode } - - override DataFlow::Node getResultNode() { result = resultNode } - - override Expr getValueArgExpr() { result = valueArgExpr } - - override Expr getResultExpr() { result = resultExpr } -} -// /** -// * Predicates/classes for identifying algorithm sinks. -// * An Algorithm Sink is a function that takes an algorithm as an argument. -// * In particular, any function that takes in an algorithm that until the call -// * the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm) -// */ -// //TODO: enforce a hierarchy of AlgorithmSinkArgument, e.g., so I can get all Asymmetric SinkArguments that includes all the strictly RSA etc. // import cpp -// // import experimental.cryptography.utils.OpenSSL.LibraryFunction -// // import experimental.cryptography.CryptoAlgorithmNames -// predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType) { arg.algType() = algType } -// abstract class AlgorithmSinkArgument extends Expr { -// AlgorithmSinkArgument() { -// exists(Call c | c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget())) +// import semmle.code.cpp.dataflow.new.DataFlow +// import LibraryDetector +// import OpenSSLKnownAlgorithmConstants +// import experimental.Quantum.Language +// class OpenSSLAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep { +// OpenSSLAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) } +// override DataFlow::Node getOutput() { +// exists(AlgorithmPassthroughCall c | c.getInNode() = this and c.getOutNode() = result) // } -// /** -// * Gets the function call in which the argument exists -// */ -// Call getSinkCall() { result.getAnArgument() = this } -// abstract string algType(); -// } -// // https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html -// predicate cipherAlgorithmSink(string funcName, int argInd) { -// funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0 -// or -// funcName = "EVP_CIPHER_fetch" and argInd = 1 // } -// class CipherAlgorithmSink extends AlgorithmSinkArgument { -// CipherAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// | -// cipherAlgorithmSink(funcName, argInd) -// ) +// module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { +// predicate isSource(DataFlow::Node source) { +// exists(OpenSSLAlgorithmGetterCall c | c.getResultNode() = source) // } -// override string algType() { result = getSymmetricEncryptionType() } -// } -// // https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch -// predicate macAlgorithmSink(string funcName, int argInd) { -// (funcName = "EVP_MAC_fetch" and argInd = 1) -// } -// class MACAlgorithmSink extends AlgorithmSinkArgument { -// MACAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// | -// macAlgorithmSink(funcName, argInd) -// ) +// predicate isSink(DataFlow::Node sink) { +// exists(Crypto::AlgorithmConsumer c | c.getInputNode() = sink) // } -// override string algType() { result = "TBD" } // } -// // https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch -// predicate messageDigestAlgorithmSink(string funcName, int argInd) { -// funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0 -// or -// funcName = "EVP_MD_fetch" and argInd = 1 +// module AlgGetterToAlgConsumerFlow = DataFlow::Global; +// abstract class AlgorithmPassthroughCall extends Call { +// abstract DataFlow::Node getInNode(); +// abstract DataFlow::Node getOutNode(); // } -// class MessageDigestAlgorithmSink extends AlgorithmSinkArgument { -// MessageDigestAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall { +// DataFlow::Node inNode; +// DataFlow::Node outNode; +// CopyAndDupAlgorithmPassthroughCall() { +// // Flow out through any return or other argument of the same type +// // Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed +// // to be involved +// // NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup +// this.getTarget().getName().toLowerCase().matches(["%_dup%", "%_copy%"]) and +// exists(Expr inArg, Type t | +// inArg = this.getAnArgument() and t = inArg.getUnspecifiedType().stripType() // | -// messageDigestAlgorithmSink(funcName, argInd) +// inNode.asIndirectExpr() = inArg and +// ( +// // Case 1: flow through another argument as an out arg of the same type +// exists(Expr outArg | +// outArg = this.getAnArgument() and +// outArg != inArg and +// outArg.getUnspecifiedType().stripType() = t +// | +// outNode.asDefiningArgument() = outArg +// ) +// or +// // Case 2: flow through the return value if the result is the same as the intput type +// exists(Expr outArg | outArg = this and outArg.getUnspecifiedType().stripType() = t | +// outNode.asIndirectExpr() = outArg +// ) +// ) // ) // } -// override string algType() { result = getHashType() } +// override DataFlow::Node getInNode() { result = inNode } +// override DataFlow::Node getOutNode() { result = outNode } // } -// // https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch -// // https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch -// predicate keyExchangeAlgorithmSink(string funcName, int argInd) { -// funcName = "EVP_KEYEXCH_fetch" and argInd = 1 -// or -// funcName = "EVP_KEM_fetch" and argInd = 1 -// } -// class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument { -// KeyExchangeAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// | -// keyExchangeAlgorithmSink(funcName, argInd) -// ) +// class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall { +// DataFlow::Node inNode; +// DataFlow::Node outNode; +// NIDToPointerPassthroughCall() { +// this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and +// inNode.asExpr() = this.getArgument(0) and +// outNode.asExpr() = this +// //outNode.asIndirectExpr() = this // } -// override string algType() { result = getKeyExchangeType() } +// override DataFlow::Node getInNode() { result = inNode } +// override DataFlow::Node getOutNode() { result = outNode } // } -// // https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch -// predicate keyManagementAlgorithmSink(string funcName, int argInd) { -// funcName = "EVP_KEYMGMT_fetch" and argInd = 1 -// } -// class KeyManagementAlgorithmSink extends AlgorithmSinkArgument { -// KeyManagementAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// | -// keyManagementAlgorithmSink(funcName, argInd) -// ) +// class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall { +// DataFlow::Node inNode; +// DataFlow::Node outNode; +// PointerToPointerPassthroughCall() { +// this.getTarget().getName() = "OBJ_txt2obj" and +// inNode.asIndirectExpr() = this.getArgument(0) and +// outNode.asIndirectExpr() = this +// or +// //outNode.asExpr() = this +// this.getTarget().getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and +// inNode.asIndirectExpr() = this.getArgument(2) and +// outNode.asDefiningArgument() = this.getArgument(0) // } -// override string algType() { result = "TBD" } +// override DataFlow::Node getInNode() { result = inNode } +// override DataFlow::Node getOutNode() { result = outNode } // } -// // https://www.openssl.org/docs/manmaster/man3/EVP_KDF -// predicate keyDerivationAlgorithmSink(string funcName, int argInd) { -// funcName = "EVP_KDF_fetch" and argInd = 1 -// } -// class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument { -// KeyDerivationAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// | -// keyDerivationAlgorithmSink(funcName, argInd) -// ) +// class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall { +// DataFlow::Node inNode; +// DataFlow::Node outNode; +// PointerToNIDPassthroughCall() { +// this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and +// ( +// inNode.asIndirectExpr() = this.getArgument(0) +// or +// inNode.asExpr() = this.getArgument(0) +// ) and +// outNode.asExpr() = this // } -// override string algType() { result = getKeyDerivationType() } +// override DataFlow::Node getInNode() { result = inNode } +// override DataFlow::Node getOutNode() { result = outNode } // } -// // https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch -// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id -// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html -// predicate asymmetricCipherAlgorithmSink(string funcName, int argInd) { -// funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1 -// or -// funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3 -// // NOTE: other cases are handled by AsymmetricAlgorithmSink +// predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) { +// exists(AlgorithmPassthroughCall c | c.getInNode() = node1 and c.getOutNode() = node2) // } -// class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument { -// AsymmetricCipherAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// | -// asymmetricCipherAlgorithmSink(funcName, argInd) -// ) -// } -// override string algType() { result = "ASYMMETRIC_ENCRYPTION" } +// abstract class OpenSSLAlgorithmGetterCall extends Call { +// abstract DataFlow::Node getValueArgNode(); +// abstract DataFlow::Node getResultNode(); +// abstract Expr getValueArgExpr(); +// abstract Expr getResultExpr(); // } -// class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { -// AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() { -// exists(Call c, string funcName | -// funcName = c.getTarget().getName() and -// this = c.getArgument(3) -// | -// funcName = "EVP_PKEY_Q_keygen" and -// c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType -// ) +// module KnownOpenSSLAlgorithmToAlgorithmGetterConfig implements DataFlow::ConfigSig { +// predicate isSource(DataFlow::Node source) { +// source.asExpr() instanceof KnownOpenSSLAlgorithmConstant // } -// override string algType() { result = "ASYMMETRIC_ENCRYPTION" } -// } -// // https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch -// predicate randomAlgorithmSink(string funcName, int argInd) { -// funcName = "EVP_RAND_fetch" and argInd = 1 -// } -// class RandomAlgorithmSink extends AlgorithmSinkArgument { -// RandomAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// | -// randomAlgorithmSink(funcName, argInd) -// ) +// predicate isSink(DataFlow::Node sink) { +// exists(OpenSSLAlgorithmGetterCall c | c.getValueArgNode() = sink) // } -// override string algType() { result = "TBD" } -// } -// // https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch -// predicate signatureAlgorithmSink(string funcName, int argInd) { -// funcName = "EVP_SIGNATURE_fetch" and argInd = 1 -// } -// class SignatureAlgorithmSink extends AlgorithmSinkArgument { -// SignatureAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// predicate isBarrier(DataFlow::Node node) { +// // False positive reducer, don't flow out through argv +// exists(VariableAccess va, Variable v | +// v.getAnAccess() = va and va = node.asExpr() +// or +// va = node.asIndirectExpr() // | -// signatureAlgorithmSink(funcName, argInd) +// v.getName().matches("%argv") // ) // } -// override string algType() { result = getSignatureType() } -// } -// // https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html -// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html -// predicate ellipticCurveAlgorithmSink(string funcName, int argInd) { -// funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0 -// or -// funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2 -// or -// funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1 -// } -// class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument { -// EllipticCurveAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// | -// ellipticCurveAlgorithmSink(funcName, argInd) -// ) +// predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { +// knownPassThroughStep(node1, node2) // } -// override string algType() { result = getEllipticCurveType() } // } +// module KnownOpenSSLAlgorithmToAlgorithmGetterFlow = +// DataFlow::Global; // /** -// * Special cased to address the fact that arg index 3 (zero offset based) is the curve name. -// * ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve +// * Cases like EVP_MD5(), +// * there is no input, rather it directly gets an algorithm +// * and returns it. // */ -// class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { -// EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() { -// exists(Call c, string funcName | -// funcName = c.getTarget().getName() and -// this = c.getArgument(3) -// | -// funcName = "EVP_PKEY_Q_keygen" and -// c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and -// c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType +// class DirectGetterCall extends OpenSSLAlgorithmGetterCall { +// DataFlow::Node resultNode; +// Expr resultExpr; +// DirectGetterCall() { +// this instanceof KnownOpenSSLAlgorithmConstant and +// this instanceof Call and +// resultExpr = this and +// resultNode.asExpr() = resultExpr +// } +// override DataFlow::Node getValueArgNode() { none() } +// override DataFlow::Node getResultNode() { result = resultNode } +// override Expr getValueArgExpr() { none() } +// override Expr getResultExpr() { result = resultExpr } +// } +// // https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html +// class EVPCipherGetterCall extends OpenSSLAlgorithmGetterCall { +// DataFlow::Node valueArgNode; +// DataFlow::Node resultNode; +// Expr valueArgExpr; +// Expr resultExpr; +// EVPCipherGetterCall() { +// resultExpr = this and +// resultNode.asExpr() = this and +// isPossibleOpenSSLFunction(this.getTarget()) and +// ( +// this.getTarget().getName() in ["EVP_get_cipherbyname", "EVP_get_cipherbyobj"] and +// valueArgExpr = this.getArgument(0) and +// valueArgNode.asExpr() = valueArgExpr +// or +// this.getTarget().getName() = "EVP_CIPHER_fetch" and +// valueArgExpr = this.getArgument(1) and +// valueArgNode.asExpr() = valueArgExpr +// or +// this.getTarget().getName() = "EVP_get_cipherbynid" and +// valueArgExpr = this.getArgument(0) and +// valueArgNode.asExpr() = valueArgExpr // ) // } -// override string algType() { result = getEllipticCurveType() } +// override DataFlow::Node getValueArgNode() { result = valueArgNode } +// override DataFlow::Node getResultNode() { result = resultNode } +// override Expr getValueArgExpr() { result = valueArgExpr } +// override Expr getResultExpr() { result = resultExpr } // } -// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html -// // https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html -// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html -// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html -// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html -// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html -// predicate asymmetricAlgorithmSink(string funcName, int argInd) { -// funcName = "EVP_PKEY_CTX_new_id" and argInd = 0 -// or -// funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1 -// or -// funcName in [ -// "EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key" -// ] and -// argInd = 0 -// or -// funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1 -// or -// // special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs -// // funcName = "EVP_PKEY_Q_keygen" and argInd = 2 -// funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1 -// // TODO consider void cases EVP_PKEY_new +// class EVPAsymCipherGetterCall extends OpenSSLAlgorithmGetterCall { +// DataFlow::Node valueArgNode; +// DataFlow::Node resultNode; +// Expr valueArgExpr; +// Expr resultExpr; +// EVPAsymCipherGetterCall() { +// isPossibleOpenSSLFunction(this.getTarget()) and +// resultExpr = this and +// resultNode.asExpr() = this and +// this.getTarget().getName() = "EVP_ASYM_CIPHER_fetch" and +// valueArgExpr = this.getArgument(1) and +// valueArgNode.asExpr() = valueArgExpr +// } +// override DataFlow::Node getValueArgNode() { result = valueArgNode } +// override DataFlow::Node getResultNode() { result = resultNode } +// override Expr getValueArgExpr() { result = valueArgExpr } +// override Expr getResultExpr() { result = resultExpr } // } -// class AsymmetricAlgorithmSink extends AlgorithmSinkArgument { -// AsymmetricAlgorithmSink() { -// exists(Call c, string funcName, int argInd | -// funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// | -// asymmetricAlgorithmSink(funcName, argInd) +// class EVPDigestGetterCall extends OpenSSLAlgorithmGetterCall { +// DataFlow::Node valueArgNode; +// DataFlow::Node resultNode; +// Expr valueArgExpr; +// Expr resultExpr; +// EVPDigestGetterCall() { +// resultExpr = this and +// resultNode.asExpr() = this and +// isPossibleOpenSSLFunction(this.getTarget()) and +// ( +// this.getTarget().getName() in [ +// "EVP_get_digestbyname", "EVP_get_digestbyobj", "EVP_get_digestbynid" +// ] and +// valueArgExpr = this.getArgument(0) and +// valueArgNode.asExpr() = valueArgExpr +// or +// this.getTarget().getName() = "EVP_MD_fetch" and +// valueArgExpr = this.getArgument(1) and +// valueArgNode.asExpr() = valueArgExpr // ) // } -// override string algType() { result = getAsymmetricType() } +// override DataFlow::Node getValueArgNode() { result = valueArgNode } +// override DataFlow::Node getResultNode() { result = resultNode } +// override Expr getValueArgExpr() { result = valueArgExpr } +// override Expr getResultExpr() { result = resultExpr } // } -// class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { -// AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() { -// exists(Call c, string funcName | -// funcName = c.getTarget().getName() and -// this = c.getArgument(2) -// | -// funcName = "EVP_PKEY_Q_keygen" and -// not exists(c.getArgument(3)) -// ) +// class EVPKDFFetch extends OpenSSLAlgorithmGetterCall { +// DataFlow::Node valueArgNode; +// DataFlow::Node resultNode; +// Expr valueArgExpr; +// Expr resultExpr; +// EVPKDFFetch() { +// resultExpr = this and +// resultNode.asExpr() = this and +// isPossibleOpenSSLFunction(this.getTarget()) and +// this.getTarget().getName() in ["EVP_KDF_fetch"] and +// valueArgExpr = this.getArgument(1) and +// valueArgNode.asExpr() = valueArgExpr // } -// override string algType() { result = getAsymmetricType() } +// override DataFlow::Node getValueArgNode() { result = valueArgNode } +// override DataFlow::Node getResultNode() { result = resultNode } +// override Expr getValueArgExpr() { result = valueArgExpr } +// override Expr getResultExpr() { result = resultExpr } // } +// // /** +// // * Predicates/classes for identifying algorithm sinks. +// // * An Algorithm Sink is a function that takes an algorithm as an argument. +// // * In particular, any function that takes in an algorithm that until the call +// // * the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm) +// // */ +// // //TODO: enforce a hierarchy of AlgorithmSinkArgument, e.g., so I can get all Asymmetric SinkArguments that includes all the strictly RSA etc. +// // import cpp +// // // import experimental.cryptography.utils.OpenSSL.LibraryFunction +// // // import experimental.cryptography.CryptoAlgorithmNames +// // predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType) { arg.algType() = algType } +// // abstract class AlgorithmSinkArgument extends Expr { +// // AlgorithmSinkArgument() { +// // exists(Call c | c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget())) +// // } +// // /** +// // * Gets the function call in which the argument exists +// // */ +// // Call getSinkCall() { result.getAnArgument() = this } +// // abstract string algType(); +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html +// // predicate cipherAlgorithmSink(string funcName, int argInd) { +// // funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0 +// // or +// // funcName = "EVP_CIPHER_fetch" and argInd = 1 +// // } +// // class CipherAlgorithmSink extends AlgorithmSinkArgument { +// // CipherAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // cipherAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = getSymmetricEncryptionType() } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch +// // predicate macAlgorithmSink(string funcName, int argInd) { +// // (funcName = "EVP_MAC_fetch" and argInd = 1) +// // } +// // class MACAlgorithmSink extends AlgorithmSinkArgument { +// // MACAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // macAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = "TBD" } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch +// // predicate messageDigestAlgorithmSink(string funcName, int argInd) { +// // funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0 +// // or +// // funcName = "EVP_MD_fetch" and argInd = 1 +// // } +// // class MessageDigestAlgorithmSink extends AlgorithmSinkArgument { +// // MessageDigestAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // messageDigestAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = getHashType() } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch +// // // https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch +// // predicate keyExchangeAlgorithmSink(string funcName, int argInd) { +// // funcName = "EVP_KEYEXCH_fetch" and argInd = 1 +// // or +// // funcName = "EVP_KEM_fetch" and argInd = 1 +// // } +// // class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument { +// // KeyExchangeAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // keyExchangeAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = getKeyExchangeType() } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch +// // predicate keyManagementAlgorithmSink(string funcName, int argInd) { +// // funcName = "EVP_KEYMGMT_fetch" and argInd = 1 +// // } +// // class KeyManagementAlgorithmSink extends AlgorithmSinkArgument { +// // KeyManagementAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // keyManagementAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = "TBD" } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_KDF +// // predicate keyDerivationAlgorithmSink(string funcName, int argInd) { +// // funcName = "EVP_KDF_fetch" and argInd = 1 +// // } +// // class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument { +// // KeyDerivationAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // keyDerivationAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = getKeyDerivationType() } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch +// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id +// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html +// // predicate asymmetricCipherAlgorithmSink(string funcName, int argInd) { +// // funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1 +// // or +// // funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3 +// // // NOTE: other cases are handled by AsymmetricAlgorithmSink +// // } +// // class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument { +// // AsymmetricCipherAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // asymmetricCipherAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = "ASYMMETRIC_ENCRYPTION" } +// // } +// // class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { +// // AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() { +// // exists(Call c, string funcName | +// // funcName = c.getTarget().getName() and +// // this = c.getArgument(3) +// // | +// // funcName = "EVP_PKEY_Q_keygen" and +// // c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType +// // ) +// // } +// // override string algType() { result = "ASYMMETRIC_ENCRYPTION" } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch +// // predicate randomAlgorithmSink(string funcName, int argInd) { +// // funcName = "EVP_RAND_fetch" and argInd = 1 +// // } +// // class RandomAlgorithmSink extends AlgorithmSinkArgument { +// // RandomAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // randomAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = "TBD" } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch +// // predicate signatureAlgorithmSink(string funcName, int argInd) { +// // funcName = "EVP_SIGNATURE_fetch" and argInd = 1 +// // } +// // class SignatureAlgorithmSink extends AlgorithmSinkArgument { +// // SignatureAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // signatureAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = getSignatureType() } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html +// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html +// // predicate ellipticCurveAlgorithmSink(string funcName, int argInd) { +// // funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0 +// // or +// // funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2 +// // or +// // funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1 +// // } +// // class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument { +// // EllipticCurveAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // ellipticCurveAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = getEllipticCurveType() } +// // } +// // /** +// // * Special cased to address the fact that arg index 3 (zero offset based) is the curve name. +// // * ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve +// // */ +// // class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { +// // EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() { +// // exists(Call c, string funcName | +// // funcName = c.getTarget().getName() and +// // this = c.getArgument(3) +// // | +// // funcName = "EVP_PKEY_Q_keygen" and +// // c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and +// // c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType +// // ) +// // } +// // override string algType() { result = getEllipticCurveType() } +// // } +// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html +// // // https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html +// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html +// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html +// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html +// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html +// // predicate asymmetricAlgorithmSink(string funcName, int argInd) { +// // funcName = "EVP_PKEY_CTX_new_id" and argInd = 0 +// // or +// // funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1 +// // or +// // funcName in [ +// // "EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key" +// // ] and +// // argInd = 0 +// // or +// // funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1 +// // or +// // // special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs +// // // funcName = "EVP_PKEY_Q_keygen" and argInd = 2 +// // funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1 +// // // TODO consider void cases EVP_PKEY_new +// // } +// // class AsymmetricAlgorithmSink extends AlgorithmSinkArgument { +// // AsymmetricAlgorithmSink() { +// // exists(Call c, string funcName, int argInd | +// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) +// // | +// // asymmetricAlgorithmSink(funcName, argInd) +// // ) +// // } +// // override string algType() { result = getAsymmetricType() } +// // } +// // class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { +// // AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() { +// // exists(Call c, string funcName | +// // funcName = c.getTarget().getName() and +// // this = c.getArgument(2) +// // | +// // funcName = "EVP_PKEY_Q_keygen" and +// // not exists(c.getArgument(3)) +// // ) +// // } +// // override string algType() { result = getAsymmetricType() } +// // } diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index cebbac2ff6bd..2f5e2ca77faa 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -318,6 +318,8 @@ module CryptographyBase Input> { abstract class CipherOutputArtifactInstance extends ArtifactInstance { final override DataFlowNode getInputNode() { none() } + + override predicate isConsumerArtifact() { none() } } // Artifacts that may be outputs or inputs @@ -967,6 +969,7 @@ module CryptographyBase Input> { newtype TPaddingType = PKCS1_v1_5() or // RSA encryption/signing padding + PSS() or PKCS7() or // Standard block cipher padding (PKCS5 for 8-byte blocks) ANSI_X9_23() or // Zero-padding except last byte = padding length NoPadding() or // Explicit no-padding @@ -988,6 +991,8 @@ module CryptographyBase Input> { final private predicate paddingToNameMapping(TPaddingType type, string name) { type instanceof PKCS1_v1_5 and name = "PKCS1_v1_5" or + type instanceof PSS and name = "PSS" + or type instanceof PKCS7 and name = "PKCS7" or type instanceof ANSI_X9_23 and name = "ANSI_X9_23" From d18dac0c8e79a738cc53eb0991bc88890cc983ed Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 20 Mar 2025 21:26:18 +0100 Subject: [PATCH 070/122] Add JCA key (generation) modelling --- java/ql/lib/experimental/Quantum/JCA.qll | 98 +++++++++++++- java/ql/lib/experimental/Quantum/Language.qll | 30 +++-- .../ql/src/experimental/Quantum/TestCipher.ql | 7 +- .../src/experimental/Quantum/TestCipherKey.ql | 17 +++ .../codeql/cryptography/Model.qll | 127 +++++++++++++++--- 5 files changed, 240 insertions(+), 39 deletions(-) create mode 100644 java/ql/src/experimental/Quantum/TestCipherKey.ql diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 336f7fe5cc55..e0e30f6d5106 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -84,7 +84,7 @@ module JCAModel { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral } predicate isSink(DataFlow::Node sink) { - exists(CipherGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) + exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode()) } } @@ -102,13 +102,13 @@ module JCAModel { class CipherStringLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance, Crypto::ModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral { - CipherGetInstanceAlgorithmArg consumer; + Crypto::AlgorithmValueConsumer consumer; CipherStringLiteralAlgorithmInstance() { - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(consumer)) + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } - CipherGetInstanceAlgorithmArg getConsumer() { result = consumer } + Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { result = this and exists(this.getRawModeAlgorithmName()) // TODO: provider defaults @@ -411,6 +411,19 @@ module JCAModel { } } + // e.g., getPublic or getPrivate + class KeyPairGetKeyCall extends MethodCall { + KeyPairGetKeyCall() { + this.getCallee().hasQualifiedName("java.security", "KeyPair", "getPublic") + or + this.getCallee().hasQualifiedName("java.security", "KeyPair", "getPrivate") + } + + DataFlow::Node getInputNode() { result.asExpr() = this.getQualifier() } + + DataFlow::Node getOutputNode() { result.asExpr() = this } + } + predicate additionalFlowSteps(DataFlow::Node node1, DataFlow::Node node2) { exists(IvParameterSpecGetIvCall m | node1.asExpr() = m.getQualifier() and @@ -421,12 +434,17 @@ module JCAModel { node1 = n.getInputNode() and node2 = n.getOutputNode() ) + or + exists(KeyPairGetKeyCall call | + node1 = call.getInputNode() and + node2 = call.getOutputNode() + ) } - class NonceAdditionalFlowInputStep extends AdditionalFlowInputStep { + class ArtifactAdditionalFlowStep extends AdditionalFlowInputStep { DataFlow::Node output; - NonceAdditionalFlowInputStep() { additionalFlowSteps(this, output) } + ArtifactAdditionalFlowStep() { additionalFlowSteps(this, output) } override DataFlow::Node getOutput() { result = output } } @@ -605,4 +623,72 @@ module JCAModel { ) } } + + class KeyGeneratorCallAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + KeyGeneratorGetInstanceCall call; + + KeyGeneratorCallAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + override DataFlow::Node getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this + } + } + + // flow from instance created by getInstance to generateKey + module KeyGeneratorGetInstanceToGenerateConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + exists(KeyGeneratorGetInstanceCall call | src.asExpr() = call) + } + + predicate isSink(DataFlow::Node sink) { + exists(KeyGeneratorGenerateCall call | sink.asExpr() = call.(MethodCall).getQualifier()) + } + } + + module KeyGeneratorGetInstanceToGenerateFlow = + DataFlow::Global; + + class KeyGeneratorGetInstanceCall extends MethodCall { + KeyGeneratorGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance") + or + this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance") + } + + Expr getAlgorithmArg() { result = super.getArgument(0) } + + predicate flowsTo(KeyGeneratorGenerateCall sink) { + KeyGeneratorGetInstanceToGenerateFlow::flow(DataFlow::exprNode(this), + DataFlow::exprNode(sink.(MethodCall).getQualifier())) + } + } + + class KeyGeneratorGenerateCall extends Crypto::KeyGenerationOperationInstance instanceof MethodCall + { + Crypto::KeyArtifactType type; + + KeyGeneratorGenerateCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "generateKey") and + type instanceof Crypto::TSymmetricKeyType + or + this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "generateKeyPair") and + type instanceof Crypto::TAsymmetricKeyType + } + + override DataFlow::Node getOutputKeyArtifact() { result.asExpr() = this } + + override Crypto::KeyArtifactType getOutputKeyType() { result = type } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + exists(KeyGeneratorGetInstanceCall getInstance | + getInstance.flowsTo(this) and result = getInstance.getAlgorithmArg() + ) + } + + Crypto::AlgorithmInstance getAKnownAlgorithm() { + result = this.getAnAlgorithmValueConsumer().getAKnownAlgorithmSource() + } + } } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 5666a0c42bb5..e8bb052b6203 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -30,6 +30,12 @@ module CryptoInput implements InputSig { result = node.asExpr() or result = node.asParameter() } + + predicate artifactOutputFlowsToGenericInput( + DataFlow::Node artifactOutput, DataFlow::Node otherInput + ) { + ArtifactUniversalFlow::flow(artifactOutput, otherInput) + } } /** @@ -70,16 +76,20 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { override string getAdditionalDescription() { result = this.toString() } } -class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { - override DataFlow::Node getOutputNode() { result.asExpr() = this } - - override predicate flowsTo(Crypto::FlowAwareElement other) { - // TODO: separate config to avoid blowing up data-flow analysis - GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - } - - override string getAdditionalDescription() { result = this.toString() } -} +/* + * class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { + * ConstantDataSource() { not this instanceof Crypto::KnownElement } + * + * override DataFlow::Node getOutputNode() { result.asExpr() = this } + * + * override predicate flowsTo(Crypto::FlowAwareElement other) { + * // TODO: separate config to avoid blowing up data-flow analysis + * GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + * } + * + * override string getAdditionalDescription() { result = this.toString() } + * } + */ /** * Random number generation, where each instance is modelled as the expression diff --git a/java/ql/src/experimental/Quantum/TestCipher.ql b/java/ql/src/experimental/Quantum/TestCipher.ql index b98347a57eca..8743ef3aa19b 100644 --- a/java/ql/src/experimental/Quantum/TestCipher.ql +++ b/java/ql/src/experimental/Quantum/TestCipher.ql @@ -7,11 +7,12 @@ import experimental.Quantum.Language from Crypto::CipherOperationNode op, Crypto::CipherAlgorithmNode a, Crypto::ModeOfOperationAlgorithmNode m, Crypto::PaddingAlgorithmNode p, - Crypto::NonceArtifactNode nonce + Crypto::NonceArtifactNode nonce, Crypto::KeyArtifactNode k where a = op.getAKnownCipherAlgorithm() and m = a.getModeOfOperation() and p = a.getPaddingAlgorithm() and - nonce = op.getANonce() + nonce = op.getANonce() and + k = op.getAKey() select op, op.getCipherOperationSubtype(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), - p, p.getRawAlgorithmName(), nonce + p, p.getRawAlgorithmName(), nonce, k, k.getSourceElement() diff --git a/java/ql/src/experimental/Quantum/TestCipherKey.ql b/java/ql/src/experimental/Quantum/TestCipherKey.ql new file mode 100644 index 000000000000..1ae0cdfd89f0 --- /dev/null +++ b/java/ql/src/experimental/Quantum/TestCipherKey.ql @@ -0,0 +1,17 @@ +/** + * @name "PQC Test" + */ + +import experimental.Quantum.Language + +from Crypto::CipherOperationNode op, Crypto::CipherAlgorithmNode a, Crypto::KeyArtifactNode k +where + a = op.getAKnownCipherAlgorithm() and + k = op.getAKey() +select op, op.getCipherOperationSubtype(), a, a.getRawAlgorithmName(), k, k.getSourceNode() +/* + * from Crypto::CipherOperationNode op + * where op.getLocation().getFile().getBaseName() = "AsymmetricEncryptionMacHybridCryptosystem.java" + * select op, op.getAKey().getSourceNode() + */ + diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 2f5e2ca77faa..2a006cd7f82d 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -22,6 +22,10 @@ signature module InputSig { class UnknownLocation instanceof Location; LocatableElement dfn_to_element(DataFlowNode node); + + predicate artifactOutputFlowsToGenericInput( + DataFlowNode artifactOutput, DataFlowNode otherFlowAwareInput + ); } module CryptographyBase Input> { @@ -51,26 +55,22 @@ module CryptographyBase Input> { ) } - NodeBase getPassthroughNodeChild(NodeBase node) { - result = node.(CipherInputNode).getChild(_) or - result = node.(NonceArtifactNode).getChild(_) - } + NodeBase getPassthroughNodeChild(NodeBase node) { result = node.getChild(_) } predicate isPassthroughNode(NodeBase node) { - node instanceof CipherInputNode or - node instanceof NonceArtifactNode + node.asElement() instanceof ArtifactConsumerAndInstance } predicate nodes_graph_impl(NodeBase node, string key, string value) { not node.isExcludedFromGraph() and - not isPassthroughNode(node) and + not isPassthroughNode(node) and // TODO: punt to fix known unknowns for passthrough nodes ( key = "semmle.label" and value = node.toString() or // CodeQL's DGML output does not include a location key = "Location" and - value = node.getLocation().toString() + value = "demo" // node.getLocation().toString() or // Known unknown edges should be reported as properties rather than edges node = node.getChild(key) and @@ -305,22 +305,17 @@ module CryptographyBase Input> { // in order for a flowsTo to be defined. At the per-modeling-instance level, extend that language-level class! abstract class OutputArtifactInstance extends ArtifactInstance { override predicate isConsumerArtifact() { none() } - } - abstract class DigestArtifactInstance extends OutputArtifactInstance { - final override DataFlowNode getInputNode() { none() } + override DataFlowNode getInputNode() { none() } } + abstract class DigestArtifactInstance extends OutputArtifactInstance { } + abstract class RandomNumberGenerationInstance extends OutputArtifactInstance { // TODO: input seed? - final override DataFlowNode getInputNode() { none() } } - abstract class CipherOutputArtifactInstance extends ArtifactInstance { - final override DataFlowNode getInputNode() { none() } - - override predicate isConsumerArtifact() { none() } - } + abstract class CipherOutputArtifactInstance extends OutputArtifactInstance { } // Artifacts that may be outputs or inputs newtype TKeyArtifactType = @@ -338,13 +333,30 @@ module CryptographyBase Input> { } } - abstract class KeyArtifactInstance extends ArtifactInstance { + abstract private class KeyArtifactInstance extends ArtifactInstance { abstract KeyArtifactType getKeyType(); } - final class KeyArtifactConsumer extends ArtifactConsumerAndInstance, KeyArtifactInstance { + final class KeyArtifactOutputInstance extends KeyArtifactInstance, OutputArtifactInstance { + KeyCreationOperationInstance creator; + + KeyArtifactOutputInstance() { Input::dfn_to_element(creator.getOutputKeyArtifact()) = this } + + final KeyCreationOperationInstance getCreator() { result = creator } + + override KeyArtifactType getKeyType() { result = creator.getOutputKeyType() } + + override DataFlowNode getOutputNode() { result = creator.getOutputKeyArtifact() } + + override predicate flowsTo(FlowAwareElement other) { + Input::artifactOutputFlowsToGenericInput(this.getOutputNode(), other.getInputNode()) + } + } + + final class KeyArtifactConsumer extends KeyArtifactInstance, ArtifactConsumerAndInstance { DataFlowNode inputNode; + // TODO: key type hint? e.g. hint: private || public KeyArtifactConsumer() { exists(CipherOperationInstance op | inputNode = op.getKeyConsumer()) and this = Input::dfn_to_element(inputNode) @@ -483,6 +495,33 @@ module CryptographyBase Input> { abstract class KeyDerivationAlgorithmInstance extends AlgorithmInstance { } + abstract private class KeyCreationOperationInstance extends OperationInstance { + abstract string getKeyCreationTypeDescription(); + + /** + * Gets the key artifact produced by this operation. + */ + abstract DataFlowNode getOutputKeyArtifact(); + + /** + * Gets the key artifact type produced. + */ + abstract KeyArtifactType getOutputKeyType(); + + /** + * Gets the key size of the key produced by this operation. + */ + string getKeySize() { none() } // TODO: punt, might need a generic value consumer? + } + + abstract class KeyGenerationOperationInstance extends KeyCreationOperationInstance { + final override string getKeyCreationTypeDescription() { result = "KeyGeneration" } + } + + abstract class KeyLoadOperationInstance extends KeyCreationOperationInstance { + final override string getKeyCreationTypeDescription() { result = "KeyLoad" } + } + private signature class AlgorithmInstanceType instanceof AlgorithmInstance; module AlgorithmInstanceOrValueConsumer { @@ -519,6 +558,8 @@ module CryptographyBase Input> { TKeyDerivationOperation(KeyDerivationOperationInstance e) or TCipherOperation(CipherOperationInstance e) or TKeyEncapsulationOperation(KeyEncapsulationOperationInstance e) or + // Key Creation Operations + TKeyCreationOperation(KeyCreationOperationInstance e) or // Algorithms (e.g., SHA-256, AES) TCipherAlgorithm(CipherAlgorithmInstanceOrValueConsumer e) or TEllipticCurveAlgorithm(EllipticCurveAlgorithmInstance e) or @@ -662,7 +703,7 @@ module CryptographyBase Input> { * * If a child class defines this predicate as `none()`, no relationship will be reported. */ - string getSourceNodeRelationship() { result = "Source" } + string getSourceNodeRelationship() { result = "Source" } // TODO: revisit why this exists override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) @@ -737,6 +778,33 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } + /** + * Gets the algorithm or unknown source nodes consumed as an algorithm associated with this operation. + */ + NodeBase getAnAlgorithmOrUnknown() { + result = this.getAKnownAlgorithm() or + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAnUnknownSourceNode() + } + + /** + * Gets a known algorithm associated with this operation + */ + CipherAlgorithmNode getAKnownAlgorithm() { + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] + edgeName = "Algorithm" and + if exists(this.getAnAlgorithmOrUnknown()) + then result = this.getAnAlgorithmOrUnknown() + else result = this + } + override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or @@ -787,6 +855,16 @@ module CryptographyBase Input> { } } + final class KeyCreationOperationNode extends OperationNode, TKeyCreationOperation { + KeyCreationOperationInstance instance; + + KeyCreationOperationNode() { this = TKeyCreationOperation(instance) } + + override LocatableElement asElement() { result = instance } + + override string getInternalType() { result = instance.getKeyCreationTypeDescription() } + } + newtype TCipherOperationSubtype = TEncryptionMode() or TDecryptionMode() or @@ -867,6 +945,11 @@ module CryptographyBase Input> { result.asElement() = this.asElement().(CipherOperationInstance).getOutputArtifact() } + KeyArtifactNode getAKey() { + result.asElement() = + Input::dfn_to_element(this.asElement().(CipherOperationInstance).getKeyConsumer()) + } + override NodeBase getChild(string key) { result = super.getChild(key) or @@ -891,6 +974,10 @@ module CryptographyBase Input> { if exists(this.getAnOutputArtifact()) then result = this.getAnOutputArtifact() else result = this + or + // [KNOWN_OR_UNKNOWN] + key = "Key" and + if exists(this.getAKey()) then result = this.getAKey() else result = this } override predicate properties(string key, string value, Location location) { From b6956413626fd8e7493fe696797d3cb7d4c90084 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Thu, 20 Mar 2025 16:27:20 -0400 Subject: [PATCH 071/122] Add signature to cipher operation --- shared/cryptography/codeql/cryptography/Model.qll | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 2f5e2ca77faa..577993e7f63b 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -792,6 +792,7 @@ module CryptographyBase Input> { TDecryptionMode() or TWrapMode() or TUnwrapMode() or + TSignatureMode() or TUnknownCipherOperationMode() abstract class CipherOperationSubtype extends TCipherOperationSubtype { @@ -814,6 +815,10 @@ module CryptographyBase Input> { override string toString() { result = "Unwrap" } } + class SignatureSubtype extends CipherOperationSubtype, TSignatureMode { + override string toString() { result = "Sign" } + } + class UnknownCipherOperationSubtype extends CipherOperationSubtype, TUnknownCipherOperationMode { override string toString() { result = "Unknown" } } From fb7c0033981e04118ad06414a7b2028334a9af05 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Sat, 22 Mar 2025 08:01:03 +0100 Subject: [PATCH 072/122] Add initial MAC support for JCA, refactor hashes --- java/ql/lib/experimental/Quantum/JCA.qll | 374 +++++++++++++++--- java/ql/lib/experimental/Quantum/Language.qll | 71 ++-- .../codeql/cryptography/Model.qll | 353 +++++++++++++---- 3 files changed, 626 insertions(+), 172 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index e0e30f6d5106..eac400183b88 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -36,6 +36,50 @@ module JCAModel { ].toUpperCase()) } + bindingset[hash] + predicate hash_names(string hash) { + hash.toUpperCase() + .matches(["SHA-%", "SHA3-%", "BLAKE2b%", "BLAKE2s%", "MD5", "RIPEMD160", "Whirlpool"] + .toUpperCase()) + } + + bindingset[name] + Crypto::THashType hash_name_to_hash_type(string name, int digestLength) { + name = "SHA-1" and result instanceof Crypto::SHA1 and digestLength = 160 + or + name = ["SHA-256", "SHA-384", "SHA-512"] and + result instanceof Crypto::SHA2 and + digestLength = name.splitAt("-", 1).toInt() + or + name = ["SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512"] and + result instanceof Crypto::SHA3 and + digestLength = name.splitAt("-", 1).toInt() + or + ( + name.matches("BLAKE2b%") and + result instanceof Crypto::BLAKE2B + or + name = "BLAKE2s" and result instanceof Crypto::BLAKE2S + ) and + ( + if exists(name.indexOf("-")) + then name.splitAt("-", 1).toInt() = digestLength + else digestLength = 512 + ) + or + name = "MD5" and + result instanceof Crypto::MD5 and + digestLength = 128 + or + name = "RIPEMD160" and + result instanceof Crypto::RIPEMD160 and + digestLength = 160 + or + name = "Whirlpool" and + result instanceof Crypto::WHIRLPOOL and + digestLength = 512 // TODO: verify + } + /** * A `StringLiteral` in the `"ALG/MODE/PADDING"` or `"ALG"` format */ @@ -99,8 +143,63 @@ module JCAModel { * * TODO: Model the case of relying on a provider default, but alert on it as a bad practice. */ - class CipherStringLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance, - Crypto::ModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral + class CipherStringLiteralPaddingAlgorithmInstance extends CipherStringLiteralAlgorithmInstance, + Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral + { + CipherStringLiteralPaddingAlgorithmInstance() { exists(super.getPadding()) } // TODO: provider defaults + + override string getRawPaddingAlgorithmName() { result = super.getPadding() } + + bindingset[name] + private predicate paddingToNameMappingKnown(Crypto::TPaddingType type, string name) { + type instanceof Crypto::NoPadding and name = "NOPADDING" + or + type instanceof Crypto::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? + or + type instanceof Crypto::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% + } + + override Crypto::TPaddingType getPaddingType() { + if this.paddingToNameMappingKnown(_, super.getPadding()) + then this.paddingToNameMappingKnown(result, super.getPadding()) + else result instanceof Crypto::OtherPadding + } + } + + class CipherStringLiteralModeAlgorithmInstance extends CipherStringLiteralPaddingAlgorithmInstance, + Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral + { + CipherStringLiteralModeAlgorithmInstance() { exists(super.getMode()) } // TODO: provider defaults + + override string getRawModeAlgorithmName() { result = super.getMode() } + + bindingset[name] + private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { + type instanceof Crypto::ECB and name = "ECB" + or + type instanceof Crypto::CBC and name = "CBC" + or + type instanceof Crypto::GCM and name = "GCM" + or + type instanceof Crypto::CTR and name = "CTR" + or + type instanceof Crypto::XTS and name = "XTS" + or + type instanceof Crypto::CCM and name = "CCM" + or + type instanceof Crypto::SIV and name = "SIV" + or + type instanceof Crypto::OCB and name = "OCB" + } + + override Crypto::TBlockCipherModeOperationType getModeType() { + if this.modeToNameMappingKnown(_, super.getMode()) + then this.modeToNameMappingKnown(result, super.getMode()) + else result instanceof Crypto::OtherMode + } + } + + class CipherStringLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof CipherStringLiteral { Crypto::AlgorithmValueConsumer consumer; @@ -111,14 +210,14 @@ module JCAModel { Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { - result = this and exists(this.getRawModeAlgorithmName()) // TODO: provider defaults + result = this // TODO: provider defaults } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { - result = this and exists(this.getRawPaddingAlgorithmName()) // TODO: provider defaults + result = this // TODO: provider defaults } - override string getRawAlgorithmName() { result = super.getValue() } + override string getRawCipherAlgorithmName() { result = super.getValue() } override Crypto::TCipherType getCipherFamily() { if this.cipherNameMappingKnown(_, super.getAlgorithmName()) @@ -155,49 +254,43 @@ module JCAModel { name = "RSA" and type instanceof Crypto::RSA } + } - private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { - type instanceof Crypto::ECB and name = "ECB" - or - type instanceof Crypto::CBC and name = "CBC" - or - type instanceof Crypto::GCM and name = "GCM" - or - type instanceof Crypto::CTR and name = "CTR" - or - type instanceof Crypto::XTS and name = "XTS" - or - type instanceof Crypto::CCM and name = "CCM" - or - type instanceof Crypto::SIV and name = "SIV" - or - type instanceof Crypto::OCB and name = "OCB" - } + bindingset[input] + predicate oaep_padding_string_components(string input, string hash, string mfg) { + exists(string regex | + regex = "OAEPWith(.*)And(.*)Padding" and + hash = input.regexpCapture(regex, 1) and + mfg = input.regexpCapture(regex, 2) + ) + } - override Crypto::TBlockCipherModeOperationType getModeType() { - if this.modeToNameMappingKnown(_, super.getMode()) - then this.modeToNameMappingKnown(result, super.getMode()) - else result instanceof Crypto::OtherMode + predicate oaep_padding_string_components_eval(string hash, string mfg) { + oaep_padding_string_components(any(CipherStringLiteral s).getPadding(), hash, mfg) + } + + class OAEPPaddingHashAlgorithmInstance extends OAEPPaddingAlgorithmInstance, + Crypto::HashAlgorithmInstance instanceof CipherStringLiteral + { + string hashName; + + OAEPPaddingHashAlgorithmInstance() { + oaep_padding_string_components(super.getPadding(), hashName, _) } - override string getRawModeAlgorithmName() { result = super.getMode() } + override string getRawHashAlgorithmName() { result = super.getPadding() } - override string getRawPaddingAlgorithmName() { result = super.getPadding() } + override Crypto::THashType getHashFamily() { result = hash_name_to_hash_type(hashName, _) } - bindingset[name] - private predicate paddingToNameMappingKnown(Crypto::TPaddingType type, string name) { - type instanceof Crypto::NoPadding and name = "NOPADDING" - or - type instanceof Crypto::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? - or - type instanceof Crypto::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% - } + override int getDigestLength() { exists(hash_name_to_hash_type(hashName, result)) } + } - override Crypto::TPaddingType getPaddingType() { - if this.paddingToNameMappingKnown(_, super.getPadding()) - then this.paddingToNameMappingKnown(result, super.getPadding()) - else result instanceof Crypto::OtherPadding - } + class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance, + CipherStringLiteralPaddingAlgorithmInstance + { + override Crypto::HashAlgorithmInstance getOAEPEncodingHashAlgorithm() { result = this } + + override Crypto::HashAlgorithmInstance getMGF1HashAlgorithm() { none() } // TODO } /** @@ -412,11 +505,13 @@ module JCAModel { } // e.g., getPublic or getPrivate - class KeyPairGetKeyCall extends MethodCall { - KeyPairGetKeyCall() { + class KeyAdditionalFlowSteps extends MethodCall { + KeyAdditionalFlowSteps() { this.getCallee().hasQualifiedName("java.security", "KeyPair", "getPublic") or this.getCallee().hasQualifiedName("java.security", "KeyPair", "getPrivate") + or + this.getCallee().hasQualifiedName("java.security", "Key", "getEncoded") } DataFlow::Node getInputNode() { result.asExpr() = this.getQualifier() } @@ -435,7 +530,7 @@ module JCAModel { node2 = n.getOutputNode() ) or - exists(KeyPairGetKeyCall call | + exists(KeyAdditionalFlowSteps call | node1 = call.getInputNode() and node2 = call.getOutputNode() ) @@ -519,21 +614,12 @@ module JCAModel { override DataFlow::Node getInputNode() { result.asExpr() = this } } - class CipherOperationCallOutput extends CipherOutputArtifact { + class CipherOperationCallOutput extends Crypto::CipherOutputArtifactInstance { CipherOperationCallOutput() { this = any(CipherOperationCall call).getOutput() } override DataFlow::Node getOutputNode() { result.asExpr() = this } } - bindingset[hash] - predicate hash_names(string hash) { - hash.toUpperCase() - .matches([ - "SHA-1", "SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256", "SHA3-384", - "SHA3-512", "BLAKE2b", "BLAKE2s" - ].toUpperCase()) - } - // flow config from a known hash algorithm literal to MessageDigest.getInstance module KnownHashAlgorithmLiteralToMessageDigestConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { hash_names(src.asExpr().(StringLiteral).getValue()) } @@ -557,10 +643,14 @@ module JCAModel { MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer } - override string getRawAlgorithmName() { result = this.(StringLiteral).getValue() } + override string getRawHashAlgorithmName() { result = this.(StringLiteral).getValue() } override Crypto::THashType getHashFamily() { - result = Crypto::OtherHashType() // TODO + result = hash_name_to_hash_type(this.getRawHashAlgorithmName(), _) + } + + override int getDigestLength() { + exists(hash_name_to_hash_type(this.getRawHashAlgorithmName(), result)) } } @@ -606,7 +696,7 @@ module JCAModel { module DigestGetInstanceToDigestFlow = DataFlow::Global; - class DigestArtifact extends DigestArtifactInstance { + class DigestArtifact extends Crypto::DigestArtifactInstance { DigestArtifact() { this = any(DigestCall call).getDigestArtifactOutput() } override DataFlow::Node getOutputNode() { result.asExpr() = this } @@ -659,7 +749,7 @@ module JCAModel { Expr getAlgorithmArg() { result = super.getArgument(0) } - predicate flowsTo(KeyGeneratorGenerateCall sink) { + predicate flowsToKeyGenerateCallQualifier(KeyGeneratorGenerateCall sink) { KeyGeneratorGetInstanceToGenerateFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(sink.(MethodCall).getQualifier())) } @@ -683,7 +773,7 @@ module JCAModel { override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { exists(KeyGeneratorGetInstanceCall getInstance | - getInstance.flowsTo(this) and result = getInstance.getAlgorithmArg() + getInstance.flowsToKeyGenerateCallQualifier(this) and result = getInstance.getAlgorithmArg() ) } @@ -691,4 +781,170 @@ module JCAModel { result = this.getAnAlgorithmValueConsumer().getAKnownAlgorithmSource() } } + + /* + * TODO: + * + * MAC Algorithms possible (JCA Default + BouncyCastle Extensions) + * + * Name Type Description + * --------------------------------------------------------------------------- + * "HmacMD5" HMAC HMAC with MD5 (not recommended) + * "HmacSHA1" HMAC HMAC with SHA-1 (not recommended) + * "HmacSHA224" HMAC HMAC with SHA-224 + * "HmacSHA256" HMAC HMAC with SHA-256 + * "HmacSHA384" HMAC HMAC with SHA-384 + * "HmacSHA512" HMAC HMAC with SHA-512 + * + * (BouncyCastle and Other Provider Extensions) + * "AESCMAC" CMAC Cipher-based MAC using AES + * "DESCMAC" CMAC CMAC with DES (legacy) + * "GMAC" GCM-based MAC Authenticates AAD only (GCM-style) + * "Poly1305" AEAD-style MAC Used with ChaCha20 + * "SipHash" Hash-based MAC Fast MAC for short inputs + * "BLAKE2BMAC" HMAC-style BLAKE2b MAC (cryptographic hash) + * "HmacRIPEMD160" HMAC HMAC with RIPEMD160 hash + */ + + bindingset[name] + predicate mac_names(string name) { + name.toUpperCase() + .matches([ + "HMAC%", "AESCMAC", "DESCMAC", "GMAC", "Poly1305", "SipHash", "BLAKE2BMAC", + "HMACRIPEMD160" + ].toUpperCase()) + } + + bindingset[name] + predicate mac_name_to_mac_type_known(Crypto::TMACType type, string name) { + type instanceof Crypto::THMAC and + name.toUpperCase().matches("HMAC%") + } + + module MACKnownAlgorithmToConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { mac_names(src.asExpr().(StringLiteral).getValue()) } + + predicate isSink(DataFlow::Node sink) { + exists(MACGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) + } + } + + module MACKnownAlgorithmToConsumerFlow = DataFlow::Global; + + module MACGetInstanceToMACOperationFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MACGetInstanceCall } + + predicate isSink(DataFlow::Node sink) { + exists(MACOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier()) or + exists(MACInitCall call | sink.asExpr() = call.(MethodCall).getQualifier()) + } + } + + module MACGetInstanceToMACOperationFlow = + DataFlow::Global; + + module MACInitCallToMACOperationFlowConfig implements DataFlow::ConfigSig { + // TODO: use flow state with one config + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MACInitCall } + + predicate isSink(DataFlow::Node sink) { + exists(MACOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier()) + } + } + + module MACInitCallToMACOperationFlow = DataFlow::Global; + + class KnownMACAlgorithm extends Crypto::MACAlgorithmInstance instanceof StringLiteral { + MACGetInstanceAlgorithmValueConsumer consumer; + + KnownMACAlgorithm() { + mac_names(this.getValue()) and + MACKnownAlgorithmToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + } + + MACGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer } + + override string getRawMACAlgorithmName() { result = super.getValue() } + + override Crypto::TMACType getMACType() { + if mac_name_to_mac_type_known(_, super.getValue()) + then mac_name_to_mac_type_known(result, super.getValue()) + else result instanceof Crypto::TOtherMACType + } + } + + class MACGetInstanceCall extends MethodCall { + MACGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Mac", "getInstance") } + + Expr getAlgorithmArg() { result = this.getArgument(0) } + + MACOperationCall getOperation() { + MACGetInstanceToMACOperationFlow::flow(DataFlow::exprNode(this), + DataFlow::exprNode(result.(MethodCall).getQualifier())) + } + + MACInitCall getInitCall() { + MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this), + DataFlow::exprNode(result.getQualifier())) + } + } + + class MACInitCall extends MethodCall { + MACInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Mac", "init") } + + Expr getKeyArg() { + result = this.getArgument(0) and this.getMethod().getParameterType(0).hasName("Key") + } + + MACOperationCall getOperation() { + MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this), + DataFlow::exprNode(result.(MethodCall).getQualifier())) + } + } + + class MACGetInstanceAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + MACGetInstanceCall call; + + MACGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + override DataFlow::Node getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(KnownMACAlgorithm l | l.getConsumer() = this and result = l) + } + } + + class MACOperationCall extends Crypto::MACOperationInstance instanceof MethodCall { + Expr output; + + MACOperationCall() { + super.getMethod().getDeclaringType().hasQualifiedName("javax.crypto", "Mac") and + ( + super.getMethod().hasStringSignature(["doFinal()", "doFinal(byte[])"]) and this = output + or + super.getMethod().hasStringSignature("doFinal(byte[], int)") and + this.getArgument(0) = output + ) + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + exists(MACGetInstanceCall instantiation | + instantiation.getOperation() = this and result = instantiation.getAlgorithmArg() + ) + } + + override DataFlow::Node getKeyConsumer() { + exists(MACGetInstanceCall instantiation, MACInitCall initCall | + instantiation.getOperation() = this and + initCall.getOperation() = this and + instantiation.getInitCall() = initCall and + result.asExpr() = initCall.getKeyArg() + ) + } + + override DataFlow::Node getMessageConsumer() { + result.asExpr() = super.getArgument(0) and + super.getMethod().getParameterType(0).hasName("byte[]") + } + } } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index e8bb052b6203..74edfec1d390 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -26,6 +26,10 @@ module CryptoInput implements InputSig { class UnknownLocation = UnknownDefaultLocation; + string locationToFileBaseNameAndLineNumberString(Location location) { + result = location.getFile().getBaseName() + ":" + location.getStartLine() + } + LocatableElement dfn_to_element(DataFlow::Node node) { result = node.asExpr() or result = node.asParameter() @@ -50,6 +54,20 @@ final class DefaultFlowSource = SourceNode; final class DefaultRemoteFlowSource = RemoteFlowSource; +class GenericUnreferencedParameterSource extends Crypto::GenericUnreferencedParameterSource { + GenericUnreferencedParameterSource() { + exists(Parameter p | this = p and not exists(p.getAnArgument())) + } + + override predicate flowsTo(Crypto::FlowAwareElement other) { + GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + } + + override DataFlow::Node getOutputNode() { result.asParameter() = this } + + override string getAdditionalDescription() { result = this.toString() } +} + class GenericLocalDataSource extends Crypto::GenericLocalDataSource { GenericLocalDataSource() { any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this @@ -76,20 +94,16 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { override string getAdditionalDescription() { result = this.toString() } } -/* - * class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { - * ConstantDataSource() { not this instanceof Crypto::KnownElement } - * - * override DataFlow::Node getOutputNode() { result.asExpr() = this } - * - * override predicate flowsTo(Crypto::FlowAwareElement other) { - * // TODO: separate config to avoid blowing up data-flow analysis - * GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - * } - * - * override string getAdditionalDescription() { result = this.toString() } - * } - */ +class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { + override DataFlow::Node getOutputNode() { result.asExpr() = this } + + override predicate flowsTo(Crypto::FlowAwareElement other) { + // TODO: separate config to avoid blowing up data-flow analysis + GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + } + + override string getAdditionalDescription() { result = this.toString() } +} /** * Random number generation, where each instance is modelled as the expression @@ -97,10 +111,6 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { */ abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance { override DataFlow::Node getOutputNode() { result.asExpr() = this } - - override predicate flowsTo(Crypto::FlowAwareElement other) { - ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - } } class SecureRandomnessInstance extends RandomnessInstance { @@ -115,17 +125,6 @@ class InsecureRandomnessInstance extends RandomnessInstance { InsecureRandomnessInstance() { exists(InsecureRandomnessSource node | this = node.asExpr()) } } -/** - * Output artifact flow logic - */ -abstract class DigestArtifactInstance extends Crypto::DigestArtifactInstance { - override predicate flowsTo(Crypto::FlowAwareElement other) { - ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - } - - override predicate isConsumerArtifact() { none() } -} - /** * Artifact output to node input configuration */ @@ -137,14 +136,6 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node { module ArtifactUniversalFlow = DataFlow::Global; -abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance { - override predicate flowsTo(Crypto::FlowAwareElement other) { - ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - } - - override predicate isConsumerArtifact() { none() } -} - /** * Generic data source to node input configuration */ @@ -167,6 +158,12 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { node1.(AdditionalFlowInputStep).getOutput() = node2 + or + exists(MethodCall m | + m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and + node1.asExpr() = m.getQualifier() and + node2.asExpr() = m + ) } } diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 2a006cd7f82d..0ed24452c0bd 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -21,6 +21,8 @@ signature module InputSig { class UnknownLocation instanceof Location; + string locationToFileBaseNameAndLineNumberString(Location location); + LocatableElement dfn_to_element(DataFlowNode node); predicate artifactOutputFlowsToGenericInput( @@ -70,7 +72,7 @@ module CryptographyBase Input> { or // CodeQL's DGML output does not include a location key = "Location" and - value = "demo" // node.getLocation().toString() + value = Input::locationToFileBaseNameAndLineNumberString(node.getLocation()) // node.getLocation().toString() or // Known unknown edges should be reported as properties rather than edges node = node.getChild(key) and @@ -146,13 +148,7 @@ module CryptographyBase Input> { /** * An element that represents a _known_ cryptographic algorithm. */ - abstract class AlgorithmInstance extends KnownElement { - /** - * Gets the raw name as it appears in source, e.g., "AES/CBC/PKCS7Padding". - * This name is not parsed or formatted. - */ - abstract string getRawAlgorithmName(); - } + abstract class AlgorithmInstance extends KnownElement { } /** * An element that represents an _unknown_ data-source with a non-statically determinable value. @@ -173,6 +169,10 @@ module CryptographyBase Input> { final override string getInternalType() { result = "ExternalCall" } // TODO: call target name or toString of source? } + abstract class GenericUnreferencedParameterSource extends GenericDataSourceInstance { + final override string getInternalType() { result = "Parameter" } // TODO: toString of source? + } + abstract class GenericRemoteDataSource extends GenericDataSourceInstance { final override string getInternalType() { result = "RemoteData" } // TODO: toString of source? } @@ -300,6 +300,17 @@ module CryptographyBase Input> { final override DataFlowNode getInputNode() { result = inputNode } } + final private class MACInputArtifactConsumer extends ArtifactConsumerAndInstance { + DataFlowNode inputNode; + + MACInputArtifactConsumer() { + exists(MACOperationInstance op | inputNode = op.getMessageConsumer()) and + this = Input::dfn_to_element(inputNode) + } + + final override DataFlowNode getInputNode() { result = inputNode } + } + // Output artifacts are determined solely by the element that produces them. // Implementation guidance: these *do* need to be defined generically at the language-level // in order for a flowsTo to be defined. At the per-modeling-instance level, extend that language-level class! @@ -307,6 +318,10 @@ module CryptographyBase Input> { override predicate isConsumerArtifact() { none() } override DataFlowNode getInputNode() { none() } + + final override predicate flowsTo(FlowAwareElement other) { + Input::artifactOutputFlowsToGenericInput(this.getOutputNode(), other.getInputNode()) + } } abstract class DigestArtifactInstance extends OutputArtifactInstance { } @@ -347,10 +362,6 @@ module CryptographyBase Input> { override KeyArtifactType getKeyType() { result = creator.getOutputKeyType() } override DataFlowNode getOutputNode() { result = creator.getOutputKeyArtifact() } - - override predicate flowsTo(FlowAwareElement other) { - Input::artifactOutputFlowsToGenericInput(this.getOutputNode(), other.getInputNode()) - } } final class KeyArtifactConsumer extends KeyArtifactInstance, ArtifactConsumerAndInstance { @@ -402,6 +413,12 @@ module CryptographyBase Input> { } abstract class CipherAlgorithmInstance extends AlgorithmInstance { + /** + * Gets the raw name as it appears in source, e.g., "AES/CBC/PKCS7Padding". + * This name is not parsed or formatted. + */ + abstract string getRawCipherAlgorithmName(); + /** * Gets the type of this cipher, e.g., "AES" or "ChaCha20". */ @@ -466,12 +483,51 @@ module CryptographyBase Input> { /** * Gets the hash algorithm used in this padding scheme. */ - abstract HashAlgorithmInstance getHashAlgorithm(); + abstract HashAlgorithmInstance getOAEPEncodingHashAlgorithm(); + + /** + * Gets the hash algorithm used by MGF1 (assumption: MGF1 is the only MGF used by OAEP) + */ + abstract HashAlgorithmInstance getMGF1HashAlgorithm(); + } + + newtype TMACType = + THMAC() or + TOtherMACType() + + abstract class MACAlgorithmInstance extends AlgorithmInstance { + /** + * Gets the type of this MAC algorithm, e.g., "HMAC" or "CMAC". + */ + abstract TMACType getMACType(); + + /** + * Gets the isolated name as it appears in source, e.g., "HMAC-SHA256" in "HMAC-SHA256/UnrelatedInformation". + * + * This name should not be parsed or formatted beyond isolating the raw MAC name if necessary. + */ + abstract string getRawMACAlgorithmName(); + } + + abstract class MACOperationInstance extends OperationInstance { + /** + * Gets the message input used in this operation. + */ + abstract DataFlowNode getMessageConsumer(); /** - * Gets the mask generation function used in this padding scheme. + * Gets the key used in this operation. */ - abstract HashAlgorithmInstance getMaskGenerationFunction(); + abstract DataFlowNode getKeyConsumer(); + } + + abstract class HMACAlgorithmInstance extends MACAlgorithmInstance { + HMACAlgorithmInstance() { this.getMACType() instanceof THMAC } + + /** + * Gets the hash algorithm used by this HMAC algorithm. + */ + abstract AlgorithmValueConsumer getHashAlgorithmValueConsumer(); } abstract class KeyEncapsulationOperationInstance extends OperationInstance { } @@ -489,6 +545,16 @@ module CryptographyBase Input> { * Gets the type of this digest algorithm, e.g., "SHA1", "SHA2", "MD5" etc. */ abstract THashType getHashFamily(); + + /** + * Gets the isolated name as it appears in source, e.g., "SHA-256" in "SHA-256/PKCS7Padding". + */ + abstract string getRawHashAlgorithmName(); + + /** + * Gets the length of the hash digest in bits. + */ + abstract int getDigestLength(); } abstract class KeyDerivationOperationInstance extends OperationInstance { } @@ -512,6 +578,10 @@ module CryptographyBase Input> { * Gets the key size of the key produced by this operation. */ string getKeySize() { none() } // TODO: punt, might need a generic value consumer? + + final KeyArtifactOutputInstance getKeyArtifactOutputInstance() { + result.getOutputNode() = this.getOutputKeyArtifact() + } } abstract class KeyGenerationOperationInstance extends KeyCreationOperationInstance { @@ -524,13 +594,18 @@ module CryptographyBase Input> { private signature class AlgorithmInstanceType instanceof AlgorithmInstance; - module AlgorithmInstanceOrValueConsumer { + private signature predicate isCandidateAVCSig(AlgorithmValueConsumer avc); + + module AlgorithmInstanceOrValueConsumer< + AlgorithmInstanceType Alg, isCandidateAVCSig/1 isCandidateAVC> + { class Union extends LocatableElement { Union() { this instanceof Alg or - this instanceof AlgorithmValueConsumer and - not exists(this.(AlgorithmValueConsumer).getASource()) + isCandidateAVC(this) and + not exists(this.(AlgorithmValueConsumer).getAKnownAlgorithmSource()) and + exists(this.(AlgorithmValueConsumer).getAnUnknownSource()) } Alg asAlg() { result = this } @@ -539,25 +614,43 @@ module CryptographyBase Input> { } } - class CipherAlgorithmInstanceOrValueConsumer = - AlgorithmInstanceOrValueConsumer::Union; + private predicate isHashAVC(AlgorithmValueConsumer avc) { + exists(HashOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) or + exists(HMACAlgorithmInstance alg | avc = alg.getAConsumer()) + } + + private predicate isCipherAVC(AlgorithmValueConsumer avc) { + exists(CipherOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) + } + + private predicate isMACAVC(AlgorithmValueConsumer avc) { + exists(MACOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) + } + + final private class CipherAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; + + final private class HashAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; - class HashAlgorithmInstanceOrValueConsumer = - AlgorithmInstanceOrValueConsumer::Union; + private class MACAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; - newtype TNode = + private newtype TNode = // Artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or TNonce(NonceArtifactConsumer e) or TCipherInput(CipherInputArtifactConsumer e) or TCipherOutput(CipherOutputArtifactInstance e) or + TMACMessageInput(MACInputArtifactConsumer e) or TRandomNumberGeneration(RandomNumberGenerationInstance e) { e.flowsTo(_) } or // Operations (e.g., hashing, encryption) THashOperation(HashOperationInstance e) or TKeyDerivationOperation(KeyDerivationOperationInstance e) or TCipherOperation(CipherOperationInstance e) or TKeyEncapsulationOperation(KeyEncapsulationOperationInstance e) or + TMACOperation(MACOperationInstance e) or // Key Creation Operations TKeyCreationOperation(KeyCreationOperationInstance e) or // Algorithms (e.g., SHA-256, AES) @@ -566,6 +659,7 @@ module CryptographyBase Input> { THashAlgorithm(HashAlgorithmInstanceOrValueConsumer e) or TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstance e) or TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or + TMACAlgorithm(MACAlgorithmInstanceOrValueConsumer e) or // Non-standalone Algorithms (e.g., Mode, Padding) // TODO: need to rename this, as "mode" is getting reused in different contexts, be precise TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or @@ -738,6 +832,8 @@ module CryptographyBase Input> { final override string getInternalType() { result = "CipherOutput" } override LocatableElement asElement() { result = instance } + + override string getSourceNodeRelationship() { none() } } /** @@ -764,6 +860,8 @@ module CryptographyBase Input> { final override string getInternalType() { result = "RandomNumberGeneration" } override LocatableElement asElement() { result = instance } + + override string getSourceNodeRelationship() { none() } // TODO: seed? } /** @@ -813,6 +911,127 @@ module CryptographyBase Input> { value = instance.getKeyType().toString() and location = this.getLocation() } + + override string getSourceNodeRelationship() { + instance.isConsumerArtifact() and + result = "Source" + } + } + + final class MACMessageInputNode extends ArtifactNode, TMACMessageInput { + MACInputArtifactConsumer instance; + + MACMessageInputNode() { this = TMACMessageInput(instance) } + + final override string getInternalType() { result = "MACMessageInput" } + + override LocatableElement asElement() { result = instance } + } + + /** + * A MAC operation that produces a MAC value. + */ + final class MACOperationNode extends OperationNode, TMACOperation { + MACOperationInstance instance; + + MACOperationNode() { this = TMACOperation(instance) } + + final override string getInternalType() { result = "MACOperation" } + + override LocatableElement asElement() { result = instance } + + MACMessageInputNode getAMessage() { + result.asElement() = Input::dfn_to_element(instance.getMessageConsumer()) + } + + KeyArtifactNode getAKey() { + result.asElement() = Input::dfn_to_element(instance.getKeyConsumer()) + } + + /** + * Gets the algorithm or unknown source nodes consumed as an algorithm associated with this operation. + */ + NodeBase getAMACAlgorithmOrUnknown() { + result = this.getAKnownMACAlgorithm() or + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAnUnknownSourceNode() + } + + /** + * Gets a known algorithm associated with this operation + */ + MACAlgorithmNode getAKnownMACAlgorithm() { + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] + edgeName = "Algorithm" and + if exists(this.getAMACAlgorithmOrUnknown()) + then result = this.getAMACAlgorithmOrUnknown() + else result = this + or + // [KNOWN_OR_UNKNOWN] + edgeName = "Message" and + if exists(this.getAMessage()) then result = this.getAMessage() else result = this + or + // [KNOWN_OR_UNKNOWN] + edgeName = "Key" and + if exists(this.getAKey()) then result = this.getAKey() else result = this + } + } + + /** + * A MAC algorithm, such as HMAC or CMAC. + */ + class MACAlgorithmNode extends AlgorithmNode, TMACAlgorithm { + MACAlgorithmInstanceOrValueConsumer instance; + + MACAlgorithmNode() { this = TMACAlgorithm(instance) } + + final override string getInternalType() { result = "MACAlgorithm" } + + override LocatableElement asElement() { result = instance } + + final override string getRawAlgorithmName() { + result = instance.asAlg().getRawMACAlgorithmName() + } + + TMACType getMACType() { result = instance.asAlg().getMACType() } + + bindingset[type] + final private predicate macToNameMapping(TMACType type, string name) { + type instanceof THMAC and + name = "HMAC" + or + type instanceof TOtherMACType and + name = this.getRawAlgorithmName() + } + + override string getAlgorithmName() { this.macToNameMapping(this.getMACType(), result) } + } + + final class HMACAlgorithmNode extends MACAlgorithmNode { + HMACAlgorithmInstance hmacInstance; + + HMACAlgorithmNode() { hmacInstance = instance.asAlg() } + + NodeBase getHashAlgorithmOrUnknown() { + result.asElement() = hmacInstance.getHashAlgorithmValueConsumer().getASource() + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] + edgeName = "H" and + if exists(this.getHashAlgorithmOrUnknown()) + then result = this.getHashAlgorithmOrUnknown() + else result = this + } } /** @@ -863,6 +1082,21 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } override string getInternalType() { result = instance.getKeyCreationTypeDescription() } + + /** + * Gets the key artifact produced by this operation. + */ + KeyArtifactNode getOutputKeyArtifact() { + instance.getKeyArtifactOutputInstance() = result.asElement() + } + + override NodeBase getChild(string key) { + result = super.getChild(key) + or + // [ALWAYS_KNOWN] + key = "Output" and + result = this.getOutputKeyArtifact() + } } newtype TCipherOperationSubtype = @@ -1101,10 +1335,12 @@ module CryptographyBase Input> { OAEPPaddingAlgorithmNode() { this = TPaddingAlgorithm(instance) } - HashAlgorithmNode getHashAlgorithm() { result.asElement() = instance.getHashAlgorithm() } + HashAlgorithmNode getOAEPEncodingHashAlgorithm() { + result.asElement() = instance.getOAEPEncodingHashAlgorithm() + } - HashAlgorithmNode getMaskGenerationFunction() { - result.asElement() = instance.getMaskGenerationFunction() + HashAlgorithmNode getMGF1HashAlgorithm() { + result.asElement() = instance.getMGF1HashAlgorithm() } override NodeBase getChild(string edgeName) { @@ -1112,12 +1348,14 @@ module CryptographyBase Input> { or // [KNOWN_OR_UNKNOWN] edgeName = "MD" and - if exists(this.getHashAlgorithm()) then result = this.getHashAlgorithm() else result = this + if exists(this.getOAEPEncodingHashAlgorithm()) + then result = this.getOAEPEncodingHashAlgorithm() + else result = this or // [KNOWN_OR_UNKNOWN] - edgeName = "MGF" and - if exists(this.getMaskGenerationFunction()) - then result = this.getMaskGenerationFunction() + edgeName = "MGF1Hash" and + if exists(this.getMGF1HashAlgorithm()) + then result = this.getMGF1HashAlgorithm() else result = this } } @@ -1184,7 +1422,9 @@ module CryptographyBase Input> { this.cipherFamilyToNameAndStructure(this.getCipherFamily(), result, _) } - final override string getRawAlgorithmName() { result = instance.asAlg().getRawAlgorithmName() } + final override string getRawAlgorithmName() { + result = instance.asAlg().getRawCipherAlgorithmName() + } /** * Gets the key size of this cipher, e.g., "128" or "256". @@ -1379,7 +1619,7 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } - override string getRawAlgorithmName() { result = instance.asAlg().getRawAlgorithmName() } + override string getRawAlgorithmName() { result = instance.asAlg().getRawHashAlgorithmName() } final predicate hashTypeToNameMapping(THashType type, string name) { type instanceof BLAKE2B and name = "BLAKE2B" @@ -1416,58 +1656,19 @@ module CryptographyBase Input> { * * When modeling a new hashing algorithm, use this predicate to specify the type of the algorithm. */ - abstract THashType getHashType(); - - override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } - - /** - * Gets the digest size of SHA2 or SHA3 algorithms. - * - * This predicate does not need to hold for other algorithms, - * as the digest size is already known based on the algorithm itself. - * - * For `OtherHashType` algorithms where a digest size should be reported, `THashType` - * should be extended to explicitly model that algorithm. If the algorithm has variable - * or multiple digest size variants, a similar predicate to this one must be defined - * for that algorithm to report the digest size. - */ - abstract string getSHA2OrSHA3DigestSize(Location location); - - bindingset[type] - private string getTypeDigestSizeFixed(THashType type) { - type instanceof MD2 and result = "128" - or - type instanceof MD4 and result = "128" - or - type instanceof MD5 and result = "128" - or - type instanceof SHA1 and result = "160" - or - type instanceof RIPEMD160 and result = "160" - or - type instanceof WHIRLPOOL and result = "512" - } + THashType getHashFamily() { result = instance.asAlg().getHashFamily() } - bindingset[type] - private string getTypeDigestSize(THashType type, Location location) { - result = this.getTypeDigestSizeFixed(type) and location = this.getLocation() - or - type instanceof SHA2 and result = this.getSHA2OrSHA3DigestSize(location) - or - type instanceof SHA3 and result = this.getSHA2OrSHA3DigestSize(location) - } + override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashFamily(), result) } - string getDigestSize(Location location) { - result = this.getTypeDigestSize(this.getHashType(), location) - } + int getDigestLength() { result = instance.asAlg().getDigestLength() } final override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or // [KNOWN_OR_UNKNOWN] key = "DigestSize" and - if exists(this.getDigestSize(location)) - then value = this.getDigestSize(location) + if exists(this.getDigestLength()) + then value = this.getDigestLength().toString() and location = this.getLocation() else ( value instanceof UnknownPropertyValue and location instanceof UnknownLocation ) From 9cd0340d21aa8e582da299bb0a1c0c981317eb59 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Wed, 26 Mar 2025 13:26:45 +0100 Subject: [PATCH 073/122] Remove rankdir=LR; from DOT output --- misc/scripts/cryptography/generate_cbom.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/scripts/cryptography/generate_cbom.py b/misc/scripts/cryptography/generate_cbom.py index 07400d6cd6a3..fa4c2cb1d023 100644 --- a/misc/scripts/cryptography/generate_cbom.py +++ b/misc/scripts/cryptography/generate_cbom.py @@ -39,8 +39,7 @@ def convert_dgml_to_dot(dgml_file, dot_file): # Form dot element sequence body_l = ["digraph cbom {", - "node [shape=box];", - "rankdir=LR;" + "node [shape=box];" ] # Process nodes From bec69ca1061875679086c7624165ffb001cc4f59 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Wed, 26 Mar 2025 13:27:32 +0100 Subject: [PATCH 074/122] Refactor consumer and generic source model --- java/ql/lib/experimental/Quantum/JCA.qll | 239 +++- java/ql/lib/experimental/Quantum/Language.qll | 20 +- .../codeql/cryptography/Model.qll | 1089 ++++++++++------- 3 files changed, 859 insertions(+), 489 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index eac400183b88..5cff0129b721 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -1,5 +1,6 @@ import java import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.TaintTracking import semmle.code.java.controlflow.Dominance module JCAModel { @@ -43,6 +44,22 @@ module JCAModel { .toUpperCase()) } + bindingset[kdf] + predicate kdf_names(string kdf) { + kdf.toUpperCase().matches(["PBKDF2With%", "PBEWith%"].toUpperCase()) + } + + bindingset[name] + Crypto::TKeyDerivationType kdf_name_to_kdf_type(string name, string withSubstring) { + name.matches("PBKDF2With%") and + result instanceof Crypto::PBKDF2 and + withSubstring = name.regexpCapture("PBKDF2With(.*)", 1) + or + name.matches("PBEWith%") and + result instanceof Crypto::PBES and + withSubstring = name.regexpCapture("PBEWith(.*)", 1) + } + bindingset[name] Crypto::THashType hash_name_to_hash_type(string name, int digestLength) { name = "SHA-1" and result instanceof Crypto::SHA1 and digestLength = 160 @@ -132,7 +149,7 @@ module JCAModel { } } - module AlgorithmStringToFetchFlow = DataFlow::Global; + module AlgorithmStringToFetchFlow = TaintTracking::Global; /** * Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable. @@ -303,7 +320,7 @@ module JCAModel { CipherGetInstanceAlgorithmArg() { this = call.getAlgorithmArg() } - override DataFlow::Node getInputNode() { result.asExpr() = this } + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } CipherStringLiteral getOrigin(string value) { AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), @@ -447,13 +464,15 @@ module JCAModel { override Crypto::CipherOperationSubtype getCipherOperationSubtype() { result = mode } - override DataFlow::Node getNonceConsumer() { + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg() } - override DataFlow::Node getInputConsumer() { result = doFinalize.getMessageArg() } + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result = doFinalize.getMessageArg() + } - override DataFlow::Node getKeyConsumer() { + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getKeyArg() } @@ -611,7 +630,7 @@ module JCAModel { class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer { CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() } - override DataFlow::Node getInputNode() { result.asExpr() = this } + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } } class CipherOperationCallOutput extends Crypto::CipherOutputArtifactInstance { @@ -659,7 +678,7 @@ module JCAModel { MessageDigestAlgorithmValueConsumer() { this = call.getAlgorithmArg() } - override DataFlow::Node getInputNode() { result.asExpr() = this } + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { exists(KnownHashAlgorithm l | l.getConsumer() = this and result = l) @@ -719,7 +738,7 @@ module JCAModel { KeyGeneratorCallAlgorithmValueConsumer() { this = call.getAlgorithmArg() } - override DataFlow::Node getInputNode() { result.asExpr() = this } + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this @@ -780,6 +799,10 @@ module JCAModel { Crypto::AlgorithmInstance getAKnownAlgorithm() { result = this.getAnAlgorithmValueConsumer().getAKnownAlgorithmSource() } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + override string getKeySizeFixed() { none() } } /* @@ -845,7 +868,9 @@ module JCAModel { module MACInitCallToMACOperationFlowConfig implements DataFlow::ConfigSig { // TODO: use flow state with one config - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MACInitCall } + predicate isSource(DataFlow::Node src) { + exists(MACInitCall init | src.asExpr() = init.getQualifier()) + } predicate isSink(DataFlow::Node sink) { exists(MACOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier()) @@ -884,7 +909,7 @@ module JCAModel { } MACInitCall getInitCall() { - MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this), + MACGetInstanceToMACOperationFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(result.getQualifier())) } } @@ -897,7 +922,7 @@ module JCAModel { } MACOperationCall getOperation() { - MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this), + MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this.getQualifier()), DataFlow::exprNode(result.(MethodCall).getQualifier())) } } @@ -907,7 +932,7 @@ module JCAModel { MACGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() } - override DataFlow::Node getInputNode() { result.asExpr() = this } + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { exists(KnownMACAlgorithm l | l.getConsumer() = this and result = l) @@ -933,7 +958,7 @@ module JCAModel { ) } - override DataFlow::Node getKeyConsumer() { + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { exists(MACGetInstanceCall instantiation, MACInitCall initCall | instantiation.getOperation() = this and initCall.getOperation() = this and @@ -942,9 +967,195 @@ module JCAModel { ) } - override DataFlow::Node getMessageConsumer() { + override Crypto::ConsumerInputDataFlowNode getMessageConsumer() { result.asExpr() = super.getArgument(0) and super.getMethod().getParameterType(0).hasName("byte[]") } } + + module SecretKeyFactoryGetInstanceToGenerateSecretFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + exists(SecretKeyFactoryGetInstanceCall call | src.asExpr() = call) + } + + predicate isSink(DataFlow::Node sink) { + exists(SecretKeyFactoryGenerateSecretCall call | + sink.asExpr() = call.(MethodCall).getQualifier() + ) + } + } + + module PBEKeySpecInstantiationToGenerateSecretFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + exists(PBEKeySpecInstantiation call | src.asExpr() = call) + } + + predicate isSink(DataFlow::Node sink) { + exists(SecretKeyFactoryGenerateSecretCall call | sink.asExpr() = call.getKeySpecArg()) + } + } + + module KDFAlgorithmStringToGetInstanceConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { kdf_names(src.asExpr().(StringLiteral).getValue()) } + + predicate isSink(DataFlow::Node sink) { + exists(SecretKeyFactoryGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) + } + } + + module SecretKeyFactoryGetInstanceToGenerateSecretFlow = + DataFlow::Global; + + module PBEKeySpecInstantiationToGenerateSecretFlow = + DataFlow::Global; + + module KDFAlgorithmStringToGetInstanceFlow = + DataFlow::Global; + + class PBEKeySpecInstantiation extends ClassInstanceExpr { + PBEKeySpecInstantiation() { + this.getConstructedType().hasQualifiedName("javax.crypto.spec", "PBEKeySpec") + } + + Expr getPasswordArg() { result = this.getArgument(0) } + + Expr getSaltArg() { result = this.getArgument(1) } + + Expr getIterationCountArg() { result = this.getArgument(2) } + + Expr getKeyLengthArg() { result = this.getArgument(3) } + } + + class SecretKeyFactoryGetInstanceCall extends MethodCall { + SecretKeyFactoryGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "SecretKeyFactory", "getInstance") + } + + Expr getAlgorithmArg() { result = this.getArgument(0) } + + SecretKeyFactoryGenerateSecretCall getOperation() { + SecretKeyFactoryGetInstanceToGenerateSecretFlow::flow(DataFlow::exprNode(this), + DataFlow::exprNode(result.(MethodCall).getQualifier())) + } + } + + class KDFAlgorithmStringLiteral extends Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral + { + SecretKeyFactoryKDFAlgorithmValueConsumer consumer; + + KDFAlgorithmStringLiteral() { + kdf_names(this.getValue()) and + KDFAlgorithmStringToGetInstanceFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + } + + override string getRawKDFAlgorithmName() { result = super.getValue() } + + override Crypto::TKeyDerivationType getKDFType() { + result = kdf_name_to_kdf_type(super.getValue(), _) + } + + SecretKeyFactoryKDFAlgorithmValueConsumer getConsumer() { result = consumer } + } + + class PBKDF2AlgorithmStringLiteral extends KDFAlgorithmStringLiteral, + Crypto::PBKDF2AlgorithmInstance, Crypto::HMACAlgorithmInstance, Crypto::HashAlgorithmInstance, + Crypto::AlgorithmValueConsumer + { + PBKDF2AlgorithmStringLiteral() { super.getKDFType() instanceof Crypto::PBKDF2 } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } + + override Crypto::THashType getHashFamily() { + result = hash_name_to_hash_type(this.getRawHashAlgorithmName(), _) + } + + override int getDigestLength() { + exists(hash_name_to_hash_type(this.getRawHashAlgorithmName(), result)) + } + + override string getRawMACAlgorithmName() { + result = super.getRawKDFAlgorithmName().splitAt("PBKDF2With", 1) + } + + override string getRawHashAlgorithmName() { + result = super.getRawKDFAlgorithmName().splitAt("WithHmac", 1) + } + + override Crypto::TMACType getMACType() { result instanceof Crypto::THMAC } + + override Crypto::AlgorithmValueConsumer getHMACAlgorithmValueConsumer() { result = this } + + override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() { result = this } + } + + class SecretKeyFactoryKDFAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof Expr + { + SecretKeyFactoryGetInstanceCall call; + + SecretKeyFactoryKDFAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(KDFAlgorithmStringLiteral l | l.getConsumer() = this and result = l) + } + + SecretKeyFactoryGetInstanceCall getInstantiation() { result = call } + } + + class SecretKeyFactoryGenerateSecretCall extends Crypto::KeyDerivationOperationInstance instanceof MethodCall + { + SecretKeyFactoryGenerateSecretCall() { + super.getCallee().hasQualifiedName("javax.crypto", "SecretKeyFactory", "generateSecret") + } + + Expr getKeySpecArg() { + result = super.getArgument(0) and + super.getMethod().getParameterType(0).hasName("KeySpec") + } + + PBEKeySpecInstantiation getInstantiation() { + PBEKeySpecInstantiationToGenerateSecretFlow::flow(DataFlow::exprNode(result), + DataFlow::exprNode(this.getKeySpecArg())) + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + exists(SecretKeyFactoryGetInstanceCall instantiation | + instantiation.getOperation() = this and result = instantiation.getAlgorithmArg() + ) + } + + override Crypto::ConsumerInputDataFlowNode getSaltConsumer() { + result.asExpr() = this.getInstantiation().getSaltArg() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = this.getInstantiation().getPasswordArg() + } + + override Crypto::ConsumerInputDataFlowNode getIterationCountConsumer() { + result.asExpr() = this.getInstantiation().getIterationCountArg() + } + + override DataFlow::Node getOutputKeyArtifact() { + result.asExpr() = this and + super.getMethod().getReturnType().hasName("SecretKey") + } + + override Crypto::ConsumerInputDataFlowNode getOutputKeySizeConsumer() { + result.asExpr() = this.getInstantiation().getKeyLengthArg() + } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + result.asExpr() = this.getInstantiation().getKeyLengthArg() + } + + override string getKeySizeFixed() { none() } + + override string getOutputKeySizeFixed() { none() } + + override string getIterationCountFixed() { none() } + } } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 74edfec1d390..1621e9436c42 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -94,7 +94,7 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { override string getAdditionalDescription() { result = this.toString() } } -class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { +class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof Literal { override DataFlow::Node getOutputNode() { result.asExpr() = this } override predicate flowsTo(Crypto::FlowAwareElement other) { @@ -141,7 +141,7 @@ module ArtifactUniversalFlow = DataFlow::Global; */ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { - source = any(Crypto::GenericDataSourceInstance i).getOutputNode() + source = any(Crypto::GenericSourceInstance i).getOutputNode() } predicate isSink(DataFlow::Node sink) { @@ -189,7 +189,21 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { } } -module GenericDataSourceUniversalFlow = DataFlow::Global; +module GenericDataSourceUniversalFlow = TaintTracking::Global; + +/* + * class LiteralOrGenericDataSource extends Element { + * DataFlow::Node node; + * + * LiteralOrGenericDataSource() { + * node = this.(Crypto::GenericSourceInstance).getOutputNode() or + * node.asExpr() = this.(Literal) + * } + * + * bindingset[other] + * predicate localFlowsTo(DataFlow::Node other) { DataFlow::localFlow(node, other) } + * } + */ // Import library-specific modeling import JCA diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index f20681923800..8252b5138600 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -37,6 +37,10 @@ module CryptographyBase Input> { final class DataFlowNode = Input::DataFlowNode; + class ConsumerInputDataFlowNode extends DataFlowNode { + ConsumerElement getConsumer() { result.getInputNode() = this } + } + final class UnknownPropertyValue extends string { UnknownPropertyValue() { this = "" } } @@ -49,7 +53,10 @@ module CryptographyBase Input> { ( if location = root or location instanceof UnknownLocation then parsed = value - else parsed = "(" + value + "," + location.toString() + ")" + else + parsed = + "(" + value + "," + Input::locationToFileBaseNameAndLineNumberString(location) + + ")" ) | parsed @@ -57,15 +64,28 @@ module CryptographyBase Input> { ) } + bindingset[node] + predicate node_as_property(GenericSourceNode node, string value, Location location) { + value = + node.getInternalType() + ":" + + node.asElement().(GenericSourceInstance).getAdditionalDescription() and + location = node.getLocation() + } + NodeBase getPassthroughNodeChild(NodeBase node) { result = node.getChild(_) } + predicate isPassthroughNodeWithSource(NodeBase node) { + isPassthroughNode(node) and + exists(node.asElement().(ArtifactConsumerAndInstance).getASource()) + } + predicate isPassthroughNode(NodeBase node) { node.asElement() instanceof ArtifactConsumerAndInstance } predicate nodes_graph_impl(NodeBase node, string key, string value) { not node.isExcludedFromGraph() and - not isPassthroughNode(node) and // TODO: punt to fix known unknowns for passthrough nodes + not isPassthroughNodeWithSource(node) and // TODO: punt to fix known unknowns for passthrough nodes ( key = "semmle.label" and value = node.toString() @@ -102,23 +122,23 @@ module CryptographyBase Input> { */ abstract class FlowAwareElement extends LocatableElement { /** - * Gets the output node for this artifact, which should usually be the same as `this`. + * Gets the output node for this element, which should usually be the same as `this`. */ abstract DataFlowNode getOutputNode(); /** - * Gets the input node for this artifact. + * Gets the input node for this element which takes in data. * * If `getInput` is implemented as `none()`, the artifact will not have inbound flow analysis. */ - abstract DataFlowNode getInputNode(); + abstract ConsumerInputDataFlowNode getInputNode(); /** - * Holds if this artifact flows to `other`. + * Holds if this element flows to `other`. * * This predicate should be defined generically per-language with library-specific extension support. - * The expected implementation is to perform flow analysis from this artifact's output to another artifact's input. - * The `other` argument should be one or more `ArtifactLocatableElement` that are sinks of the flow. + * The expected implementation is to perform flow analysis from this element's output to another element's input. + * The `other` argument should be one or more `FlowAwareElement`s that are sinks of the flow. * * If `flowsTo` is implemented as `none()`, the artifact will not have outbound flow analysis. */ @@ -128,8 +148,8 @@ module CryptographyBase Input> { /** * An element that represents a _known_ cryptographic asset with a determinable value OR an artifact. * - * CROSS PRODUCT WARNING: Do not model any *other* element that is a `FlowAwareElement` to the same - * instance in the database, as every other `KnownElement` will share that output artifact's flow. + * CROSS PRODUCT WARNING: Modeling any *other* element that is a `FlowAwareElement` to the same + * instance in the database will result in every `FlowAwareElement` sharing the output flow. */ abstract class KnownElement extends LocatableElement { final ConsumerElement getAConsumer() { result.getAKnownSource() = this } @@ -151,33 +171,71 @@ module CryptographyBase Input> { abstract class AlgorithmInstance extends KnownElement { } /** - * An element that represents an _unknown_ data-source with a non-statically determinable value. + * An element that represents a generic source of data. + * + * A generic source of data is either: + * 1. A value (e.g., a string or integer literal) *or* + * 1. An input for which a value cannot be determined (e.g., `argv`, file system reads, and web request headers) */ - abstract class GenericDataSourceInstance extends FlowAwareElement { - final override DataFlowNode getInputNode() { none() } + abstract class GenericSourceInstance extends FlowAwareElement { + final override ConsumerInputDataFlowNode getInputNode() { none() } abstract string getInternalType(); string getAdditionalDescription() { none() } } - abstract class GenericConstantOrAllocationSource extends GenericDataSourceInstance { - final override string getInternalType() { result = "ConstantData" } // TODO: toString of source? + /** + * An element that has a constant value and is a generic source of data. + */ + abstract class GenericValueSourceInstance extends GenericSourceInstance { } + + /** + * An element with a constant value, such as a literal. + */ + abstract class GenericConstantSourceInstance extends GenericValueSourceInstance { + final override string getInternalType() { result = "Constant" } + } + + /** + * An element representing statically or dynamically allocated data. + */ + abstract class GenericAllocationSourceInstance extends GenericValueSourceInstance { + final override string getInternalType() { result = "Allocation" } } - abstract class GenericExternalCallSource extends GenericDataSourceInstance { + /** + * An element that does not have a determinable constant value and is a generic source of data. + */ + abstract class GenericNoValueSourceInstance extends GenericSourceInstance { } + + /** + * A call to or an output argument of an external function with no definition. + */ + abstract class GenericExternalCallSource extends GenericNoValueSourceInstance { final override string getInternalType() { result = "ExternalCall" } // TODO: call target name or toString of source? } - abstract class GenericUnreferencedParameterSource extends GenericDataSourceInstance { + /** + * A parameter of a function which has no identifiable callsite. + */ + abstract class GenericUnreferencedParameterSource extends GenericNoValueSourceInstance { final override string getInternalType() { result = "Parameter" } // TODO: toString of source? } - abstract class GenericRemoteDataSource extends GenericDataSourceInstance { + /** + * A source of remote or external data, such as web request headers. + */ + abstract class GenericRemoteDataSource extends GenericNoValueSourceInstance { + // TODO: avoid duplication with the above types?.. perhaps define the above generically then override final override string getInternalType() { result = "RemoteData" } // TODO: toString of source? } - abstract class GenericLocalDataSource extends GenericDataSourceInstance { + /** + * A source of local or environment data, such as environment variables or a local filesystem. + */ + abstract class GenericLocalDataSource extends GenericNoValueSourceInstance { + // TODO: avoid duplication with the above types final override string getInternalType() { result = "LocalData" } // TODO: toString of source? } @@ -185,7 +243,7 @@ module CryptographyBase Input> { * An element that consumes _known_ or _unknown_ cryptographic assets. * * Note that known assets are to be modeled explicitly with the `getAKnownSource` predicate, whereas - * unknown assets are modeled implicitly via flow analysis from any `GenericDataSourceInstance` to this element. + * unknown assets are modeled implicitly via flow analysis from any `GenericSourceInstance` to this element. * * A consumer can consume multiple instances and types of assets at once, e.g., both a `PaddingAlgorithm` and `CipherAlgorithm`. */ @@ -196,23 +254,46 @@ module CryptographyBase Input> { override DataFlowNode getOutputNode() { none() } - // for internal use only - final GenericDataSourceInstance getAnUnknownSource() { + final GenericSourceInstance getAGenericSource() { result.flowsTo(this) and not result = this.getAKnownSource() } - final GenericSourceNode getAnUnknownSourceNode() { - result.asElement() = this.getAnUnknownSource() + final GenericSourceNode getAGenericSourceNode() { + result.asElement() = this.getAGenericSource() } final NodeBase getAKnownSourceNode() { result.asElement() = this.getAKnownSource() } final LocatableElement getASource() { - result = this.getAnUnknownSource() or + result = this.getAGenericSource() or result = this.getAKnownSource() } } + /** + * A generic value consumer, e.g. for inputs such as key length. + * TODO: type hints or per-instantiation type hint config on the source/sink pairs. + */ + final private class GenericValueConsumer extends ConsumerElement { + ConsumerInputDataFlowNode input; + + GenericValueConsumer() { + ( + exists(KeyCreationOperationInstance op | input = op.getKeySizeConsumer()) + or + exists(KeyDerivationOperationInstance op | + input = op.getIterationCountConsumer() or + input = op.getOutputKeySizeConsumer() + ) + ) and + this = Input::dfn_to_element(input) + } + + final override KnownElement getAKnownSource() { none() } + + final override ConsumerInputDataFlowNode getInputNode() { result = input } + } + abstract class AlgorithmValueConsumer extends ConsumerElement { /** * DO NOT USE. @@ -279,45 +360,49 @@ module CryptographyBase Input> { } final private class NonceArtifactConsumer extends ArtifactConsumerAndInstance { - DataFlowNode inputNode; + ConsumerInputDataFlowNode inputNode; NonceArtifactConsumer() { exists(CipherOperationInstance op | inputNode = op.getNonceConsumer()) and this = Input::dfn_to_element(inputNode) } - final override DataFlowNode getInputNode() { result = inputNode } + final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } } - final private class CipherInputArtifactConsumer extends ArtifactConsumerAndInstance { - DataFlowNode inputNode; + final private class MessageArtifactConsumer extends ArtifactConsumerAndInstance { + ConsumerInputDataFlowNode inputNode; - CipherInputArtifactConsumer() { - exists(CipherOperationInstance op | inputNode = op.getInputConsumer()) and + MessageArtifactConsumer() { + ( + exists(CipherOperationInstance op | inputNode = op.getInputConsumer()) + or + exists(KeyDerivationOperationInstance op | inputNode = op.getInputConsumer()) + or + exists(MACOperationInstance op | inputNode = op.getMessageConsumer()) + ) and this = Input::dfn_to_element(inputNode) } - final override DataFlowNode getInputNode() { result = inputNode } + final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } } - final private class MACInputArtifactConsumer extends ArtifactConsumerAndInstance { - DataFlowNode inputNode; + final private class SaltArtifactConsumer extends ArtifactConsumerAndInstance { + ConsumerInputDataFlowNode inputNode; - MACInputArtifactConsumer() { - exists(MACOperationInstance op | inputNode = op.getMessageConsumer()) and + SaltArtifactConsumer() { + exists(KeyDerivationOperationInstance op | inputNode = op.getSaltConsumer()) and this = Input::dfn_to_element(inputNode) } - final override DataFlowNode getInputNode() { result = inputNode } + final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } } // Output artifacts are determined solely by the element that produces them. - // Implementation guidance: these *do* need to be defined generically at the language-level - // in order for a flowsTo to be defined. At the per-modeling-instance level, extend that language-level class! abstract class OutputArtifactInstance extends ArtifactInstance { override predicate isConsumerArtifact() { none() } - override DataFlowNode getInputNode() { none() } + override ConsumerInputDataFlowNode getInputNode() { none() } final override predicate flowsTo(FlowAwareElement other) { Input::artifactOutputFlowsToGenericInput(this.getOutputNode(), other.getInputNode()) @@ -365,17 +450,20 @@ module CryptographyBase Input> { } final class KeyArtifactConsumer extends KeyArtifactInstance, ArtifactConsumerAndInstance { - DataFlowNode inputNode; + ConsumerInputDataFlowNode inputNode; // TODO: key type hint? e.g. hint: private || public KeyArtifactConsumer() { - exists(CipherOperationInstance op | inputNode = op.getKeyConsumer()) and + ( + exists(CipherOperationInstance op | inputNode = op.getKeyConsumer()) or + exists(MACOperationInstance op | inputNode = op.getKeyConsumer()) + ) and this = Input::dfn_to_element(inputNode) } override KeyArtifactType getKeyType() { result instanceof TUnknownKeyType } - final override DataFlowNode getInputNode() { result = inputNode } + final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } } /** @@ -390,17 +478,17 @@ module CryptographyBase Input> { /** * Gets the consumer of nonces/IVs associated with this cipher operation. */ - abstract DataFlowNode getNonceConsumer(); + abstract ConsumerInputDataFlowNode getNonceConsumer(); /** * Gets the consumer of plaintext or ciphertext input associated with this cipher operation. */ - abstract DataFlowNode getInputConsumer(); + abstract ConsumerInputDataFlowNode getInputConsumer(); /** * Gets the consumer of a key. */ - abstract DataFlowNode getKeyConsumer(); + abstract ConsumerInputDataFlowNode getKeyConsumer(); /** * Gets the output artifact of this cipher operation. @@ -513,12 +601,12 @@ module CryptographyBase Input> { /** * Gets the message input used in this operation. */ - abstract DataFlowNode getMessageConsumer(); + abstract ConsumerInputDataFlowNode getMessageConsumer(); /** * Gets the key used in this operation. */ - abstract DataFlowNode getKeyConsumer(); + abstract ConsumerInputDataFlowNode getKeyConsumer(); } abstract class HMACAlgorithmInstance extends MACAlgorithmInstance { @@ -557,10 +645,6 @@ module CryptographyBase Input> { abstract int getDigestLength(); } - abstract class KeyDerivationOperationInstance extends OperationInstance { } - - abstract class KeyDerivationAlgorithmInstance extends AlgorithmInstance { } - abstract private class KeyCreationOperationInstance extends OperationInstance { abstract string getKeyCreationTypeDescription(); @@ -574,16 +658,69 @@ module CryptographyBase Input> { */ abstract KeyArtifactType getOutputKeyType(); - /** - * Gets the key size of the key produced by this operation. - */ - string getKeySize() { none() } // TODO: punt, might need a generic value consumer? + // Defaults or fixed values + abstract string getKeySizeFixed(); + + // Consumer input nodes + abstract ConsumerInputDataFlowNode getKeySizeConsumer(); final KeyArtifactOutputInstance getKeyArtifactOutputInstance() { result.getOutputNode() = this.getOutputKeyArtifact() } } + abstract class KeyDerivationOperationInstance extends KeyCreationOperationInstance { + final override KeyArtifactType getOutputKeyType() { result instanceof TSymmetricKeyType } + + final override string getKeyCreationTypeDescription() { result = "KeyDerivation" } + + // Defaults or fixed values + abstract string getIterationCountFixed(); + + abstract string getOutputKeySizeFixed(); + + // Generic consumer input nodes + abstract ConsumerInputDataFlowNode getIterationCountConsumer(); + + abstract ConsumerInputDataFlowNode getOutputKeySizeConsumer(); + + // Artifact consumer input nodes + abstract ConsumerInputDataFlowNode getInputConsumer(); + + abstract ConsumerInputDataFlowNode getSaltConsumer(); + } + + newtype TKeyDerivationType = + PBKDF2() or + PBES() or + HKDF() or + ARGON2() or + OtherKeyDerivationType() + + abstract class KeyDerivationAlgorithmInstance extends AlgorithmInstance { + /** + * Gets the type of this key derivation algorithm, e.g., "PBKDF2" or "HKDF". + */ + abstract TKeyDerivationType getKDFType(); + + /** + * Gets the isolated name as it appears in source, e.g., "PBKDF2WithHmacSHA256" in "PBKDF2WithHmacSHA256/UnrelatedInformation". + */ + abstract string getRawKDFAlgorithmName(); + } + + abstract class PBKDF2AlgorithmInstance extends KeyDerivationAlgorithmInstance { + PBKDF2AlgorithmInstance() { this.getKDFType() instanceof PBKDF2 } + + /** + * Gets the HMAC algorithm used by this PBKDF2 algorithm. + * + * Note: Other PRFs are not supported, as most cryptographic libraries + * only support HMAC for PBKDF2's PRF input. + */ + abstract AlgorithmValueConsumer getHMACAlgorithmValueConsumer(); + } + abstract class KeyGenerationOperationInstance extends KeyCreationOperationInstance { final override string getKeyCreationTypeDescription() { result = "KeyGeneration" } } @@ -601,11 +738,13 @@ module CryptographyBase Input> { { class Union extends LocatableElement { Union() { + // Either an AlgorithmInstance this instanceof Alg or + // Or an AlgorithmValueConsumer with unknown sources and no known sources isCandidateAVC(this) and not exists(this.(AlgorithmValueConsumer).getAKnownAlgorithmSource()) and - exists(this.(AlgorithmValueConsumer).getAnUnknownSource()) + exists(this.(AlgorithmValueConsumer).getAGenericSource()) } Alg asAlg() { result = this } @@ -624,7 +763,12 @@ module CryptographyBase Input> { } private predicate isMACAVC(AlgorithmValueConsumer avc) { - exists(MACOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) + exists(MACOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) or + exists(PBKDF2AlgorithmInstance alg | avc = alg.getHMACAlgorithmValueConsumer()) + } + + private predicate isKeyDerivationAVC(AlgorithmValueConsumer avc) { + exists(KeyDerivationOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) } final private class CipherAlgorithmInstanceOrValueConsumer = @@ -633,21 +777,24 @@ module CryptographyBase Input> { final private class HashAlgorithmInstanceOrValueConsumer = AlgorithmInstanceOrValueConsumer::Union; - private class MACAlgorithmInstanceOrValueConsumer = + final private class MACAlgorithmInstanceOrValueConsumer = AlgorithmInstanceOrValueConsumer::Union; + final private class KeyDerivationAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; + private newtype TNode = // Artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or - TNonce(NonceArtifactConsumer e) or - TCipherInput(CipherInputArtifactConsumer e) or TCipherOutput(CipherOutputArtifactInstance e) or - TMACMessageInput(MACInputArtifactConsumer e) or + // Input artifact nodes (synthetic, used to differentiate input as entities) + TNonceInput(NonceArtifactConsumer e) or + TMessageInput(MessageArtifactConsumer e) or + TSaltInput(SaltArtifactConsumer e) or TRandomNumberGeneration(RandomNumberGenerationInstance e) { e.flowsTo(_) } or // Operations (e.g., hashing, encryption) THashOperation(HashOperationInstance e) or - TKeyDerivationOperation(KeyDerivationOperationInstance e) or TCipherOperation(CipherOperationInstance e) or TKeyEncapsulationOperation(KeyEncapsulationOperationInstance e) or TMACOperation(MACOperationInstance e) or @@ -657,7 +804,7 @@ module CryptographyBase Input> { TCipherAlgorithm(CipherAlgorithmInstanceOrValueConsumer e) or TEllipticCurveAlgorithm(EllipticCurveAlgorithmInstance e) or THashAlgorithm(HashAlgorithmInstanceOrValueConsumer e) or - TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstance e) or + TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstanceOrValueConsumer e) or TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or TMACAlgorithm(MACAlgorithmInstanceOrValueConsumer e) or // Non-standalone Algorithms (e.g., Mode, Padding) @@ -670,8 +817,15 @@ module CryptographyBase Input> { TKeyAgreementHybridCryptosystem(CipherAlgorithmInstance ka) or TAsymmetricEncryptionMacHybridCryptosystem(CipherAlgorithmInstance enc) or TPostQuantumHybridCryptosystem(CipherAlgorithmInstance enc) or - // Unknown source node - TGenericSourceNode(GenericDataSourceInstance e) { e.flowsTo(_) } + // Generic source nodes + TGenericSourceNode(GenericSourceInstance e) { + // An element modelled as a `GenericSourceInstance` can also be modelled as a `KnownElement` + // For example, a string literal "AES" could be a generic constant but also an algorithm instance. + // Only create generic nodes tied to instances which are not also a `KnownElement`. + not e instanceof KnownElement and + // Only create nodes for generic sources which flow to other elements + e.flowsTo(_) + } /** * The base class for all cryptographic assets, such as operations and algorithms. @@ -689,11 +843,12 @@ module CryptographyBase Input> { * * _Example 1_: A seed of a random number generation algorithm has meaning beyond its value, as its reuse in multiple * random number generation algorithms is more relevant than its underlying value. In contrast, a key size is only - * relevant to analysis in terms of its underlying value. Therefore, an RNG seed is a node; a key size is not. + * relevant to analysis in terms of its underlying value. Therefore, an RNG seed is a node; a key size is not. However, + * the key size might have a `GenericSourceNode` source, even if it itself is not a node. * * _Example 2_: A salt for a key derivation function *is* an `ArtifactNode`. * - * _Example 3_: The iteration count of a key derivation function is *not* a node. + * _Example 3_: The iteration count of a key derivation function is *not* a node, but it may link to a generic node. * * _Example 4_: A nonce for a cipher operation *is* an `ArtifactNode`. */ @@ -743,11 +898,32 @@ module CryptographyBase Input> { predicate isExcludedFromGraph() { none() } } + signature string getDefaultValueSig(); + + signature ConsumerInputDataFlowNode getConsumerSig(); + + signature class NodeBaseSig instanceof NodeBase; + + module PropertyOutput { + bindingset[root] + predicate get(NodeBase root, string value, Location location) { + if not exists(getDefault()) and not exists(getConsumer().getConsumer().getASource()) + then value instanceof UnknownPropertyValue and location instanceof UnknownLocation + else ( + if exists(getDefault()) + then + value = "Default:" + getDefault() and + location = root.getLocation() + else node_as_property(getConsumer().getConsumer().getAGenericSourceNode(), value, location) + ) + } + } + /** * A generic source node is a source of data that is not resolvable to a specific value or type. */ private class GenericSourceNode extends NodeBase, TGenericSourceNode { - GenericDataSourceInstance instance; + GenericSourceInstance instance; GenericSourceNode() { this = TGenericSourceNode(instance) } @@ -765,12 +941,74 @@ module CryptographyBase Input> { } override predicate isExcludedFromGraph() { - not exists(NodeBase other | not other = this and other.getChild(_) = this) + // Exclude generic source instances that are not child nodes of another node + not exists(NodeBase other | other != this and other.getChild(_) = this) } } class AssetNode = NodeBase; + /** + * A cryptographic operation, such as hashing or encryption. + */ + abstract class OperationNode extends AssetNode { + /** + * Holds if `node` is a potential candidate for a known algorithm node. + * This predicate should be used to restrict the set of candidate algorithm node types. + */ + abstract predicate isCandidateKnownAlgorithmNode(AlgorithmNode node); + + /** + * Gets the algorithm or generic source nodes consumed as an algorithm associated with this operation. + */ + NodeBase getAnAlgorithmOrGenericSource() { + result = this.getAKnownAlgorithm() or + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAGenericSourceNode() + } + + /** + * Gets a known algorithm associated with this operation, subject to `isCandidateKnownAlgorithmNode`. + */ + AlgorithmNode getAKnownAlgorithm() { + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() and + this.isCandidateKnownAlgorithmNode(result) + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] + edgeName = "Algorithm" and + if exists(this.getAnAlgorithmOrGenericSource()) + then result = this.getAnAlgorithmOrGenericSource() + else result = this + } + } + + abstract class AlgorithmNode extends AssetNode { + /** + * Gets the name of this algorithm, e.g., "AES" or "SHA". + */ + abstract string getAlgorithmName(); + + /** + * Gets the raw name of this algorithm from source (no parsing or formatting) + */ + abstract string getRawAlgorithmName(); + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [ONLY_KNOWN] + key = "Name" and value = this.getAlgorithmName() and location = this.getLocation() + or + // [ONLY_KNOWN] + key = "RawName" and value = this.getRawAlgorithmName() and location = this.getLocation() + } + } + /** * An artifact is an instance of data that is used in a cryptographic operation or produced by one. */ @@ -803,18 +1041,18 @@ module CryptographyBase Input> { result = super.getChild(edgeName) or // [KNOWN_OR_UNKNOWN] - edgeName = this.getSourceNodeRelationship() and // only holds if not set to none() + edgeName = this.getSourceNodeRelationship() and // TODO: only holds if not set to none().. revisit this if exists(this.getSourceNode()) then result = this.getSourceNode() else result = this } } /** - * A nonce or initialization vector + * A nonce or initialization vector input */ - final class NonceArtifactNode extends ArtifactNode, TNonce { + final class NonceArtifactNode extends ArtifactNode, TNonceInput { NonceArtifactConsumer instance; - NonceArtifactNode() { this = TNonce(instance) } + NonceArtifactNode() { this = TNonceInput(instance) } final override string getInternalType() { result = "Nonce" } @@ -822,31 +1060,44 @@ module CryptographyBase Input> { } /** - * Output text from a cipher operation + * A message or plaintext/ciphertext input */ - final class CipherOutputNode extends ArtifactNode, TCipherOutput { - CipherOutputArtifactInstance instance; + final class MessageArtifactNode extends ArtifactNode, TMessageInput { + MessageArtifactConsumer instance; - CipherOutputNode() { this = TCipherOutput(instance) } + MessageArtifactNode() { this = TMessageInput(instance) } - final override string getInternalType() { result = "CipherOutput" } + final override string getInternalType() { result = "Message" } override LocatableElement asElement() { result = instance } + } - override string getSourceNodeRelationship() { none() } + /** + * A salt input + */ + final class SaltArtifactNode extends ArtifactNode, TSaltInput { + SaltArtifactConsumer instance; + + SaltArtifactNode() { this = TSaltInput(instance) } + + final override string getInternalType() { result = "Salt" } + + override LocatableElement asElement() { result = instance } } /** - * Input text to a cipher operation + * Output text from a cipher operation */ - final class CipherInputNode extends ArtifactNode, TCipherInput { - CipherInputArtifactConsumer instance; + final class CipherOutputNode extends ArtifactNode, TCipherOutput { + CipherOutputArtifactInstance instance; - CipherInputNode() { this = TCipherInput(instance) } + CipherOutputNode() { this = TCipherOutput(instance) } - final override string getInternalType() { result = "CipherInput" } + final override string getInternalType() { result = "CipherOutput" } override LocatableElement asElement() { result = instance } + + override string getSourceNodeRelationship() { none() } } /** @@ -876,30 +1127,28 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } - /** - * Gets the algorithm or unknown source nodes consumed as an algorithm associated with this operation. - */ - NodeBase getAnAlgorithmOrUnknown() { + NodeBase getAKnownAlgorithmOrGenericSourceNode() { result = this.getAKnownAlgorithm() or result = - this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAnUnknownSourceNode() + instance + .(KeyCreationOperationInstance) + .getAnAlgorithmValueConsumer() + .getAGenericSourceNode() } - /** - * Gets a known algorithm associated with this operation - */ CipherAlgorithmNode getAKnownAlgorithm() { result = - this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + instance.(KeyCreationOperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() } override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or - // [KNOWN_OR_UNKNOWN] + // [KNOWN_OR_UNKNOWN] - only if asymmetric edgeName = "Algorithm" and - if exists(this.getAnAlgorithmOrUnknown()) - then result = this.getAnAlgorithmOrUnknown() + instance.getKeyType() instanceof TAsymmetricKeyType and + if exists(this.getAKnownAlgorithmOrGenericSourceNode()) + then result = this.getAKnownAlgorithmOrGenericSourceNode() else result = this } @@ -918,16 +1167,44 @@ module CryptographyBase Input> { } } - final class MACMessageInputNode extends ArtifactNode, TMACMessageInput { - MACInputArtifactConsumer instance; + /** + * A digest produced by a hash operation. + */ + final class DigestArtifactNode extends ArtifactNode, TDigest { + DigestArtifactInstance instance; - MACMessageInputNode() { this = TMACMessageInput(instance) } + DigestArtifactNode() { this = TDigest(instance) } - final override string getInternalType() { result = "MACMessageInput" } + final override string getInternalType() { result = "Digest" } override LocatableElement asElement() { result = instance } } + abstract class KeyCreationOperationNode extends OperationNode, TKeyCreationOperation { + KeyCreationOperationInstance instance; + + KeyCreationOperationNode() { this = TKeyCreationOperation(instance) } + + override LocatableElement asElement() { result = instance } + + override string getInternalType() { result = instance.getKeyCreationTypeDescription() } + + /** + * Gets the key artifact produced by this operation. + */ + KeyArtifactNode getOutputKeyArtifact() { + instance.getKeyArtifactOutputInstance() = result.asElement() + } + + override NodeBase getChild(string key) { + result = super.getChild(key) + or + // [ALWAYS_KNOWN] + key = "Output" and + result = this.getOutputKeyArtifact() + } + } + /** * A MAC operation that produces a MAC value. */ @@ -940,41 +1217,20 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } - MACMessageInputNode getAMessage() { - result.asElement() = Input::dfn_to_element(instance.getMessageConsumer()) + override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + node instanceof MACAlgorithmNode } - KeyArtifactNode getAKey() { - result.asElement() = Input::dfn_to_element(instance.getKeyConsumer()) + MessageArtifactNode getAMessage() { + result.asElement() = instance.getMessageConsumer().getConsumer() } - /** - * Gets the algorithm or unknown source nodes consumed as an algorithm associated with this operation. - */ - NodeBase getAMACAlgorithmOrUnknown() { - result = this.getAKnownMACAlgorithm() or - result = - this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAnUnknownSourceNode() - } - - /** - * Gets a known algorithm associated with this operation - */ - MACAlgorithmNode getAKnownMACAlgorithm() { - result = - this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() - } + KeyArtifactNode getAKey() { result.asElement() = instance.getKeyConsumer().getConsumer() } override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or // [KNOWN_OR_UNKNOWN] - edgeName = "Algorithm" and - if exists(this.getAMACAlgorithmOrUnknown()) - then result = this.getAMACAlgorithmOrUnknown() - else result = this - or - // [KNOWN_OR_UNKNOWN] edgeName = "Message" and if exists(this.getAMessage()) then result = this.getAMessage() else result = this or @@ -1006,9 +1262,6 @@ module CryptographyBase Input> { final private predicate macToNameMapping(TMACType type, string name) { type instanceof THMAC and name = "HMAC" - or - type instanceof TOtherMACType and - name = this.getRawAlgorithmName() } override string getAlgorithmName() { this.macToNameMapping(this.getMACType(), result) } @@ -1034,70 +1287,253 @@ module CryptographyBase Input> { } } - /** - * A digest produced by a hash operation. - */ - final class DigestArtifactNode extends ArtifactNode, TDigest { - DigestArtifactInstance instance; + class KeyGenerationOperationNode extends KeyCreationOperationNode { + KeyGenerationOperationInstance keyGenInstance; - DigestArtifactNode() { this = TDigest(instance) } + KeyGenerationOperationNode() { keyGenInstance = instance } - final override string getInternalType() { result = "Digest" } + override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + node instanceof CipherAlgorithmNode + } - override LocatableElement asElement() { result = instance } + override NodeBase getChild(string key) { + result = super.getChild(key) + or + // [ALWAYS_KNOWN] + key = "Output" and + result = this.getOutputKeyArtifact() + } } - /** - * A cryptographic operation, such as hashing or encryption. - */ - abstract class OperationNode extends AssetNode { } + class KeyDerivationOperationNode extends KeyCreationOperationNode { + KeyDerivationOperationInstance kdfInstance; - abstract class AlgorithmNode extends AssetNode { - /** - * Gets the name of this algorithm, e.g., "AES" or "SHA". - */ - abstract string getAlgorithmName(); + KeyDerivationOperationNode() { kdfInstance = instance } - /** - * Gets the raw name of this algorithm from source (no parsing or formatting) - */ - abstract string getRawAlgorithmName(); + MessageArtifactNode getInput() { + result.asElement() = kdfInstance.getInputConsumer().getConsumer() + } + + SaltArtifactNode getSalt() { + result.asElement().(SaltArtifactConsumer).getInputNode() = kdfInstance.getSaltConsumer() + } + + GenericSourceNode getIterationCount() { + result.asElement() = kdfInstance.getIterationCountConsumer().getConsumer().getAGenericSource() + } + + GenericSourceNode getOutputKeySize() { + result.asElement() = kdfInstance.getOutputKeySizeConsumer().getConsumer().getAGenericSource() + } + + override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + node instanceof KeyDerivationAlgorithmNode + } + + override NodeBase getChild(string key) { + result = super.getChild(key) + or + // [KNOWN_OR_UNKNOWN] + key = "Input" and + if exists(this.getInput()) then result = this.getInput() else result = this + or + // [KNOWN_OR_UNKNOWN] + key = "Salt" and + if exists(this.getSalt()) then result = this.getSalt() else result = this + } override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or // [ONLY_KNOWN] - key = "Name" and value = this.getAlgorithmName() and location = this.getLocation() + key = "DefaultIterations" and + value = kdfInstance.getIterationCountFixed() and + location = this.getLocation() or // [ONLY_KNOWN] - key = "RawName" and value = this.getRawAlgorithmName() and location = this.getLocation() + key = "DefaultKeySize" and + value = kdfInstance.getKeySizeFixed() and + location = this.getLocation() + or + // [ONLY_KNOWN] - TODO: refactor for known unknowns + key = "Iterations" and + node_as_property(this.getIterationCount(), value, location) + or + // [ONLY_KNOWN] - TODO: refactor for known unknowns + key = "KeySize" and + node_as_property(this.getOutputKeySize(), value, location) } } - final class KeyCreationOperationNode extends OperationNode, TKeyCreationOperation { - KeyCreationOperationInstance instance; + class KeyDerivationAlgorithmNode extends AlgorithmNode, TKeyDerivationAlgorithm { + KeyDerivationAlgorithmInstanceOrValueConsumer instance; - KeyCreationOperationNode() { this = TKeyCreationOperation(instance) } + KeyDerivationAlgorithmNode() { this = TKeyDerivationAlgorithm(instance) } + + final override string getInternalType() { result = "KeyDerivationAlgorithm" } override LocatableElement asElement() { result = instance } - override string getInternalType() { result = instance.getKeyCreationTypeDescription() } + final override string getRawAlgorithmName() { + result = instance.asAlg().getRawKDFAlgorithmName() + } - /** - * Gets the key artifact produced by this operation. - */ - KeyArtifactNode getOutputKeyArtifact() { - instance.getKeyArtifactOutputInstance() = result.asElement() + final override string getAlgorithmName() { result = this.getRawAlgorithmName() } + } + + /** + * PBKDF2 key derivation function + */ + class PBKDF2AlgorithmNode extends KeyDerivationAlgorithmNode { + PBKDF2AlgorithmInstance pbkdf2Instance; + + PBKDF2AlgorithmNode() { pbkdf2Instance = instance.asAlg() } + + HMACAlgorithmNode getHMACAlgorithm() { + result.asElement() = pbkdf2Instance.getHMACAlgorithmValueConsumer().getASource() } override NodeBase getChild(string key) { result = super.getChild(key) or - // [ALWAYS_KNOWN] - key = "Output" and - result = this.getOutputKeyArtifact() - } - } + // [KNOWN_OR_UNKNOWN] + key = "PRF" and + if exists(this.getHMACAlgorithm()) then result = this.getHMACAlgorithm() else result = this + } + } + + // /** + // * PKCS12KDF key derivation function + // */ + // abstract class PKCS12KDF extends KeyDerivationWithDigestParameterNode { + // override string getAlgorithmName() { result = "PKCS12KDF" } + // /** + // * Gets the iteration count of this key derivation algorithm. + // */ + // abstract string getIterationCount(Location location); + // /** + // * Gets the raw ID argument specifying the intended use of the derived key. + // * + // * The intended use is defined in RFC 7292, appendix B.3, as follows: + // * + // * This standard specifies 3 different values for the ID byte mentioned above: + // * + // * 1. If ID=1, then the pseudorandom bits being produced are to be used + // * as key material for performing encryption or decryption. + // * + // * 2. If ID=2, then the pseudorandom bits being produced are to be used + // * as an IV (Initial Value) for encryption or decryption. + // * + // * 3. If ID=3, then the pseudorandom bits being produced are to be used + // * as an integrity key for MACing. + // */ + // abstract string getIDByte(Location location); + // override predicate properties(string key, string value, Location location) { + // super.properties(key, value, location) + // or + // ( + // // [KNOWN_OR_UNKNOWN] + // key = "Iterations" and + // if exists(this.getIterationCount(location)) + // then value = this.getIterationCount(location) + // else ( + // value instanceof UnknownPropertyValue and location instanceof UnknownLocation + // ) + // ) + // or + // ( + // // [KNOWN_OR_UNKNOWN] + // key = "IdByte" and + // if exists(this.getIDByte(location)) + // then value = this.getIDByte(location) + // else ( + // value instanceof UnknownPropertyValue and location instanceof UnknownLocation + // ) + // ) + // } + // } + // /** + // * scrypt key derivation function + // */ + // abstract class SCRYPT extends KeyDerivationAlgorithmNode { + // final override string getAlgorithmName() { result = "scrypt" } + // /** + // * Gets the iteration count (`N`) argument + // */ + // abstract string get_N(Location location); + // /** + // * Gets the block size (`r`) argument + // */ + // abstract string get_r(Location location); + // /** + // * Gets the parallelization factor (`p`) argument + // */ + // abstract string get_p(Location location); + // /** + // * Gets the derived key length argument + // */ + // abstract string getDerivedKeyLength(Location location); + // override predicate properties(string key, string value, Location location) { + // super.properties(key, value, location) + // or + // ( + // // [KNOWN_OR_UNKNOWN] + // key = "N" and + // if exists(this.get_N(location)) + // then value = this.get_N(location) + // else ( + // value instanceof UnknownPropertyValue and location instanceof UnknownLocation + // ) + // ) + // or + // ( + // // [KNOWN_OR_UNKNOWN] + // key = "r" and + // if exists(this.get_r(location)) + // then value = this.get_r(location) + // else ( + // value instanceof UnknownPropertyValue and location instanceof UnknownLocation + // ) + // ) + // or + // ( + // // [KNOWN_OR_UNKNOWN] + // key = "p" and + // if exists(this.get_p(location)) + // then value = this.get_p(location) + // else ( + // value instanceof UnknownPropertyValue and location instanceof UnknownLocation + // ) + // ) + // or + // ( + // // [KNOWN_OR_UNKNOWN] + // key = "KeyLength" and + // if exists(this.getDerivedKeyLength(location)) + // then value = this.getDerivedKeyLength(location) + // else ( + // value instanceof UnknownPropertyValue and location instanceof UnknownLocation + // ) + // ) + // } + // } + /* + * TODO: + * + * Rule: No newtype representing a type of algorithm should be modelled with multiple interfaces + * + * Example 1: HKDF and PKCS12KDF are both key derivation algorithms. + * However, PKCS12KDF also has a property: the iteration count. + * + * If we have HKDF and PKCS12KDF under TKeyDerivationType, + * someone modelling a library might try to make a generic identification of both of those algorithms. + * + * They will therefore not use the specialized type for PKCS12KDF, + * meaning "from PKCS12KDF algo select algo" will have no results. + * + * Example 2: Each type below represents a common family of elliptic curves, with a shared interface, i.e., + * predicates for library modellers to implement as well as the properties and edges reported. + */ newtype TCipherOperationSubtype = TEncryptionMode() or @@ -1149,56 +1585,28 @@ module CryptographyBase Input> { override string getInternalType() { result = "CipherOperation" } - /** - * Gets the algorithm or unknown source nodes consumed as an algorithm associated with this operation. - */ - NodeBase getACipherAlgorithmOrUnknown() { - result = this.getAKnownCipherAlgorithm() or - result = - this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAnUnknownSourceNode() - } - - /** - * Gets a known algorithm associated with this operation - */ - CipherAlgorithmNode getAKnownCipherAlgorithm() { - result = - this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + node instanceof CipherAlgorithmNode } CipherOperationSubtype getCipherOperationSubtype() { result = instance.getCipherOperationSubtype() } - NonceArtifactNode getANonce() { - result.asElement() = - Input::dfn_to_element(this.asElement().(CipherOperationInstance).getNonceConsumer()) - } + NonceArtifactNode getANonce() { result.asElement() = instance.getNonceConsumer().getConsumer() } - CipherInputNode getAnInputArtifact() { - result.asElement() = - Input::dfn_to_element(this.asElement().(CipherOperationInstance).getInputConsumer()) + MessageArtifactNode getAnInputArtifact() { + result.asElement() = instance.getInputConsumer().getConsumer() } - CipherOutputNode getAnOutputArtifact() { - result.asElement() = this.asElement().(CipherOperationInstance).getOutputArtifact() - } + CipherOutputNode getAnOutputArtifact() { result.asElement() = instance.getOutputArtifact() } - KeyArtifactNode getAKey() { - result.asElement() = - Input::dfn_to_element(this.asElement().(CipherOperationInstance).getKeyConsumer()) - } + KeyArtifactNode getAKey() { result.asElement() = instance.getKeyConsumer().getConsumer() } override NodeBase getChild(string key) { result = super.getChild(key) or // [KNOWN_OR_UNKNOWN] - key = "Algorithm" and - if exists(this.getACipherAlgorithmOrUnknown()) - then result = this.getACipherAlgorithmOrUnknown() - else result = this - or - // [KNOWN_OR_UNKNOWN] key = "Nonce" and if exists(this.getANonce()) then result = this.getANonce() else result = this or @@ -1286,8 +1694,6 @@ module CryptographyBase Input> { type instanceof CFB and name = "CFB" or type instanceof OFB and name = "OFB" - or - type instanceof OtherMode and name = this.getRawAlgorithmName() } override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } @@ -1326,8 +1732,6 @@ module CryptographyBase Input> { type instanceof NoPadding and name = "NoPadding" or type instanceof OAEP and name = "OAEP" - or - type instanceof OtherPadding and name = this.getRawAlgorithmName() } override string getAlgorithmName() { this.paddingToNameMapping(this.getPaddingType(), result) } @@ -1500,7 +1904,7 @@ module CryptographyBase Input> { type instanceof SM4 and name = "SM4" and s = Block() or type instanceof OtherCipherType and - name = this.getRawAlgorithmName() and + name instanceof UnknownPropertyValue and s = UnknownCipherStructureType() } @@ -1555,41 +1959,20 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } - /** - * Gets the algorithm or unknown source nodes consumed as an algorithm associated with this operation. - */ - NodeBase getAHashAlgorithmOrUnknown() { - result = this.getAKnownHashAlgorithm() or - result = - this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAnUnknownSourceNode() - } - - /** - * Gets a known algorithm associated with this operation - */ - HashAlgorithmNode getAKnownHashAlgorithm() { - result = - this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + node instanceof HashAlgorithmNode } /** * Gets the output digest node */ - DigestArtifactNode getDigest() { - result.asElement() = this.asElement().(HashOperationInstance).getDigestArtifact() - } + DigestArtifactNode getDigest() { result.asElement() = instance.getDigestArtifact() } - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - // [KNOWN_OR_UNKNOWN] - edgeName = "Algorithm" and - if exists(this.getAHashAlgorithmOrUnknown()) - then result = this.getAHashAlgorithmOrUnknown() - else result = this + override NodeBase getChild(string key) { + result = super.getChild(key) or // [KNOWN_OR_UNKNOWN] - edgeName = "Digest" and + key = "Digest" and if exists(this.getDigest()) then result = this.getDigest() else result = this } } @@ -1652,8 +2035,6 @@ module CryptographyBase Input> { type instanceof SM3 and name = "SM3" or type instanceof WHIRLPOOL and name = "WHIRLPOOL" - or - type instanceof OtherHashType and name = this.getRawAlgorithmName() } /** @@ -1680,242 +2061,6 @@ module CryptographyBase Input> { } } - /** - * An operation that derives one or more keys from an input value. - */ - abstract class KeyDerivationOperationNode extends OperationNode, TKeyDerivationOperation { - final override Location getLocation() { - exists(LocatableElement le | this = TKeyDerivationOperation(le) and result = le.getLocation()) - } - - override string getInternalType() { result = "KeyDerivationOperation" } - } - - /** - * An algorithm that derives one or more keys from an input value. - * - * Only use this class to model UNKNOWN key derivation algorithms. - * - * For known algorithms, use the specialized classes, e.g., `HKDF` and `PKCS12KDF`. - */ - abstract class KeyDerivationAlgorithmNode extends AlgorithmNode, TKeyDerivationAlgorithm { - final override Location getLocation() { - exists(LocatableElement le | this = TKeyDerivationAlgorithm(le) and result = le.getLocation()) - } - - override string getInternalType() { result = "KeyDerivationAlgorithm" } - - override string getAlgorithmName() { result = this.getRawAlgorithmName() } - } - - /** - * An algorithm that derives one or more keys from an input value, using a configurable digest algorithm. - */ - abstract private class KeyDerivationWithDigestParameterNode extends KeyDerivationAlgorithmNode { - abstract HashAlgorithmNode getHashAlgorithm(); - - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - ( - // [KNOWN_OR_UNKNOWN] - edgeName = "Uses" and - if exists(this.getHashAlgorithm()) then result = this.getHashAlgorithm() else result = this - ) - } - } - - /** - * HKDF key derivation function - */ - abstract class HKDFNode extends KeyDerivationWithDigestParameterNode { - final override string getAlgorithmName() { result = "HKDF" } - } - - /** - * PBKDF2 key derivation function - */ - abstract class PBKDF2Node extends KeyDerivationWithDigestParameterNode { - final override string getAlgorithmName() { result = "PBKDF2" } - - /** - * Gets the iteration count of this key derivation algorithm. - */ - abstract string getIterationCount(Location location); - - /** - * Gets the bit-length of the derived key. - */ - abstract string getKeyLength(Location location); - - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "Iterations" and - if exists(this.getIterationCount(location)) - then value = this.getIterationCount(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "KeyLength" and - if exists(this.getKeyLength(location)) - then value = this.getKeyLength(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - } - } - - /** - * PKCS12KDF key derivation function - */ - abstract class PKCS12KDF extends KeyDerivationWithDigestParameterNode { - override string getAlgorithmName() { result = "PKCS12KDF" } - - /** - * Gets the iteration count of this key derivation algorithm. - */ - abstract string getIterationCount(Location location); - - /** - * Gets the raw ID argument specifying the intended use of the derived key. - * - * The intended use is defined in RFC 7292, appendix B.3, as follows: - * - * This standard specifies 3 different values for the ID byte mentioned above: - * - * 1. If ID=1, then the pseudorandom bits being produced are to be used - * as key material for performing encryption or decryption. - * - * 2. If ID=2, then the pseudorandom bits being produced are to be used - * as an IV (Initial Value) for encryption or decryption. - * - * 3. If ID=3, then the pseudorandom bits being produced are to be used - * as an integrity key for MACing. - */ - abstract string getIDByte(Location location); - - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "Iterations" and - if exists(this.getIterationCount(location)) - then value = this.getIterationCount(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "IdByte" and - if exists(this.getIDByte(location)) - then value = this.getIDByte(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - } - } - - /** - * scrypt key derivation function - */ - abstract class SCRYPT extends KeyDerivationAlgorithmNode { - final override string getAlgorithmName() { result = "scrypt" } - - /** - * Gets the iteration count (`N`) argument - */ - abstract string get_N(Location location); - - /** - * Gets the block size (`r`) argument - */ - abstract string get_r(Location location); - - /** - * Gets the parallelization factor (`p`) argument - */ - abstract string get_p(Location location); - - /** - * Gets the derived key length argument - */ - abstract string getDerivedKeyLength(Location location); - - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "N" and - if exists(this.get_N(location)) - then value = this.get_N(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "r" and - if exists(this.get_r(location)) - then value = this.get_r(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "p" and - if exists(this.get_p(location)) - then value = this.get_p(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - or - ( - // [KNOWN_OR_UNKNOWN] - key = "KeyLength" and - if exists(this.getDerivedKeyLength(location)) - then value = this.getDerivedKeyLength(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - ) - } - } - - /* - * TODO: - * - * Rule: No newtype representing a type of algorithm should be modelled with multiple interfaces - * - * Example 1: HKDF and PKCS12KDF are both key derivation algorithms. - * However, PKCS12KDF also has a property: the iteration count. - * - * If we have HKDF and PKCS12KDF under TKeyDerivationType, - * someone modelling a library might try to make a generic identification of both of those algorithms. - * - * They will therefore not use the specialized type for PKCS12KDF, - * meaning "from PKCS12KDF algo select algo" will have no results. - * - * Example 2: Each type below represents a common family of elliptic curves, with a shared interface, i.e., - * predicates for library modellers to implement as well as the properties and edges reported. - */ - /** * Elliptic curve algorithms */ From ac96649a02e78549110002fd42dc240bbd1279aa Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Tue, 1 Apr 2025 16:15:49 -0400 Subject: [PATCH 075/122] Misc. modifications to support elliptic curves and hooking them up to keygeneration. --- java/ql/lib/experimental/Quantum/JCA.qll | 239 ++++++++++++++---- java/ql/lib/experimental/Quantum/Language.qll | 8 + .../codeql/cryptography/Model.qll | 172 +++++++++++-- 3 files changed, 340 insertions(+), 79 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 5cff0129b721..bce4b117522e 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -6,6 +6,10 @@ import semmle.code.java.controlflow.Dominance module JCAModel { import Language + abstract class JCAAlgorithmInstance extends Crypto::AlgorithmInstance { + abstract Crypto::AlgorithmValueConsumer getConsumer(); + } + // TODO: Verify that the PBEWith% case works correctly bindingset[algo] predicate cipher_names(string algo) { @@ -49,6 +53,9 @@ module JCAModel { kdf.toUpperCase().matches(["PBKDF2With%", "PBEWith%"].toUpperCase()) } + bindingset[name] + predicate elliptic_curve_names(string name) { Crypto::isEllipticCurveAlgorithmName(name) } + bindingset[name] Crypto::TKeyDerivationType kdf_name_to_kdf_type(string name, string withSubstring) { name.matches("PBKDF2With%") and @@ -110,6 +117,12 @@ module JCAModel { string getPadding() { result = this.getValue().splitAt("/", 2) } } + class EllipticCurveStringLiteral extends StringLiteral { + EllipticCurveStringLiteral() { elliptic_curve_names(this.getValue()) } + + string getStandardEllipticCurveName() { result = this.getValue() } + } + class CipherGetInstanceCall extends Call { CipherGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") @@ -139,9 +152,9 @@ module JCAModel { } /** - * Data-flow configuration modelling flow from a cipher string literal to a `CipherGetInstanceCall` argument. + * Data-flow configuration modelling flow from a cipher string literal to a value consumer argument. */ - private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + private module CipherAlgorithmStringToFetchConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral } predicate isSink(DataFlow::Node sink) { @@ -149,7 +162,22 @@ module JCAModel { } } - module AlgorithmStringToFetchFlow = TaintTracking::Global; + module CipherAlgorithmStringToFetchFlow = + TaintTracking::Global; + + /** + * Data-flow configuration modelling flow from a cipher string literal to a value consumer argument. + */ + private module EllipticCurveAlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof EllipticCurveStringLiteral } + + predicate isSink(DataFlow::Node sink) { + exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode()) + } + } + + module EllipticCurveAlgorithmStringToFetchFlow = + TaintTracking::Global; /** * Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable. @@ -160,8 +188,8 @@ module JCAModel { * * TODO: Model the case of relying on a provider default, but alert on it as a bad practice. */ - class CipherStringLiteralPaddingAlgorithmInstance extends CipherStringLiteralAlgorithmInstance, - Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral + class CipherStringLiteralPaddingAlgorithmInstance extends JCAAlgorithmInstance, + CipherStringLiteralAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral { CipherStringLiteralPaddingAlgorithmInstance() { exists(super.getPadding()) } // TODO: provider defaults @@ -183,8 +211,8 @@ module JCAModel { } } - class CipherStringLiteralModeAlgorithmInstance extends CipherStringLiteralPaddingAlgorithmInstance, - Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral + class CipherStringLiteralModeAlgorithmInstance extends JCAAlgorithmInstance, + CipherStringLiteralPaddingAlgorithmInstance, Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral { CipherStringLiteralModeAlgorithmInstance() { exists(super.getMode()) } // TODO: provider defaults @@ -216,15 +244,141 @@ module JCAModel { } } - class CipherStringLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof CipherStringLiteral + class KeyGeneratorGetInstanceCall extends MethodCall { + KeyGeneratorGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance") + or + this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance") + } + + Expr getAlgorithmArg() { result = super.getArgument(0) } + } + + // For general elliptic curves, getInstance("EC") is used + // and java.security.spec.ECGenParameterSpec("") is what sets the specific curve. + // The result of ECGenParameterSpec is passed to KeyPairGenerator.initialize + // If the curve is not specified, the default is used. + // We would trace the use of this inside a KeyPairGenerator.initialize + class ECGenParameterSpecCall extends ClassInstanceExpr { + ECGenParameterSpecCall() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("java.security.spec", "ECGenParameterSpec") + } + + Expr getAlgorithmArg() { result = super.getArgument(0) } + + KeyPairGeneratorInitializeCall getInitializeConsumerCall() { + exists(DataFlow::Node sink | + ECGenParameterSpecCallToInitializeFlow::flow(DataFlow::exprNode(this), sink) and + result.getAnArgument() = sink.asExpr() + ) + } + } + + abstract class KeyGenAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + // abstract predicate flowsToKeyGenerateCallQualifier(KeyGeneratorGenerateCall sink); + abstract DataFlow::Node getResultNode(); + } + + class KeyPairGeneratorInitializeCall extends MethodCall { + KeyPairGeneratorInitializeCall() { + this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "initialize") + } + + Expr getKeyArg() { + result = this.getArgument(0) and + result.getType() instanceof IntegralType + } + } + + private module ECGenParameterSpecCallToInitializeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ECGenParameterSpecCall } + + predicate isSink(DataFlow::Node sink) { + exists(KeyPairGeneratorInitializeCall c | c.getAnArgument() = sink.asExpr()) + } + } + + module ECGenParameterSpecCallToInitializeFlow = + DataFlow::Global; + + class ECGenParameterSpecAlgorithmValueConsumer extends KeyGenAlgorithmValueConsumer { + ECGenParameterSpecCall call; + + ECGenParameterSpecAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override DataFlow::Node getResultNode() { + // Traversing to the initialilzer directly and calling this the 'result' + // to simplify the trace. In theory you would trace from the call + // through the initializer, but we already have a trace to the initializer + // so using this instead of altering/creating data flow configs. + call.getInitializeConsumerCall().getQualifier() = result.asExpr() + } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(JCAAlgorithmInstance).getConsumer() = this + } + } + + class KeyGeneratorGetInstanceAlgorithmValueConsumer extends KeyGenAlgorithmValueConsumer { + KeyGeneratorGetInstanceCall call; + + KeyGeneratorGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override DataFlow::Node getResultNode() { result.asExpr() = call } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + // The source is any instance whose consumer is this + result.(JCAAlgorithmInstance).getConsumer() = this + } + } + + class EllipticCurveStringLiteralAlgorithmInstance extends JCAAlgorithmInstance, + Crypto::EllipticCurveAlgorithmInstance instanceof StringLiteral + { + Crypto::AlgorithmValueConsumer consumer; + + EllipticCurveStringLiteralAlgorithmInstance() { + // Trace a known elliptic curve algorithm string literal to a key gen consumer + EllipticCurveAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), + consumer.getInputNode()) + } + + override Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } + + override string getRawEllipticCurveAlgorithmName() { result = super.getValue() } + + override string getStandardCurveName() { result = this.getRawEllipticCurveAlgorithmName() } + + override Crypto::TEllipticCurveType getEllipticCurveFamily() { + Crypto::isEllipticCurveAlgorithm(this.getRawEllipticCurveAlgorithmName(), _, result) + } + + override string getKeySize() { + exists(int keySize | + Crypto::isEllipticCurveAlgorithm(this.getRawEllipticCurveAlgorithmName(), keySize, _) and + result = keySize.toString() + ) + } + } + + class CipherStringLiteralAlgorithmInstance extends JCAAlgorithmInstance, + Crypto::CipherAlgorithmInstance instanceof CipherStringLiteral { + // NOTE: this consumer is generic, but cipher algorithms can be consumed + // by getInstance as well as key generation Crypto::AlgorithmValueConsumer consumer; CipherStringLiteralAlgorithmInstance() { - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + CipherAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } - Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } + override Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { result = this // TODO: provider defaults @@ -302,8 +456,8 @@ module JCAModel { override int getDigestLength() { exists(hash_name_to_hash_type(hashName, result)) } } - class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance, - CipherStringLiteralPaddingAlgorithmInstance + class OAEPPaddingAlgorithmInstance extends JCAAlgorithmInstance, + Crypto::OAEPPaddingAlgorithmInstance, CipherStringLiteralPaddingAlgorithmInstance { override Crypto::HashAlgorithmInstance getOAEPEncodingHashAlgorithm() { result = this } @@ -323,7 +477,7 @@ module JCAModel { override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } CipherStringLiteral getOrigin(string value) { - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), + CipherAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), DataFlow::exprNode(this.(Expr).getAChildExpr*())) and value = result.getValue() } @@ -651,7 +805,8 @@ module JCAModel { module KnownHashAlgorithmLiteralToMessageDigestFlow = DataFlow::Global; - class KnownHashAlgorithm extends Crypto::HashAlgorithmInstance instanceof StringLiteral { + class KnownHashAlgorithm extends JCAAlgorithmInstance, Crypto::HashAlgorithmInstance instanceof StringLiteral + { MessageDigestAlgorithmValueConsumer consumer; KnownHashAlgorithm() { @@ -660,7 +815,7 @@ module JCAModel { consumer.getInputNode()) } - MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer } + override MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer } override string getRawHashAlgorithmName() { result = this.(StringLiteral).getValue() } @@ -733,22 +888,10 @@ module JCAModel { } } - class KeyGeneratorCallAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - KeyGeneratorGetInstanceCall call; - - KeyGeneratorCallAlgorithmValueConsumer() { this = call.getAlgorithmArg() } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this - } - } - // flow from instance created by getInstance to generateKey - module KeyGeneratorGetInstanceToGenerateConfig implements DataFlow::ConfigSig { + module KeyGeneratorAlgValueConsumerToGenerateConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { - exists(KeyGeneratorGetInstanceCall call | src.asExpr() = call) + exists(KeyGenAlgorithmValueConsumer consumer | consumer.getResultNode() = src) } predicate isSink(DataFlow::Node sink) { @@ -756,23 +899,8 @@ module JCAModel { } } - module KeyGeneratorGetInstanceToGenerateFlow = - DataFlow::Global; - - class KeyGeneratorGetInstanceCall extends MethodCall { - KeyGeneratorGetInstanceCall() { - this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance") - or - this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance") - } - - Expr getAlgorithmArg() { result = super.getArgument(0) } - - predicate flowsToKeyGenerateCallQualifier(KeyGeneratorGenerateCall sink) { - KeyGeneratorGetInstanceToGenerateFlow::flow(DataFlow::exprNode(this), - DataFlow::exprNode(sink.(MethodCall).getQualifier())) - } - } + module KeyGeneratorAlgValueConsumerToGenerateFlow = + DataFlow::Global; class KeyGeneratorGenerateCall extends Crypto::KeyGenerationOperationInstance instanceof MethodCall { @@ -791,8 +919,10 @@ module JCAModel { override Crypto::KeyArtifactType getOutputKeyType() { result = type } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - exists(KeyGeneratorGetInstanceCall getInstance | - getInstance.flowsToKeyGenerateCallQualifier(this) and result = getInstance.getAlgorithmArg() + exists(KeyGenAlgorithmValueConsumer consumer | + KeyGeneratorAlgValueConsumerToGenerateFlow::flow(consumer.getResultNode(), + DataFlow::exprNode(this.(Call).getQualifier())) and + result = consumer ) } @@ -879,7 +1009,8 @@ module JCAModel { module MACInitCallToMACOperationFlow = DataFlow::Global; - class KnownMACAlgorithm extends Crypto::MACAlgorithmInstance instanceof StringLiteral { + class KnownMACAlgorithm extends JCAAlgorithmInstance, Crypto::MACAlgorithmInstance instanceof StringLiteral + { MACGetInstanceAlgorithmValueConsumer consumer; KnownMACAlgorithm() { @@ -887,7 +1018,7 @@ module JCAModel { MACKnownAlgorithmToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } - MACGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer } + override MACGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer } override string getRawMACAlgorithmName() { result = super.getValue() } @@ -1039,7 +1170,8 @@ module JCAModel { } } - class KDFAlgorithmStringLiteral extends Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral + class KDFAlgorithmStringLiteral extends JCAAlgorithmInstance, + Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral { SecretKeyFactoryKDFAlgorithmValueConsumer consumer; @@ -1054,10 +1186,10 @@ module JCAModel { result = kdf_name_to_kdf_type(super.getValue(), _) } - SecretKeyFactoryKDFAlgorithmValueConsumer getConsumer() { result = consumer } + override SecretKeyFactoryKDFAlgorithmValueConsumer getConsumer() { result = consumer } } - class PBKDF2AlgorithmStringLiteral extends KDFAlgorithmStringLiteral, + class PBKDF2AlgorithmStringLiteral extends JCAAlgorithmInstance, KDFAlgorithmStringLiteral, Crypto::PBKDF2AlgorithmInstance, Crypto::HMACAlgorithmInstance, Crypto::HashAlgorithmInstance, Crypto::AlgorithmValueConsumer { @@ -1158,4 +1290,5 @@ module JCAModel { override string getIterationCountFixed() { none() } } + // TODO: flow the GCGenParametersSpecCall to an init, and the init to the operations } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 1621e9436c42..55d4f497f8ef 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -95,6 +95,14 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { } class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof Literal { + ConstantDataSource() { + // TODO: this is an API specific workaround for JCA, as 'EC' is a constant that may be used + // where typical algorithms are specified, but EC specifically means set up a + // default curve container, that will later be specified explicitly (or if not a default) + // curve is used. + this = any(Literal l | l.getValue() != "EC") + } + override DataFlow::Node getOutputNode() { result.asExpr() = this } override predicate flowsTo(Crypto::FlowAwareElement other) { diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 8252b5138600..f202db6f8e67 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -622,7 +622,30 @@ module CryptographyBase Input> { abstract class KeyEncapsulationAlgorithmInstance extends AlgorithmInstance { } - abstract class EllipticCurveAlgorithmInstance extends AlgorithmInstance { } + abstract class EllipticCurveAlgorithmInstance extends AlgorithmInstance { + /** + * Gets the isolated name as it appears in source + * + * This name should not be parsed or formatted beyond isolating the raw name if necessary. + */ + abstract string getRawEllipticCurveAlgorithmName(); + + /** + * The 'standard' curve name, e.g., "P-256" or "secp256r1". + * meaning the full name of the curve, including the family, key size, and other + * typical parameters found on the name. In many cases this will + * be equivalent to `getRawEllipticCurveAlgorithmName()`, but not always + * (e.g., if the curve is specified through a raw NID). + * In cases like an NID, we want the standardized name so users can quickly + * understand what the curve is, while also parsing out the family and key size + * separately. + */ + abstract string getStandardCurveName(); + + abstract TEllipticCurveType getEllipticCurveFamily(); + + abstract string getKeySize(); + } abstract class HashOperationInstance extends OperationInstance { abstract DigestArtifactInstance getDigestArtifact(); @@ -783,6 +806,9 @@ module CryptographyBase Input> { final private class KeyDerivationAlgorithmInstanceOrValueConsumer = AlgorithmInstanceOrValueConsumer::Union; + final private class EllipticCurveAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; + private newtype TNode = // Artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or @@ -802,7 +828,7 @@ module CryptographyBase Input> { TKeyCreationOperation(KeyCreationOperationInstance e) or // Algorithms (e.g., SHA-256, AES) TCipherAlgorithm(CipherAlgorithmInstanceOrValueConsumer e) or - TEllipticCurveAlgorithm(EllipticCurveAlgorithmInstance e) or + TEllipticCurveAlgorithm(EllipticCurveAlgorithmInstanceOrValueConsumer e) or THashAlgorithm(HashAlgorithmInstanceOrValueConsumer e) or TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstanceOrValueConsumer e) or TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or @@ -2077,39 +2103,133 @@ module CryptographyBase Input> { ES() or OtherEllipticCurveType() - abstract class EllipticCurve extends AlgorithmNode, TEllipticCurveAlgorithm { - abstract string getKeySize(Location location); + private predicate isBrainpoolCurve(string curveName, int keySize) { + // ALL BRAINPOOL CURVES + keySize in [160, 192, 224, 256, 320, 384, 512] and + ( + curveName = "BRAINPOOLP" + keySize.toString() + "R1" + or + curveName = "BRAINPOOLP" + keySize.toString() + "T1" + ) + } + + private predicate isSecCurve(string curveName, int keySize) { + // ALL SEC CURVES + keySize in [112, 113, 128, 131, 160, 163, 192, 193, 224, 233, 239, 256, 283, 384, 409, 521, 571] and + exists(string suff | suff in ["R1", "R2", "K1"] | + curveName = "SECT" + keySize.toString() + suff or + curveName = "SECP" + keySize.toString() + suff + ) + } + + private predicate isC2Curve(string curveName, int keySize) { + // ALL C2 CURVES + keySize in [163, 176, 191, 208, 239, 272, 304, 359, 368, 431] and + exists(string pre, string suff | + pre in ["PNB", "ONB", "TNB"] and suff in ["V1", "V2", "V3", "V4", "V5", "W1", "R1"] + | + curveName = "C2" + pre + keySize.toString() + suff + ) + } + + private predicate isPrimeCurve(string curveName, int keySize) { + // ALL PRIME CURVES + keySize in [192, 239, 256] and + exists(string suff | suff in ["V1", "V2", "V3"] | + curveName = "PRIME" + keySize.toString() + suff + ) + } + + private predicate isNumsCurve(string curveName, int keySize) { + // ALL NUMS CURVES + keySize in [256, 384, 512] and + exists(string suff | suff in ["T1"] | curveName = "NUMSP" + keySize.toString() + suff) + } + + bindingset[curveName] + predicate isEllipticCurveAlgorithmName(string curveName) { + isEllipticCurveAlgorithm(curveName, _, _) + } + + /** + * Holds if `name` corresponds to a known elliptic curve. + */ + bindingset[rawName] + predicate isEllipticCurveAlgorithm(string rawName, int keySize, TEllipticCurveType curveFamily) { + exists(string curveName | curveName = rawName.toUpperCase() | + isSecCurve(curveName, keySize) and curveFamily = SEC() + or + isBrainpoolCurve(curveName, keySize) and curveFamily = BRAINPOOL() + or + isC2Curve(curveName, keySize) and curveFamily = C2() + or + isPrimeCurve(curveName, keySize) and curveFamily = PRIME() + or + isNumsCurve(curveName, keySize) and curveFamily = NUMS() + or + curveName = "ES256" and keySize = 256 and curveFamily = ES() + or + curveName = "CURVE25519" and keySize = 255 and curveFamily = CURVE25519() + or + curveName = "X25519" and keySize = 255 and curveFamily = CURVE25519() + or + curveName = "ED25519" and keySize = 255 and curveFamily = CURVE25519() + or + curveName = "CURVE448" and keySize = 448 and curveFamily = CURVE448() + or + curveName = "ED448" and keySize = 448 and curveFamily = CURVE448() + or + curveName = "X448" and keySize = 448 and curveFamily = CURVE448() + or + curveName = "SM2" and keySize in [256, 512] and curveFamily = SM2() + ) + } + + final class EllipticCurveNode extends AlgorithmNode, TEllipticCurveAlgorithm { + EllipticCurveAlgorithmInstanceOrValueConsumer instance; + + EllipticCurveNode() { this = TEllipticCurveAlgorithm(instance) } - abstract TEllipticCurveType getCurveFamily(); + override string getInternalType() { result = "EllipticCurveAlgorithm" } + + final override string getRawAlgorithmName() { + result = instance.asAlg().getRawEllipticCurveAlgorithmName() + } + + // NICK QUESTION: do I repeat the key size and curve family predicates here as wrappers of the instance? + override LocatableElement asElement() { result = instance } + + TEllipticCurveType getEllipticCurveFamily() { + result = instance.asAlg().getEllipticCurveFamily() + } override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or - // [KNOWN_OR_UNKNOWN] + // [ONLY_KNOWN] key = "KeySize" and - if exists(this.getKeySize(location)) - then value = this.getKeySize(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - // other properties, like field type are possible, but not modeled until considered necessary + value = instance.asAlg().getKeySize() and + location = this.getLocation() + or + key = "StdCurveName" and + value = instance.asAlg().getStandardCurveName().toUpperCase() and + location = this.getLocation() } override string getAlgorithmName() { result = this.getRawAlgorithmName().toUpperCase() } - - /** - * Mandating that for Elliptic Curves specifically, users are responsible - * for providing as the 'raw' name, the official name of the algorithm. - * - * Casing doesn't matter, we will enforce further naming restrictions on - * `getAlgorithmName` by default. - * - * Rationale: elliptic curve names can have a lot of variation in their components - * (e.g., "secp256r1" vs "P-256"), trying to produce generalized set of properties - * is possible to capture all cases, but such modeling is likely not necessary. - * if all properties need to be captured, we can reassess how names are generated. - */ - abstract override string getRawAlgorithmName(); + // /** + // * Mandating that for Elliptic Curves specifically, users are responsible + // * for providing as the 'raw' name, the official name of the algorithm. + // * + // * Casing doesn't matter, we will enforce further naming restrictions on + // * `getAlgorithmName` by default. + // * + // * Rationale: elliptic curve names can have a lot of variation in their components + // * (e.g., "secp256r1" vs "P-256"), trying to produce generalized set of properties + // * is possible to capture all cases, but such modeling is likely not necessary. + // * if all properties need to be captured, we can reassess how names are generated. + // */ + // abstract override string getRawAlgorithmName(); } abstract class KEMAlgorithm extends TKeyEncapsulationAlgorithm, AlgorithmNode { From 50507586ac3faf9b00ff9a9d0c9fe47e3b92f88c Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Wed, 2 Apr 2025 19:44:57 +0200 Subject: [PATCH 076/122] Refactor output artifact type --- java/ql/lib/experimental/Quantum/JCA.qll | 6 +- java/ql/lib/experimental/Quantum/Language.qll | 14 ----- .../codeql/cryptography/Model.qll | 57 +++++++------------ 3 files changed, 24 insertions(+), 53 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 5cff0129b721..4a9082bf91c2 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -419,7 +419,7 @@ module JCAModel { src.asExpr() instanceof CipherGetInstanceCall } - predicate isSink(DataFlow::Node sink, FlowState state) { none() } + predicate isSink(DataFlow::Node sink, FlowState state) { none() } // TODO: document this, but this is intentional (avoid cross products?) predicate isSink(DataFlow::Node sink) { exists(CipherOperationCall c | c.getQualifier() = sink.asExpr()) @@ -786,7 +786,7 @@ module JCAModel { type instanceof Crypto::TAsymmetricKeyType } - override DataFlow::Node getOutputKeyArtifact() { result.asExpr() = this } + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result.asExpr() = this } override Crypto::KeyArtifactType getOutputKeyType() { result = type } @@ -1139,7 +1139,7 @@ module JCAModel { result.asExpr() = this.getInstantiation().getIterationCountArg() } - override DataFlow::Node getOutputKeyArtifact() { + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result.asExpr() = this and super.getMethod().getReturnType().hasName("SecretKey") } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 1621e9436c42..9e27fedf7faf 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -191,19 +191,5 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { module GenericDataSourceUniversalFlow = TaintTracking::Global; -/* - * class LiteralOrGenericDataSource extends Element { - * DataFlow::Node node; - * - * LiteralOrGenericDataSource() { - * node = this.(Crypto::GenericSourceInstance).getOutputNode() or - * node.asExpr() = this.(Literal) - * } - * - * bindingset[other] - * predicate localFlowsTo(DataFlow::Node other) { DataFlow::localFlow(node, other) } - * } - */ - // Import library-specific modeling import JCA diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 8252b5138600..33c75c556ef3 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -41,6 +41,10 @@ module CryptographyBase Input> { ConsumerElement getConsumer() { result.getInputNode() = this } } + class ArtifactOutputDataFlowNode extends DataFlowNode { + OutputArtifactInstance getArtifact() { result.getOutputNode() = this } + } + final class UnknownPropertyValue extends string { UnknownPropertyValue() { this = "" } } @@ -461,7 +465,7 @@ module CryptographyBase Input> { this = Input::dfn_to_element(inputNode) } - override KeyArtifactType getKeyType() { result instanceof TUnknownKeyType } + override KeyArtifactType getKeyType() { result instanceof TUnknownKeyType } // A consumer node does not have a key type, refer to source (TODO: refine, should this be none()) final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } } @@ -651,7 +655,7 @@ module CryptographyBase Input> { /** * Gets the key artifact produced by this operation. */ - abstract DataFlowNode getOutputKeyArtifact(); + abstract ArtifactOutputDataFlowNode getOutputKeyArtifact(); /** * Gets the key artifact type produced. @@ -898,29 +902,8 @@ module CryptographyBase Input> { predicate isExcludedFromGraph() { none() } } - signature string getDefaultValueSig(); - - signature ConsumerInputDataFlowNode getConsumerSig(); - - signature class NodeBaseSig instanceof NodeBase; - - module PropertyOutput { - bindingset[root] - predicate get(NodeBase root, string value, Location location) { - if not exists(getDefault()) and not exists(getConsumer().getConsumer().getASource()) - then value instanceof UnknownPropertyValue and location instanceof UnknownLocation - else ( - if exists(getDefault()) - then - value = "Default:" + getDefault() and - location = root.getLocation() - else node_as_property(getConsumer().getConsumer().getAGenericSourceNode(), value, location) - ) - } - } - /** - * A generic source node is a source of data that is not resolvable to a specific value or type. + * A generic source node is a source of data that is not resolvable to a specific asset. */ private class GenericSourceNode extends NodeBase, TGenericSourceNode { GenericSourceInstance instance; @@ -956,7 +939,7 @@ module CryptographyBase Input> { * Holds if `node` is a potential candidate for a known algorithm node. * This predicate should be used to restrict the set of candidate algorithm node types. */ - abstract predicate isCandidateKnownAlgorithmNode(AlgorithmNode node); + abstract predicate isCandidateAlgorithmNode(AlgorithmNode node); /** * Gets the algorithm or generic source nodes consumed as an algorithm associated with this operation. @@ -968,12 +951,12 @@ module CryptographyBase Input> { } /** - * Gets a known algorithm associated with this operation, subject to `isCandidateKnownAlgorithmNode`. + * Gets a known algorithm associated with this operation, subject to `isCandidateAlgorithmNode`. */ AlgorithmNode getAKnownAlgorithm() { result = this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() and - this.isCandidateKnownAlgorithmNode(result) + this.isCandidateAlgorithmNode(result) } override NodeBase getChild(string edgeName) { @@ -1147,9 +1130,11 @@ module CryptographyBase Input> { // [KNOWN_OR_UNKNOWN] - only if asymmetric edgeName = "Algorithm" and instance.getKeyType() instanceof TAsymmetricKeyType and - if exists(this.getAKnownAlgorithmOrGenericSourceNode()) - then result = this.getAKnownAlgorithmOrGenericSourceNode() - else result = this + ( + if exists(this.getAKnownAlgorithmOrGenericSourceNode()) + then result = this.getAKnownAlgorithmOrGenericSourceNode() + else result = this + ) } override predicate properties(string key, string value, Location location) { @@ -1217,7 +1202,7 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } - override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { node instanceof MACAlgorithmNode } @@ -1292,7 +1277,7 @@ module CryptographyBase Input> { KeyGenerationOperationNode() { keyGenInstance = instance } - override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { node instanceof CipherAlgorithmNode } @@ -1326,7 +1311,7 @@ module CryptographyBase Input> { result.asElement() = kdfInstance.getOutputKeySizeConsumer().getConsumer().getAGenericSource() } - override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { node instanceof KeyDerivationAlgorithmNode } @@ -1585,7 +1570,7 @@ module CryptographyBase Input> { override string getInternalType() { result = "CipherOperation" } - override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { node instanceof CipherAlgorithmNode } @@ -1904,7 +1889,7 @@ module CryptographyBase Input> { type instanceof SM4 and name = "SM4" and s = Block() or type instanceof OtherCipherType and - name instanceof UnknownPropertyValue and + name instanceof UnknownPropertyValue and // TODO: get rid of this hack to bind structure and type s = UnknownCipherStructureType() } @@ -1959,7 +1944,7 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } - override predicate isCandidateKnownAlgorithmNode(AlgorithmNode node) { + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { node instanceof HashAlgorithmNode } From 697c9f0bb0e03272a6d6c964a4891582e1526697 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 4 Apr 2025 09:02:09 -0400 Subject: [PATCH 077/122] Elliptic curve AVCs are incorrect, but I'm not sure how to fix them generally. Putting in a stop gap to use 'isCipherAVC' for now. --- shared/cryptography/codeql/cryptography/Model.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 1c2e655314cf..09f67f156c2a 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -811,7 +811,7 @@ module CryptographyBase Input> { AlgorithmInstanceOrValueConsumer::Union; final private class EllipticCurveAlgorithmInstanceOrValueConsumer = - AlgorithmInstanceOrValueConsumer::Union; + AlgorithmInstanceOrValueConsumer::Union; private newtype TNode = // Artifacts (data that is not an operation or algorithm, e.g., a key) From a2fe19af38acc7e4a7acef3c61ff66ea93c6605a Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 4 Apr 2025 16:00:05 -0400 Subject: [PATCH 078/122] Initial progress on key agreement. --- java/ql/lib/experimental/Quantum/JCA.qll | 135 +++++++++++++++++- .../codeql/cryptography/Model.qll | 53 +++++++ 2 files changed, 186 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 2bce7fa68438..7d4b36ebe276 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -56,6 +56,9 @@ module JCAModel { bindingset[name] predicate elliptic_curve_names(string name) { Crypto::isEllipticCurveAlgorithmName(name) } + bindingset[name] + predicate key_agreement_names(string name) { Crypto::isKeyAgreementAlgorithmName(name) } + bindingset[name] Crypto::TKeyDerivationType kdf_name_to_kdf_type(string name, string withSubstring) { name.matches("PBKDF2With%") and @@ -123,6 +126,10 @@ module JCAModel { string getStandardEllipticCurveName() { result = this.getValue() } } + class KeyAgreementStringLiteral extends StringLiteral { + KeyAgreementStringLiteral() { key_agreement_names(this.getValue()) } + } + class CipherGetInstanceCall extends Call { CipherGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") @@ -166,7 +173,7 @@ module JCAModel { TaintTracking::Global; /** - * Data-flow configuration modelling flow from a cipher string literal to a value consumer argument. + * Data-flow configuration modelling flow from a elliptic curve literal to a value consumer argument. */ private module EllipticCurveAlgorithmStringToFetchConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof EllipticCurveStringLiteral } @@ -211,6 +218,89 @@ module JCAModel { } } + /** + * Data-flow configuration modelling flow from a key agreement literal to a value consumer argument. + */ + private module KeyAgreementAlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeyAgreementStringLiteral } + + predicate isSink(DataFlow::Node sink) { + exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode()) + } + } + + module KeyAgreementAlgorithmStringToFetchFlow = + TaintTracking::Global; + + class KeyAgreementInitCall extends MethodCall { + KeyAgreementInitCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "init") + } + + Expr getServerKeyArg() { result = this.getArgument(0) } + } + + private module KeyAgreementInitQualifierToSecretGenQualifierConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + exists(KeyAgreementInitCall init | src.asExpr() = init.getQualifier()) + } + + predicate isSink(DataFlow::Node sink) { + exists(KeyAgreementGenerateSecretCall c | sink.asExpr() = c.getQualifier()) + } + + /** + * Barrier if we go into another init, assume the second init overwrites the first + */ + predicate isBarrierIn(DataFlow::Node node) { isSource(node) } + } + + module KeyAgreementInitQualifierToSecretGenQualifierFlow = + DataFlow::Global; + + class KeyAgreementGenerateSecretCall extends MethodCall { + KeyAgreementGenerateSecretCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "generateSecret") + } + + KeyAgreementInitCall getKeyAgreementInitCall() { + KeyAgreementInitQualifierToSecretGenQualifierFlow::flow(DataFlow::exprNode(result + .getQualifier()), DataFlow::exprNode(this.getQualifier())) + } + } + + private module KeyAgreementAVCToInitQualifierConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + exists(KeyAgreementAlgorithmValueConsumer consumer | consumer.getResultNode() = src) + } + + predicate isSink(DataFlow::Node sink) { + exists(KeyAgreementInitCall init | sink.asExpr() = init.getQualifier()) + } + } + + module KeyAgreementAVCToInitQualifierFlow = + DataFlow::Global; + + class KeyAgreementSecretGenerationOperationInstance extends Crypto::KeyAgreementSecretGenerationOperationInstance instanceof KeyAgreementGenerateSecretCall + { + override Crypto::ConsumerInputDataFlowNode getServerKeyConsumer() { + this.(KeyAgreementGenerateSecretCall).getKeyAgreementInitCall().getServerKeyArg() = + result.asExpr() + } + + override Crypto::ConsumerInputDataFlowNode getPeerKeyConsumer() { + none() //TODO + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + none() // TODO: key agreeement has it's own algorithm consumer, separate from the key + // TODO: the char pred must trace from the consumer to here, + // in theory, along that path we would get the init and doPhase, but can I just get those + // separately avoiding a complicated config state for dataflow? + } + } + class CipherStringLiteralModeAlgorithmInstance extends JCAAlgorithmInstance, CipherStringLiteralPaddingAlgorithmInstance, Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral { @@ -339,7 +429,7 @@ module JCAModel { } class EllipticCurveStringLiteralAlgorithmInstance extends JCAAlgorithmInstance, - Crypto::EllipticCurveAlgorithmInstance instanceof StringLiteral + Crypto::EllipticCurveAlgorithmInstance instanceof EllipticCurveStringLiteral { Crypto::AlgorithmValueConsumer consumer; @@ -367,6 +457,47 @@ module JCAModel { } } + class KeyAgreementGetInstanceCall extends MethodCall { + KeyAgreementGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "getInstance") + } + + Expr getAlgorithmArg() { result = super.getArgument(0) } + } + + class KeyAgreementAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + KeyAgreementGetInstanceCall call; + + KeyAgreementAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + DataFlow::Node getResultNode() { result.asExpr() = call } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(KeyAgreementStringLiteralAlgorithmInstance).getConsumer() = this + } + } + + class KeyAgreementStringLiteralAlgorithmInstance extends JCAAlgorithmInstance, + Crypto::KeyAgreementAlgorithmInstance instanceof KeyAgreementStringLiteral + { + Crypto::AlgorithmValueConsumer consumer; + + KeyAgreementStringLiteralAlgorithmInstance() { + KeyAgreementAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + } + + override Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } + // override Crypto::EllipticCurveAlgorithmInstance getEllipticCurveAlgorithm() { + // this.(KeyAgreementStringLiteral).getValue().toUpperCase() in ["X25519", "X448"] and + // // NOTE: this relies upon modeling the elliptic curve on 'this' separately + // result = this + // // TODO: or is ecdh and go find the curve + // // this.(KeyAgreementStringLiteral).toString().toUpperCase() = ["ECDH"] + // } + } + class CipherStringLiteralAlgorithmInstance extends JCAAlgorithmInstance, Crypto::CipherAlgorithmInstance instanceof CipherStringLiteral { diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 09f67f156c2a..fe397c6247f3 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -837,6 +837,7 @@ module CryptographyBase Input> { TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstanceOrValueConsumer e) or TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or TMACAlgorithm(MACAlgorithmInstanceOrValueConsumer e) or + TKeyAgreementAlgorithm(KeyAgreementAlgorithmInstance e) or // Non-standalone Algorithms (e.g., Mode, Padding) // TODO: need to rename this, as "mode" is getting reused in different contexts, be precise TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or @@ -2220,4 +2221,56 @@ module CryptographyBase Input> { abstract class KEMAlgorithm extends TKeyEncapsulationAlgorithm, AlgorithmNode { final override string getInternalType() { result = "KeyEncapsulationAlgorithm" } } + + /** + * Key agreement algorithms + */ + newtype TKeyAgreementType = + DH() or // Diffie-Hellman + EDH() or // Ephemeral Diffie-Hellman + ECDH() or // Elliptic Curve Diffie-Hellman + // Note: x25519 and x448 are applications of ECDH + OtherKeyAgreementType() + + bindingset[name] + predicate isKeyAgreementAlgorithmName(string name) { isKeyAgreementAlgorithm(name, _) } + + bindingset[name] + predicate isKeyAgreementAlgorithm(string name, TKeyAgreementType type) { + exists(string name2 | name2 = name.toUpperCase() | + name2 = "DH" and type = DH() + or + name2 = "EDH" and type = EDH() + or + name2 = "ECDH" and type = ECDH() + or + name2 = "X25519" and type = ECDH() + or + name2 = "X448" and type = ECDH() + ) + } + + abstract class KeyAgreementAlgorithmInstance extends AlgorithmInstance { + // /** + // * If the key agreement uses a curve, (e.g., ECDH) point to the curve instance. + // * none() if the agreement is not curve based (e.g., plain DH). + // * Note that if the curve is inherent to the algorithm (e.g., x25519), this will be + // * the key agreement algorithm instance itself (this). + // */ + // abstract EllipticCurveAlgorithmInstance getEllipticCurveAlgorithm(); + } + + abstract class KeyAgreementSecretGenerationOperationInstance extends OperationInstance { + /** + * The private key used in the key agreement operation. + * This key represents the local party in the key agreement. + */ + abstract ConsumerInputDataFlowNode getServerKeyConsumer(); + + /** + * The public key used in the key agreement operation, coming + * from the peer (the other party in the key agreement). + */ + abstract ConsumerInputDataFlowNode getPeerKeyConsumer(); + } } From b9d0abda632b05ddaa74e3747defa1eb8cc2b0a4 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Wed, 9 Apr 2025 21:19:00 +0200 Subject: [PATCH 079/122] Move CipherOperation into KeyOperation, refactor - KeyOperation and Algorithm now encompasses encryption, decryption, wrapping, unwrapping, signing, and verifying. - Removed elliptic curve implementation from JCA.qll pending rewrite - Removed JCAAlgorithmInstance abstraction from JCA.qll - Cleaned up and removed JCA-specific naming from Model.qll - Added and clarified documentation --- cpp/ql/lib/experimental/Quantum/Language.qll | 2 +- java/ql/lib/experimental/Quantum/JCA.qll | 754 +++++------ .../InsecureOrUnknownNonceAtOperation.ql | 9 +- .../Quantum/PossibleReusedNonce.ql | 17 +- .../ql/src/experimental/Quantum/TestCipher.ql | 8 +- .../src/experimental/Quantum/TestCipherKey.ql | 4 +- .../codeql/cryptography/Model.qll | 1176 +++++++++-------- 7 files changed, 970 insertions(+), 1000 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index 2dc17f5e267a..0d0caf7f49b5 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -112,7 +112,7 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { module ArtifactUniversalFlow = DataFlow::Global; -abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance { +abstract class CipherOutputArtifact extends Crypto::KeyOperationOutputArtifactInstance { override predicate flowsTo(Crypto::FlowAwareElement other) { ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) } diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 7d4b36ebe276..b75c00c35498 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -5,10 +5,7 @@ import semmle.code.java.controlflow.Dominance module JCAModel { import Language - - abstract class JCAAlgorithmInstance extends Crypto::AlgorithmInstance { - abstract Crypto::AlgorithmValueConsumer getConsumer(); - } + import Crypto::KeyOpAlg as KeyOpAlg // TODO: Verify that the PBEWith% case works correctly bindingset[algo] @@ -54,10 +51,48 @@ module JCAModel { } bindingset[name] - predicate elliptic_curve_names(string name) { Crypto::isEllipticCurveAlgorithmName(name) } + predicate elliptic_curve_names(string name) { + // Note: as a one-off exception, we use the internal Crypto module implementation of `isEllipticCurveAlgorithmName` + Crypto::isEllipticCurveAlgorithmName(name) + } + + /* + * TODO: + * + * MAC Algorithms possible (JCA Default + BouncyCastle Extensions) + * + * Name Type Description + * --------------------------------------------------------------------------- + * "HmacMD5" HMAC HMAC with MD5 (not recommended) + * "HmacSHA1" HMAC HMAC with SHA-1 (not recommended) + * "HmacSHA224" HMAC HMAC with SHA-224 + * "HmacSHA256" HMAC HMAC with SHA-256 + * "HmacSHA384" HMAC HMAC with SHA-384 + * "HmacSHA512" HMAC HMAC with SHA-512 + * + * (BouncyCastle and Other Provider Extensions) + * "AESCMAC" CMAC Cipher-based MAC using AES + * "DESCMAC" CMAC CMAC with DES (legacy) + * "GMAC" GCM-based MAC Authenticates AAD only (GCM-style) + * "Poly1305" AEAD-style MAC Used with ChaCha20 + * "SipHash" Hash-based MAC Fast MAC for short inputs + * "BLAKE2BMAC" HMAC-style BLAKE2b MAC (cryptographic hash) + * "HmacRIPEMD160" HMAC HMAC with RIPEMD160 hash + */ + + bindingset[name] + predicate mac_names(string name) { + name.toUpperCase() + .matches([ + "HMAC%", "AESCMAC", "DESCMAC", "GMAC", "Poly1305", "SipHash", "BLAKE2BMAC", + "HMACRIPEMD160" + ].toUpperCase()) + } bindingset[name] - predicate key_agreement_names(string name) { Crypto::isKeyAgreementAlgorithmName(name) } + predicate key_agreement_names(string name) { + name.toUpperCase().matches(["DH", "EDH", "ECDH", "X25519", "X448"].toUpperCase()) + } bindingset[name] Crypto::TKeyDerivationType kdf_name_to_kdf_type(string name, string withSubstring) { @@ -107,6 +142,77 @@ module JCAModel { digestLength = 512 // TODO: verify } + bindingset[name] + private predicate mode_name_to_type_known( + Crypto::TBlockCipherModeOfOperationType type, string name + ) { + type = Crypto::ECB() and name = "ECB" + or + type = Crypto::CBC() and name = "CBC" + or + type = Crypto::GCM() and name = "GCM" + or + type = Crypto::CTR() and name = "CTR" + or + type = Crypto::XTS() and name = "XTS" + or + type = Crypto::CCM() and name = "CCM" + or + type = Crypto::SIV() and name = "SIV" + or + type = Crypto::OCB() and name = "OCB" + } + + bindingset[name] + private predicate cipher_name_to_type_known(KeyOpAlg::TAlgorithm type, string name) { + exists(string upper | upper = name.toUpperCase() | + upper.matches("AES%") and + type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::AES()) + or + upper = "DES" and + type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DES()) + or + upper = "TRIPLEDES" and + type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::TripleDES()) + or + upper = "IDEA" and + type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::IDEA()) + or + upper = "CAST5" and + type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CAST5()) + or + upper = "CHACHA20" and + type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CHACHA20()) + or + upper = "RC4" and + type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC4()) + or + upper = "RC5" and + type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC5()) + or + upper = "RSA" and + type = KeyOpAlg::TAsymmetricCipher(KeyOpAlg::RSA()) + ) + } + + bindingset[name] + predicate mac_name_to_mac_type_known(Crypto::TMACType type, string name) { + type = Crypto::THMAC() and + name.toUpperCase().matches("HMAC%") + } + + bindingset[name] + predicate key_agreement_name_to_type_known(Crypto::TKeyAgreementType type, string name) { + type = Crypto::DH() and + name.toUpperCase() = "DH" + or + type = Crypto::EDH() and + name.toUpperCase() = "EDH" + or + type = Crypto::ECDH() and + name.toUpperCase() in ["ECDH", "X25519", "X448"] + } + /** * A `StringLiteral` in the `"ALG/MODE/PADDING"` or `"ALG"` format */ @@ -120,16 +226,6 @@ module JCAModel { string getPadding() { result = this.getValue().splitAt("/", 2) } } - class EllipticCurveStringLiteral extends StringLiteral { - EllipticCurveStringLiteral() { elliptic_curve_names(this.getValue()) } - - string getStandardEllipticCurveName() { result = this.getValue() } - } - - class KeyAgreementStringLiteral extends StringLiteral { - KeyAgreementStringLiteral() { key_agreement_names(this.getValue()) } - } - class CipherGetInstanceCall extends Call { CipherGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") @@ -159,9 +255,9 @@ module JCAModel { } /** - * Data-flow configuration modelling flow from a cipher string literal to a value consumer argument. + * Data-flow configuration modelling flow from a cipher string literal to a `CipherGetInstanceCall` argument. */ - private module CipherAlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral } predicate isSink(DataFlow::Node sink) { @@ -169,22 +265,7 @@ module JCAModel { } } - module CipherAlgorithmStringToFetchFlow = - TaintTracking::Global; - - /** - * Data-flow configuration modelling flow from a elliptic curve literal to a value consumer argument. - */ - private module EllipticCurveAlgorithmStringToFetchConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof EllipticCurveStringLiteral } - - predicate isSink(DataFlow::Node sink) { - exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode()) - } - } - - module EllipticCurveAlgorithmStringToFetchFlow = - TaintTracking::Global; + module AlgorithmStringToFetchFlow = TaintTracking::Global; /** * Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable. @@ -195,8 +276,8 @@ module JCAModel { * * TODO: Model the case of relying on a provider default, but alert on it as a bad practice. */ - class CipherStringLiteralPaddingAlgorithmInstance extends JCAAlgorithmInstance, - CipherStringLiteralAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral + class CipherStringLiteralPaddingAlgorithmInstance extends CipherStringLiteralAlgorithmInstance, + Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral { CipherStringLiteralPaddingAlgorithmInstance() { exists(super.getPadding()) } // TODO: provider defaults @@ -218,298 +299,29 @@ module JCAModel { } } - /** - * Data-flow configuration modelling flow from a key agreement literal to a value consumer argument. - */ - private module KeyAgreementAlgorithmStringToFetchConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeyAgreementStringLiteral } - - predicate isSink(DataFlow::Node sink) { - exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode()) - } - } - - module KeyAgreementAlgorithmStringToFetchFlow = - TaintTracking::Global; - - class KeyAgreementInitCall extends MethodCall { - KeyAgreementInitCall() { - this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "init") - } - - Expr getServerKeyArg() { result = this.getArgument(0) } - } - - private module KeyAgreementInitQualifierToSecretGenQualifierConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { - exists(KeyAgreementInitCall init | src.asExpr() = init.getQualifier()) - } - - predicate isSink(DataFlow::Node sink) { - exists(KeyAgreementGenerateSecretCall c | sink.asExpr() = c.getQualifier()) - } - - /** - * Barrier if we go into another init, assume the second init overwrites the first - */ - predicate isBarrierIn(DataFlow::Node node) { isSource(node) } - } - - module KeyAgreementInitQualifierToSecretGenQualifierFlow = - DataFlow::Global; - - class KeyAgreementGenerateSecretCall extends MethodCall { - KeyAgreementGenerateSecretCall() { - this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "generateSecret") - } - - KeyAgreementInitCall getKeyAgreementInitCall() { - KeyAgreementInitQualifierToSecretGenQualifierFlow::flow(DataFlow::exprNode(result - .getQualifier()), DataFlow::exprNode(this.getQualifier())) - } - } - - private module KeyAgreementAVCToInitQualifierConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { - exists(KeyAgreementAlgorithmValueConsumer consumer | consumer.getResultNode() = src) - } - - predicate isSink(DataFlow::Node sink) { - exists(KeyAgreementInitCall init | sink.asExpr() = init.getQualifier()) - } - } - - module KeyAgreementAVCToInitQualifierFlow = - DataFlow::Global; - - class KeyAgreementSecretGenerationOperationInstance extends Crypto::KeyAgreementSecretGenerationOperationInstance instanceof KeyAgreementGenerateSecretCall - { - override Crypto::ConsumerInputDataFlowNode getServerKeyConsumer() { - this.(KeyAgreementGenerateSecretCall).getKeyAgreementInitCall().getServerKeyArg() = - result.asExpr() - } - - override Crypto::ConsumerInputDataFlowNode getPeerKeyConsumer() { - none() //TODO - } - - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - none() // TODO: key agreeement has it's own algorithm consumer, separate from the key - // TODO: the char pred must trace from the consumer to here, - // in theory, along that path we would get the init and doPhase, but can I just get those - // separately avoiding a complicated config state for dataflow? - } - } - - class CipherStringLiteralModeAlgorithmInstance extends JCAAlgorithmInstance, - CipherStringLiteralPaddingAlgorithmInstance, Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral + class CipherStringLiteralModeAlgorithmInstance extends CipherStringLiteralPaddingAlgorithmInstance, + Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral { CipherStringLiteralModeAlgorithmInstance() { exists(super.getMode()) } // TODO: provider defaults override string getRawModeAlgorithmName() { result = super.getMode() } - bindingset[name] - private predicate modeToNameMappingKnown(Crypto::TBlockCipherModeOperationType type, string name) { - type instanceof Crypto::ECB and name = "ECB" - or - type instanceof Crypto::CBC and name = "CBC" - or - type instanceof Crypto::GCM and name = "GCM" - or - type instanceof Crypto::CTR and name = "CTR" - or - type instanceof Crypto::XTS and name = "XTS" - or - type instanceof Crypto::CCM and name = "CCM" - or - type instanceof Crypto::SIV and name = "SIV" - or - type instanceof Crypto::OCB and name = "OCB" - } - - override Crypto::TBlockCipherModeOperationType getModeType() { - if this.modeToNameMappingKnown(_, super.getMode()) - then this.modeToNameMappingKnown(result, super.getMode()) + override Crypto::TBlockCipherModeOfOperationType getModeType() { + if mode_name_to_type_known(_, super.getMode()) + then mode_name_to_type_known(result, super.getMode()) else result instanceof Crypto::OtherMode } } - class KeyGeneratorGetInstanceCall extends MethodCall { - KeyGeneratorGetInstanceCall() { - this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance") - or - this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance") - } - - Expr getAlgorithmArg() { result = super.getArgument(0) } - } - - // For general elliptic curves, getInstance("EC") is used - // and java.security.spec.ECGenParameterSpec("") is what sets the specific curve. - // The result of ECGenParameterSpec is passed to KeyPairGenerator.initialize - // If the curve is not specified, the default is used. - // We would trace the use of this inside a KeyPairGenerator.initialize - class ECGenParameterSpecCall extends ClassInstanceExpr { - ECGenParameterSpecCall() { - this.(ClassInstanceExpr) - .getConstructedType() - .hasQualifiedName("java.security.spec", "ECGenParameterSpec") - } - - Expr getAlgorithmArg() { result = super.getArgument(0) } - - KeyPairGeneratorInitializeCall getInitializeConsumerCall() { - exists(DataFlow::Node sink | - ECGenParameterSpecCallToInitializeFlow::flow(DataFlow::exprNode(this), sink) and - result.getAnArgument() = sink.asExpr() - ) - } - } - - abstract class KeyGenAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - // abstract predicate flowsToKeyGenerateCallQualifier(KeyGeneratorGenerateCall sink); - abstract DataFlow::Node getResultNode(); - } - - class KeyPairGeneratorInitializeCall extends MethodCall { - KeyPairGeneratorInitializeCall() { - this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "initialize") - } - - Expr getKeyArg() { - result = this.getArgument(0) and - result.getType() instanceof IntegralType - } - } - - private module ECGenParameterSpecCallToInitializeConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ECGenParameterSpecCall } - - predicate isSink(DataFlow::Node sink) { - exists(KeyPairGeneratorInitializeCall c | c.getAnArgument() = sink.asExpr()) - } - } - - module ECGenParameterSpecCallToInitializeFlow = - DataFlow::Global; - - class ECGenParameterSpecAlgorithmValueConsumer extends KeyGenAlgorithmValueConsumer { - ECGenParameterSpecCall call; - - ECGenParameterSpecAlgorithmValueConsumer() { this = call.getAlgorithmArg() } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } - - override DataFlow::Node getResultNode() { - // Traversing to the initialilzer directly and calling this the 'result' - // to simplify the trace. In theory you would trace from the call - // through the initializer, but we already have a trace to the initializer - // so using this instead of altering/creating data flow configs. - call.getInitializeConsumerCall().getQualifier() = result.asExpr() - } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result.(JCAAlgorithmInstance).getConsumer() = this - } - } - - class KeyGeneratorGetInstanceAlgorithmValueConsumer extends KeyGenAlgorithmValueConsumer { - KeyGeneratorGetInstanceCall call; - - KeyGeneratorGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } - - override DataFlow::Node getResultNode() { result.asExpr() = call } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - // The source is any instance whose consumer is this - result.(JCAAlgorithmInstance).getConsumer() = this - } - } - - class EllipticCurveStringLiteralAlgorithmInstance extends JCAAlgorithmInstance, - Crypto::EllipticCurveAlgorithmInstance instanceof EllipticCurveStringLiteral - { - Crypto::AlgorithmValueConsumer consumer; - - EllipticCurveStringLiteralAlgorithmInstance() { - // Trace a known elliptic curve algorithm string literal to a key gen consumer - EllipticCurveAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), - consumer.getInputNode()) - } - - override Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } - - override string getRawEllipticCurveAlgorithmName() { result = super.getValue() } - - override string getStandardCurveName() { result = this.getRawEllipticCurveAlgorithmName() } - - override Crypto::TEllipticCurveType getEllipticCurveFamily() { - Crypto::isEllipticCurveAlgorithm(this.getRawEllipticCurveAlgorithmName(), _, result) - } - - override string getKeySize() { - exists(int keySize | - Crypto::isEllipticCurveAlgorithm(this.getRawEllipticCurveAlgorithmName(), keySize, _) and - result = keySize.toString() - ) - } - } - - class KeyAgreementGetInstanceCall extends MethodCall { - KeyAgreementGetInstanceCall() { - this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "getInstance") - } - - Expr getAlgorithmArg() { result = super.getArgument(0) } - } - - class KeyAgreementAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - KeyAgreementGetInstanceCall call; - - KeyAgreementAlgorithmValueConsumer() { this = call.getAlgorithmArg() } - - DataFlow::Node getResultNode() { result.asExpr() = call } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result.(KeyAgreementStringLiteralAlgorithmInstance).getConsumer() = this - } - } - - class KeyAgreementStringLiteralAlgorithmInstance extends JCAAlgorithmInstance, - Crypto::KeyAgreementAlgorithmInstance instanceof KeyAgreementStringLiteral + class CipherStringLiteralAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof CipherStringLiteral { Crypto::AlgorithmValueConsumer consumer; - KeyAgreementStringLiteralAlgorithmInstance() { - KeyAgreementAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) - } - - override Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } - // override Crypto::EllipticCurveAlgorithmInstance getEllipticCurveAlgorithm() { - // this.(KeyAgreementStringLiteral).getValue().toUpperCase() in ["X25519", "X448"] and - // // NOTE: this relies upon modeling the elliptic curve on 'this' separately - // result = this - // // TODO: or is ecdh and go find the curve - // // this.(KeyAgreementStringLiteral).toString().toUpperCase() = ["ECDH"] - // } - } - - class CipherStringLiteralAlgorithmInstance extends JCAAlgorithmInstance, - Crypto::CipherAlgorithmInstance instanceof CipherStringLiteral - { - // NOTE: this consumer is generic, but cipher algorithms can be consumed - // by getInstance as well as key generation - Crypto::AlgorithmValueConsumer consumer; - CipherStringLiteralAlgorithmInstance() { - CipherAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } - override Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } + Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { result = this // TODO: provider defaults @@ -519,42 +331,16 @@ module JCAModel { result = this // TODO: provider defaults } - override string getRawCipherAlgorithmName() { result = super.getValue() } + override string getRawAlgorithmName() { result = super.getValue() } - override Crypto::TCipherType getCipherFamily() { - if this.cipherNameMappingKnown(_, super.getAlgorithmName()) - then this.cipherNameMappingKnown(result, super.getAlgorithmName()) - else result instanceof Crypto::OtherCipherType + override KeyOpAlg::Algorithm getAlgorithmType() { + if cipher_name_to_type_known(_, super.getAlgorithmName()) + then cipher_name_to_type_known(result, super.getAlgorithmName()) + else result instanceof KeyOpAlg::TUnknownKeyOperationAlgorithmType } - bindingset[name] - private predicate cipherNameMappingKnown(Crypto::TCipherType type, string name) { - name = "AES" and - type instanceof Crypto::AES - or - name = "DES" and - type instanceof Crypto::DES - or - name = "TripleDES" and - type instanceof Crypto::TripleDES - or - name = "IDEA" and - type instanceof Crypto::IDEA - or - name = "CAST5" and - type instanceof Crypto::CAST5 - or - name = "ChaCha20" and - type instanceof Crypto::CHACHA20 - or - name = "RC4" and - type instanceof Crypto::RC4 - or - name = "RC5" and - type instanceof Crypto::RC5 - or - name = "RSA" and - type instanceof Crypto::RSA + override string getKeySize() { + none() // TODO: implement to handle variants such as AES-128 } } @@ -587,12 +373,14 @@ module JCAModel { override int getDigestLength() { exists(hash_name_to_hash_type(hashName, result)) } } - class OAEPPaddingAlgorithmInstance extends JCAAlgorithmInstance, - Crypto::OAEPPaddingAlgorithmInstance, CipherStringLiteralPaddingAlgorithmInstance + class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance, + CipherStringLiteralPaddingAlgorithmInstance { override Crypto::HashAlgorithmInstance getOAEPEncodingHashAlgorithm() { result = this } override Crypto::HashAlgorithmInstance getMGF1HashAlgorithm() { none() } // TODO + + override string getKeySize() { none() } } /** @@ -608,7 +396,7 @@ module JCAModel { override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } CipherStringLiteral getOrigin(string value) { - CipherAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), DataFlow::exprNode(this.(Expr).getAChildExpr*())) and value = result.getValue() } @@ -650,14 +438,14 @@ module JCAModel { this = TInitializedCipherModeFlowState(_) and result = "initialized" } - abstract Crypto::CipherOperationSubtype getCipherOperationMode(); + abstract Crypto::KeyOperationSubtype getKeyOperationMode(); } private class UninitializedCipherModeFlowState extends CipherModeFlowState, TUninitializedCipherModeFlowState { - override Crypto::CipherOperationSubtype getCipherOperationMode() { - result instanceof Crypto::UnknownCipherOperationSubtype + override Crypto::KeyOperationSubtype getKeyOperationMode() { + result instanceof Crypto::TUnknownKeyOperationMode } } @@ -667,7 +455,7 @@ module JCAModel { CipherInitCall call; DataFlow::Node node1; DataFlow::Node node2; - Crypto::CipherOperationSubtype mode; + Crypto::KeyOperationSubtype mode; InitializedCipherModeFlowState() { this = TInitializedCipherModeFlowState(call) and @@ -687,7 +475,7 @@ module JCAModel { */ DataFlow::Node getSndNode() { result = node2 } - override Crypto::CipherOperationSubtype getCipherOperationMode() { result = mode } + override Crypto::KeyOperationSubtype getKeyOperationMode() { result = mode } } /** @@ -730,8 +518,8 @@ module JCAModel { module CipherGetInstanceToCipherOperationFlow = DataFlow::GlobalWithState; - class CipherOperationInstance extends Crypto::CipherOperationInstance instanceof Call { - Crypto::CipherOperationSubtype mode; + class CipherOperationInstance extends Crypto::KeyOperationInstance instanceof Call { + Crypto::KeyOperationSubtype mode; CipherGetInstanceToCipherOperationFlow::PathNode sink; CipherOperationCall doFinalize; CipherGetInstanceAlgorithmArg consumer; @@ -741,13 +529,13 @@ module JCAModel { CipherGetInstanceToCipherOperationFlow::flowPath(src, sink) and src.getNode().asExpr() = getCipher and sink.getNode().asExpr() = doFinalize.getQualifier() and - sink.getState().(CipherModeFlowState).getCipherOperationMode() = mode and + sink.getState().(CipherModeFlowState).getKeyOperationMode() = mode and this = doFinalize and consumer = getCipher.getAlgorithmArg() ) } - override Crypto::CipherOperationSubtype getCipherOperationSubtype() { result = mode } + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { result = mode } override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg() @@ -763,8 +551,8 @@ module JCAModel { override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = consumer } - override Crypto::CipherOutputArtifactInstance getOutputArtifact() { - result = doFinalize.getOutput() + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = doFinalize.getOutput() } } @@ -865,15 +653,15 @@ module JCAModel { module JavaxCipherModeAccessToInitFlow = DataFlow::Global; private predicate cipher_mode_str_to_cipher_mode_known( - string mode, Crypto::CipherOperationSubtype cipher_mode + string mode, Crypto::KeyOperationSubtype cipher_mode ) { - mode = "ENCRYPT_MODE" and cipher_mode instanceof Crypto::EncryptionSubtype + mode = "ENCRYPT_MODE" and cipher_mode = Crypto::TEncryptMode() or - mode = "WRAP_MODE" and cipher_mode instanceof Crypto::WrapSubtype + mode = "WRAP_MODE" and cipher_mode = Crypto::TWrapMode() or - mode = "DECRYPT_MODE" and cipher_mode instanceof Crypto::DecryptionSubtype + mode = "DECRYPT_MODE" and cipher_mode = Crypto::TDecryptMode() or - mode = "UNWRAP_MODE" and cipher_mode instanceof Crypto::UnwrapSubtype + mode = "UNWRAP_MODE" and cipher_mode = Crypto::TUnwrapMode() } class CipherInitCall extends MethodCall { @@ -896,10 +684,10 @@ module JCAModel { ) } - Crypto::CipherOperationSubtype getCipherOperationModeType() { + Crypto::KeyOperationSubtype getCipherOperationModeType() { if cipher_mode_str_to_cipher_mode_known(this.getModeOrigin().getField().getName(), _) then cipher_mode_str_to_cipher_mode_known(this.getModeOrigin().getField().getName(), result) - else result instanceof Crypto::UnknownCipherOperationSubtype + else result = Crypto::TUnknownKeyOperationMode() } Expr getKeyArg() { @@ -918,12 +706,6 @@ module JCAModel { override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } } - class CipherOperationCallOutput extends Crypto::CipherOutputArtifactInstance { - CipherOperationCallOutput() { this = any(CipherOperationCall call).getOutput() } - - override DataFlow::Node getOutputNode() { result.asExpr() = this } - } - // flow config from a known hash algorithm literal to MessageDigest.getInstance module KnownHashAlgorithmLiteralToMessageDigestConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { hash_names(src.asExpr().(StringLiteral).getValue()) } @@ -936,8 +718,7 @@ module JCAModel { module KnownHashAlgorithmLiteralToMessageDigestFlow = DataFlow::Global; - class KnownHashAlgorithm extends JCAAlgorithmInstance, Crypto::HashAlgorithmInstance instanceof StringLiteral - { + class KnownHashAlgorithm extends Crypto::HashAlgorithmInstance instanceof StringLiteral { MessageDigestAlgorithmValueConsumer consumer; KnownHashAlgorithm() { @@ -946,7 +727,7 @@ module JCAModel { consumer.getInputNode()) } - override MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer } + MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer } override string getRawHashAlgorithmName() { result = this.(StringLiteral).getValue() } @@ -1019,10 +800,22 @@ module JCAModel { } } + class KeyGeneratorCallAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + KeyGeneratorGetInstanceCall call; + + KeyGeneratorCallAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this + } + } + // flow from instance created by getInstance to generateKey - module KeyGeneratorAlgValueConsumerToGenerateConfig implements DataFlow::ConfigSig { + module KeyGeneratorGetInstanceToGenerateConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { - exists(KeyGenAlgorithmValueConsumer consumer | consumer.getResultNode() = src) + exists(KeyGeneratorGetInstanceCall call | src.asExpr() = call) } predicate isSink(DataFlow::Node sink) { @@ -1030,8 +823,23 @@ module JCAModel { } } - module KeyGeneratorAlgValueConsumerToGenerateFlow = - DataFlow::Global; + module KeyGeneratorGetInstanceToGenerateFlow = + DataFlow::Global; + + class KeyGeneratorGetInstanceCall extends MethodCall { + KeyGeneratorGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance") + or + this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance") + } + + Expr getAlgorithmArg() { result = super.getArgument(0) } + + predicate flowsToKeyGenerateCallQualifier(KeyGeneratorGenerateCall sink) { + KeyGeneratorGetInstanceToGenerateFlow::flow(DataFlow::exprNode(this), + DataFlow::exprNode(sink.(MethodCall).getQualifier())) + } + } class KeyGeneratorGenerateCall extends Crypto::KeyGenerationOperationInstance instanceof MethodCall { @@ -1050,10 +858,8 @@ module JCAModel { override Crypto::KeyArtifactType getOutputKeyType() { result = type } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - exists(KeyGenAlgorithmValueConsumer consumer | - KeyGeneratorAlgValueConsumerToGenerateFlow::flow(consumer.getResultNode(), - DataFlow::exprNode(this.(Call).getQualifier())) and - result = consumer + exists(KeyGeneratorGetInstanceCall getInstance | + getInstance.flowsToKeyGenerateCallQualifier(this) and result = getInstance.getAlgorithmArg() ) } @@ -1066,45 +872,6 @@ module JCAModel { override string getKeySizeFixed() { none() } } - /* - * TODO: - * - * MAC Algorithms possible (JCA Default + BouncyCastle Extensions) - * - * Name Type Description - * --------------------------------------------------------------------------- - * "HmacMD5" HMAC HMAC with MD5 (not recommended) - * "HmacSHA1" HMAC HMAC with SHA-1 (not recommended) - * "HmacSHA224" HMAC HMAC with SHA-224 - * "HmacSHA256" HMAC HMAC with SHA-256 - * "HmacSHA384" HMAC HMAC with SHA-384 - * "HmacSHA512" HMAC HMAC with SHA-512 - * - * (BouncyCastle and Other Provider Extensions) - * "AESCMAC" CMAC Cipher-based MAC using AES - * "DESCMAC" CMAC CMAC with DES (legacy) - * "GMAC" GCM-based MAC Authenticates AAD only (GCM-style) - * "Poly1305" AEAD-style MAC Used with ChaCha20 - * "SipHash" Hash-based MAC Fast MAC for short inputs - * "BLAKE2BMAC" HMAC-style BLAKE2b MAC (cryptographic hash) - * "HmacRIPEMD160" HMAC HMAC with RIPEMD160 hash - */ - - bindingset[name] - predicate mac_names(string name) { - name.toUpperCase() - .matches([ - "HMAC%", "AESCMAC", "DESCMAC", "GMAC", "Poly1305", "SipHash", "BLAKE2BMAC", - "HMACRIPEMD160" - ].toUpperCase()) - } - - bindingset[name] - predicate mac_name_to_mac_type_known(Crypto::TMACType type, string name) { - type instanceof Crypto::THMAC and - name.toUpperCase().matches("HMAC%") - } - module MACKnownAlgorithmToConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { mac_names(src.asExpr().(StringLiteral).getValue()) } @@ -1140,8 +907,7 @@ module JCAModel { module MACInitCallToMACOperationFlow = DataFlow::Global; - class KnownMACAlgorithm extends JCAAlgorithmInstance, Crypto::MACAlgorithmInstance instanceof StringLiteral - { + class KnownMACAlgorithm extends Crypto::MACAlgorithmInstance instanceof StringLiteral { MACGetInstanceAlgorithmValueConsumer consumer; KnownMACAlgorithm() { @@ -1149,7 +915,7 @@ module JCAModel { MACKnownAlgorithmToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } - override MACGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer } + MACGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer } override string getRawMACAlgorithmName() { result = super.getValue() } @@ -1301,8 +1067,7 @@ module JCAModel { } } - class KDFAlgorithmStringLiteral extends JCAAlgorithmInstance, - Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral + class KDFAlgorithmStringLiteral extends Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral { SecretKeyFactoryKDFAlgorithmValueConsumer consumer; @@ -1317,10 +1082,10 @@ module JCAModel { result = kdf_name_to_kdf_type(super.getValue(), _) } - override SecretKeyFactoryKDFAlgorithmValueConsumer getConsumer() { result = consumer } + SecretKeyFactoryKDFAlgorithmValueConsumer getConsumer() { result = consumer } } - class PBKDF2AlgorithmStringLiteral extends JCAAlgorithmInstance, KDFAlgorithmStringLiteral, + class PBKDF2AlgorithmStringLiteral extends KDFAlgorithmStringLiteral, Crypto::PBKDF2AlgorithmInstance, Crypto::HMACAlgorithmInstance, Crypto::HashAlgorithmInstance, Crypto::AlgorithmValueConsumer { @@ -1421,5 +1186,132 @@ module JCAModel { override string getIterationCountFixed() { none() } } - // TODO: flow the GCGenParametersSpecCall to an init, and the init to the operations + + class KeyAgreementStringLiteral extends StringLiteral { + KeyAgreementStringLiteral() { key_agreement_names(this.getValue()) } + } + + /** + * Data-flow configuration modelling flow from a key agreement literal to a value consumer argument. + */ + private module KeyAgreementAlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeyAgreementStringLiteral } + + predicate isSink(DataFlow::Node sink) { + exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode()) + } + } + + module KeyAgreementAlgorithmStringToFetchFlow = + TaintTracking::Global; + + class KeyAgreementInitCall extends MethodCall { + KeyAgreementInitCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "init") + } + + Expr getServerKeyArg() { result = this.getArgument(0) } + } + + private module KeyAgreementInitQualifierToSecretGenQualifierConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + exists(KeyAgreementInitCall init | src.asExpr() = init.getQualifier()) + } + + predicate isSink(DataFlow::Node sink) { + exists(KeyAgreementGenerateSecretCall c | sink.asExpr() = c.getQualifier()) + } + + /** + * Barrier if we go into another init, assume the second init overwrites the first + */ + predicate isBarrierIn(DataFlow::Node node) { isSource(node) } + } + + module KeyAgreementInitQualifierToSecretGenQualifierFlow = + DataFlow::Global; + + class KeyAgreementGetInstanceCall extends MethodCall { + KeyAgreementGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "getInstance") + } + + Expr getAlgorithmArg() { result = super.getArgument(0) } + } + + class KeyAgreementAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + KeyAgreementGetInstanceCall call; + + KeyAgreementAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + DataFlow::Node getResultNode() { result.asExpr() = call } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(KeyAgreementStringLiteralAlgorithmInstance).getConsumer() = this + } + } + + class KeyAgreementStringLiteralAlgorithmInstance extends Crypto::KeyAgreementAlgorithmInstance instanceof KeyAgreementStringLiteral + { + Crypto::AlgorithmValueConsumer consumer; + + KeyAgreementStringLiteralAlgorithmInstance() { + KeyAgreementAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + } + + Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } + + override string getRawKeyAgreementAlgorithmName() { result = super.getValue() } + + override Crypto::TKeyAgreementType getKeyAgreementType() { + if key_agreement_name_to_type_known(_, super.getValue()) + then key_agreement_name_to_type_known(result, super.getValue()) + else result = Crypto::UnknownKeyAgreementType() + } + } + + class KeyAgreementGenerateSecretCall extends MethodCall { + KeyAgreementGenerateSecretCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "generateSecret") + } + + KeyAgreementInitCall getKeyAgreementInitCall() { + KeyAgreementInitQualifierToSecretGenQualifierFlow::flow(DataFlow::exprNode(result + .getQualifier()), DataFlow::exprNode(this.getQualifier())) + } + } + + private module KeyAgreementAVCToInitQualifierConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + exists(KeyAgreementAlgorithmValueConsumer consumer | consumer.getResultNode() = src) + } + + predicate isSink(DataFlow::Node sink) { + exists(KeyAgreementInitCall init | sink.asExpr() = init.getQualifier()) + } + } + + module KeyAgreementAVCToInitQualifierFlow = + DataFlow::Global; + + class KeyAgreementSecretGenerationOperationInstance extends Crypto::KeyAgreementSecretGenerationOperationInstance instanceof KeyAgreementGenerateSecretCall + { + override Crypto::ConsumerInputDataFlowNode getServerKeyConsumer() { + this.(KeyAgreementGenerateSecretCall).getKeyAgreementInitCall().getServerKeyArg() = + result.asExpr() + } + + override Crypto::ConsumerInputDataFlowNode getPeerKeyConsumer() { + none() //TODO + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + none() // TODO: key agreeement has its own algorithm consumer, separate from the key + // TODO: the char pred must trace from the consumer to here, + // in theory, along that path we would get the init and doPhase, but can I just get those + // separately avoiding a complicated config state for dataflow? + } + } } diff --git a/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql b/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql index 7fea3515b7dd..a9eb70076a0a 100644 --- a/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql +++ b/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql @@ -7,18 +7,17 @@ import experimental.Quantum.Language from - Crypto::NonceArtifactNode n, Crypto::CipherOperationNode op, Crypto::FlowAwareElement src, - string msg + Crypto::NonceArtifactNode n, Crypto::KeyOperationNode op, Crypto::FlowAwareElement src, string msg where op.getANonce() = n and // Only encryption mode is relevant for insecure nonces, consder any 'unknown' subtype // as possibly encryption. ( - op.getCipherOperationSubtype() instanceof Crypto::EncryptionSubtype + op.getKeyOperationSubtype() instanceof Crypto::EncryptionSubtype or - op.getCipherOperationSubtype() instanceof Crypto::WrapSubtype + op.getKeyOperationSubtype() instanceof Crypto::WrapSubtype or - op.getCipherOperationSubtype() instanceof Crypto::UnwrapSubtype + op.getKeyOperationSubtype() instanceof Crypto::UnwrapSubtype ) and ( // Known sources cases that are not secure diff --git a/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql b/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql index f9fc7a873db1..2263f05d56eb 100644 --- a/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql +++ b/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql @@ -8,9 +8,8 @@ import experimental.Quantum.Language import semmle.code.java.dataflow.DataFlow from - Crypto::CipherOperationNode op1, Crypto::CipherOperationNode op2, - Crypto::NonceArtifactNode nonce1, Crypto::NonceArtifactNode nonce2, Crypto::FlowAwareElement src1, - Crypto::FlowAwareElement src2 + Crypto::KeyOperationNode op1, Crypto::KeyOperationNode op2, Crypto::NonceArtifactNode nonce1, + Crypto::NonceArtifactNode nonce2, Crypto::FlowAwareElement src1, Crypto::FlowAwareElement src2 where // NOTE: not looking at value of the nonce, if we knew value, it would be insecure (hard coded) // Instead trying to find nonce sources that trace to multiple operations. @@ -18,14 +17,14 @@ where // (the encryption happened else where) or we are able to see the encryption and decryption operation and // reuse for encryption is the concern) ( - op1.getCipherOperationSubtype() instanceof Crypto::EncryptionSubtype or - op1.getCipherOperationSubtype() instanceof Crypto::WrapSubtype or - op1.getCipherOperationSubtype() instanceof Crypto::UnknownCipherOperationSubtype + op1.getKeyOperationSubtype() instanceof Crypto::EncryptionSubtype or + op1.getKeyOperationSubtype() instanceof Crypto::WrapSubtype or + op1.getKeyOperationSubtype() instanceof Crypto::UnknownCipherOperationSubtype ) and ( - op2.getCipherOperationSubtype() instanceof Crypto::EncryptionSubtype or - op2.getCipherOperationSubtype() instanceof Crypto::WrapSubtype or - op2.getCipherOperationSubtype() instanceof Crypto::UnknownCipherOperationSubtype + op2.getKeyOperationSubtype() instanceof Crypto::EncryptionSubtype or + op2.getKeyOperationSubtype() instanceof Crypto::WrapSubtype or + op2.getKeyOperationSubtype() instanceof Crypto::UnknownCipherOperationSubtype ) and nonce1 = op1.getANonce() and nonce2 = op2.getANonce() and diff --git a/java/ql/src/experimental/Quantum/TestCipher.ql b/java/ql/src/experimental/Quantum/TestCipher.ql index 8743ef3aa19b..d56e16804408 100644 --- a/java/ql/src/experimental/Quantum/TestCipher.ql +++ b/java/ql/src/experimental/Quantum/TestCipher.ql @@ -5,14 +5,14 @@ import experimental.Quantum.Language from - Crypto::CipherOperationNode op, Crypto::CipherAlgorithmNode a, + Crypto::CipherOperationNode op, Crypto::KeyOperationAlgorithmNode a, Crypto::ModeOfOperationAlgorithmNode m, Crypto::PaddingAlgorithmNode p, Crypto::NonceArtifactNode nonce, Crypto::KeyArtifactNode k where - a = op.getAKnownCipherAlgorithm() and + a = op.getAKnownAlgorithm() and m = a.getModeOfOperation() and p = a.getPaddingAlgorithm() and nonce = op.getANonce() and k = op.getAKey() -select op, op.getCipherOperationSubtype(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), - p, p.getRawAlgorithmName(), nonce, k, k.getSourceElement() +select op, op.getKeyOperationSubtype(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), p, + p.getRawAlgorithmName(), nonce, k, k.getSourceElement() diff --git a/java/ql/src/experimental/Quantum/TestCipherKey.ql b/java/ql/src/experimental/Quantum/TestCipherKey.ql index 1ae0cdfd89f0..c489320528d0 100644 --- a/java/ql/src/experimental/Quantum/TestCipherKey.ql +++ b/java/ql/src/experimental/Quantum/TestCipherKey.ql @@ -4,11 +4,11 @@ import experimental.Quantum.Language -from Crypto::CipherOperationNode op, Crypto::CipherAlgorithmNode a, Crypto::KeyArtifactNode k +from Crypto::KeyOperationNode op, Crypto::CipherAlgorithmNode a, Crypto::KeyArtifactNode k where a = op.getAKnownCipherAlgorithm() and k = op.getAKey() -select op, op.getCipherOperationSubtype(), a, a.getRawAlgorithmName(), k, k.getSourceNode() +select op, op.getKeyOperationSubtype(), a, a.getRawAlgorithmName(), k, k.getSourceNode() /* * from Crypto::CipherOperationNode op * where op.getLocation().getFile().getBaseName() = "AsymmetricEncryptionMacHybridCryptosystem.java" diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index fe397c6247f3..782c53e1043f 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -334,7 +334,6 @@ module CryptographyBase Input> { */ abstract class ArtifactConsumer extends ConsumerElement { /** - * DO NOT USE: * Use `getAKnownArtifactSource() instead. The behaviour of these two predicates is equivalent. */ final override KnownElement getAKnownSource() { result = this.getAKnownArtifactSource() } @@ -348,26 +347,22 @@ module CryptographyBase Input> { * For example: * A `NonceArtifactConsumer` is always the `NonceArtifactInstance` itself, since data only becomes (i.e., is determined to be) * a `NonceArtifactInstance` when it is consumed in a context that expects a nonce (e.g., an argument expecting nonce data). - * In this case, the artifact (nonce) is fully defined by the context in which it is consumed, and the consumer embodies + * + * In this case, the artifact (nonce) is fully defined by the context in which it is consumed, and the consumer embodies * that identity without the need for additional differentiation. Without the context a consumer provides, that data could * otherwise be any other type of artifact or even simply random data. * - * TODO: what if a Nonce from hypothetical func `generateNonce()` flows to this instance which is also a Nonce? - * TODO: potential solution is creating another artifact type called NonceData or treating it as a generic source. - * - * TODO: An alternative is simply having a predicate DataFlowNode getNonceInputNode() on (for example) operations. - * Under the hood, in Model.qll, we would create the instance for the modeller, thus avoiding the need for the modeller - * to create a separate consumer class / instance themselves using this class. + * This class is used to create synthetic nodes for the artifact at any place where it is consumed. */ abstract private class ArtifactConsumerAndInstance extends ArtifactConsumer, ArtifactInstance { - override predicate isConsumerArtifact() { any() } + final override predicate isConsumerArtifact() { any() } } final private class NonceArtifactConsumer extends ArtifactConsumerAndInstance { ConsumerInputDataFlowNode inputNode; NonceArtifactConsumer() { - exists(CipherOperationInstance op | inputNode = op.getNonceConsumer()) and + exists(KeyOperationInstance op | inputNode = op.getNonceConsumer()) and this = Input::dfn_to_element(inputNode) } @@ -379,7 +374,7 @@ module CryptographyBase Input> { MessageArtifactConsumer() { ( - exists(CipherOperationInstance op | inputNode = op.getInputConsumer()) + exists(KeyOperationInstance op | inputNode = op.getInputConsumer()) or exists(KeyDerivationOperationInstance op | inputNode = op.getInputConsumer()) or @@ -402,7 +397,9 @@ module CryptographyBase Input> { final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } } - // Output artifacts are determined solely by the element that produces them. + /** + * An artifact that is produced by an operation, representing a concrete artifact instance rather than a synthetic consumer artifact. + */ abstract class OutputArtifactInstance extends ArtifactInstance { override predicate isConsumerArtifact() { none() } @@ -413,13 +410,33 @@ module CryptographyBase Input> { } } + /** + * An artifact representing a hash function's digest output. + */ abstract class DigestArtifactInstance extends OutputArtifactInstance { } + /** + * An artifact representing a random number generator's output. + */ abstract class RandomNumberGenerationInstance extends OutputArtifactInstance { // TODO: input seed? } - abstract class CipherOutputArtifactInstance extends OutputArtifactInstance { } + /** + * An artifact representing a key operation's output, e.g.: + * 1. Encryption/decryption output (ciphertext or plaintext) + * 1. Signing output (signature) + * 1. Key encapsulation output (wrapped or unwrapped key) + */ + final class KeyOperationOutputArtifactInstance extends OutputArtifactInstance { + KeyOperationInstance creator; + + KeyOperationOutputArtifactInstance() { + Input::dfn_to_element(creator.getOutputArtifact()) = this + } + + override DataFlowNode getOutputNode() { result = creator.getOutputArtifact() } + } // Artifacts that may be outputs or inputs newtype TKeyArtifactType = @@ -459,7 +476,7 @@ module CryptographyBase Input> { // TODO: key type hint? e.g. hint: private || public KeyArtifactConsumer() { ( - exists(CipherOperationInstance op | inputNode = op.getKeyConsumer()) or + exists(KeyOperationInstance op | inputNode = op.getKeyConsumer()) or exists(MACOperationInstance op | inputNode = op.getKeyConsumer()) ) and this = Input::dfn_to_element(inputNode) @@ -471,68 +488,291 @@ module CryptographyBase Input> { } /** - * A cipher operation instance, such as encryption or decryption. + * The `KeyOpAlg` module defines key operation algorithms types (e.g., symmetric ciphers, signatures, etc.) + * and provides mapping of those types to string names and structural properties. */ - abstract class CipherOperationInstance extends OperationInstance { + module KeyOpAlg { /** - * Gets the subtype of this cipher operation, distinguishing encryption, decryption, key wrapping, and key unwrapping. + * An algorithm used in key operations. */ - abstract CipherOperationSubtype getCipherOperationSubtype(); + newtype TAlgorithm = + TSymmetricCipher(TSymmetricCipherType t) or + TAsymmetricCipher(TAsymmetricCipherType t) or + TSignature(TSignatureAlgorithmType t) or + TKeyEncapsulation(TKEMAlgorithmType t) or + TUnknownKeyOperationAlgorithmType() + + // Parameterized algorithm types + newtype TSymmetricCipherType = + AES() or + ARIA() or + BLOWFISH() or + CAMELLIA() or + CAST5() or + CHACHA20() or + DES() or + DESX() or + GOST() or + IDEA() or + KUZNYECHIK() or + MAGMA() or + TripleDES() or + DoubleDES() or + RC2() or + RC4() or + RC5() or + SEED() or + SM4() or + OtherSymmetricCipherType() + + newtype TAsymmetricCipherType = + RSA() or + OtherAsymmetricCipherType() + + newtype TSignatureAlgorithmType = + DSA() or + ECDSA() or + Ed25519() or + Ed448() or + OtherSignatureAlgorithmType() + + newtype TKEMAlgorithmType = + Kyber() or + FrodoKEM() or + OtherKEMAlgorithmType() + + newtype TCipherStructureType = + Block() or + Stream() or + UnknownCipherStructureType() + + class CipherStructureType extends TCipherStructureType { + string toString() { + result = "Block" and this = Block() + or + result = "Stream" and this = Stream() + or + result = "Unknown" and this = UnknownCipherStructureType() + } + } + + bindingset[type] + predicate symmetric_cipher_to_name_and_structure( + TSymmetricCipherType type, string name, CipherStructureType s + ) { + type = AES() and name = "AES" and s = Block() + or + type = ARIA() and name = "ARIA" and s = Block() + or + type = BLOWFISH() and name = "Blowfish" and s = Block() + or + type = CAMELLIA() and name = "Camellia" and s = Block() + or + type = CAST5() and name = "CAST5" and s = Block() + or + type = CHACHA20() and name = "ChaCha20" and s = Stream() + or + type = DES() and name = "DES" and s = Block() + or + type = DESX() and name = "DESX" and s = Block() + or + type = GOST() and name = "GOST" and s = Block() + or + type = IDEA() and name = "IDEA" and s = Block() + or + type = KUZNYECHIK() and name = "Kuznyechik" and s = Block() + or + type = MAGMA() and name = "Magma" and s = Block() + or + type = TripleDES() and name = "TripleDES" and s = Block() + or + type = DoubleDES() and name = "DoubleDES" and s = Block() + or + type = RC2() and name = "RC2" and s = Block() + or + type = RC4() and name = "RC4" and s = Stream() + or + type = RC5() and name = "RC5" and s = Block() + or + type = SEED() and name = "SEED" and s = Block() + or + type = SM4() and name = "SM4" and s = Block() + or + type = OtherSymmetricCipherType() and + name = "UnknownSymmetricCipher" and + s = UnknownCipherStructureType() + } + + bindingset[type] + predicate type_to_name(Algorithm type, string name) { + // Symmetric cipher algorithm + symmetric_cipher_to_name_and_structure(type.(SymmetricCipherAlgorithm).getType(), name, _) + or + // Asymmetric cipher algorithms + type = TAsymmetricCipher(RSA()) and name = "RSA" + or + type = TAsymmetricCipher(OtherAsymmetricCipherType()) and name = "UnknownAsymmetricCipher" + or + // Signature algorithms + type = TSignature(DSA()) and name = "DSA" + or + type = TSignature(ECDSA()) and name = "ECDSA" + or + type = TSignature(Ed25519()) and name = "Ed25519" + or + type = TSignature(Ed448()) and name = "Ed448" + or + type = TSignature(OtherSignatureAlgorithmType()) and name = "UnknownSignature" + or + // Key Encapsulation Mechanisms + type = TKeyEncapsulation(Kyber()) and name = "Kyber" + or + type = TKeyEncapsulation(FrodoKEM()) and name = "FrodoKEM" + or + type = TKeyEncapsulation(OtherKEMAlgorithmType()) and name = "UnknownKEM" + or + // Unknown + type = TUnknownKeyOperationAlgorithmType() and name = "Unknown" + } + + class Algorithm extends TAlgorithm { + string toString() { type_to_name(this, result) } + } + + class SymmetricCipherAlgorithm extends Algorithm, TSymmetricCipher { + TSymmetricCipherType type; + + SymmetricCipherAlgorithm() { this = TSymmetricCipher(type) } + + TSymmetricCipherType getType() { result = type } + } + } + + /** + * A key-based cryptographic operation instance, encompassing: + * 1. **Ciphers**: Encryption and decryption, both symmetric and asymmetric + * 1. **Signing**: Signing and verifying, **NOT** including MACs (see `MACOperationInstance`) + * 1. **Key encapsulation**: Key wrapping and unwrapping + * + * This class represents a generic key operation that transforms input data + * using a cryptographic key, producing an output artifact such as ciphertext, + * plaintext, a signature, or an (un-)wrapped key. + */ + abstract class KeyOperationInstance extends OperationInstance { + final KeyOperationOutputArtifactInstance getOutputArtifactInstance() { + result.getOutputNode() = this.getOutputArtifact() + } /** - * Gets the consumer of nonces/IVs associated with this cipher operation. + * Gets the subtype of this key operation, distinguishing operations such as + * encryption, decryption, signing, verification, key wrapping, and key unwrapping. */ - abstract ConsumerInputDataFlowNode getNonceConsumer(); + abstract KeyOperationSubtype getKeyOperationSubtype(); /** - * Gets the consumer of plaintext or ciphertext input associated with this cipher operation. + * Gets the consumer of the cryptographic key used in this key operation. + * The key may be symmetric or asymmetric, depending on the operation subtype. */ - abstract ConsumerInputDataFlowNode getInputConsumer(); + abstract ConsumerInputDataFlowNode getKeyConsumer(); /** - * Gets the consumer of a key. + * Gets the consumer of nonces or initialization vectors (IVs) associated with this key operation. + * These are typically required for encryption, AEAD, or wrap modes. + * + * If the operation does not require a nonce, this predicate should be implemented as `none()`. */ - abstract ConsumerInputDataFlowNode getKeyConsumer(); + abstract ConsumerInputDataFlowNode getNonceConsumer(); /** - * Gets the output artifact of this cipher operation. + * Gets the consumer of the primary message input for this key operation. + * For example: plaintext (for encryption), ciphertext (for decryption), + * message to be signed, or wrapped key to be unwrapped. + */ + abstract ConsumerInputDataFlowNode getInputConsumer(); + + /** + * Gets the output artifact produced by this key operation. + * This may represent ciphertext, a digital signature, a wrapped key, or any + * other data resulting from the operation. * * Implementation guidelines: - * 1. Each unique output target should have an artifact. - * 1. Discarded outputs from intermittent calls should not be artifacts. + * 1. Each semantically meaningful output should result in an artifact. + * 2. Discarded or transient intermediate values should not be artifacts. */ - abstract CipherOutputArtifactInstance getOutputArtifact(); + abstract ArtifactOutputDataFlowNode getOutputArtifact(); } - abstract class CipherAlgorithmInstance extends AlgorithmInstance { + /** + * A key-based algorithm instance used in cryptographic operations such as encryption, decryption, + * signing, verification, and key wrapping. + */ + abstract class KeyOperationAlgorithmInstance extends AlgorithmInstance { /** - * Gets the raw name as it appears in source, e.g., "AES/CBC/PKCS7Padding". - * This name is not parsed or formatted. + * Gets the raw algorithm name as provided in source, e.g., "AES/CBC/PKCS7Padding". + * This name is not parsed or normalized. */ - abstract string getRawCipherAlgorithmName(); + abstract string getRawAlgorithmName(); /** - * Gets the type of this cipher, e.g., "AES" or "ChaCha20". + * Gets the key operation algorithm type, e.g., `TSignature(Ed25519())` or `TSymmetricCipher(AES())`. + * + * If the category of algorithm is known, but the precise algorithm is not, the following type hints should be used: + * - `TSymmetricCipher(OtherSymmetricCipherType())` + * - `TAsymmetricCipher(OtherAsymmetricCipherType())` + * - `TSignature(OtherSignatureAlgorithmType())` + * - `TKeyEncapsulation(OtherKEMAlgorithmType())` + * + * If the category of algorithm is not known, the following type should be used: + * - `TUnknownKeyOperationAlgorithmType()` + * + * This predicate should always hold. */ - abstract TCipherType getCipherFamily(); + abstract KeyOpAlg::Algorithm getAlgorithmType(); /** - * Gets the mode of operation of this cipher, e.g., "GCM" or "CBC". + * Gets the mode of operation, such as "CBC", "GCM", or "ECB". * - * IMPLEMENTATION NOTE: as a tradeoff, this is not a consumer but always either an instance or unknown. - * A mode of operation is therefore assumed to always be part of the cipher algorithm itself. + * Edge-cases and modeling guidance: + * - Mode of operation not identifiable: result is `none()`. + * - No mode possible (e.g., RSA, DSA, or ChaCha20): result is `none()`. + * - Mode of operation explicitly specified as none: result is `ModeOfOperationAlgorithmInstance`. + * + * IMPLEMENTATION NOTE: This is treated as part of the algorithm identity and + * not modeled as a separate algorithm value consumer. */ abstract ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm(); /** - * Gets the padding scheme of this cipher, e.g., "PKCS7" or "NoPadding". + * Gets the padding scheme, such as "PKCS7", "OAEP", or "NoPadding". * - * IMPLEMENTATION NOTE: as a tradeoff, this is not a consumer but always either an instance or unknown. - * A padding algorithm is therefore assumed to always be defined as part of the cipher algorithm itself. + * See the modeling guidance for `getModeOfOperationAlgorithm` for modeling guidance. */ abstract PaddingAlgorithmInstance getPaddingAlgorithm(); + + /** + * Gets the key size in bits specified for this algorithm variant, for example, "128" for "AES-128". This predicate is only + * necessary to specify if there are multiple variants of the algorithm defined by key size and a specific key size is known. + * + * If a specific key size is unknown, this predicate should be implemented as `none()`. + * + * If the algorithm accepts a range of key sizes without a particular one specified, this predicate should be implemented as `none()`. + */ + abstract string getKeySize(); } + newtype TBlockCipherModeOfOperationType = + ECB() or // Not secure, widely used + CBC() or // Vulnerable to padding oracle attacks + CFB() or + GCM() or // Widely used AEAD mode (TLS 1.3, SSH, IPsec) + CTR() or // Fast stream-like encryption (SSH, disk encryption) + XTS() or // Standard for full-disk encryption (BitLocker, LUKS, FileVault) + CCM() or // Used in lightweight cryptography (IoT, WPA2) + SIV() or // Misuse-resistant encryption, used in secure storage + OCB() or // Efficient AEAD mode + OFB() or + OtherMode() + abstract class ModeOfOperationAlgorithmInstance extends AlgorithmInstance { /** * Gets the type of this mode of operation, e.g., "ECB" or "CBC". @@ -541,7 +781,7 @@ module CryptographyBase Input> { * * If a type cannot be determined, the result is `OtherMode`. */ - abstract TBlockCipherModeOperationType getModeType(); + abstract TBlockCipherModeOfOperationType getModeType(); /** * Gets the isolated name as it appears in source, e.g., "CBC" in "AES/CBC/PKCS7Padding". @@ -570,7 +810,7 @@ module CryptographyBase Input> { } abstract class OAEPPaddingAlgorithmInstance extends PaddingAlgorithmInstance { - OAEPPaddingAlgorithmInstance() { this.getPaddingType() = OAEP() } + OAEPPaddingAlgorithmInstance() { this.getPaddingType() instanceof OAEP } /** * Gets the hash algorithm used in this padding scheme. @@ -585,6 +825,7 @@ module CryptographyBase Input> { newtype TMACType = THMAC() or + TCMAC() or TOtherMACType() abstract class MACAlgorithmInstance extends AlgorithmInstance { @@ -622,33 +863,31 @@ module CryptographyBase Input> { abstract AlgorithmValueConsumer getHashAlgorithmValueConsumer(); } - abstract class KeyEncapsulationOperationInstance extends OperationInstance { } - - abstract class KeyEncapsulationAlgorithmInstance extends AlgorithmInstance { } - - abstract class EllipticCurveAlgorithmInstance extends AlgorithmInstance { + abstract class EllipticCurveInstance extends AlgorithmInstance { /** * Gets the isolated name as it appears in source * * This name should not be parsed or formatted beyond isolating the raw name if necessary. */ - abstract string getRawEllipticCurveAlgorithmName(); + abstract string getRawEllipticCurveName(); + + abstract TEllipticCurveType getEllipticCurveFamily(); + + abstract string getKeySize(); /** - * The 'standard' curve name, e.g., "P-256" or "secp256r1". - * meaning the full name of the curve, including the family, key size, and other - * typical parameters found on the name. In many cases this will - * be equivalent to `getRawEllipticCurveAlgorithmName()`, but not always - * (e.g., if the curve is specified through a raw NID). + * The 'parsed' curve name, e.g., "P-256" or "secp256r1" + * The parsed name is full name of the curve, including the family, key size, and other + * typical parameters found on the name. + * + * In many cases this will be equivalent to `getRawEllipticCurveAlgorithmName()`, + * but not always (e.g., if the curve is specified through a raw NID). + * * In cases like an NID, we want the standardized name so users can quickly * understand what the curve is, while also parsing out the family and key size * separately. */ - abstract string getStandardCurveName(); - - abstract TEllipticCurveType getEllipticCurveFamily(); - - abstract string getKeySize(); + string getParsedEllipticCurveName() { result = this.getRawEllipticCurveName() } } abstract class HashOperationInstance extends OperationInstance { @@ -686,7 +925,7 @@ module CryptographyBase Input> { abstract KeyArtifactType getOutputKeyType(); // Defaults or fixed values - abstract string getKeySizeFixed(); + string getKeySizeFixed() { none() } // Consumer input nodes abstract ConsumerInputDataFlowNode getKeySizeConsumer(); @@ -702,9 +941,9 @@ module CryptographyBase Input> { final override string getKeyCreationTypeDescription() { result = "KeyDerivation" } // Defaults or fixed values - abstract string getIterationCountFixed(); + string getIterationCountFixed() { none() } - abstract string getOutputKeySizeFixed(); + string getOutputKeySizeFixed() { none() } // Generic consumer input nodes abstract ConsumerInputDataFlowNode getIterationCountConsumer(); @@ -722,6 +961,7 @@ module CryptographyBase Input> { PBES() or HKDF() or ARGON2() or + SCRYPT() or OtherKeyDerivationType() abstract class KeyDerivationAlgorithmInstance extends AlgorithmInstance { @@ -748,6 +988,18 @@ module CryptographyBase Input> { abstract AlgorithmValueConsumer getHMACAlgorithmValueConsumer(); } + abstract class ScryptAlgorithmInstance extends KeyDerivationAlgorithmInstance { + ScryptAlgorithmInstance() { this.getKDFType() instanceof SCRYPT } + + /** + * Gets the HMAC algorithm used by this PBKDF2 algorithm. + * + * Note: Other PRFs are not supported, as most cryptographic libraries + * only support HMAC for PBKDF2's PRF input. + */ + abstract AlgorithmValueConsumer getHMACAlgorithmValueConsumer(); + } + abstract class KeyGenerationOperationInstance extends KeyCreationOperationInstance { final override string getKeyCreationTypeDescription() { result = "KeyGeneration" } } @@ -756,10 +1008,55 @@ module CryptographyBase Input> { final override string getKeyCreationTypeDescription() { result = "KeyLoad" } } + // Key agreement algorithms + newtype TKeyAgreementType = + DH() or // Diffie-Hellman + EDH() or // Ephemeral Diffie-Hellman + ECDH() or // Elliptic Curve Diffie-Hellman + // Note: x25519 and x448 are applications of ECDH + UnknownKeyAgreementType() + + abstract class KeyAgreementAlgorithmInstance extends AlgorithmInstance { + abstract TKeyAgreementType getKeyAgreementType(); + + abstract string getRawKeyAgreementAlgorithmName(); + } + + abstract class ECDHKeyAgreementAlgorithmInstance extends KeyAgreementAlgorithmInstance { + ECDHKeyAgreementAlgorithmInstance() { this.getKeyAgreementType() instanceof ECDH } + + /** + * Gets the consumer for the elliptic curve used in the key agreement operation. + */ + abstract AlgorithmValueConsumer getEllipticCurveAlgorithmValueConsumer(); + } + + abstract class KeyAgreementSecretGenerationOperationInstance extends OperationInstance { + /** + * The private key used in the key agreement operation. + * This key represents the local party in the key agreement. + */ + abstract ConsumerInputDataFlowNode getServerKeyConsumer(); + + /** + * The public key used in the key agreement operation, coming + * from the peer (the other party in the key agreement). + */ + abstract ConsumerInputDataFlowNode getPeerKeyConsumer(); + } + private signature class AlgorithmInstanceType instanceof AlgorithmInstance; private signature predicate isCandidateAVCSig(AlgorithmValueConsumer avc); + /** + * An element that is either an `AlgorithmInstance` or an `AlgorithmValueConsumer` with no known sources. + * + * This concept is used to model consumers that have no known source as an algorithm node. + * + * The `isCandidateAVCSig` predicate is used to restrict the set of consumers that expect inputs of `AlgorithmInstanceType`. + * These "total unknown" algorithm nodes would otherwise not exist if not modelled as a consumer node. + */ module AlgorithmInstanceOrValueConsumer< AlgorithmInstanceType Alg, isCandidateAVCSig/1 isCandidateAVC> { @@ -785,8 +1082,8 @@ module CryptographyBase Input> { exists(HMACAlgorithmInstance alg | avc = alg.getAConsumer()) } - private predicate isCipherAVC(AlgorithmValueConsumer avc) { - exists(CipherOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) + private predicate isKeyOperationAlgorithmAVC(AlgorithmValueConsumer avc) { + exists(KeyOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) } private predicate isMACAVC(AlgorithmValueConsumer avc) { @@ -798,8 +1095,14 @@ module CryptographyBase Input> { exists(KeyDerivationOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) } - final private class CipherAlgorithmInstanceOrValueConsumer = - AlgorithmInstanceOrValueConsumer::Union; + private predicate isEllipticCurveAVC(AlgorithmValueConsumer avc) { + exists(ECDHKeyAgreementAlgorithmInstance alg | + avc = alg.getEllipticCurveAlgorithmValueConsumer() + ) + } + + final private class KeyOperationAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; final private class HashAlgorithmInstanceOrValueConsumer = AlgorithmInstanceOrValueConsumer::Union; @@ -810,51 +1113,47 @@ module CryptographyBase Input> { final private class KeyDerivationAlgorithmInstanceOrValueConsumer = AlgorithmInstanceOrValueConsumer::Union; - final private class EllipticCurveAlgorithmInstanceOrValueConsumer = - AlgorithmInstanceOrValueConsumer::Union; + final private class EllipticCurveInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; private newtype TNode = - // Artifacts (data that is not an operation or algorithm, e.g., a key) + // Output artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or - TCipherOutput(CipherOutputArtifactInstance e) or - // Input artifact nodes (synthetic, used to differentiate input as entities) + // Input artifacts (synthetic nodes, used to differentiate input as entities) TNonceInput(NonceArtifactConsumer e) or TMessageInput(MessageArtifactConsumer e) or TSaltInput(SaltArtifactConsumer e) or TRandomNumberGeneration(RandomNumberGenerationInstance e) { e.flowsTo(_) } or - // Operations (e.g., hashing, encryption) + // Key Creation Operation union type (e.g., key generation, key load) + TKeyCreationOperation(KeyCreationOperationInstance e) or + // Key operations, algorithms, and artifacts + // These types are union types of encryption, signing, encapsulation and their algorithms/artifacts. + // The artifacts are the outputs, e.g., ciphertext, signature, wrapped key. + TKeyOperation(KeyOperationInstance e) or + TKeyOperationAlgorithm(KeyOperationAlgorithmInstanceOrValueConsumer e) or + TKeyOperationOutput(KeyOperationOutputArtifactInstance e) or + // Non-Standalone Algorithms (e.g., Mode, Padding) + // These algorithms are always tied to a key operation algorithm + TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or + TPaddingAlgorithm(PaddingAlgorithmInstance e) or + // All other operations THashOperation(HashOperationInstance e) or - TCipherOperation(CipherOperationInstance e) or - TKeyEncapsulationOperation(KeyEncapsulationOperationInstance e) or TMACOperation(MACOperationInstance e) or - // Key Creation Operations - TKeyCreationOperation(KeyCreationOperationInstance e) or - // Algorithms (e.g., SHA-256, AES) - TCipherAlgorithm(CipherAlgorithmInstanceOrValueConsumer e) or - TEllipticCurveAlgorithm(EllipticCurveAlgorithmInstanceOrValueConsumer e) or + // All other algorithms + TEllipticCurve(EllipticCurveInstanceOrValueConsumer e) or THashAlgorithm(HashAlgorithmInstanceOrValueConsumer e) or TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstanceOrValueConsumer e) or - TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or TMACAlgorithm(MACAlgorithmInstanceOrValueConsumer e) or TKeyAgreementAlgorithm(KeyAgreementAlgorithmInstance e) or - // Non-standalone Algorithms (e.g., Mode, Padding) - // TODO: need to rename this, as "mode" is getting reused in different contexts, be precise - TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or - TPaddingAlgorithm(PaddingAlgorithmInstance e) or - // Composite and hybrid cryptosystems (e.g., RSA-OAEP used with AES, post-quantum hybrid cryptosystems) - // These nodes are always parent nodes and are not modeled but rather defined via library-agnostic patterns. - TKemDemHybridCryptosystem(CipherAlgorithmNode dem) or // TODO, change this relation and the below ones - TKeyAgreementHybridCryptosystem(CipherAlgorithmInstance ka) or - TAsymmetricEncryptionMacHybridCryptosystem(CipherAlgorithmInstance enc) or - TPostQuantumHybridCryptosystem(CipherAlgorithmInstance enc) or - // Generic source nodes + // Generic source nodes, i.e., sources of data that are not resolvable to a specific known asset. TGenericSourceNode(GenericSourceInstance e) { // An element modelled as a `GenericSourceInstance` can also be modelled as a `KnownElement` // For example, a string literal "AES" could be a generic constant but also an algorithm instance. - // Only create generic nodes tied to instances which are not also a `KnownElement`. + // + // Therefore, only create generic nodes tied to instances which are not also a `KnownElement`... not e instanceof KnownElement and - // Only create nodes for generic sources which flow to other elements + // ... and that flow to other elements e.flowsTo(_) } @@ -863,25 +1162,8 @@ module CryptographyBase Input> { * * Each `NodeBase` is a node in a graph of cryptographic operations, where the edges are the relationships between the nodes. * - * A node, as opposed to a property, is a construct that can reference or be referenced by more than one node. - * For example: a key size is a single value configuring a cipher algorithm, but a single mode of operation algorithm - * can be referenced by multiple disjoint cipher algorithms. For example, even if the same key size value is reused - * for multiple cipher algorithms, the key size holds no information when devolved to that simple value, and it is - * therefore not a "construct" or "element" being reused by multiple nodes. - * - * As a rule of thumb, a node is an algorithm or the use of an algorithm (an operation), as well as structured data - * consumed by or produced by an operation or algorithm (an artifact) that represents a construct beyond its data. - * - * _Example 1_: A seed of a random number generation algorithm has meaning beyond its value, as its reuse in multiple - * random number generation algorithms is more relevant than its underlying value. In contrast, a key size is only - * relevant to analysis in terms of its underlying value. Therefore, an RNG seed is a node; a key size is not. However, - * the key size might have a `GenericSourceNode` source, even if it itself is not a node. - * - * _Example 2_: A salt for a key derivation function *is* an `ArtifactNode`. - * - * _Example 3_: The iteration count of a key derivation function is *not* a node, but it may link to a generic node. - * - * _Example 4_: A nonce for a cipher operation *is* an `ArtifactNode`. + * As a rule of thumb, a node is an algorithm or the use of an algorithm (an operation), as well as structured data (an artifact) + * consumed by or produced by an operation or algorithm. */ abstract class NodeBase extends TNode { /** @@ -1057,7 +1339,7 @@ module CryptographyBase Input> { } /** - * A nonce or initialization vector input + * A nonce or initialization vector input. */ final class NonceArtifactNode extends ArtifactNode, TNonceInput { NonceArtifactConsumer instance; @@ -1070,7 +1352,7 @@ module CryptographyBase Input> { } /** - * A message or plaintext/ciphertext input + * A message or plaintext/ciphertext input. */ final class MessageArtifactNode extends ArtifactNode, TMessageInput { MessageArtifactConsumer instance; @@ -1083,7 +1365,7 @@ module CryptographyBase Input> { } /** - * A salt input + * A salt input. */ final class SaltArtifactNode extends ArtifactNode, TSaltInput { SaltArtifactConsumer instance; @@ -1096,14 +1378,16 @@ module CryptographyBase Input> { } /** - * Output text from a cipher operation + * The base class for output nodes from key operations. + * + * This class represents the output of key generation, key derivation, encryption, decryption, signing, and verification. */ - final class CipherOutputNode extends ArtifactNode, TCipherOutput { - CipherOutputArtifactInstance instance; + class KeyOperationOutputNode extends ArtifactNode, TKeyOperationOutput { + KeyOperationOutputArtifactInstance instance; - CipherOutputNode() { this = TCipherOutput(instance) } + KeyOperationOutputNode() { this = TKeyOperationOutput(instance) } - final override string getInternalType() { result = "CipherOutput" } + final override string getInternalType() { result = "KeyOperationOutput" } override LocatableElement asElement() { result = instance } @@ -1111,7 +1395,7 @@ module CryptographyBase Input> { } /** - * A source of random number generation + * A source of random number generation. */ final class RandomNumberGenerationNode extends ArtifactNode, TRandomNumberGeneration { RandomNumberGenerationInstance instance; @@ -1125,6 +1409,22 @@ module CryptographyBase Input> { override string getSourceNodeRelationship() { none() } // TODO: seed? } + /** + * A union type of all algorithm types that can be used in key creation operations. + */ + class TKeyCreationCandidateAlgorithm = + TKeyOperationAlgorithm or TEllipticCurve or TKeyAgreementAlgorithm or TKeyDerivationAlgorithm; + + /** + * A candidate algorithm node for key creation. + * + * Note: This is not an independent node type, but a subset of `AlgorithmNode` that is of type `TKeyCreationCandidateAlgorithm`. + */ + private class KeyCreationCandidateAlgorithmNode extends TKeyCreationCandidateAlgorithm instanceof AlgorithmNode + { + string toString() { result = super.getAlgorithmName() } + } + /** * A cryptographic key, such as a symmetric key or asymmetric key pair. */ @@ -1146,7 +1446,7 @@ module CryptographyBase Input> { .getAGenericSourceNode() } - CipherAlgorithmNode getAKnownAlgorithm() { + KeyCreationCandidateAlgorithmNode getAKnownAlgorithm() { result = instance.(KeyCreationOperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() } @@ -1180,7 +1480,7 @@ module CryptographyBase Input> { } /** - * A digest produced by a hash operation. + * A digest artifact produced by a hash operation. */ final class DigestArtifactNode extends ArtifactNode, TDigest { DigestArtifactInstance instance; @@ -1305,7 +1605,7 @@ module CryptographyBase Input> { KeyGenerationOperationNode() { keyGenInstance = instance } override predicate isCandidateAlgorithmNode(AlgorithmNode node) { - node instanceof CipherAlgorithmNode + node instanceof KeyCreationCandidateAlgorithmNode } override NodeBase getChild(string key) { @@ -1390,7 +1690,7 @@ module CryptographyBase Input> { result = instance.asAlg().getRawKDFAlgorithmName() } - final override string getAlgorithmName() { result = this.getRawAlgorithmName() } + override string getAlgorithmName() { result = this.getRawAlgorithmName() } // TODO: standardize? } /** @@ -1414,195 +1714,97 @@ module CryptographyBase Input> { } } - // /** - // * PKCS12KDF key derivation function - // */ - // abstract class PKCS12KDF extends KeyDerivationWithDigestParameterNode { - // override string getAlgorithmName() { result = "PKCS12KDF" } - // /** - // * Gets the iteration count of this key derivation algorithm. - // */ - // abstract string getIterationCount(Location location); - // /** - // * Gets the raw ID argument specifying the intended use of the derived key. - // * - // * The intended use is defined in RFC 7292, appendix B.3, as follows: - // * - // * This standard specifies 3 different values for the ID byte mentioned above: - // * - // * 1. If ID=1, then the pseudorandom bits being produced are to be used - // * as key material for performing encryption or decryption. - // * - // * 2. If ID=2, then the pseudorandom bits being produced are to be used - // * as an IV (Initial Value) for encryption or decryption. - // * - // * 3. If ID=3, then the pseudorandom bits being produced are to be used - // * as an integrity key for MACing. - // */ - // abstract string getIDByte(Location location); - // override predicate properties(string key, string value, Location location) { - // super.properties(key, value, location) - // or - // ( - // // [KNOWN_OR_UNKNOWN] - // key = "Iterations" and - // if exists(this.getIterationCount(location)) - // then value = this.getIterationCount(location) - // else ( - // value instanceof UnknownPropertyValue and location instanceof UnknownLocation - // ) - // ) - // or - // ( - // // [KNOWN_OR_UNKNOWN] - // key = "IdByte" and - // if exists(this.getIDByte(location)) - // then value = this.getIDByte(location) - // else ( - // value instanceof UnknownPropertyValue and location instanceof UnknownLocation - // ) - // ) - // } - // } - // /** - // * scrypt key derivation function - // */ - // abstract class SCRYPT extends KeyDerivationAlgorithmNode { - // final override string getAlgorithmName() { result = "scrypt" } - // /** - // * Gets the iteration count (`N`) argument - // */ - // abstract string get_N(Location location); - // /** - // * Gets the block size (`r`) argument - // */ - // abstract string get_r(Location location); - // /** - // * Gets the parallelization factor (`p`) argument - // */ - // abstract string get_p(Location location); - // /** - // * Gets the derived key length argument - // */ - // abstract string getDerivedKeyLength(Location location); - // override predicate properties(string key, string value, Location location) { - // super.properties(key, value, location) - // or - // ( - // // [KNOWN_OR_UNKNOWN] - // key = "N" and - // if exists(this.get_N(location)) - // then value = this.get_N(location) - // else ( - // value instanceof UnknownPropertyValue and location instanceof UnknownLocation - // ) - // ) - // or - // ( - // // [KNOWN_OR_UNKNOWN] - // key = "r" and - // if exists(this.get_r(location)) - // then value = this.get_r(location) - // else ( - // value instanceof UnknownPropertyValue and location instanceof UnknownLocation - // ) - // ) - // or - // ( - // // [KNOWN_OR_UNKNOWN] - // key = "p" and - // if exists(this.get_p(location)) - // then value = this.get_p(location) - // else ( - // value instanceof UnknownPropertyValue and location instanceof UnknownLocation - // ) - // ) - // or - // ( - // // [KNOWN_OR_UNKNOWN] - // key = "KeyLength" and - // if exists(this.getDerivedKeyLength(location)) - // then value = this.getDerivedKeyLength(location) - // else ( - // value instanceof UnknownPropertyValue and location instanceof UnknownLocation - // ) - // ) - // } - // } - /* - * TODO: - * - * Rule: No newtype representing a type of algorithm should be modelled with multiple interfaces - * - * Example 1: HKDF and PKCS12KDF are both key derivation algorithms. - * However, PKCS12KDF also has a property: the iteration count. - * - * If we have HKDF and PKCS12KDF under TKeyDerivationType, - * someone modelling a library might try to make a generic identification of both of those algorithms. - * - * They will therefore not use the specialized type for PKCS12KDF, - * meaning "from PKCS12KDF algo select algo" will have no results. - * - * Example 2: Each type below represents a common family of elliptic curves, with a shared interface, i.e., - * predicates for library modellers to implement as well as the properties and edges reported. + /** + * scrypt key derivation function */ + class ScryptAlgorithmNode extends KeyDerivationAlgorithmNode { + ScryptAlgorithmInstance scryptInstance; - newtype TCipherOperationSubtype = - TEncryptionMode() or - TDecryptionMode() or - TWrapMode() or - TUnwrapMode() or - TSignatureMode() or - TUnknownCipherOperationMode() - - abstract class CipherOperationSubtype extends TCipherOperationSubtype { - abstract string toString(); - } - - class EncryptionSubtype extends CipherOperationSubtype, TEncryptionMode { - override string toString() { result = "Encrypt" } - } + ScryptAlgorithmNode() { scryptInstance = instance.asAlg() } - class DecryptionSubtype extends CipherOperationSubtype, TDecryptionMode { - override string toString() { result = "Decrypt" } - } + /** + * Gets the iteration count (`N`) argument + */ + GenericSourceNode get_N() { none() } // TODO - class WrapSubtype extends CipherOperationSubtype, TWrapMode { - override string toString() { result = "Wrap" } - } + /** + * Gets the block size (`r`) argument + */ + GenericSourceNode get_r() { none() } // TODO - class UnwrapSubtype extends CipherOperationSubtype, TUnwrapMode { - override string toString() { result = "Unwrap" } + /** + * Gets the parallelization factor (`p`) argument + */ + GenericSourceNode get_p() { none() } // TODO } - class SignatureSubtype extends CipherOperationSubtype, TSignatureMode { - override string toString() { result = "Sign" } - } + /** + * A type defining the subtype type of a key operation. + */ + newtype TKeyOperationSubtype = + TEncryptMode() or + TDecryptMode() or + TWrapMode() or + TUnwrapMode() or + TSignMode() or + TVerifyMode() or + TUnknownKeyOperationMode() - class UnknownCipherOperationSubtype extends CipherOperationSubtype, TUnknownCipherOperationMode { - override string toString() { result = "Unknown" } + /** + * A class defining the subtype of a key operation. + */ + class KeyOperationSubtype extends TKeyOperationSubtype { + string toString() { + result = "Encrypt" and this = TEncryptMode() + or + result = "Decrypt" and this = TDecryptMode() + or + result = "Wrap" and this = TWrapMode() + or + result = "Unwrap" and this = TUnwrapMode() + or + result = "Sign" and this = TSignMode() + or + result = "Verify" and this = TVerifyMode() + or + result = "Unknown" and this = TUnknownKeyOperationMode() + } } /** - * An encryption operation that processes plaintext to generate a ciphertext. - * This operation takes an input message (plaintext) of arbitrary content and length - * and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding). + * A key-based cryptographic transformation that operates on data using either a symmetric or asymmetric cryptographic key. + * + * This operation class covers operations based on symmetric ciphers or broader asymmetric algorithms, including: + * + * - **Encryption / Decryption**: + * Symmetric (e.g., AES-GCM) or asymmetric (e.g., RSA-OAEP, ECIES) encryption of plaintext to ciphertext or vice-versa. + * + * - **Key Wrapping / Unwrapping**: + * Encapsulation of symmetric keys using algorithms such as Kyber, AES-KW, RSA-KEM, RSA-OAEP, etc. + * + * - **Signing / Verifying**: + * Digital signatures using private/public keypairs (e.g., Ed25519, RSA-PSS, ECDSA) + * + * Each sub-operation is represented by a `CipherOperationSubtype`, such as `Encrypt`, `Sign`, `Wrap`, etc. + * + * Note: This class does _not_ include symmetric message authentication operations (MACs) like HMAC or CMAC. + * These are handled separately in the `MacOperationNode` class. */ - final class CipherOperationNode extends OperationNode, TCipherOperation { - CipherOperationInstance instance; + class KeyOperationNode extends OperationNode, TKeyOperation { + KeyOperationInstance instance; + + KeyOperationNode() { this = TKeyOperation(instance) } - CipherOperationNode() { this = TCipherOperation(instance) } + final KeyOperationSubtype getKeyOperationSubtype() { + result = instance.getKeyOperationSubtype() + } override LocatableElement asElement() { result = instance } - override string getInternalType() { result = "CipherOperation" } + override string getInternalType() { result = "KeyOperation" } override predicate isCandidateAlgorithmNode(AlgorithmNode node) { - node instanceof CipherAlgorithmNode - } - - CipherOperationSubtype getCipherOperationSubtype() { - result = instance.getCipherOperationSubtype() + node instanceof KeyOperationAlgorithmNode } NonceArtifactNode getANonce() { result.asElement() = instance.getNonceConsumer().getConsumer() } @@ -1611,25 +1813,29 @@ module CryptographyBase Input> { result.asElement() = instance.getInputConsumer().getConsumer() } - CipherOutputNode getAnOutputArtifact() { result.asElement() = instance.getOutputArtifact() } + KeyOperationOutputNode getAnOutputArtifact() { + result.asElement() = instance.getOutputArtifactInstance() + } KeyArtifactNode getAKey() { result.asElement() = instance.getKeyConsumer().getConsumer() } override NodeBase getChild(string key) { result = super.getChild(key) or - // [KNOWN_OR_UNKNOWN] + // [KNOWN_OR_UNKNOWN] - but only if not sign/verify + not this instanceof SignatureOperationNode and key = "Nonce" and if exists(this.getANonce()) then result = this.getANonce() else result = this or // [KNOWN_OR_UNKNOWN] - key = "InputText" and + key = "Input" and if exists(this.getAnInputArtifact()) then result = this.getAnInputArtifact() else result = this or - // [KNOWN_OR_UNKNOWN] - key = "OutputText" and + // [KNOWN_OR_UNKNOWN] - but only if not verify + not this.getKeyOperationSubtype() instanceof TVerifyMode and + key = "Output" and if exists(this.getAnOutputArtifact()) then result = this.getAnOutputArtifact() else result = this @@ -1638,33 +1844,47 @@ module CryptographyBase Input> { key = "Key" and if exists(this.getAKey()) then result = this.getAKey() else result = this } + } - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) + class CipherOperationNode extends KeyOperationNode { + string nodeName; + + CipherOperationNode() { + this.getKeyOperationSubtype() = TEncryptMode() and nodeName = "EncryptOperation" or - // [ALWAYS_KNOWN] - Unknown is handled in getCipherOperationMode() - key = "Operation" and - value = this.getCipherOperationSubtype().toString() and - location = this.getLocation() + this.getKeyOperationSubtype() = TDecryptMode() and nodeName = "DecryptOperation" } + + override string getInternalType() { result = nodeName } + } + + class KeyEncapsulationOperationNode extends KeyOperationNode { + string nodeName; + + KeyEncapsulationOperationNode() { + this.getKeyOperationSubtype() = TWrapMode() and nodeName = "WrapOperation" + or + this.getKeyOperationSubtype() = TUnwrapMode() and nodeName = "UnwrapOperation" + } + + override string getInternalType() { result = nodeName } + } + + class SignatureOperationNode extends KeyOperationNode { + string nodeName; + + SignatureOperationNode() { + this.getKeyOperationSubtype() = TSignMode() and nodeName = "SignOperation" + or + this.getKeyOperationSubtype() = TVerifyMode() and nodeName = "VerifyOperation" + } + + override string getInternalType() { result = nodeName } } /** * Block cipher modes of operation algorithms */ - newtype TBlockCipherModeOperationType = - ECB() or // Not secure, widely used - CBC() or // Vulnerable to padding oracle attacks - CFB() or - GCM() or // Widely used AEAD mode (TLS 1.3, SSH, IPsec) - CTR() or // Fast stream-like encryption (SSH, disk encryption) - XTS() or // Standard for full-disk encryption (BitLocker, LUKS, FileVault) - CCM() or // Used in lightweight cryptography (IoT, WPA2) - SIV() or // Misuse-resistant encryption, used in secure storage - OCB() or // Efficient AEAD mode - OFB() or - OtherMode() - class ModeOfOperationAlgorithmNode extends AlgorithmNode, TModeOfOperationAlgorithm { ModeOfOperationAlgorithmInstance instance; @@ -1683,29 +1903,29 @@ module CryptographyBase Input> { * * If a type cannot be determined, the result is `OtherMode`. */ - TBlockCipherModeOperationType getModeType() { result = instance.getModeType() } + TBlockCipherModeOfOperationType getModeType() { result = instance.getModeType() } bindingset[type] - final private predicate modeToNameMapping(TBlockCipherModeOperationType type, string name) { - type instanceof ECB and name = "ECB" + final private predicate modeToNameMapping(TBlockCipherModeOfOperationType type, string name) { + type = ECB() and name = "ECB" or - type instanceof CBC and name = "CBC" + type = CBC() and name = "CBC" or - type instanceof GCM and name = "GCM" + type = GCM() and name = "GCM" or - type instanceof CTR and name = "CTR" + type = CTR() and name = "CTR" or - type instanceof XTS and name = "XTS" + type = XTS() and name = "XTS" or - type instanceof CCM and name = "CCM" + type = CCM() and name = "CCM" or - type instanceof SIV and name = "SIV" + type = SIV() and name = "SIV" or - type instanceof OCB and name = "OCB" + type = OCB() and name = "OCB" or - type instanceof CFB and name = "CFB" + type = CFB() and name = "CFB" or - type instanceof OFB and name = "OFB" + type = OFB() and name = "OFB" } override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } @@ -1733,17 +1953,17 @@ module CryptographyBase Input> { bindingset[type] final private predicate paddingToNameMapping(TPaddingType type, string name) { - type instanceof PKCS1_v1_5 and name = "PKCS1_v1_5" + type = ANSI_X9_23() and name = "ANSI_X9_23" or - type instanceof PSS and name = "PSS" + type = NoPadding() and name = "NoPadding" or - type instanceof PKCS7 and name = "PKCS7" + type = OAEP() and name = "OAEP" or - type instanceof ANSI_X9_23 and name = "ANSI_X9_23" + type = PKCS1_v1_5() and name = "PKCS1_v1_5" or - type instanceof NoPadding and name = "NoPadding" + type = PKCS7() and name = "PKCS7" or - type instanceof OAEP and name = "OAEP" + type = PSS() and name = "PSS" } override string getAlgorithmName() { this.paddingToNameMapping(this.getPaddingType(), result) } @@ -1781,81 +2001,36 @@ module CryptographyBase Input> { } } - /** - * A helper type for distinguishing between block and stream ciphers. - */ - newtype TCipherStructureType = - Block() or - Stream() or - Asymmetric() or - UnknownCipherStructureType() - - private string getCipherStructureTypeString(TCipherStructureType type) { - type instanceof Block and result = "Block" - or - type instanceof Stream and result = "Stream" - or - type instanceof Asymmetric and result = "Asymmetric" - or - type instanceof UnknownCipherStructureType and result instanceof UnknownPropertyValue - } + class KeyOperationAlgorithmNode extends AlgorithmNode, TKeyOperationAlgorithm { + KeyOperationAlgorithmInstanceOrValueConsumer instance; - /** - * Symmetric algorithms - */ - newtype TCipherType = - AES() or - ARIA() or - BLOWFISH() or - CAMELLIA() or - CAST5() or - CHACHA20() or - DES() or - DESX() or - GOST() or - IDEA() or - KUZNYECHIK() or - MAGMA() or - TripleDES() or - DoubleDES() or - RC2() or - RC4() or - RC5() or - RSA() or - SEED() or - SM4() or - OtherCipherType() - - final class CipherAlgorithmNode extends AlgorithmNode, TCipherAlgorithm { - CipherAlgorithmInstanceOrValueConsumer instance; - - CipherAlgorithmNode() { this = TCipherAlgorithm(instance) } + KeyOperationAlgorithmNode() { this = TKeyOperationAlgorithm(instance) } override LocatableElement asElement() { result = instance } - override string getInternalType() { result = "CipherAlgorithm" } + override string getInternalType() { result = "KeyOperationAlgorithm" } - final TCipherStructureType getCipherStructure() { - this.cipherFamilyToNameAndStructure(this.getCipherFamily(), _, result) + final KeyOpAlg::CipherStructureType getSymmetricCipherStructure() { + KeyOpAlg::symmetric_cipher_to_name_and_structure(this.getAlgorithmType() + .(KeyOpAlg::SymmetricCipherAlgorithm) + .getType(), _, result) } final override string getAlgorithmName() { - this.cipherFamilyToNameAndStructure(this.getCipherFamily(), result, _) + KeyOpAlg::type_to_name(this.getAlgorithmType(), result) } - final override string getRawAlgorithmName() { - result = instance.asAlg().getRawCipherAlgorithmName() - } + final override string getRawAlgorithmName() { result = instance.asAlg().getRawAlgorithmName() } /** - * Gets the key size of this cipher, e.g., "128" or "256". + * Gets the key size variant of this algorithm in bits, e.g., 128 for "AES-128". */ - string getKeySize(Location location) { none() } // TODO + string getKeySize() { result = instance.asAlg().getKeySize() } // TODO: key sizes for known algorithms /** - * Gets the type of this cipher, e.g., "AES" or "ChaCha20". + * Gets the type of this key operation algorithm, e.g., "SymmetricEncryption(_)" or "" */ - TCipherType getCipherFamily() { result = instance.asAlg().getCipherFamily() } + KeyOpAlg::Algorithm getAlgorithmType() { result = instance.asAlg().getAlgorithmType() } /** * Gets the mode of operation of this cipher, e.g., "GCM" or "CBC". @@ -1871,55 +2046,6 @@ module CryptographyBase Input> { result.asElement() = instance.asAlg().getPaddingAlgorithm() } - bindingset[type] - final private predicate cipherFamilyToNameAndStructure( - TCipherType type, string name, TCipherStructureType s - ) { - type instanceof AES and name = "AES" and s = Block() - or - type instanceof ARIA and name = "ARIA" and s = Block() - or - type instanceof BLOWFISH and name = "Blowfish" and s = Block() - or - type instanceof CAMELLIA and name = "Camellia" and s = Block() - or - type instanceof CAST5 and name = "CAST5" and s = Block() - or - type instanceof CHACHA20 and name = "ChaCha20" and s = Stream() - or - type instanceof DES and name = "DES" and s = Block() - or - type instanceof DESX and name = "DESX" and s = Block() - or - type instanceof GOST and name = "GOST" and s = Block() - or - type instanceof IDEA and name = "IDEA" and s = Block() - or - type instanceof KUZNYECHIK and name = "Kuznyechik" and s = Block() - or - type instanceof MAGMA and name = "Magma" and s = Block() - or - type instanceof TripleDES and name = "TripleDES" and s = Block() - or - type instanceof DoubleDES and name = "DoubleDES" and s = Block() - or - type instanceof RC2 and name = "RC2" and s = Block() - or - type instanceof RC4 and name = "RC4" and s = Stream() - or - type instanceof RC5 and name = "RC5" and s = Block() - or - type instanceof RSA and name = "RSA" and s = Asymmetric() - or - type instanceof SEED and name = "SEED" and s = Block() - or - type instanceof SM4 and name = "SM4" and s = Block() - or - type instanceof OtherCipherType and - name instanceof UnknownPropertyValue and // TODO: get rid of this hack to bind structure and type - s = UnknownCipherStructureType() - } - override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or @@ -1939,16 +2065,16 @@ module CryptographyBase Input> { override predicate properties(string key, string value, Location location) { super.properties(key, value, location) or - // [ALWAYS_KNOWN] - unknown case is handled in `getCipherStructureTypeString` + // [ONLY_KNOWN] - only if symmetric, unknown case is handled in `toString` key = "Structure" and - getCipherStructureTypeString(this.getCipherStructure()) = value and - location instanceof UnknownLocation + this.getSymmetricCipherStructure().toString() = value and + location = this.getLocation() or ( // [KNOWN_OR_UNKNOWN] key = "KeySize" and - if exists(this.getKeySize(location)) - then value = this.getKeySize(location) + if exists(this.getKeySize()) + then value = this.getKeySize() else ( value instanceof UnknownPropertyValue and location instanceof UnknownLocation ) @@ -2021,32 +2147,32 @@ module CryptographyBase Input> { override string getRawAlgorithmName() { result = instance.asAlg().getRawHashAlgorithmName() } - final predicate hashTypeToNameMapping(THashType type, string name) { - type instanceof BLAKE2B and name = "BLAKE2B" + final private predicate hashTypeToNameMapping(THashType type, string name) { + type = BLAKE2B() and name = "BLAKE2B" or - type instanceof BLAKE2S and name = "BLAKE2S" + type = BLAKE2S() and name = "BLAKE2S" or - type instanceof RIPEMD160 and name = "RIPEMD160" + type = RIPEMD160() and name = "RIPEMD160" or - type instanceof MD2 and name = "MD2" + type = MD2() and name = "MD2" or - type instanceof MD4 and name = "MD4" + type = MD4() and name = "MD4" or - type instanceof MD5 and name = "MD5" + type = MD5() and name = "MD5" or - type instanceof POLY1305 and name = "POLY1305" + type = POLY1305() and name = "POLY1305" or - type instanceof SHA1 and name = "SHA1" + type = SHA1() and name = "SHA1" or - type instanceof SHA2 and name = "SHA2" + type = SHA2() and name = "SHA2" or - type instanceof SHA3 and name = "SHA3" + type = SHA3() and name = "SHA3" or - type instanceof SHAKE and name = "SHAKE" + type = SHAKE() and name = "SHAKE" or - type instanceof SM3 and name = "SM3" + type = SM3() and name = "SM3" or - type instanceof WHIRLPOOL and name = "WHIRLPOOL" + type = WHIRLPOOL() and name = "WHIRLPOOL" } /** @@ -2129,19 +2255,32 @@ module CryptographyBase Input> { private predicate isNumsCurve(string curveName, int keySize) { // ALL NUMS CURVES keySize in [256, 384, 512] and - exists(string suff | suff in ["T1"] | curveName = "NUMSP" + keySize.toString() + suff) + exists(string suff | suff = "T1" | curveName = "NUMSP" + keySize.toString() + suff) } + /** + * Holds if `name` corresponds to a known elliptic curve. + * + * Note: As an exception, this predicate may be used for library modelling, as curve names are largely standardized. + * + * When modelling, verify that this predicate offers sufficient coverage for the library and handle edge-cases. + */ bindingset[curveName] predicate isEllipticCurveAlgorithmName(string curveName) { - isEllipticCurveAlgorithm(curveName, _, _) + ellipticCurveNameToKeySizeAndFamilyMapping(curveName, _, _) } /** - * Holds if `name` corresponds to a known elliptic curve. + * Relates elliptic curve names to their key size and family. + * + * Note: As an exception, this predicate may be used for library modelling, as curve names are largely standardized. + * + * When modelling, verify that this predicate offers sufficient coverage for the library and handle edge-cases. */ bindingset[rawName] - predicate isEllipticCurveAlgorithm(string rawName, int keySize, TEllipticCurveType curveFamily) { + predicate ellipticCurveNameToKeySizeAndFamilyMapping( + string rawName, int keySize, TEllipticCurveType curveFamily + ) { exists(string curveName | curveName = rawName.toUpperCase() | isSecCurve(curveName, keySize) and curveFamily = SEC() or @@ -2171,19 +2310,30 @@ module CryptographyBase Input> { ) } - final class EllipticCurveNode extends AlgorithmNode, TEllipticCurveAlgorithm { - EllipticCurveAlgorithmInstanceOrValueConsumer instance; + final class EllipticCurveNode extends AlgorithmNode, TEllipticCurve { + EllipticCurveInstanceOrValueConsumer instance; - EllipticCurveNode() { this = TEllipticCurveAlgorithm(instance) } + EllipticCurveNode() { this = TEllipticCurve(instance) } - override string getInternalType() { result = "EllipticCurveAlgorithm" } + override string getInternalType() { result = "EllipticCurve" } + + override LocatableElement asElement() { result = instance } final override string getRawAlgorithmName() { - result = instance.asAlg().getRawEllipticCurveAlgorithmName() + result = instance.asAlg().getRawEllipticCurveName() } - // NICK QUESTION: do I repeat the key size and curve family predicates here as wrappers of the instance? - override LocatableElement asElement() { result = instance } + /* + * Mandating that for Elliptic Curves specifically, users are *only* responsible + * for providing as the 'raw' name within source code. + * + * Rationale: elliptic curve names can have a lot of variation in their components + * (e.g., "secp256r1" vs "P-256"), trying to produce generalized set of properties + * is possible to capture all cases, but such modeling is likely not necessary. + * if all properties need to be captured, we can reassess how names are generated. + */ + + override string getAlgorithmName() { result = this.getRawAlgorithmName() } TEllipticCurveType getEllipticCurveFamily() { result = instance.asAlg().getEllipticCurveFamily() @@ -2197,80 +2347,10 @@ module CryptographyBase Input> { value = instance.asAlg().getKeySize() and location = this.getLocation() or - key = "StdCurveName" and - value = instance.asAlg().getStandardCurveName().toUpperCase() and + // [ONLY_KNOWN] + key = "ParsedName" and + value = instance.asAlg().getParsedEllipticCurveName() and location = this.getLocation() } - - override string getAlgorithmName() { result = this.getRawAlgorithmName().toUpperCase() } - // /** - // * Mandating that for Elliptic Curves specifically, users are responsible - // * for providing as the 'raw' name, the official name of the algorithm. - // * - // * Casing doesn't matter, we will enforce further naming restrictions on - // * `getAlgorithmName` by default. - // * - // * Rationale: elliptic curve names can have a lot of variation in their components - // * (e.g., "secp256r1" vs "P-256"), trying to produce generalized set of properties - // * is possible to capture all cases, but such modeling is likely not necessary. - // * if all properties need to be captured, we can reassess how names are generated. - // */ - // abstract override string getRawAlgorithmName(); - } - - abstract class KEMAlgorithm extends TKeyEncapsulationAlgorithm, AlgorithmNode { - final override string getInternalType() { result = "KeyEncapsulationAlgorithm" } - } - - /** - * Key agreement algorithms - */ - newtype TKeyAgreementType = - DH() or // Diffie-Hellman - EDH() or // Ephemeral Diffie-Hellman - ECDH() or // Elliptic Curve Diffie-Hellman - // Note: x25519 and x448 are applications of ECDH - OtherKeyAgreementType() - - bindingset[name] - predicate isKeyAgreementAlgorithmName(string name) { isKeyAgreementAlgorithm(name, _) } - - bindingset[name] - predicate isKeyAgreementAlgorithm(string name, TKeyAgreementType type) { - exists(string name2 | name2 = name.toUpperCase() | - name2 = "DH" and type = DH() - or - name2 = "EDH" and type = EDH() - or - name2 = "ECDH" and type = ECDH() - or - name2 = "X25519" and type = ECDH() - or - name2 = "X448" and type = ECDH() - ) - } - - abstract class KeyAgreementAlgorithmInstance extends AlgorithmInstance { - // /** - // * If the key agreement uses a curve, (e.g., ECDH) point to the curve instance. - // * none() if the agreement is not curve based (e.g., plain DH). - // * Note that if the curve is inherent to the algorithm (e.g., x25519), this will be - // * the key agreement algorithm instance itself (this). - // */ - // abstract EllipticCurveAlgorithmInstance getEllipticCurveAlgorithm(); - } - - abstract class KeyAgreementSecretGenerationOperationInstance extends OperationInstance { - /** - * The private key used in the key agreement operation. - * This key represents the local party in the key agreement. - */ - abstract ConsumerInputDataFlowNode getServerKeyConsumer(); - - /** - * The public key used in the key agreement operation, coming - * from the peer (the other party in the key agreement). - */ - abstract ConsumerInputDataFlowNode getPeerKeyConsumer(); } } From 1958c192ecdc1ed7cbb88038d1eaf72daaf22e2d Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Tue, 15 Apr 2025 23:00:12 -0400 Subject: [PATCH 080/122] Reimplement EC, MAC, key creation logic + consumer --- java/ql/lib/experimental/Quantum/JCA.qll | 992 +++++++++++------- java/ql/lib/experimental/Quantum/Language.qll | 8 +- .../ql/src/experimental/Quantum/TestCipher.ql | 4 +- .../codeql/cryptography/Model.qll | 125 ++- 4 files changed, 746 insertions(+), 383 deletions(-) diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index b75c00c35498..7507c9edbd50 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -2,11 +2,20 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.controlflow.Dominance +import codeql.util.Option module JCAModel { import Language import Crypto::KeyOpAlg as KeyOpAlg + abstract class CipherAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } + + abstract class EllipticCurveAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } + + abstract class HashAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } + + abstract class KeyAgreementAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } + // TODO: Verify that the PBEWith% case works correctly bindingset[algo] predicate cipher_names(string algo) { @@ -103,10 +112,11 @@ module JCAModel { name.matches("PBEWith%") and result instanceof Crypto::PBES and withSubstring = name.regexpCapture("PBEWith(.*)", 1) + // TODO: add additional } bindingset[name] - Crypto::THashType hash_name_to_hash_type(string name, int digestLength) { + Crypto::THashType hash_name_to_type_known(string name, int digestLength) { name = "SHA-1" and result instanceof Crypto::SHA1 and digestLength = 160 or name = ["SHA-256", "SHA-384", "SHA-512"] and @@ -226,7 +236,7 @@ module JCAModel { string getPadding() { result = this.getValue().splitAt("/", 2) } } - class CipherGetInstanceCall extends Call { + class CipherGetInstanceCall extends MethodCall { CipherGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") } @@ -236,17 +246,21 @@ module JCAModel { Expr getProviderArg() { result = this.getArgument(1) } } + // TODO: handle key artifact produced by unwrap private class CipherOperationCall extends MethodCall { CipherOperationCall() { - exists(string s | s in ["doFinal", "wrap", "unwrap"] | - this.getMethod().hasQualifiedName("javax.crypto", "Cipher", s) - ) + this.getMethod() + .hasQualifiedName("javax.crypto", "Cipher", ["update", "doFinal", "wrap", "unwrap"]) } + predicate isIntermediate() { this.getMethod().getName() = "update" } + Expr getInput() { result = this.getArgument(0) } Expr getOutput() { - result = this.getArgument(3) + exists(int outputIndex | this.getMethod().getParameter(outputIndex).getName() = "output" | + result = this.getArgument(outputIndex) + ) or this.getMethod().getReturnType().hasName("byte[]") and result = this } @@ -255,17 +269,18 @@ module JCAModel { } /** - * Data-flow configuration modelling flow from a cipher string literal to a `CipherGetInstanceCall` argument. + * Data-flow configuration modelling flow from a cipher string literal to a cipher algorithm consumer. */ - private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + private module CipherAlgorithmStringToCipherConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral } predicate isSink(DataFlow::Node sink) { - exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode()) + sink = any(CipherAlgorithmValueConsumer call).getInputNode() } } - module AlgorithmStringToFetchFlow = TaintTracking::Global; + module CipherAlgorithmStringToFetchFlow = + TaintTracking::Global; /** * Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable. @@ -315,13 +330,13 @@ module JCAModel { class CipherStringLiteralAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof CipherStringLiteral { - Crypto::AlgorithmValueConsumer consumer; + CipherAlgorithmValueConsumer consumer; CipherStringLiteralAlgorithmInstance() { - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + CipherAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } - Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } + CipherAlgorithmValueConsumer getConsumer() { result = consumer } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { result = this // TODO: provider defaults @@ -368,9 +383,9 @@ module JCAModel { override string getRawHashAlgorithmName() { result = super.getPadding() } - override Crypto::THashType getHashFamily() { result = hash_name_to_hash_type(hashName, _) } + override Crypto::THashType getHashFamily() { result = hash_name_to_type_known(hashName, _) } - override int getDigestLength() { exists(hash_name_to_hash_type(hashName, result)) } + override int getDigestLength() { exists(hash_name_to_type_known(hashName, result)) } } class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance, @@ -388,19 +403,13 @@ module JCAModel { * * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`. */ - class CipherGetInstanceAlgorithmArg extends Crypto::AlgorithmValueConsumer instanceof Expr { + class CipherGetInstanceAlgorithmArg extends CipherAlgorithmValueConsumer instanceof Expr { CipherGetInstanceCall call; CipherGetInstanceAlgorithmArg() { this = call.getAlgorithmArg() } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } - CipherStringLiteral getOrigin(string value) { - AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), - DataFlow::exprNode(this.(Expr).getAChildExpr*())) and - value = result.getValue() - } - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this } @@ -426,133 +435,208 @@ module JCAModel { } } - private newtype TCipherModeFlowState = - TUninitializedCipherModeFlowState() or - TInitializedCipherModeFlowState(CipherInitCall call) or - TUsedCipherModeFlowState(CipherInitCall init) + signature class GetInstanceCallSig instanceof MethodCall; - abstract private class CipherModeFlowState extends TCipherModeFlowState { - string toString() { - this = TUninitializedCipherModeFlowState() and result = "uninitialized" - or - this = TInitializedCipherModeFlowState(_) and result = "initialized" - } + signature class InitCallSig instanceof MethodCall; - abstract Crypto::KeyOperationSubtype getKeyOperationMode(); + signature class UseCallSig instanceof MethodCall { + /** + * Holds if the use is not a final use, such as an `update()` call before `doFinal()` + */ + predicate isIntermediate(); } - private class UninitializedCipherModeFlowState extends CipherModeFlowState, - TUninitializedCipherModeFlowState + module MethodCallOption = Option; + + /** + * An generic analysis module for analyzing the `getInstance` to `initialize` to `doOperation` pattern in the JCA. + * + * For example: + * ``` + * kpg = KeyPairGenerator.getInstance(); + * kpg.initialize(...); + * kpg.generate(...); + * ``` + */ + module GetInstanceInitUseFlowAnalysis< + GetInstanceCallSig GetInstance, InitCallSig Init, UseCallSig Use> { - override Crypto::KeyOperationSubtype getKeyOperationMode() { - result instanceof Crypto::TUnknownKeyOperationMode + newtype TFlowState = + TUninitialized() or + TInitialized(Init call) or + TIntermediateUse(Use call) + + abstract class InitFlowState extends TFlowState { + string toString() { + this = TUninitialized() and result = "Uninitialized" + or + this = TInitialized(_) and result = "Initialized" + // TODO: add intermediate use + } } - } - private class InitializedCipherModeFlowState extends CipherModeFlowState, - TInitializedCipherModeFlowState - { - CipherInitCall call; - DataFlow::Node node1; - DataFlow::Node node2; - Crypto::KeyOperationSubtype mode; + class UninitializedFlowState extends InitFlowState, TUninitialized { } - InitializedCipherModeFlowState() { - this = TInitializedCipherModeFlowState(call) and - DataFlow::localFlowStep(node1, node2) and - node2.asExpr() = call.getQualifier() and - // TODO: does this make this predicate inefficient as it binds with anything? - not node1.asExpr() = call.getQualifier() and - mode = call.getCipherOperationModeType() - } + class InitializedFlowState extends InitFlowState, TInitialized { + Init call; + DataFlow::Node node1; + DataFlow::Node node2; - CipherInitCall getInitCall() { result = call } + InitializedFlowState() { + this = TInitialized(call) and + node2.asExpr() = call.(MethodCall).getQualifier() and + DataFlow::localFlowStep(node1, node2) and + node1 != node2 + } - DataFlow::Node getFstNode() { result = node1 } + Init getInitCall() { result = call } - /** - * Returns the node *to* which the state-changing step occurs - */ - DataFlow::Node getSndNode() { result = node2 } + DataFlow::Node getFstNode() { result = node1 } - override Crypto::KeyOperationSubtype getKeyOperationMode() { result = mode } - } + DataFlow::Node getSndNode() { result = node2 } + } - /** - * Trace from cipher initialization to a cryptographic operation, - * specifically `Cipher.doFinal()`, `Cipher.wrap()`, or `Cipher.unwrap()`. - * - * TODO: handle `Cipher.update()` - */ - private module CipherGetInstanceToCipherOperationConfig implements DataFlow::StateConfigSig { - class FlowState = TCipherModeFlowState; + class IntermediateUseState extends InitFlowState, TIntermediateUse { + Use call; + DataFlow::Node node1; + DataFlow::Node node2; + + IntermediateUseState() { + this = TIntermediateUse(call) and + call.isIntermediate() and + node1.asExpr() = call.(MethodCall).getQualifier() and + node2 = node1 + } - predicate isSource(DataFlow::Node src, FlowState state) { - state instanceof UninitializedCipherModeFlowState and - src.asExpr() instanceof CipherGetInstanceCall + Use getUseCall() { result = call } + + DataFlow::Node getFstNode() { result = node1 } + + DataFlow::Node getSndNode() { result = node2 } } - predicate isSink(DataFlow::Node sink, FlowState state) { none() } // TODO: document this, but this is intentional (avoid cross products?) + module GetInstanceToInitToUseConfig implements DataFlow::StateConfigSig { + class FlowState = InitFlowState; - predicate isSink(DataFlow::Node sink) { - exists(CipherOperationCall c | c.getQualifier() = sink.asExpr()) + predicate isSource(DataFlow::Node src, FlowState state) { + state instanceof UninitializedFlowState and + src.asExpr() instanceof GetInstance + or + src = state.(InitializedFlowState).getSndNode() + or + src = state.(IntermediateUseState).getSndNode() + } + + // TODO: document this, but this is intentional (avoid cross products?) + predicate isSink(DataFlow::Node sink, FlowState state) { none() } + + predicate isSink(DataFlow::Node sink) { + exists(Init c | c.(MethodCall).getQualifier() = sink.asExpr()) + or + exists(Use c | not c.isIntermediate() and c.(MethodCall).getQualifier() = sink.asExpr()) + } + + predicate isAdditionalFlowStep( + DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 + ) { + state1 = state1 and + ( + node1 = state2.(InitializedFlowState).getFstNode() and + node2 = state2.(InitializedFlowState).getSndNode() + or + node1 = state2.(IntermediateUseState).getFstNode() and + node2 = state2.(IntermediateUseState).getSndNode() + ) + } + + predicate isBarrier(DataFlow::Node node, FlowState state) { + exists(CipherInitCall call | node.asExpr() = call.getQualifier() | + state instanceof UninitializedFlowState + or + state.(InitializedFlowState).getInitCall() != call + ) + } + } + + module GetInstanceToInitToUseFlow = DataFlow::GlobalWithState; + + GetInstance getInstantiationFromUse( + Use use, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink + ) { + src.getNode().asExpr() = result and + sink.getNode().asExpr() = use.(MethodCall).getQualifier() and + GetInstanceToInitToUseFlow::flowPath(src, sink) } - predicate isAdditionalFlowStep( - DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 + Init getInitFromUse( + Use use, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink ) { - state1 = state1 and - node1 = state2.(InitializedCipherModeFlowState).getFstNode() and - node2 = state2.(InitializedCipherModeFlowState).getSndNode() + src.getNode().asExpr() = result.(MethodCall).getQualifier() and + sink.getNode().asExpr() = use.(MethodCall).getQualifier() and + GetInstanceToInitToUseFlow::flowPath(src, sink) } - predicate isBarrier(DataFlow::Node node, FlowState state) { - exists(CipherInitCall call | node.asExpr() = call.getQualifier() | - state instanceof UninitializedCipherModeFlowState - or - state.(InitializedCipherModeFlowState).getInitCall() != call - ) + predicate hasInit(Use use) { exists(getInitFromUse(use, _, _)) } + + Use getAnIntermediateUseFromFinalUse( + Use final, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink + ) { + not final.isIntermediate() and + result.isIntermediate() and + src.getNode().asExpr() = result.(MethodCall).getQualifier() and + sink.getNode().asExpr() = final.(MethodCall).getQualifier() and + GetInstanceToInitToUseFlow::flowPath(src, sink) } } - module CipherGetInstanceToCipherOperationFlow = - DataFlow::GlobalWithState; + module CipherFlowAnalysisImpl = + GetInstanceInitUseFlowAnalysis; - class CipherOperationInstance extends Crypto::KeyOperationInstance instanceof Call { - Crypto::KeyOperationSubtype mode; - CipherGetInstanceToCipherOperationFlow::PathNode sink; - CipherOperationCall doFinalize; - CipherGetInstanceAlgorithmArg consumer; + module CipherFlow = CipherFlowAnalysisImpl::GetInstanceToInitToUseFlow; - CipherOperationInstance() { - exists(CipherGetInstanceToCipherOperationFlow::PathNode src, CipherGetInstanceCall getCipher | - CipherGetInstanceToCipherOperationFlow::flowPath(src, sink) and - src.getNode().asExpr() = getCipher and - sink.getNode().asExpr() = doFinalize.getQualifier() and - sink.getState().(CipherModeFlowState).getKeyOperationMode() = mode and - this = doFinalize and - consumer = getCipher.getAlgorithmArg() - ) + Crypto::KeyOperationSubtype getKeyOperationSubtypeFromState( + CipherFlowAnalysisImpl::InitFlowState state + ) { + state instanceof CipherFlowAnalysisImpl::UninitializedFlowState and + result = Crypto::TUnknownKeyOperationMode() + or + exists(CipherInitCall call | state = CipherFlowAnalysisImpl::TInitialized(call) | + result = call.getCipherOperationModeType() + ) + } + + class CipherOperationInstance extends Crypto::KeyOperationInstance instanceof CipherOperationCall { + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if CipherFlowAnalysisImpl::hasInit(this) + then result = CipherFlowAnalysisImpl::getInitFromUse(this, _, _).getCipherOperationModeType() + else result = Crypto::TUnknownKeyOperationMode() + } + + CipherGetInstanceCall getInstantiationCall() { + result = CipherFlowAnalysisImpl::getInstantiationFromUse(this, _, _) } - override Crypto::KeyOperationSubtype getKeyOperationSubtype() { result = mode } + CipherInitCall getInitCall() { result = CipherFlowAnalysisImpl::getInitFromUse(this, _, _) } override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { - result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg() + result.asExpr() = this.getInitCall().getNonceArg() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = doFinalize.getMessageArg() + result = super.getMessageArg() or + result = CipherFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getMessageArg() } override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { - result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getKeyArg() + result.asExpr() = this.getInitCall().getKeyArg() } - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = consumer } + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result = this.getInstantiationCall().getAlgorithmArg() + } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result.asExpr() = doFinalize.getOutput() + result.asExpr() = super.getOutput() } } @@ -596,7 +680,6 @@ module JCAModel { } } - // e.g., getPublic or getPrivate class KeyAdditionalFlowSteps extends MethodCall { KeyAdditionalFlowSteps() { this.getCallee().hasQualifiedName("java.security", "KeyPair", "getPublic") @@ -706,12 +789,18 @@ module JCAModel { override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } } - // flow config from a known hash algorithm literal to MessageDigest.getInstance + /* + * Hash Functions + */ + + /** + * Flow from a known hash algorithm name to a `MessageDigest.getInstance(sink)` call. + */ module KnownHashAlgorithmLiteralToMessageDigestConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { hash_names(src.asExpr().(StringLiteral).getValue()) } predicate isSink(DataFlow::Node sink) { - exists(MessageDigestGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) + exists(HashAlgorithmValueConsumer consumer | sink = consumer.getInputNode()) } } @@ -719,7 +808,7 @@ module JCAModel { DataFlow::Global; class KnownHashAlgorithm extends Crypto::HashAlgorithmInstance instanceof StringLiteral { - MessageDigestAlgorithmValueConsumer consumer; + HashAlgorithmValueConsumer consumer; KnownHashAlgorithm() { hash_names(this.getValue()) and @@ -727,23 +816,25 @@ module JCAModel { consumer.getInputNode()) } - MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer } + HashAlgorithmValueConsumer getConsumer() { result = consumer } override string getRawHashAlgorithmName() { result = this.(StringLiteral).getValue() } override Crypto::THashType getHashFamily() { - result = hash_name_to_hash_type(this.getRawHashAlgorithmName(), _) + result = hash_name_to_type_known(this.getRawHashAlgorithmName(), _) } override int getDigestLength() { - exists(hash_name_to_hash_type(this.getRawHashAlgorithmName(), result)) + exists(hash_name_to_type_known(this.getRawHashAlgorithmName(), result)) } } - class MessageDigestAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + class MessageDigestGetInstanceAlgorithmValueConsumer extends HashAlgorithmValueConsumer { MessageDigestGetInstanceCall call; - MessageDigestAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + MessageDigestGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + MessageDigestGetInstanceCall getInstantiationCall() { result = call } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } @@ -794,228 +885,244 @@ module JCAModel { } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - exists(MessageDigestGetInstanceCall call | - call.getDigestCall() = this and result = call.getAlgorithmArg() + exists(MessageDigestGetInstanceCall getInstanceCall | + getInstanceCall.getDigestCall() = this and + getInstanceCall = + result.(MessageDigestGetInstanceAlgorithmValueConsumer).getInstantiationCall() ) } } - class KeyGeneratorCallAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - KeyGeneratorGetInstanceCall call; + /* + * Key Generation + */ - KeyGeneratorCallAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + module KeyGeneratorFlowAnalysisImpl = + GetInstanceInitUseFlowAnalysis; - override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + module KeyGeneratorFlow = KeyGeneratorFlowAnalysisImpl::GetInstanceToInitToUseFlow; - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this + abstract class KeyGeneratorParameterSpecClassInstanceExpr extends ClassInstanceExpr { + KeyGeneratorInitCall getAnInitCallUse() { + exists(DataFlow::Node sink | + KeyGeneratorParameterSpecToInitializeFlow::flow(DataFlow::exprNode(this), sink) and + result.getAlgorithmParameterSpecArg() = sink.asExpr() + ) } } - // flow from instance created by getInstance to generateKey - module KeyGeneratorGetInstanceToGenerateConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { - exists(KeyGeneratorGetInstanceCall call | src.asExpr() = call) + class DHGenParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { + DHGenParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") } - predicate isSink(DataFlow::Node sink) { - exists(KeyGeneratorGenerateCall call | sink.asExpr() = call.(MethodCall).getQualifier()) - } - } + Expr getPrimeSizeArg() { result = this.getArgument(0) } - module KeyGeneratorGetInstanceToGenerateFlow = - DataFlow::Global; + Expr getExponentSizeArg() { result = this.getArgument(1) } + } - class KeyGeneratorGetInstanceCall extends MethodCall { - KeyGeneratorGetInstanceCall() { - this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance") - or - this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance") + class DSAParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { + DSAParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("java.security.spec", "DSAParameterSpec") } - Expr getAlgorithmArg() { result = super.getArgument(0) } + Expr getPArg() { result = this.getArgument(0) } - predicate flowsToKeyGenerateCallQualifier(KeyGeneratorGenerateCall sink) { - KeyGeneratorGetInstanceToGenerateFlow::flow(DataFlow::exprNode(this), - DataFlow::exprNode(sink.(MethodCall).getQualifier())) - } - } + Expr getQArg() { result = this.getArgument(1) } - class KeyGeneratorGenerateCall extends Crypto::KeyGenerationOperationInstance instanceof MethodCall - { - Crypto::KeyArtifactType type; + Expr getSeedLenArg() { result = this.getArgument(2) } + } - KeyGeneratorGenerateCall() { - this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "generateKey") and - type instanceof Crypto::TSymmetricKeyType - or - this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "generateKeyPair") and - type instanceof Crypto::TAsymmetricKeyType + class ECGenParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { + ECGenParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("java.security.spec", "ECGenParameterSpec") } - override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result.asExpr() = this } - - override Crypto::KeyArtifactType getOutputKeyType() { result = type } + Expr getCurveNameArg() { result = this.getArgument(0) } - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - exists(KeyGeneratorGetInstanceCall getInstance | - getInstance.flowsToKeyGenerateCallQualifier(this) and result = getInstance.getAlgorithmArg() - ) - } + Expr getRandomNumberGeneratorArg() { result = this.getArgument(1) } + } - Crypto::AlgorithmInstance getAKnownAlgorithm() { - result = this.getAnAlgorithmValueConsumer().getAKnownAlgorithmSource() + class RSAGenParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { + RSAGenParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("java.security.spec", "RSAGenParameterSpec") } - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } - - override string getKeySizeFixed() { none() } + Expr getKeySizeArg() { result = this.getArgument(0) } } - module MACKnownAlgorithmToConsumerConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { mac_names(src.asExpr().(StringLiteral).getValue()) } + private module KeyGeneratorParameterSpecToInitializeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + src.asExpr() instanceof KeyGeneratorParameterSpecClassInstanceExpr + } predicate isSink(DataFlow::Node sink) { - exists(MACGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) + exists(KeyGeneratorInitCall c | + c.getKeyType() = Crypto::TAsymmetricKeyType() and + c.getArgument(0) = sink.asExpr() + ) } } - module MACKnownAlgorithmToConsumerFlow = DataFlow::Global; - - module MACGetInstanceToMACOperationFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MACGetInstanceCall } + module KeyGeneratorParameterSpecToInitializeFlow = + DataFlow::Global; - predicate isSink(DataFlow::Node sink) { - exists(MACOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier()) or - exists(MACInitCall call | sink.asExpr() = call.(MethodCall).getQualifier()) + class ECGenParameterSpecClassInstanceExpr extends KeyGeneratorParameterSpecClassInstanceExpr { + ECGenParameterSpecClassInstanceExpr() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("java.security.spec", "ECGenParameterSpec") } + + Expr getAlgorithmArg() { result = this.getArgument(0) } } - module MACGetInstanceToMACOperationFlow = - DataFlow::Global; + class KeyGenerationAlgorithmValueConsumer extends CipherAlgorithmValueConsumer, + KeyAgreementAlgorithmValueConsumer, EllipticCurveAlgorithmValueConsumer instanceof Expr + { + KeyGeneratorGetInstanceCall instantiationCall; - module MACInitCallToMACOperationFlowConfig implements DataFlow::ConfigSig { - // TODO: use flow state with one config - predicate isSource(DataFlow::Node src) { - exists(MACInitCall init | src.asExpr() = init.getQualifier()) + KeyGenerationAlgorithmValueConsumer() { + // This is only an algorithm value consumer if it accepts a spec rather than a key size (integral) + this = instantiationCall.getAlgorithmArg() and not super.getType() instanceof IntegralType + or + // However, for general elliptic curves, getInstance("EC") is used + // and java.security.spec.ECGenParameterSpec("") is what sets the specific curve. + // The result of ECGenParameterSpec is passed to KeyPairGenerator.initialize + // If the curve is not specified, the default is used. + // We would trace the use of this inside a KeyPairGenerator.initialize + exists(KeyGeneratorInitCall initCall, ECGenParameterSpecClassInstanceExpr spec | + KeyGeneratorFlow::flow(DataFlow::exprNode(instantiationCall), + DataFlow::exprNode(initCall.getQualifier())) and + spec.getAnInitCallUse() = initCall and + spec.getAlgorithmArg() = this + ) } - predicate isSink(DataFlow::Node sink) { - exists(MACOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier()) - } - } + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } - module MACInitCallToMACOperationFlow = DataFlow::Global; + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this or + result.(KeyAgreementStringLiteralAlgorithmInstance).getConsumer() = this or + result.(EllipticCurveStringLiteralInstance).getConsumer() = this + } - class KnownMACAlgorithm extends Crypto::MACAlgorithmInstance instanceof StringLiteral { - MACGetInstanceAlgorithmValueConsumer consumer; + KeyGeneratorGetInstanceCall getInstantiationCall() { result = instantiationCall } + } - KnownMACAlgorithm() { - mac_names(this.getValue()) and - MACKnownAlgorithmToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + // TODO: Link getAlgorithm from KeyPairGenerator to algorithm instances or AVCs? High priority. + class KeyGeneratorGetInstanceCall extends MethodCall { + KeyGeneratorGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance") + or + this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance") } - MACGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer } + Expr getAlgorithmArg() { result = super.getArgument(0) } + } - override string getRawMACAlgorithmName() { result = super.getValue() } + class KeyGeneratorInitCall extends MethodCall { + Crypto::TKeyArtifactType keyType; - override Crypto::TMACType getMACType() { - if mac_name_to_mac_type_known(_, super.getValue()) - then mac_name_to_mac_type_known(result, super.getValue()) - else result instanceof Crypto::TOtherMACType + KeyGeneratorInitCall() { + this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "initialize") and + keyType = Crypto::TAsymmetricKeyType() + or + this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", ["init", "initialize"]) and + keyType = Crypto::TSymmetricKeyType() } - } - class MACGetInstanceCall extends MethodCall { - MACGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Mac", "getInstance") } + Crypto::TKeyArtifactType getKeyType() { result = keyType } - Expr getAlgorithmArg() { result = this.getArgument(0) } + Expr getAlgorithmParameterSpecArg() { + result = this.getArgument(0) and + this.getMethod().getParameterType(0).hasName("AlgorithmParameterSpec") + } - MACOperationCall getOperation() { - MACGetInstanceToMACOperationFlow::flow(DataFlow::exprNode(this), - DataFlow::exprNode(result.(MethodCall).getQualifier())) + Expr getKeySizeArg() { + result = this.getArgument(0) and + this.getMethod().getParameterType(0) instanceof IntegralType } - MACInitCall getInitCall() { - MACGetInstanceToMACOperationFlow::flow(DataFlow::exprNode(this), - DataFlow::exprNode(result.getQualifier())) + Expr getRandomnessSourceArg() { + exists(int index | + this.getMethod().getParameterType(index).hasName("SecureRandom") and + result = this.getArgument(index) + ) } } - class MACInitCall extends MethodCall { - MACInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Mac", "init") } - - Expr getKeyArg() { - result = this.getArgument(0) and this.getMethod().getParameterType(0).hasName("Key") - } + class KeyGeneratorGenerateCall extends Crypto::KeyGenerationOperationInstance instanceof MethodCall + { + Crypto::KeyArtifactType type; - MACOperationCall getOperation() { - MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this.getQualifier()), - DataFlow::exprNode(result.(MethodCall).getQualifier())) + KeyGeneratorGenerateCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "generateKey") and + type instanceof Crypto::TSymmetricKeyType + or + this.getCallee() + .hasQualifiedName("java.security", "KeyPairGenerator", ["generateKeyPair", "genKeyPair"]) and + type instanceof Crypto::TAsymmetricKeyType } - } - class MACGetInstanceAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - MACGetInstanceCall call; + predicate isIntermediate() { none() } - MACGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result.asExpr() = this } - override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + override Crypto::KeyArtifactType getOutputKeyType() { result = type } - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - exists(KnownMACAlgorithm l | l.getConsumer() = this and result = l) + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + KeyGeneratorFlowAnalysisImpl::getInstantiationFromUse(this, _, _) = + result.(KeyGenerationAlgorithmValueConsumer).getInstantiationCall() } - } - class MACOperationCall extends Crypto::MACOperationInstance instanceof MethodCall { - Expr output; - - MACOperationCall() { - super.getMethod().getDeclaringType().hasQualifiedName("javax.crypto", "Mac") and - ( - super.getMethod().hasStringSignature(["doFinal()", "doFinal(byte[])"]) and this = output - or - super.getMethod().hasStringSignature("doFinal(byte[], int)") and - this.getArgument(0) = output - ) + Crypto::AlgorithmInstance getAKnownAlgorithm() { + result = this.getAnAlgorithmValueConsumer().getAKnownAlgorithmSource() } - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - exists(MACGetInstanceCall instantiation | - instantiation.getOperation() = this and result = instantiation.getAlgorithmArg() - ) + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + KeyGeneratorFlowAnalysisImpl::getInitFromUse(this, _, _).getKeySizeArg() = result.asExpr() } - override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { - exists(MACGetInstanceCall instantiation, MACInitCall initCall | - instantiation.getOperation() = this and - initCall.getOperation() = this and - instantiation.getInitCall() = initCall and - result.asExpr() = initCall.getKeyArg() - ) - } + override string getKeySizeFixed() { none() } + } - override Crypto::ConsumerInputDataFlowNode getMessageConsumer() { - result.asExpr() = super.getArgument(0) and - super.getMethod().getParameterType(0).hasName("byte[]") + /* + * Key Derivation Functions (KDFs) + */ + + class KeySpecInstantiation extends ClassInstanceExpr { + KeySpecInstantiation() { + this.getConstructedType() + .hasQualifiedName("javax.crypto.spec", + ["PBEKeySpec", "SecretKeySpec", "PBEKeySpec", "DESedeKeySpec"]) } + + Expr getPasswordArg() { result = this.getArgument(0) } } - module SecretKeyFactoryGetInstanceToGenerateSecretFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { - exists(SecretKeyFactoryGetInstanceCall call | src.asExpr() = call) - } + class PBEKeySpecInstantiation extends KeySpecInstantiation { + PBEKeySpecInstantiation() { this.getConstructedType().hasName("PBEKeySpec") } - predicate isSink(DataFlow::Node sink) { - exists(SecretKeyFactoryGenerateSecretCall call | - sink.asExpr() = call.(MethodCall).getQualifier() - ) - } + Expr getSaltArg() { result = this.getArgument(1) } + + Expr getIterationCountArg() { result = this.getArgument(2) } + + Expr getKeyLengthArg() { result = this.getArgument(3) } } - module PBEKeySpecInstantiationToGenerateSecretFlowConfig implements DataFlow::ConfigSig { + module KeySpecInstantiationToGenerateSecretFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { - exists(PBEKeySpecInstantiation call | src.asExpr() = call) + exists(KeySpecInstantiation call | src.asExpr() = call) } predicate isSink(DataFlow::Node sink) { @@ -1031,28 +1138,21 @@ module JCAModel { } } - module SecretKeyFactoryGetInstanceToGenerateSecretFlow = - DataFlow::Global; - - module PBEKeySpecInstantiationToGenerateSecretFlow = - DataFlow::Global; + module KeySpecInstantiationToGenerateSecretFlow = + DataFlow::Global; module KDFAlgorithmStringToGetInstanceFlow = DataFlow::Global; - class PBEKeySpecInstantiation extends ClassInstanceExpr { - PBEKeySpecInstantiation() { - this.getConstructedType().hasQualifiedName("javax.crypto.spec", "PBEKeySpec") - } - - Expr getPasswordArg() { result = this.getArgument(0) } + class DUMMY_UNUSED_METHODCALL extends MethodCall { + DUMMY_UNUSED_METHODCALL() { none() } + } - Expr getSaltArg() { result = this.getArgument(1) } + module SecretKeyFactoryFlowAnalysisImpl = + GetInstanceInitUseFlowAnalysis; - Expr getIterationCountArg() { result = this.getArgument(2) } - - Expr getKeyLengthArg() { result = this.getArgument(3) } - } + module SecretKeyFactoryFlow = SecretKeyFactoryFlowAnalysisImpl::GetInstanceToInitToUseFlow; class SecretKeyFactoryGetInstanceCall extends MethodCall { SecretKeyFactoryGetInstanceCall() { @@ -1060,11 +1160,16 @@ module JCAModel { } Expr getAlgorithmArg() { result = this.getArgument(0) } + } - SecretKeyFactoryGenerateSecretCall getOperation() { - SecretKeyFactoryGetInstanceToGenerateSecretFlow::flow(DataFlow::exprNode(this), - DataFlow::exprNode(result.(MethodCall).getQualifier())) + class SecretKeyFactoryGenerateSecretCall extends MethodCall { + SecretKeyFactoryGenerateSecretCall() { + this.getCallee().hasQualifiedName("javax.crypto", "SecretKeyFactory", "generateSecret") } + + Expr getKeySpecArg() { result = this.getArgument(0) } + + predicate isIntermediate() { none() } } class KDFAlgorithmStringLiteral extends Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral @@ -1096,11 +1201,11 @@ module JCAModel { override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } override Crypto::THashType getHashFamily() { - result = hash_name_to_hash_type(this.getRawHashAlgorithmName(), _) + result = hash_name_to_type_known(this.getRawHashAlgorithmName(), _) } override int getDigestLength() { - exists(hash_name_to_hash_type(this.getRawHashAlgorithmName(), result)) + exists(hash_name_to_type_known(this.getRawHashAlgorithmName(), result)) } override string getRawMACAlgorithmName() { @@ -1133,51 +1238,41 @@ module JCAModel { SecretKeyFactoryGetInstanceCall getInstantiation() { result = call } } - class SecretKeyFactoryGenerateSecretCall extends Crypto::KeyDerivationOperationInstance instanceof MethodCall + class KeyDerivationOperationCall extends Crypto::KeyDerivationOperationInstance instanceof SecretKeyFactoryGenerateSecretCall { - SecretKeyFactoryGenerateSecretCall() { - super.getCallee().hasQualifiedName("javax.crypto", "SecretKeyFactory", "generateSecret") - } - - Expr getKeySpecArg() { - result = super.getArgument(0) and - super.getMethod().getParameterType(0).hasName("KeySpec") - } + KeyDerivationOperationCall() { not super.isIntermediate() } - PBEKeySpecInstantiation getInstantiation() { - PBEKeySpecInstantiationToGenerateSecretFlow::flow(DataFlow::exprNode(result), - DataFlow::exprNode(this.getKeySpecArg())) + KeySpecInstantiation getKeySpecInstantiation() { + KeySpecInstantiationToGenerateSecretFlow::flow(DataFlow::exprNode(result), + DataFlow::exprNode(super.getKeySpecArg())) } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - exists(SecretKeyFactoryGetInstanceCall instantiation | - instantiation.getOperation() = this and result = instantiation.getAlgorithmArg() - ) + result.(SecretKeyFactoryKDFAlgorithmValueConsumer).getInstantiation() = + SecretKeyFactoryFlowAnalysisImpl::getInstantiationFromUse(this, _, _) } override Crypto::ConsumerInputDataFlowNode getSaltConsumer() { - result.asExpr() = this.getInstantiation().getSaltArg() + result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getSaltArg() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result.asExpr() = this.getInstantiation().getPasswordArg() + result.asExpr() = this.getKeySpecInstantiation().getPasswordArg() } override Crypto::ConsumerInputDataFlowNode getIterationCountConsumer() { - result.asExpr() = this.getInstantiation().getIterationCountArg() + result.asExpr() = + this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getIterationCountArg() } - override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { - result.asExpr() = this and - super.getMethod().getReturnType().hasName("SecretKey") - } + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result.asExpr() = this } override Crypto::ConsumerInputDataFlowNode getOutputKeySizeConsumer() { - result.asExpr() = this.getInstantiation().getKeyLengthArg() + result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getKeyLengthArg() } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { - result.asExpr() = this.getInstantiation().getKeyLengthArg() + result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getKeyLengthArg() } override string getKeySizeFixed() { none() } @@ -1187,23 +1282,31 @@ module JCAModel { override string getIterationCountFixed() { none() } } + /* + * Key agreement + */ + + module KeyAgreementFlowAnalysisImpl = + GetInstanceInitUseFlowAnalysis; + class KeyAgreementStringLiteral extends StringLiteral { KeyAgreementStringLiteral() { key_agreement_names(this.getValue()) } } /** - * Data-flow configuration modelling flow from a key agreement literal to a value consumer argument. + * Data-flow configuration modelling flow from a key agreement string literal to a key agreement algorithm consumer. */ - private module KeyAgreementAlgorithmStringToFetchConfig implements DataFlow::ConfigSig { + private module KeyAgreementAlgorithmStringToConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeyAgreementStringLiteral } predicate isSink(DataFlow::Node sink) { - exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode()) + sink = any(KeyAgreementAlgorithmValueConsumer consumer).getInputNode() } } - module KeyAgreementAlgorithmStringToFetchFlow = - TaintTracking::Global; + module KeyAgreementAlgorithmStringToConsumerFlow = + TaintTracking::Global; class KeyAgreementInitCall extends MethodCall { KeyAgreementInitCall() { @@ -1213,24 +1316,6 @@ module JCAModel { Expr getServerKeyArg() { result = this.getArgument(0) } } - private module KeyAgreementInitQualifierToSecretGenQualifierConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { - exists(KeyAgreementInitCall init | src.asExpr() = init.getQualifier()) - } - - predicate isSink(DataFlow::Node sink) { - exists(KeyAgreementGenerateSecretCall c | sink.asExpr() = c.getQualifier()) - } - - /** - * Barrier if we go into another init, assume the second init overwrites the first - */ - predicate isBarrierIn(DataFlow::Node node) { isSource(node) } - } - - module KeyAgreementInitQualifierToSecretGenQualifierFlow = - DataFlow::Global; - class KeyAgreementGetInstanceCall extends MethodCall { KeyAgreementGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "getInstance") @@ -1239,30 +1324,30 @@ module JCAModel { Expr getAlgorithmArg() { result = super.getArgument(0) } } - class KeyAgreementAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + private class KeyAgreementGetInstanceAlgorithmArgValueConsumer extends KeyAgreementAlgorithmValueConsumer + { KeyAgreementGetInstanceCall call; - KeyAgreementAlgorithmValueConsumer() { this = call.getAlgorithmArg() } - - DataFlow::Node getResultNode() { result.asExpr() = call } + KeyAgreementGetInstanceAlgorithmArgValueConsumer() { this = call.getAlgorithmArg() } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result.(KeyAgreementStringLiteralAlgorithmInstance).getConsumer() = this } + + KeyAgreementGetInstanceCall getInstantiationCall() { result = call } } class KeyAgreementStringLiteralAlgorithmInstance extends Crypto::KeyAgreementAlgorithmInstance instanceof KeyAgreementStringLiteral { - Crypto::AlgorithmValueConsumer consumer; + KeyAgreementAlgorithmValueConsumer consumer; KeyAgreementStringLiteralAlgorithmInstance() { - KeyAgreementAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + KeyAgreementAlgorithmStringToConsumerFlow::flow(DataFlow::exprNode(this), + consumer.getInputNode()) } - Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } - override string getRawKeyAgreementAlgorithmName() { result = super.getValue() } override Crypto::TKeyAgreementType getKeyAgreementType() { @@ -1270,48 +1355,231 @@ module JCAModel { then key_agreement_name_to_type_known(result, super.getValue()) else result = Crypto::UnknownKeyAgreementType() } + + KeyAgreementAlgorithmValueConsumer getConsumer() { result = consumer } + } + + class KeyAgreementCall extends MethodCall { + KeyAgreementCall() { + this.getCallee() + .hasQualifiedName("javax.crypto", "KeyAgreement", ["generateSecret", "doPhase"]) + } + + predicate isIntermediate() { this.getCallee().getName() = "doPhase" } + + DataFlow::Node getOutputNode() { + result.asExpr() = this and + not this.isIntermediate() + } + + Expr getPeerKeyArg() { + this.isIntermediate() and + result = this.getArgument(0) and + this.getCallee().getName() = "doPhase" + } + } + + class KeyAgreementSecretGenerationOperationInstance extends Crypto::KeyAgreementSecretGenerationOperationInstance instanceof KeyAgreementCall + { + KeyAgreementSecretGenerationOperationInstance() { + // exclude doPhase (only include generateSecret) + not super.isIntermediate() + } + + override Crypto::ConsumerInputDataFlowNode getServerKeyConsumer() { + result.asExpr() = KeyAgreementFlowAnalysisImpl::getInitFromUse(this, _, _).getServerKeyArg() + } + + override Crypto::ConsumerInputDataFlowNode getPeerKeyConsumer() { + result.asExpr() = + KeyAgreementFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getPeerKeyArg() + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result.(KeyAgreementGetInstanceAlgorithmArgValueConsumer).getInstantiationCall() = + KeyAgreementFlowAnalysisImpl::getInstantiationFromUse(this, _, _) + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result.asExpr() = this } } - class KeyAgreementGenerateSecretCall extends MethodCall { - KeyAgreementGenerateSecretCall() { - this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "generateSecret") + /* + * MACs + */ + + module MACKnownAlgorithmToConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { mac_names(src.asExpr().(StringLiteral).getValue()) } + + predicate isSink(DataFlow::Node sink) { + exists(MACGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) } + } + + module MACKnownAlgorithmToConsumerFlow = DataFlow::Global; - KeyAgreementInitCall getKeyAgreementInitCall() { - KeyAgreementInitQualifierToSecretGenQualifierFlow::flow(DataFlow::exprNode(result - .getQualifier()), DataFlow::exprNode(this.getQualifier())) + module MACGetInstanceToMACOperationFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MACGetInstanceCall } + + predicate isSink(DataFlow::Node sink) { + exists(MACOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier()) or + exists(MACInitCall call | sink.asExpr() = call.(MethodCall).getQualifier()) } } - private module KeyAgreementAVCToInitQualifierConfig implements DataFlow::ConfigSig { + module MACGetInstanceToMACOperationFlow = + DataFlow::Global; + + module MACInitCallToMACOperationFlowConfig implements DataFlow::ConfigSig { + // TODO: use flow state with one config predicate isSource(DataFlow::Node src) { - exists(KeyAgreementAlgorithmValueConsumer consumer | consumer.getResultNode() = src) + exists(MACInitCall init | src.asExpr() = init.getQualifier()) + } + + predicate isSink(DataFlow::Node sink) { + exists(MACOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier()) } + } + + module MACInitCallToMACOperationFlow = DataFlow::Global; + + class KnownMACAlgorithm extends Crypto::MACAlgorithmInstance instanceof StringLiteral { + MACGetInstanceAlgorithmValueConsumer consumer; + + KnownMACAlgorithm() { + mac_names(this.getValue()) and + MACKnownAlgorithmToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + } + + MACGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer } + + override string getRawMACAlgorithmName() { result = super.getValue() } + + override Crypto::TMACType getMACType() { + if mac_name_to_mac_type_known(_, super.getValue()) + then mac_name_to_mac_type_known(result, super.getValue()) + else result instanceof Crypto::TOtherMACType + } + } + + class MACGetInstanceCall extends MethodCall { + MACGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Mac", "getInstance") } + + Expr getAlgorithmArg() { result = this.getArgument(0) } + + MACOperationCall getOperation() { + MACGetInstanceToMACOperationFlow::flow(DataFlow::exprNode(this), + DataFlow::exprNode(result.(MethodCall).getQualifier())) + } + + MACInitCall getInitCall() { + MACGetInstanceToMACOperationFlow::flow(DataFlow::exprNode(this), + DataFlow::exprNode(result.getQualifier())) + } + } + + class MACInitCall extends MethodCall { + MACInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Mac", "init") } + + Expr getKeyArg() { + result = this.getArgument(0) and this.getMethod().getParameterType(0).hasName("Key") + } + + MACOperationCall getOperation() { + MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this.getQualifier()), + DataFlow::exprNode(result.(MethodCall).getQualifier())) + } + } + + class MACGetInstanceAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + MACGetInstanceCall call; + + MACGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(KnownMACAlgorithm l | l.getConsumer() = this and result = l) + } + } + + class MACOperationCall extends Crypto::MACOperationInstance instanceof MethodCall { + Expr output; + + MACOperationCall() { + super.getMethod().getDeclaringType().hasQualifiedName("javax.crypto", "Mac") and + ( + super.getMethod().hasStringSignature(["doFinal()", "doFinal(byte[])"]) and this = output + or + super.getMethod().hasStringSignature("doFinal(byte[], int)") and + this.getArgument(0) = output + ) + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + exists(MACGetInstanceCall instantiation | + instantiation.getOperation() = this and result = instantiation.getAlgorithmArg() + ) + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + exists(MACGetInstanceCall instantiation, MACInitCall initCall | + instantiation.getOperation() = this and + initCall.getOperation() = this and + instantiation.getInitCall() = initCall and + result.asExpr() = initCall.getKeyArg() + ) + } + + override Crypto::ConsumerInputDataFlowNode getMessageConsumer() { + result.asExpr() = super.getArgument(0) and + super.getMethod().getParameterType(0).hasName("byte[]") + } + } + + /* + * Elliptic Curves (EC) + */ + + module EllipticCurveStringToConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof EllipticCurveStringLiteral } predicate isSink(DataFlow::Node sink) { - exists(KeyAgreementInitCall init | sink.asExpr() = init.getQualifier()) + exists(EllipticCurveAlgorithmValueConsumer consumer | sink = consumer.getInputNode()) } } - module KeyAgreementAVCToInitQualifierFlow = - DataFlow::Global; + module EllipticCurveStringToConsumerFlow = DataFlow::Global; - class KeyAgreementSecretGenerationOperationInstance extends Crypto::KeyAgreementSecretGenerationOperationInstance instanceof KeyAgreementGenerateSecretCall + class EllipticCurveStringLiteral extends StringLiteral { + EllipticCurveStringLiteral() { elliptic_curve_names(this.getValue()) } + } + + class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance instanceof EllipticCurveStringLiteral { - override Crypto::ConsumerInputDataFlowNode getServerKeyConsumer() { - this.(KeyAgreementGenerateSecretCall).getKeyAgreementInitCall().getServerKeyArg() = - result.asExpr() + EllipticCurveAlgorithmValueConsumer consumer; + + EllipticCurveStringLiteralInstance() { + EllipticCurveStringToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } - override Crypto::ConsumerInputDataFlowNode getPeerKeyConsumer() { - none() //TODO + override string getRawEllipticCurveName() { result = super.getValue() } + + override Crypto::TEllipticCurveType getEllipticCurveType() { + if Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), _, _) + then + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), _, result) + else result = Crypto::OtherEllipticCurveType() } - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - none() // TODO: key agreeement has its own algorithm consumer, separate from the key - // TODO: the char pred must trace from the consumer to here, - // in theory, along that path we would get the init and doPhase, but can I just get those - // separately avoiding a complicated config state for dataflow? + override string getKeySize() { + exists(int keySize | + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), keySize, + _) + | + result = keySize.toString() + ) } + + EllipticCurveAlgorithmValueConsumer getConsumer() { result = consumer } } } diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 8027ef33df7b..1e12f6f1ac8e 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -100,7 +100,7 @@ class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceo // where typical algorithms are specified, but EC specifically means set up a // default curve container, that will later be specified explicitly (or if not a default) // curve is used. - this = any(Literal l | l.getValue() != "EC") + this.getValue() != "EC" } override DataFlow::Node getOutputNode() { result.asExpr() = this } @@ -194,6 +194,12 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { node1.(AdditionalFlowInputStep).getOutput() = node2 + or + exists(MethodCall m | + m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and + node1.asExpr() = m.getQualifier() and + node2.asExpr() = m + ) } } diff --git a/java/ql/src/experimental/Quantum/TestCipher.ql b/java/ql/src/experimental/Quantum/TestCipher.ql index d56e16804408..6779a64f608e 100644 --- a/java/ql/src/experimental/Quantum/TestCipher.ql +++ b/java/ql/src/experimental/Quantum/TestCipher.ql @@ -5,7 +5,7 @@ import experimental.Quantum.Language from - Crypto::CipherOperationNode op, Crypto::KeyOperationAlgorithmNode a, + Crypto::KeyOperationNode op, Crypto::KeyOperationAlgorithmNode a, Crypto::ModeOfOperationAlgorithmNode m, Crypto::PaddingAlgorithmNode p, Crypto::NonceArtifactNode nonce, Crypto::KeyArtifactNode k where @@ -15,4 +15,4 @@ where nonce = op.getANonce() and k = op.getAKey() select op, op.getKeyOperationSubtype(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), p, - p.getRawAlgorithmName(), nonce, k, k.getSourceElement() + p.getRawAlgorithmName(), nonce, k diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 782c53e1043f..510f334bc626 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -438,6 +438,19 @@ module CryptographyBase Input> { override DataFlowNode getOutputNode() { result = creator.getOutputArtifact() } } + /** + * An artifact representing the shared secret generated by key agreement operations. + */ + final class KeyAgreementSharedSecretOutputArtifactInstance extends OutputArtifactInstance { + KeyAgreementSecretGenerationOperationInstance creator; + + KeyAgreementSharedSecretOutputArtifactInstance() { + Input::dfn_to_element(creator.getOutputArtifact()) = this + } + + override DataFlowNode getOutputNode() { result = creator.getOutputArtifact() } + } + // Artifacts that may be outputs or inputs newtype TKeyArtifactType = TSymmetricKeyType() or @@ -871,7 +884,7 @@ module CryptographyBase Input> { */ abstract string getRawEllipticCurveName(); - abstract TEllipticCurveType getEllipticCurveFamily(); + abstract TEllipticCurveType getEllipticCurveType(); abstract string getKeySize(); @@ -1043,6 +1056,13 @@ module CryptographyBase Input> { * from the peer (the other party in the key agreement). */ abstract ConsumerInputDataFlowNode getPeerKeyConsumer(); + + /** + * The output artifact produced by the key agreement operation. + * This artifact represents the shared secret generated during + * the key agreement process. + */ + abstract ArtifactOutputDataFlowNode getOutputArtifact(); } private signature class AlgorithmInstanceType instanceof AlgorithmInstance; @@ -1066,14 +1086,17 @@ module CryptographyBase Input> { this instanceof Alg or // Or an AlgorithmValueConsumer with unknown sources and no known sources + not this instanceof Alg and isCandidateAVC(this) and not exists(this.(AlgorithmValueConsumer).getAKnownAlgorithmSource()) and - exists(this.(AlgorithmValueConsumer).getAGenericSource()) + exists(GenericSourceInstance src | + src = this.(AlgorithmValueConsumer).getAGenericSource() and src != this + ) } Alg asAlg() { result = this } - AlgorithmValueConsumer asAVC() { result = this } + AlgorithmValueConsumer asAVC() { result = this and not this instanceof Alg } } } @@ -1098,6 +1121,13 @@ module CryptographyBase Input> { private predicate isEllipticCurveAVC(AlgorithmValueConsumer avc) { exists(ECDHKeyAgreementAlgorithmInstance alg | avc = alg.getEllipticCurveAlgorithmValueConsumer() + ) or + exists(KeyGenerationOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) + } + + private predicate isKeyAgreementAVC(AlgorithmValueConsumer avc) { + exists(KeyAgreementSecretGenerationOperationInstance op | + op.getAnAlgorithmValueConsumer() = avc ) } @@ -1116,10 +1146,14 @@ module CryptographyBase Input> { final private class EllipticCurveInstanceOrValueConsumer = AlgorithmInstanceOrValueConsumer::Union; + final private class KeyAgreementAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; + private newtype TNode = // Output artifacts (data that is not an operation or algorithm, e.g., a key) TDigest(DigestArtifactInstance e) or TKey(KeyArtifactInstance e) or + TSharedSecret(KeyAgreementSharedSecretOutputArtifactInstance e) or // Input artifacts (synthetic nodes, used to differentiate input as entities) TNonceInput(NonceArtifactConsumer e) or TMessageInput(MessageArtifactConsumer e) or @@ -1140,12 +1174,13 @@ module CryptographyBase Input> { // All other operations THashOperation(HashOperationInstance e) or TMACOperation(MACOperationInstance e) or + TKeyAgreementOperation(KeyAgreementSecretGenerationOperationInstance e) or // All other algorithms TEllipticCurve(EllipticCurveInstanceOrValueConsumer e) or THashAlgorithm(HashAlgorithmInstanceOrValueConsumer e) or TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstanceOrValueConsumer e) or TMACAlgorithm(MACAlgorithmInstanceOrValueConsumer e) or - TKeyAgreementAlgorithm(KeyAgreementAlgorithmInstance e) or + TKeyAgreementAlgorithm(KeyAgreementAlgorithmInstanceOrValueConsumer e) or // Generic source nodes, i.e., sources of data that are not resolvable to a specific known asset. TGenericSourceNode(GenericSourceInstance e) { // An element modelled as a `GenericSourceInstance` can also be modelled as a `KnownElement` @@ -1492,6 +1527,19 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } } + /** + * A shared secret artifact produced by a key agreement operation. + */ + final class SharedSecretArtifactNode extends ArtifactNode, TSharedSecret { + KeyAgreementSharedSecretOutputArtifactInstance instance; + + SharedSecretArtifactNode() { this = TSharedSecret(instance) } + + final override string getInternalType() { result = "SharedSecret" } + + override LocatableElement asElement() { result = instance } + } + abstract class KeyCreationOperationNode extends OperationNode, TKeyCreationOperation { KeyCreationOperationInstance instance; @@ -1599,6 +1647,48 @@ module CryptographyBase Input> { } } + class KeyAgreementOperationNode extends OperationNode, TKeyAgreementOperation { + KeyAgreementSecretGenerationOperationInstance instance; + + KeyAgreementOperationNode() { this = TKeyAgreementOperation(instance) } + + final override string getInternalType() { result = "KeyAgreementOperation" } + + override LocatableElement asElement() { result = instance } + + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { + node instanceof KeyAgreementAlgorithmNode + } + + SharedSecretArtifactNode getOutput() { + result.asElement() = instance.getOutputArtifact().getArtifact() + } + + override NodeBase getChild(string key) { + result = super.getChild(key) + or + // [ALWAYS_KNOWN] + key = "Output" and + result = this.getOutput() + } + } + + class KeyAgreementAlgorithmNode extends AlgorithmNode, TKeyAgreementAlgorithm { + KeyAgreementAlgorithmInstanceOrValueConsumer instance; + + KeyAgreementAlgorithmNode() { this = TKeyAgreementAlgorithm(instance) } + + final override string getInternalType() { result = "KeyAgreementAlgorithm" } + + override LocatableElement asElement() { result = instance } + + final override string getRawAlgorithmName() { + result = instance.asAlg().getRawKeyAgreementAlgorithmName() + } + + override string getAlgorithmName() { result = this.getRawAlgorithmName() } // TODO: standardize? + } + class KeyGenerationOperationNode extends KeyCreationOperationNode { KeyGenerationOperationInstance keyGenInstance; @@ -2296,16 +2386,17 @@ module CryptographyBase Input> { or curveName = "CURVE25519" and keySize = 255 and curveFamily = CURVE25519() or - curveName = "X25519" and keySize = 255 and curveFamily = CURVE25519() - or - curveName = "ED25519" and keySize = 255 and curveFamily = CURVE25519() - or - curveName = "CURVE448" and keySize = 448 and curveFamily = CURVE448() - or - curveName = "ED448" and keySize = 448 and curveFamily = CURVE448() - or - curveName = "X448" and keySize = 448 and curveFamily = CURVE448() - or + // TODO: separate these into key agreement logic or sign/verify (ECDSA / ECDH) + // or + // curveName = "X25519" and keySize = 255 and curveFamily = CURVE25519() + // or + // curveName = "ED25519" and keySize = 255 and curveFamily = CURVE25519() + // or + // curveName = "ED448" and keySize = 448 and curveFamily = CURVE448() + // curveName = "CURVE448" and keySize = 448 and curveFamily = CURVE448() + // or + // or + // curveName = "X448" and keySize = 448 and curveFamily = CURVE448() curveName = "SM2" and keySize in [256, 512] and curveFamily = SM2() ) } @@ -2335,9 +2426,7 @@ module CryptographyBase Input> { override string getAlgorithmName() { result = this.getRawAlgorithmName() } - TEllipticCurveType getEllipticCurveFamily() { - result = instance.asAlg().getEllipticCurveFamily() - } + TEllipticCurveType getEllipticCurveType() { result = instance.asAlg().getEllipticCurveType() } override predicate properties(string key, string value, Location location) { super.properties(key, value, location) @@ -2347,7 +2436,7 @@ module CryptographyBase Input> { value = instance.asAlg().getKeySize() and location = this.getLocation() or - // [ONLY_KNOWN] + // [KNOWN_OR_UNKNOWN] key = "ParsedName" and value = instance.asAlg().getParsedEllipticCurveName() and location = this.getLocation() From 28ccc833464e64d3e5dbdc5fd34c7fb59bde3bbb Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 28 Apr 2025 11:33:37 -0400 Subject: [PATCH 081/122] Adding a means to distinguish asymmetric algorithms. --- shared/cryptography/codeql/cryptography/Model.qll | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 510f334bc626..1ea862b470d2 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -2122,6 +2122,12 @@ module CryptographyBase Input> { */ KeyOpAlg::Algorithm getAlgorithmType() { result = instance.asAlg().getAlgorithmType() } + predicate isAsymmetric() { + this.getAlgorithmType() instanceof KeyOpAlg::TAsymmetricCipher + or + this.getAlgorithmType() instanceof KeyOpAlg::TSignature + } + /** * Gets the mode of operation of this cipher, e.g., "GCM" or "CBC". */ @@ -2442,4 +2448,11 @@ module CryptographyBase Input> { location = this.getLocation() } } + + predicate isAsymmetricAlgorithm(AlgorithmNode node) { + node instanceof EllipticCurveNode + or + node instanceof KeyOperationAlgorithmNode and node.(KeyOperationAlgorithmNode).isAsymmetric() + // TODO: get unknown algorithms from known asymmetric operations + } } From fdd09a4dbffafa8e5e1e5ff08589f117b706980a Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 28 Apr 2025 11:34:16 -0400 Subject: [PATCH 082/122] Adding a new means for determining if there is nonce reuse. --- .../InventoryFilters/ArtifactReuse.qll | 70 +++++++++++++++++++ .../Quantum/InventoryFilters/ReusedNonce.ql | 12 ++++ .../Quantum/PossibleReusedNonce.ql | 36 ---------- 3 files changed, 82 insertions(+), 36 deletions(-) create mode 100644 java/ql/src/experimental/Quantum/InventoryFilters/ArtifactReuse.qll create mode 100644 java/ql/src/experimental/Quantum/InventoryFilters/ReusedNonce.ql delete mode 100644 java/ql/src/experimental/Quantum/PossibleReusedNonce.ql diff --git a/java/ql/src/experimental/Quantum/InventoryFilters/ArtifactReuse.qll b/java/ql/src/experimental/Quantum/InventoryFilters/ArtifactReuse.qll new file mode 100644 index 000000000000..de283f89775e --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventoryFilters/ArtifactReuse.qll @@ -0,0 +1,70 @@ +import java +import semmle.code.java.dataflow.DataFlow +import experimental.Quantum.Language + +/** + * Flow from any function that appears to return a value + * to an artifact node. + * NOTE: TODO: need to handle call by refernece for now. Need to re-evaluate (see notes below) + * Such functions may be 'wrappers' for some derived value. + */ +private module WrapperConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(Call c | + c = source.asExpr() + // not handling references yet, I think we want to flat say references are only ok + // if I know the source, otherwise, it has to be through an additional flow step, which + // we filter as a source, i.e., references are only allowed as sources only, + // no inferrece? Not sure if that would work + //or + // source.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = c.getAnArgument() + ) and + // Filter out sources that are known additional flow steps, as these are likely not the + // kind of wrapper source we are looking for. + not exists(AdditionalFlowInputStep s | s.getOutput() = source) + } + + // Flow through additional flow steps + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node1.(AdditionalFlowInputStep).getOutput() = node2 + } + + predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(Crypto::ArtifactNode i).asElement() } +} + +module WrapperFlow = DataFlow::Global; + +/** + * Using a set approach to determine if reuse of an artifact exists. + * This predicate produces a set of 'wrappers' that flow to the artifact node. + * This set can be compared with the set to another artifact node to determine if they are the same. + */ +private DataFlow::Node getWrapperSet(Crypto::NonceArtifactNode a) { + WrapperFlow::flow(result, DataFlow::exprNode(a.asElement())) + or + result.asExpr() = a.getSourceElement() +} + +/** + * Two different artifact nodes are considered reuse if any of the following conditions are met: + * 1. The source for artifact `a` and artifact `b` are the same and the source is a literal. + * 2. The source for artifact `a` and artifact `b` are not the same and the source is a literal of the same value. + * 3. For all 'wrappers' that return the source of artifact `a`, and that wrapper also exists for artifact `b`. + * 4. For all 'wrappers' that return the source of artifact `b`, and that wrapper also exists for artifact `a`. + */ +predicate isArtifactReuse(Crypto::ArtifactNode a, Crypto::ArtifactNode b) { + a != b and + ( + a.getSourceElement() = b.getSourceElement() and a.getSourceElement() instanceof Literal + or + a.getSourceElement().(Literal).getValue() = b.getSourceElement().(Literal).getValue() + or + forex(DataFlow::Node e | e = getWrapperSet(a) | + exists(DataFlow::Node e2 | e2 = getWrapperSet(b) | e = e2) + ) + or + forex(DataFlow::Node e | e = getWrapperSet(b) | + exists(DataFlow::Node e2 | e2 = getWrapperSet(a) | e = e2) + ) + ) +} diff --git a/java/ql/src/experimental/Quantum/InventoryFilters/ReusedNonce.ql b/java/ql/src/experimental/Quantum/InventoryFilters/ReusedNonce.ql new file mode 100644 index 000000000000..d8adccace557 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventoryFilters/ReusedNonce.ql @@ -0,0 +1,12 @@ +/** + * @name Detects reuse of the same nonce in multiple operations + * @id java/crypto_inventory_filter/nonce_reuse + * @kind problem + */ + +import java +import ArtifactReuse + +from Crypto::NonceArtifactNode nonce1, Crypto::NonceArtifactNode nonce2 +where isArtifactReuse(nonce1, nonce2) +select nonce1, "Reuse with nonce $@", nonce2, nonce2.toString() diff --git a/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql b/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql deleted file mode 100644 index 2263f05d56eb..000000000000 --- a/java/ql/src/experimental/Quantum/PossibleReusedNonce.ql +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @name Possible Nonce Reuse: Produces false positives if reuse occurs in a source that is a re-entry point. - * @id java/possible-nonce-reuse - * @kind problem - */ - -import experimental.Quantum.Language -import semmle.code.java.dataflow.DataFlow - -from - Crypto::KeyOperationNode op1, Crypto::KeyOperationNode op2, Crypto::NonceArtifactNode nonce1, - Crypto::NonceArtifactNode nonce2, Crypto::FlowAwareElement src1, Crypto::FlowAwareElement src2 -where - // NOTE: not looking at value of the nonce, if we knew value, it would be insecure (hard coded) - // Instead trying to find nonce sources that trace to multiple operations. - // Only looking for encryption operations, presumably if reuse for decryption either wouldn't be observable - // (the encryption happened else where) or we are able to see the encryption and decryption operation and - // reuse for encryption is the concern) - ( - op1.getKeyOperationSubtype() instanceof Crypto::EncryptionSubtype or - op1.getKeyOperationSubtype() instanceof Crypto::WrapSubtype or - op1.getKeyOperationSubtype() instanceof Crypto::UnknownCipherOperationSubtype - ) and - ( - op2.getKeyOperationSubtype() instanceof Crypto::EncryptionSubtype or - op2.getKeyOperationSubtype() instanceof Crypto::WrapSubtype or - op2.getKeyOperationSubtype() instanceof Crypto::UnknownCipherOperationSubtype - ) and - nonce1 = op1.getANonce() and - nonce2 = op2.getANonce() and - op1 != op2 and - nonce1.getSourceElement() = src1 and - nonce2.getSourceElement() = src2 and - src1 = src2 -// TODO: need to clarify that a reuse in a non-finalize is ok, need to check if 'finalize' through a modeled predicate -select op1, "Operation has a possible reused nonce with source $@", src1, src1.toString() From 7b7ed61beb039071e32a263921d38714329b59e0 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 28 Apr 2025 11:34:32 -0400 Subject: [PATCH 083/122] Adding an asymmetric algorithm slice. --- .../Quantum/InventorySlices/AsymmetricAlgorithm.ql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/AsymmetricAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/AsymmetricAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/AsymmetricAlgorithm.ql new file mode 100644 index 000000000000..1d4519784007 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/AsymmetricAlgorithm.ql @@ -0,0 +1,12 @@ +/** + * @name Detects known uses of asymmetric algorithms + * @id java/crypto_inventory_slices/known_asymmetric_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::AlgorithmNode a +where Crypto::isAsymmetricAlgorithm(a) +select a, "Instance of asymmetric algorithm " + a.getAlgorithmName() From 1fd7643ab3ad1f2b15817ca0839e67ae69129cec Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 28 Apr 2025 14:54:38 -0400 Subject: [PATCH 084/122] Adding example slicing queries. --- .../KnownAsymmetricAlgorithm.ql | 12 ++++++++++ .../KnownAsymmetricOperationAlgorithm.ql | 12 ++++++++++ .../KnownEllipticCurveAlgorithm.ql | 11 ++++++++++ .../InventorySlices/KnownHashingAlgorithm.ql | 11 ++++++++++ .../InventorySlices/KnownHashingOperation.ql | 11 ++++++++++ .../KnownHashingOperationAlgorithm.ql | 12 ++++++++++ .../KnownKeyDerivationAlgorithm.ql | 11 ++++++++++ .../KnownKeyDerivationOperation.ql | 11 ++++++++++ .../KnownKeyDerivationOperationAlgorithm.ql | 12 ++++++++++ .../LikelyCryptoAPIFunction.ql | 16 ++++++++++++++ .../UnknownOperationAlgorithm.ql | 22 +++++++++++++++++++ 11 files changed, 141 insertions(+) create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql new file mode 100644 index 000000000000..1e5a7c5bced1 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql @@ -0,0 +1,12 @@ +/** + * @name Detects known asymmetric algorithms + * @id java/crypto_inventory_slices/known_asymmetric_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::AlgorithmNode a +where Crypto::isKnownAsymmetricAlgorithm(a) +select a, "Instance of asymmetric algorithm " + a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql new file mode 100644 index 000000000000..0900401b80d7 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql @@ -0,0 +1,12 @@ +/** + * @name Detects operations where the algorithm applied is a known asymmetric algorithms + * @id java/crypto_inventory_slices/known_asymmetric_operation_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::OperationNode op, Crypto::AlgorithmNode a +where a = op.getAKnownAlgorithm() and Crypto::isKnownAsymmetricAlgorithm(a) +select op, "Operation using asymmetric algorithm $@", a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql new file mode 100644 index 000000000000..0112e9d96dff --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql @@ -0,0 +1,11 @@ +/** + * @name Detects known elliptic curve algorithms + * @id java/crypto_inventory_slices/known_elliptic_curvee_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::EllipticCurveNode a +select a, "Instance of elliptic curve algorithm " + a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql new file mode 100644 index 000000000000..632872725e71 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql @@ -0,0 +1,11 @@ +/** + * @name Detects algorithms that are known hashing algorithms + * @id java/crypto_inventory_slices/known_hashing_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::HashAlgorithmNode a +select a, "Instance of hashing algorithm " + a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql new file mode 100644 index 000000000000..b3556393173d --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql @@ -0,0 +1,11 @@ +/** + * @name Detects uses of hashing operations (operations exlicitly for hashing only, irrespective of the algorithm used) + * @id java/crypto_inventory_slices/known_hashing_operation + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::HashOperationNode op +select op, "Known hashing operation" diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql new file mode 100644 index 000000000000..ce0f4d37d4d1 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql @@ -0,0 +1,12 @@ +/** + * @name Detects operations where the algorithm applied is a known hashing algorithm + * @id java/crypto_inventory_slices/operation_with_known_hashing_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::OperationNode op, Crypto::HashAlgorithmNode a +where a = op.getAKnownAlgorithm() +select op, "Operation using hashing algorithm $@", a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql new file mode 100644 index 000000000000..584ffef0bbfa --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql @@ -0,0 +1,11 @@ +/** + * @name Detects known key derivation algorithms + * @id java/crypto_inventory_slices/known_key_derivation_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::KeyDerivationAlgorithmNode alg +select alg, "Known key derivation algorithm " + alg.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql new file mode 100644 index 000000000000..8c77b2aa984d --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql @@ -0,0 +1,11 @@ +/** + * @name Detects uses of key derivation operations (operations exlicitly for key derivation only, irrespective of the algorithm used) + * @id java/crypto_inventory_slices/known_key_derivation_operation + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::KeyDerivationOperationNode op +select op, "Known key derivation operation" diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql new file mode 100644 index 000000000000..cf9a4e96f4da --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql @@ -0,0 +1,12 @@ +/** + * @name Detects operations where the algorithm applied is a known key derivation algorithm + * @id java/crypto_inventory_slices/operation_with_known_key_derivation_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::OperationNode op, Crypto::KeyDerivationAlgorithmNode a +where a = op.getAKnownAlgorithm() +select op, "Operation using key derivation algorithm $@", a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql b/java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql new file mode 100644 index 000000000000..0076c478dec9 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql @@ -0,0 +1,16 @@ +/** + * @name Detects functions that take in crypto configuration parameters but calls are not detected in source. + * @id java/crypto_inventory_slices/likely_crypto_api_function + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Callable f, Parameter p, Crypto::OperationNode op +where + op.asElement().(Expr).getEnclosingCallable() = f and + op.getAnAlgorithmOrGenericSource().asElement() = p +select f, + "Likely crypto API function: Operation $@ configured by parameter $@ with no known configuring call", + op, op.toString(), p, p.toString() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql new file mode 100644 index 000000000000..61a27c75dac2 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql @@ -0,0 +1,22 @@ +/** + * @name Detects operations where the algorithm applied is unknown + * @id java/crypto_inventory_slices/unknown_operation_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +//TODO: can we have an unknown node concept? +from Crypto::OperationNode op, Element e, string msg +where + not exists(op.getAnAlgorithmOrGenericSource()) and + e = op.asElement() and + msg = "Operation with unconfigured algorithm (no known sources)." + or + exists(Crypto::GenericSourceNode n | + n = op.getAnAlgorithmOrGenericSource() and + e = n.asElement() + ) and + msg = "Operation with unknown algorithm source: $@" +select op, msg, e, e.toString() From 5a8bffac11d352f02e83219d24d357021368ef7b Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 28 Apr 2025 14:56:08 -0400 Subject: [PATCH 085/122] Removing old asymmetric algorithm slice --- .../Quantum/InventorySlices/AsymmetricAlgorithm.ql | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 java/ql/src/experimental/Quantum/InventorySlices/AsymmetricAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/AsymmetricAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/AsymmetricAlgorithm.ql deleted file mode 100644 index 1d4519784007..000000000000 --- a/java/ql/src/experimental/Quantum/InventorySlices/AsymmetricAlgorithm.ql +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @name Detects known uses of asymmetric algorithms - * @id java/crypto_inventory_slices/known_asymmetric_algorithm - * @kind problem - */ - -import java -import experimental.Quantum.Language - -from Crypto::AlgorithmNode a -where Crypto::isAsymmetricAlgorithm(a) -select a, "Instance of asymmetric algorithm " + a.getAlgorithmName() From ce3eabf05ac98a255ccddf483ae9b1b120b1a120 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 28 Apr 2025 14:56:28 -0400 Subject: [PATCH 086/122] Updating model to support slicing. --- shared/cryptography/codeql/cryptography/Model.qll | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 1ea862b470d2..704be38362bf 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -1249,7 +1249,7 @@ module CryptographyBase Input> { /** * A generic source node is a source of data that is not resolvable to a specific asset. */ - private class GenericSourceNode extends NodeBase, TGenericSourceNode { + final class GenericSourceNode extends NodeBase, TGenericSourceNode { GenericSourceInstance instance; GenericSourceNode() { this = TGenericSourceNode(instance) } @@ -2449,10 +2449,9 @@ module CryptographyBase Input> { } } - predicate isAsymmetricAlgorithm(AlgorithmNode node) { + predicate isKnownAsymmetricAlgorithm(AlgorithmNode node) { node instanceof EllipticCurveNode or node instanceof KeyOperationAlgorithmNode and node.(KeyOperationAlgorithmNode).isAsymmetric() - // TODO: get unknown algorithms from known asymmetric operations } } From 219476cee01bb9042945345f1d08f19f959ba731 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 28 Apr 2025 15:47:58 -0400 Subject: [PATCH 087/122] Adding KDF iteration count inventory filters. --- .../KnownWeakKDFIterationCount.ql | 15 +++++++++++++++ .../UnknownKDFIterationCount.ql | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 java/ql/src/experimental/Quantum/InventoryFilters/KnownWeakKDFIterationCount.ql create mode 100644 java/ql/src/experimental/Quantum/InventoryFilters/UnknownKDFIterationCount.ql diff --git a/java/ql/src/experimental/Quantum/InventoryFilters/KnownWeakKDFIterationCount.ql b/java/ql/src/experimental/Quantum/InventoryFilters/KnownWeakKDFIterationCount.ql new file mode 100644 index 000000000000..439295f74e36 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventoryFilters/KnownWeakKDFIterationCount.ql @@ -0,0 +1,15 @@ +/** + * @name Detects known weak KDf iteration counts (less than 100k and the count is statically known) + * @id java/crypto_inventory_filters/known_weak_kdf_iteration_count + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::KeyDerivationOperationNode op, Literal l +where + op.getIterationCount().asElement() = l and + l.getValue().toInt() < 100000 +select op, "Key derivation operation configures iteration count below 100k: $@", l, + l.getValue().toString() diff --git a/java/ql/src/experimental/Quantum/InventoryFilters/UnknownKDFIterationCount.ql b/java/ql/src/experimental/Quantum/InventoryFilters/UnknownKDFIterationCount.ql new file mode 100644 index 000000000000..0c91e66d52bc --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventoryFilters/UnknownKDFIterationCount.ql @@ -0,0 +1,19 @@ +/** + * @name Detects unknown KDf iteration counts + * @id java/crypto_inventory_filters/unknown_kdf_iteration_count + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::KeyDerivationOperationNode op, Element e, string msg +where + e = op.getIterationCount().asElement() and + not e instanceof Literal and + msg = "Key derivation operation with unknown iteration: $@" + or + not exists(op.getIterationCount()) and + e = op.asElement() and + msg = "Key derivation operation with no iteration configuration." +select op, msg, e, e.toString() From ac3675bdac65175d5f39df239223afa68764d127 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 28 Apr 2025 16:00:41 -0400 Subject: [PATCH 088/122] Typo fix --- .../Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql index 0112e9d96dff..048bcd8182ce 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql @@ -1,6 +1,6 @@ /** * @name Detects known elliptic curve algorithms - * @id java/crypto_inventory_slices/known_elliptic_curvee_algorithm + * @id java/crypto_inventory_slices/known_elliptic_curve_algorithm * @kind problem */ From ac798f2bc6dc5ca0a952682805767077442b641d Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Mon, 28 Apr 2025 16:03:41 -0400 Subject: [PATCH 089/122] Cipher Algorithm Slices --- .../KnownAsymmetricCipherAlgorithm.ql | 12 ++++++++++++ .../InventorySlices/KnownCipherAlgorithm.ql | 15 +++++++++++++++ .../KnownSymmetricCipherAlgorithm.ql | 0 shared/cryptography/codeql/cryptography/Model.qll | 8 ++++++++ 4 files changed, 35 insertions(+) create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql create mode 100644 java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql new file mode 100644 index 000000000000..962a6b720159 --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql @@ -0,0 +1,12 @@ +/** + * @name Detects known asymmetric cipher algorithms + * @id java/crypto_inventory_slices/known_symmetric_cipher_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::KeyOperationAlgorithmNode a +where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::AsymmetricCipherAlgorithm +select a, "Instance of asymmetric cipher algorithm " + a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql new file mode 100644 index 000000000000..4096fe16d29c --- /dev/null +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql @@ -0,0 +1,15 @@ +/** + * @name Detects known cipher algorithms + * @id java/crypto_inventory_slices/known_cipher_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +// TODO: should there be a cipher algorithm node? +from Crypto::KeyOperationAlgorithmNode a +where + a.getAlgorithmType() instanceof Crypto::KeyOpAlg::AsymmetricCipherAlgorithm or + a.getAlgorithmType() instanceof Crypto::KeyOpAlg::SymmetricCipherAlgorithm +select a, "Instance of cipher algorithm " + a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 704be38362bf..bfc527e108db 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -659,6 +659,14 @@ module CryptographyBase Input> { TSymmetricCipherType getType() { result = type } } + + class AsymmetricCipherAlgorithm extends Algorithm, TAsymmetricCipher { + TAsymmetricCipherType type; + + AsymmetricCipherAlgorithm() { this = TAsymmetricCipher(type) } + + TAsymmetricCipherType getType() { result = type } + } } /** From 7f24a2557dab63a2e1a241ef83c5e11a0622a28b Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Wed, 30 Apr 2025 16:28:31 +0200 Subject: [PATCH 090/122] Add modelling for JCA key gen cipher algorithm --- java/ql/lib/experimental/Quantum/JCA.qll | 83 +++++++---- java/ql/lib/experimental/Quantum/Language.qll | 35 +++-- .../Quantum/InsecureNonceSource.ql | 20 +++ .../experimental/Quantum/TestAESGCMNonce.ql | 16 +++ .../ql/src/experimental/Quantum/TestCipher.ql | 2 +- java/ql/src/experimental/Quantum/TestHash.ql | 4 +- misc/scripts/cryptography/generate_cbom.py | 10 +- .../codeql/cryptography/Model.qll | 135 ++++++++++++++---- 8 files changed, 234 insertions(+), 71 deletions(-) create mode 100644 java/ql/src/experimental/Quantum/InsecureNonceSource.ql create mode 100644 java/ql/src/experimental/Quantum/TestAESGCMNonce.ql diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 7507c9edbd50..6589cbbc9d5d 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -2,7 +2,6 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.controlflow.Dominance -import codeql.util.Option module JCAModel { import Language @@ -354,9 +353,11 @@ module JCAModel { else result instanceof KeyOpAlg::TUnknownKeyOperationAlgorithmType } - override string getKeySize() { + override string getKeySizeFixed() { none() // TODO: implement to handle variants such as AES-128 } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } } bindingset[input] @@ -394,8 +395,6 @@ module JCAModel { override Crypto::HashAlgorithmInstance getOAEPEncodingHashAlgorithm() { result = this } override Crypto::HashAlgorithmInstance getMGF1HashAlgorithm() { none() } // TODO - - override string getKeySize() { none() } } /** @@ -446,8 +445,6 @@ module JCAModel { predicate isIntermediate(); } - module MethodCallOption = Option; - /** * An generic analysis module for analyzing the `getInstance` to `initialize` to `doOperation` pattern in the JCA. * @@ -568,6 +565,14 @@ module JCAModel { GetInstanceToInitToUseFlow::flowPath(src, sink) } + GetInstance getInstantiationFromInit( + Init init, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink + ) { + src.getNode().asExpr() = result and + sink.getNode().asExpr() = init.(MethodCall).getQualifier() and + GetInstanceToInitToUseFlow::flowPath(src, sink) + } + Init getInitFromUse( Use use, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink ) { @@ -829,6 +834,9 @@ module JCAModel { } } + module MessageDigestFlowAnalysisImpl = + GetInstanceInitUseFlowAnalysis; + class MessageDigestGetInstanceAlgorithmValueConsumer extends HashAlgorithmValueConsumer { MessageDigestGetInstanceCall call; @@ -849,17 +857,18 @@ module JCAModel { } Expr getAlgorithmArg() { result = this.getArgument(0) } - - DigestHashOperation getDigestCall() { - DigestGetInstanceToDigestFlow::flow(DataFlow::exprNode(this), - DataFlow::exprNode(result.(DigestCall).getQualifier())) - } } class DigestCall extends MethodCall { - DigestCall() { this.getCallee().hasQualifiedName("java.security", "MessageDigest", "digest") } + DigestCall() { + this.getCallee().hasQualifiedName("java.security", "MessageDigest", ["update", "digest"]) + } Expr getDigestArtifactOutput() { result = this } + + Expr getInputArg() { result = this.getArgument(0) } + + predicate isIntermediate() { this.getMethod().getName() = "update" } } // flow config from MessageDigest.getInstance to MessageDigest.digest @@ -873,23 +882,22 @@ module JCAModel { module DigestGetInstanceToDigestFlow = DataFlow::Global; - class DigestArtifact extends Crypto::DigestArtifactInstance { - DigestArtifact() { this = any(DigestCall call).getDigestArtifactOutput() } - - override DataFlow::Node getOutputNode() { result.asExpr() = this } - } - class DigestHashOperation extends Crypto::HashOperationInstance instanceof DigestCall { - override Crypto::DigestArtifactInstance getDigestArtifact() { - result = this.(DigestCall).getDigestArtifactOutput() + DigestHashOperation() { not super.isIntermediate() } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = super.getDigestArtifactOutput() } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - exists(MessageDigestGetInstanceCall getInstanceCall | - getInstanceCall.getDigestCall() = this and - getInstanceCall = - result.(MessageDigestGetInstanceAlgorithmValueConsumer).getInstantiationCall() - ) + MessageDigestFlowAnalysisImpl::getInstantiationFromUse(this, _, _) = + result.(MessageDigestGetInstanceAlgorithmValueConsumer).getInstantiationCall() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = super.getInputArg() or + result.asExpr() = + MessageDigestFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getInputArg() } } @@ -997,6 +1005,7 @@ module JCAModel { or // However, for general elliptic curves, getInstance("EC") is used // and java.security.spec.ECGenParameterSpec("") is what sets the specific curve. + // If init is not specified, the default (P-) // The result of ECGenParameterSpec is passed to KeyPairGenerator.initialize // If the curve is not specified, the default is used. // We would trace the use of this inside a KeyPairGenerator.initialize @@ -1096,6 +1105,30 @@ module JCAModel { override string getKeySizeFixed() { none() } } + class KeyGeneratorCipherAlgorithm extends CipherStringLiteralAlgorithmInstance { + KeyGeneratorCipherAlgorithm() { consumer instanceof KeyGenerationAlgorithmValueConsumer } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + exists(KeyGeneratorGetInstanceCall getInstance, KeyGeneratorInitCall init | + getInstance = + this.getConsumer().(KeyGenerationAlgorithmValueConsumer).getInstantiationCall() and + getInstance = KeyGeneratorFlowAnalysisImpl::getInstantiationFromInit(init, _, _) and + init.getKeySizeArg() = result.asExpr() + ) + } + + predicate isOnlyConsumedByKeyGen() { + forall(Crypto::AlgorithmValueConsumer c | + c = this.getConsumer() and + c instanceof KeyGenerationAlgorithmValueConsumer + ) + } + + override predicate shouldHaveModeOfOperation() { this.isOnlyConsumedByKeyGen() } + + override predicate shouldHavePaddingScheme() { this.isOnlyConsumedByKeyGen() } + } + /* * Key Derivation Functions (KDFs) */ diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 1e12f6f1ac8e..6f7dc88157b2 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -38,7 +38,7 @@ module CryptoInput implements InputSig { predicate artifactOutputFlowsToGenericInput( DataFlow::Node artifactOutput, DataFlow::Node otherInput ) { - ArtifactUniversalFlow::flow(artifactOutput, otherInput) + ArtifactFlow::flow(artifactOutput, otherInput) } } @@ -60,7 +60,7 @@ class GenericUnreferencedParameterSource extends Crypto::GenericUnreferencedPara } override predicate flowsTo(Crypto::FlowAwareElement other) { - GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode()) } override DataFlow::Node getOutputNode() { result.asParameter() = this } @@ -76,7 +76,7 @@ class GenericLocalDataSource extends Crypto::GenericLocalDataSource { override DataFlow::Node getOutputNode() { result.asExpr() = this } override predicate flowsTo(Crypto::FlowAwareElement other) { - GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode()) } override string getAdditionalDescription() { result = this.toString() } @@ -88,7 +88,7 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { override DataFlow::Node getOutputNode() { result.asExpr() = this } override predicate flowsTo(Crypto::FlowAwareElement other) { - GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode()) } override string getAdditionalDescription() { result = this.toString() } @@ -107,7 +107,7 @@ class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceo override predicate flowsTo(Crypto::FlowAwareElement other) { // TODO: separate config to avoid blowing up data-flow analysis - GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) + GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode()) } override string getAdditionalDescription() { result = this.toString() } @@ -122,15 +122,24 @@ abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance } class SecureRandomnessInstance extends RandomnessInstance { + RandomDataSource source; + SecureRandomnessInstance() { - exists(RandomDataSource s | this = s.getOutput() | - s.getSourceOfRandomness() instanceof SecureRandomNumberGenerator - ) + this = source.getOutput() and + source.getSourceOfRandomness() instanceof SecureRandomNumberGenerator } + + override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() } } class InsecureRandomnessInstance extends RandomnessInstance { - InsecureRandomnessInstance() { exists(InsecureRandomnessSource node | this = node.asExpr()) } + RandomDataSource source; + + InsecureRandomnessInstance() { + any(InsecureRandomnessSource src).asExpr() = this and source.getOutput() = this + } + + override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() } } /** @@ -142,12 +151,12 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node { final DataFlow::Node getInput() { result = this } } -module ArtifactUniversalFlow = DataFlow::Global; +module ArtifactFlow = DataFlow::Global; /** * Generic data source to node input configuration */ -module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { +module GenericDataSourceFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source = any(Crypto::GenericSourceInstance i).getOutputNode() } @@ -175,7 +184,7 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { } } -module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { +module ArtifactFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source = any(Crypto::ArtifactInstance artifact).getOutputNode() } @@ -203,7 +212,7 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { } } -module GenericDataSourceUniversalFlow = TaintTracking::Global; +module GenericDataSourceFlow = TaintTracking::Global; // Import library-specific modeling import JCA diff --git a/java/ql/src/experimental/Quantum/InsecureNonceSource.ql b/java/ql/src/experimental/Quantum/InsecureNonceSource.ql new file mode 100644 index 000000000000..9c06884328bb --- /dev/null +++ b/java/ql/src/experimental/Quantum/InsecureNonceSource.ql @@ -0,0 +1,20 @@ +/** + * @name Insecure nonce at a cipher operation + * @id java/insecure-nonce + * @kind problem + * @problem.severity error + * @precision high + * @description A nonce is generated from a source that is not secure. This can lead to + * vulnerabilities such as replay attacks or key recovery. + */ + +import experimental.Quantum.Language + +predicate isInsecureNonceSource(Crypto::NonceArtifactNode n, Crypto::NodeBase src) { + src = n.getSourceNode() and + not src.asElement() instanceof SecureRandomnessInstance +} + +from Crypto::KeyOperationNode op, Crypto::NodeBase src +where isInsecureNonceSource(op.getANonce(), src) +select op, "Operation uses insecure nonce source $@", src, src.toString() diff --git a/java/ql/src/experimental/Quantum/TestAESGCMNonce.ql b/java/ql/src/experimental/Quantum/TestAESGCMNonce.ql new file mode 100644 index 000000000000..985527318ffa --- /dev/null +++ b/java/ql/src/experimental/Quantum/TestAESGCMNonce.ql @@ -0,0 +1,16 @@ +/** + * @name "PQC Test" + */ + +import experimental.Quantum.Language + +class AESGCMAlgorithmNode extends Crypto::KeyOperationAlgorithmNode { + AESGCMAlgorithmNode() { + this.getAlgorithmType() = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES()) and + this.getModeOfOperation().getModeType() = Crypto::GCM() + } +} + +from Crypto::KeyOperationNode op, Crypto::NonceArtifactNode nonce +where op.getAKnownAlgorithm() instanceof AESGCMAlgorithmNode and nonce = op.getANonce() +select op, nonce.getSourceNode() diff --git a/java/ql/src/experimental/Quantum/TestCipher.ql b/java/ql/src/experimental/Quantum/TestCipher.ql index 6779a64f608e..503d60039229 100644 --- a/java/ql/src/experimental/Quantum/TestCipher.ql +++ b/java/ql/src/experimental/Quantum/TestCipher.ql @@ -1,5 +1,5 @@ /** - * @name "PQC Test" + * @name "Key operation slice table demo query" */ import experimental.Quantum.Language diff --git a/java/ql/src/experimental/Quantum/TestHash.ql b/java/ql/src/experimental/Quantum/TestHash.ql index 96f99193cba4..76ef6951a7e8 100644 --- a/java/ql/src/experimental/Quantum/TestHash.ql +++ b/java/ql/src/experimental/Quantum/TestHash.ql @@ -1,9 +1,9 @@ /** - * @name TestHashOperations + * @name "Hash operation slice table demo query" */ import experimental.Quantum.Language from Crypto::HashOperationNode op, Crypto::HashAlgorithmNode alg -where alg = op.getAKnownHashAlgorithm() +where alg = op.getAKnownAlgorithm() select op, op.getDigest(), alg, alg.getRawAlgorithmName() diff --git a/misc/scripts/cryptography/generate_cbom.py b/misc/scripts/cryptography/generate_cbom.py index fa4c2cb1d023..6bd0d19712c5 100644 --- a/misc/scripts/cryptography/generate_cbom.py +++ b/misc/scripts/cryptography/generate_cbom.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import os +import re import sys import argparse import subprocess @@ -86,6 +87,7 @@ def main(): parser.add_argument("-c", "--codeql", required=True, help="Path to CodeQL CLI executable.") parser.add_argument("-d", "--database", required=True, help="Path to the CodeQL database.") parser.add_argument("-q", "--query", required=True, help="Path to the .ql query file.") + parser.add_argument("--queryid", required=True, help="Query ID for the analysis.") parser.add_argument("-o", "--output", required=True, help="Output directory for analysis results.") args = parser.parse_args() @@ -94,7 +96,13 @@ def main(): run_codeql_analysis(args.codeql, args.database, args.query, args.output) # Locate DGML file - dgml_file = os.path.join(args.output, "java", "print-cbom-graph.dgml") + ALLOWED_QUERY_ID = re.compile(r'^[a-zA-Z0-9_\-]+$') + + if not ALLOWED_QUERY_ID.match(args.queryid): + print("Invalid query_id provided: '%s'. Allowed characters: letters, digits, '_', and '-'.", args.queryid) + sys.exit(1) + + dgml_file = os.path.join(args.output, "java", '{}.dgml'.format(args.queryid)) dot_file = dgml_file.replace(".dgml", ".dot") if os.path.exists(dgml_file): diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 510f334bc626..b3c104cbc5ed 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -3,7 +3,6 @@ */ import codeql.util.Location -import codeql.util.Option import codeql.util.Either signature module InputSig { @@ -379,6 +378,8 @@ module CryptographyBase Input> { exists(KeyDerivationOperationInstance op | inputNode = op.getInputConsumer()) or exists(MACOperationInstance op | inputNode = op.getMessageConsumer()) + or + exists(HashOperationInstance op | inputNode = op.getInputConsumer()) ) and this = Input::dfn_to_element(inputNode) } @@ -410,16 +411,11 @@ module CryptographyBase Input> { } } - /** - * An artifact representing a hash function's digest output. - */ - abstract class DigestArtifactInstance extends OutputArtifactInstance { } - /** * An artifact representing a random number generator's output. */ abstract class RandomNumberGenerationInstance extends OutputArtifactInstance { - // TODO: input seed? + abstract string getGeneratorName(); } /** @@ -438,6 +434,17 @@ module CryptographyBase Input> { override DataFlowNode getOutputNode() { result = creator.getOutputArtifact() } } + /** + * An artifact representing the message digest output of a hash operation. + */ + final class HashOutputArtifactInstance extends OutputArtifactInstance { + HashOperationInstance creator; + + HashOutputArtifactInstance() { Input::dfn_to_element(creator.getOutputArtifact()) = this } + + override DataFlowNode getOutputNode() { result = creator.getOutputArtifact() } + } + /** * An artifact representing the shared secret generated by key agreement operations. */ @@ -489,8 +496,14 @@ module CryptographyBase Input> { // TODO: key type hint? e.g. hint: private || public KeyArtifactConsumer() { ( - exists(KeyOperationInstance op | inputNode = op.getKeyConsumer()) or + exists(KeyOperationInstance op | inputNode = op.getKeyConsumer()) + or exists(MACOperationInstance op | inputNode = op.getKeyConsumer()) + or + exists(KeyAgreementSecretGenerationOperationInstance op | + inputNode = op.getServerKeyConsumer() or + inputNode = op.getPeerKeyConsumer() + ) ) and this = Input::dfn_to_element(inputNode) } @@ -770,7 +783,22 @@ module CryptographyBase Input> { * * If the algorithm accepts a range of key sizes without a particular one specified, this predicate should be implemented as `none()`. */ - abstract string getKeySize(); + abstract string getKeySizeFixed(); + + /** + * Gets a consumer for the key size in bits specified for this algorithm variant. + */ + abstract ConsumerInputDataFlowNode getKeySizeConsumer(); + + /** + * Holds if this algorithm is expected to have a mode specified. + */ + predicate shouldHaveModeOfOperation() { any() } + + /** + * Holds if this algorithm is expected to have a padding scheme specified. + */ + predicate shouldHavePaddingScheme() { any() } } newtype TBlockCipherModeOfOperationType = @@ -904,7 +932,9 @@ module CryptographyBase Input> { } abstract class HashOperationInstance extends OperationInstance { - abstract DigestArtifactInstance getDigestArtifact(); + abstract ArtifactOutputDataFlowNode getOutputArtifact(); + + abstract ConsumerInputDataFlowNode getInputConsumer(); } abstract class HashAlgorithmInstance extends AlgorithmInstance { @@ -1151,7 +1181,7 @@ module CryptographyBase Input> { private newtype TNode = // Output artifacts (data that is not an operation or algorithm, e.g., a key) - TDigest(DigestArtifactInstance e) or + TDigest(HashOutputArtifactInstance e) or TKey(KeyArtifactInstance e) or TSharedSecret(KeyAgreementSharedSecretOutputArtifactInstance e) or // Input artifacts (synthetic nodes, used to differentiate input as entities) @@ -1442,6 +1472,15 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } override string getSourceNodeRelationship() { none() } // TODO: seed? + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [ONLY_KNOWN] + key = "Description" and + value = instance.getGeneratorName() and + location = this.getLocation() + } } /** @@ -1518,7 +1557,7 @@ module CryptographyBase Input> { * A digest artifact produced by a hash operation. */ final class DigestArtifactNode extends ArtifactNode, TDigest { - DigestArtifactInstance instance; + HashOutputArtifactInstance instance; DigestArtifactNode() { this = TDigest(instance) } @@ -1664,12 +1703,28 @@ module CryptographyBase Input> { result.asElement() = instance.getOutputArtifact().getArtifact() } + KeyArtifactNode getServerKey() { + result.asElement() = instance.getServerKeyConsumer().getConsumer() + } + + KeyArtifactNode getPeerKey() { + result.asElement() = instance.getPeerKeyConsumer().getConsumer() + } + override NodeBase getChild(string key) { result = super.getChild(key) or // [ALWAYS_KNOWN] key = "Output" and result = this.getOutput() + or + // [KNOWN_OR_UNKOWN] + key = "ServerKey" and + if exists(this.getServerKey()) then result = this.getServerKey() else result = this + or + // [KNOWN_OR_UNKOWN] + key = "PeerKey" and + if exists(this.getPeerKey()) then result = this.getPeerKey() else result = this } } @@ -2115,7 +2170,14 @@ module CryptographyBase Input> { /** * Gets the key size variant of this algorithm in bits, e.g., 128 for "AES-128". */ - string getKeySize() { result = instance.asAlg().getKeySize() } // TODO: key sizes for known algorithms + string getKeySizeFixed() { result = instance.asAlg().getKeySizeFixed() } // TODO: key sizes for known algorithms + + /** + * Gets the key size generic source node. + */ + GenericSourceNode getKeySize() { + result = instance.asAlg().getKeySizeConsumer().getConsumer().getAGenericSourceNode() + } /** * Gets the type of this key operation algorithm, e.g., "SymmetricEncryption(_)" or "" @@ -2139,17 +2201,23 @@ module CryptographyBase Input> { override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or - // [KNOWN_OR_UNKNOWN] + // [KNOWN_OR_UNKNOWN] - but only if not suppressed edgeName = "Mode" and - if exists(this.getModeOfOperation()) - then result = this.getModeOfOperation() - else result = this + ( + if exists(this.getModeOfOperation()) + then result = this.getModeOfOperation() + else result = this + ) and + instance.asAlg().shouldHaveModeOfOperation() or - // [KNOWN_OR_UNKNOWN] + // [KNOWN_OR_UNKNOWN] - but only if not suppressed edgeName = "Padding" and - if exists(this.getPaddingAlgorithm()) - then result = this.getPaddingAlgorithm() - else result = this + ( + if exists(this.getPaddingAlgorithm()) + then result = this.getPaddingAlgorithm() + else result = this + ) and + instance.asAlg().shouldHavePaddingScheme() } override predicate properties(string key, string value, Location location) { @@ -2160,14 +2228,13 @@ module CryptographyBase Input> { this.getSymmetricCipherStructure().toString() = value and location = this.getLocation() or + // [ONLY_KNOWN] + key = "KeySize" and ( - // [KNOWN_OR_UNKNOWN] - key = "KeySize" and - if exists(this.getKeySize()) - then value = this.getKeySize() - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) + value = this.getKeySizeFixed() and + location = this.getLocation() + or + node_as_property(this.getKeySize(), value, location) ) } } @@ -2191,10 +2258,16 @@ module CryptographyBase Input> { node instanceof HashAlgorithmNode } + MessageArtifactNode getInputArtifact() { + result.asElement() = instance.getInputConsumer().getConsumer() + } + /** * Gets the output digest node */ - DigestArtifactNode getDigest() { result.asElement() = instance.getDigestArtifact() } + DigestArtifactNode getDigest() { + result.asElement() = instance.getOutputArtifact().getArtifact() + } override NodeBase getChild(string key) { result = super.getChild(key) @@ -2202,6 +2275,10 @@ module CryptographyBase Input> { // [KNOWN_OR_UNKNOWN] key = "Digest" and if exists(this.getDigest()) then result = this.getDigest() else result = this + or + // [KNOWN_OR_UNKNOWN] + key = "Message" and + if exists(this.getInputArtifact()) then result = this.getInputArtifact() else result = this } } From c80588cda15b873c2f5c4196726104feb7c72065 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Wed, 30 Apr 2025 11:41:28 -0400 Subject: [PATCH 091/122] Adding content to KnownSymmetricCipherAlgorithmql. --- .../InventorySlices/KnownSymmetricCipherAlgorithm.ql | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql index e69de29bb2d1..21949f1c8c6d 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql @@ -0,0 +1,12 @@ +/** + * @name Detects known symmetric cipher algorithms + * @id java/crypto_inventory_slices/known_symmetric_cipher_algorithm + * @kind problem + */ + +import java +import experimental.Quantum.Language + +from Crypto::KeyOperationAlgorithmNode a +where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::SymmetricCipherAlgorithm +select a, "Instance of symmetric cipher algorithm " + a.getAlgorithmName() From 5694f029de96449cffc8160e48ac9eb5113b744b Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 2 May 2025 14:03:50 -0400 Subject: [PATCH 092/122] Misc. cleanup --- cpp/ql/lib/experimental/Quantum/Language.qll | 88 +++--- .../experimental/Quantum/OpenSSL/CtxFlow.qll | 8 +- .../experimental/Quantum/OpenSSL/OpenSSL.qll | 271 +----------------- 3 files changed, 53 insertions(+), 314 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index 0d0caf7f49b5..7e1857cc68af 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -1,6 +1,7 @@ private import codeql.cryptography.Model import semmle.code.cpp.ir.IR import semmle.code.cpp.security.FlowSources as FlowSources +import semmle.code.cpp.dataflow.new.DataFlow private import cpp as Lang module CryptoInput implements InputSig { @@ -15,10 +16,44 @@ module CryptoInput implements InputSig { result = node.asParameter() or result = node.asVariable() } + + string locationToFileBaseNameAndLineNumberString(Location location) { + result = location.getFile().getBaseName() + ":" + location.getStartLine() + } + + predicate artifactOutputFlowsToGenericInput( + DataFlow::Node artifactOutput, DataFlow::Node otherInput + ) { + ArtifactFlow::flow(artifactOutput, otherInput) + } } module Crypto = CryptographyBase; +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 ArtifactFlow = DataFlow::Global; + /** * Artifact output to node input configuration */ @@ -31,9 +66,9 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node { /** * Generic data source to node input configuration */ -module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { +module GenericDataSourceFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { - source = any(Crypto::GenericDataSourceInstance i).getOutputNode() + source = any(Crypto::GenericSourceInstance i).getOutputNode() } predicate isSink(DataFlow::Node sink) { @@ -53,41 +88,6 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig { } } -// // // TODO: I think this will be inefficient, no? -// // class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal { -// // override DataFlow::Node getOutputNode() { -// // result.asExpr() = this -// // } -// // override predicate flowsTo(Crypto::FlowAwareElement other) { -// // // TODO: separate config to avoid blowing up data-flow analysis -// // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) -// // } -// // override string getAdditionalDescription() { result = this.toString() } -// // } -// /** -// * Definitions of various generic data sources -// */ -// // final class DefaultFlowSource = SourceNode; -// // final class DefaultRemoteFlowSource = RemoteFlowSource; -// // class GenericLocalDataSource extends Crypto::GenericLocalDataSource { -// // GenericLocalDataSource() { -// // any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this -// // } -// // override DataFlow::Node getOutputNode() { result.asExpr() = this } -// // override predicate flowsTo(Crypto::FlowAwareElement other) { -// // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) -// // } -// // override string getAdditionalDescription() { result = this.toString() } -// // } -// // class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { -// // GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this } -// // override DataFlow::Node getOutputNode() { result.asExpr() = this } -// // override predicate flowsTo(Crypto::FlowAwareElement other) { -// // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) -// // } -// // override string getAdditionalDescription() { result = this.toString() } -// // } -// module GenericDataSourceUniversalFlow = DataFlow::Global; module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source = any(Crypto::ArtifactInstance artifact).getOutputNode() @@ -112,10 +112,12 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { module ArtifactUniversalFlow = DataFlow::Global; -abstract class CipherOutputArtifact extends Crypto::KeyOperationOutputArtifactInstance { - override predicate flowsTo(Crypto::FlowAwareElement other) { - ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - } -} - +// abstract class CipherOutputArtifact extends Crypto::KeyOperationOutputArtifactInstance { +// override predicate flowsTo(Crypto::FlowAwareElement other) { +// ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) +// } +// } +// // final override predicate flowsTo(FlowAwareElement other) { +// // Input::artifactOutputFlowsToGenericInput(this.getOutputNode(), other.getInputNode()) +// // } import OpenSSL.OpenSSL diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll index c07902022d89..88e4a1c378b0 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll @@ -1,9 +1,11 @@ +//TODO: model as data on open APIs should be able to get common flows, and obviate some of this +// e.g., copy/dup calls, need to ingest those models for openSSL and refactor. /** * In OpenSSL, flow between 'context' parameters is often used to * store state/config of how an operation will eventually be performed. * Tracing algorithms and configurations to operations therefore - * requires tracing context parameters for many OpenSSL apis. - * + * requires tracing context parameters for many OpenSSL apis. + * * This library provides a dataflow analysis to track context parameters * between any two functions accepting openssl context parameters. * The dataflow takes into consideration flowing through duplication and copy calls @@ -88,7 +90,7 @@ module OpenSSLCTXArgumentFlowConfig implements DataFlow::ConfigSig { module OpenSSLCTXArgumentFlow = DataFlow::Global; -predicate ctxFlowsTo(CTXPointerArgument source, CTXPointerArgument sink) { +predicate ctxArgFlowsToCtxArg(CTXPointerArgument source, CTXPointerArgument sink) { exists(DataFlow::Node a, DataFlow::Node b | OpenSSLCTXArgumentFlow::flow(a, b) and a.asExpr() = source and diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll index 1f9fb9a54bae..6b28c4ee8e4b 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll @@ -3,272 +3,7 @@ import semmle.code.cpp.dataflow.new.DataFlow module OpenSSLModel { import experimental.Quantum.Language - import experimental.Quantum.OpenSSL.AlgorithmInstances.Instances - import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers - import experimental.Quantum.OpenSSL.Operations.Operations - // import experimental.Quantum.OpenSSL.EVPCipherOperation - // import experimental.Quantum.OpenSSL.EVPHashOperation - // import experimental.Quantum.OpenSSL.EVPCipherAlgorithmSource - // import experimental.Quantum.OpenSSL.EVPHashAlgorithmSource - // import experimental.Quantum.OpenSSL.Random - // // Imports the additional algorithm flow step for OpenSSL - // import experimental.Quantum.OpenSSL.OpenSSLAlgorithmGetter - // // TODO: trace CTX from init variants to the context arg of EVP update calls - // //https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis - // abstract class EVP_Cipher_Init_Call extends Call { - // Expr getContextArg() { result = this.getArgument(0) } - // abstract Expr getKeyArg(); - // abstract Expr getIVArg(); - // abstract Crypto::CipherOperationSubtype getCipherOperationSubtype(); - // } - // abstract class EVP_Cipher_EX_Init_Call extends EVP_Cipher_Init_Call { - // override Expr getKeyArg() { result = this.getArgument(3) } - // override Expr getIVArg() { result = this.getArgument(4) } - // } - // abstract class EVP_Cipher_EX2_Init_Call extends EVP_Cipher_Init_Call { - // override Expr getKeyArg() { result = this.getArgument(2) } - // override Expr getIVArg() { result = this.getArgument(3) } - // } - // abstract class EVP_Cipher_Operation_Call extends Crypto::CipherOperationInstance instanceof Call { - // Expr getContextArg() { result = this.(Call).getArgument(0) } - // abstract Expr getInputArg(); - // Expr getOutputArg() { result = this.(Call).getArgument(1) } - // abstract Expr getInitCall(); - // } - // abstract class EVP_Update_Call extends EVP_Cipher_Operation_Call { - // override Expr getInputArg() { result = this.(Call).getArgument(3) } - // } - // abstract class EVP_Final_Call extends EVP_Cipher_Operation_Call{ - // override Expr getInputArg() { none() } - // } - // class EVP_Cipher_Call extends EVP_Cipher_Operation_Call{ - // // TODO/QUESTION: what is the better way to do this? - // EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } - // override Expr getInputArg() { result = this.(Call).getArgument(2) } - // override Expr getOutputArg() { result = this.(Call).getArgument(1) } - // override Crypto::CipherOperationSubtype getCipherOperationSubtype(){ - // result instanceof Crypto::EncryptionSubtype - // } - // override Expr getInitCall(){ - // //TODO: - // none() - // } - // override Crypto::NonceArtifactConsumer getNonceConsumer(){ - // none() - // } - // override Crypto::CipherInputConsumer getInputConsumer(){ - // none() - // } - // override Crypto::CipherOutputArtifactInstance getOutputArtifact(){ - // none() - // } - // override Crypto::AlgorithmConsumer getAlgorithmConsumer(){ - // none() - // } - // } - //TODO: what about EVP_CIpher - // class EVP_EncryptUpdateCall extends Crypto::CipherOperationInstance instanceof Call { - // // NICK QUESTION: is there a better way to tie this to openssl? - // EVP_EncryptUpdateCall() { this.getTarget().getName() = "EVP_EncryptUpdate" } - // Expr getContextArg() { result = super.getArgument(0) } - // Expr getInputArg() { result = super.getArgument(3) } - // Expr getOutputArg() { result = super.getArgument(1) } - // override Crypto::CipherOperationSubtype getCipherOperationSubtype(){ - // result instanceof Crypto::EncryptionSubtype - // } - // override Crypto::NonceArtifactConsumer getNonceConsumer(){ - // none() - // } - // override Crypto::CipherInputConsumer getInputConsumer(){ - // none() - // } - // override Crypto::CipherOutputArtifactInstance getOutputArtifact(){ - // none() - // } - // override Crypto::AlgorithmConsumer getAlgorithmConsumer(){ - // none() - // } - // } - //EVP_EncryptUpdate - // /** - // * Hash function references in OpenSSL. - // */ - // predicate hash_ref_type_mapping_known(string name, Crypto::THashType algo) { - // // `ma` name has an LN_ or SN_ prefix, which we want to ignore - // // capture any name after the _ prefix using regex matching - // name = ["sha1", "sha160"] and algo instanceof Crypto::SHA1 - // or - // name = ["sha224", "sha256", "sha384", "sha512"] and algo instanceof Crypto::SHA2 - // or - // name = ["sha3-224", "sha3-256", "sha3-384", "sha3-512"] and algo instanceof Crypto::SHA3 - // or - // name = "md2" and algo instanceof Crypto::MD2 - // or - // name = "md4" and algo instanceof Crypto::MD4 - // or - // name = "md5" and algo instanceof Crypto::MD5 - // or - // name = "ripemd160" and algo instanceof Crypto::RIPEMD160 - // or - // name = "whirlpool" and algo instanceof Crypto::WHIRLPOOL - // } - // predicate hash_ref_type_mapping(FunctionCallOrMacroAccess ref, string name, Crypto::THashType algo) { - // name = ref.getTargetName().regexpCapture("(?:SN_|LN_|EVP_)([a-z0-9]+)", 1) and - // hash_ref_type_mapping_known(name, algo) - // } - // class FunctionCallOrMacroAccess extends Element { - // FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess } - // string getTargetName() { - // result = this.(FunctionCall).getTarget().getName() - // or - // result = this.(MacroAccess).getMacroName() - // } - // } - // class HashAlgorithmCallOrMacro extends Crypto::HashAlgorithmInstance instanceof FunctionCallOrMacroAccess - // { - // HashAlgorithmCallOrMacro() { hash_ref_type_mapping(this, _, _) } - // string getTargetName() { result = this.(FunctionCallOrMacroAccess).getTargetName() } - // } - // class HashAlgorithm extends Crypto::HashAlgorithm { - // HashAlgorithmCallOrMacro instance; - // HashAlgorithm() { this = Crypto::THashAlgorithm(instance) } - // override string getSHA2OrSHA3DigestSize(Location location) { - // ( - // this.getHashType() instanceof Crypto::SHA2 or - // this.getHashType() instanceof Crypto::SHA3 - // ) and - // exists(string name | - // hash_ref_type_mapping(instance, name, this.getHashType()) and - // result = name.regexpFind("\\d{3}", 0, _) and - // location = instance.getLocation() - // ) - // } - // override string getRawAlgorithmName() { result = instance.getTargetName() } - // override Crypto::THashType getHashType() { hash_ref_type_mapping(instance, _, result) } - // Element getInstance() { result = instance } - // override Location getLocation() { result = instance.getLocation() } - // } - // /** - // * Data-flow configuration for key derivation algorithm flow to EVP_KDF_derive. - // */ - // module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig { - // predicate isSource(DataFlow::Node source) { - // source.asExpr() = any(KeyDerivationAlgorithm a).getInstance() - // } - // predicate isSink(DataFlow::Node sink) { - // exists(EVP_KDF_derive kdo | - // sink.asExpr() = kdo.getCall().getAlgorithmArg() - // or - // sink.asExpr() = kdo.getCall().getContextArg() // via `EVP_KDF_CTX_set_params` - // ) - // } - // predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - // none() // TODO - // } - // } - // module AlgorithmToEVPKeyDeriveFlow = DataFlow::Global; - // predicate algorithm_to_EVP_KDF_derive(KeyDerivationAlgorithm algo, EVP_KDF_derive derive) { - // none() - // } - // /** - // * Key derivation operation (e.g., `EVP_KDF_derive`) - // */ - // class EVP_KDF_derive_FunctionCall extends Crypto::KeyDerivationOperationInstance instanceof FunctionCall - // { - // EVP_KDF_derive_FunctionCall() { this.getTarget().getName() = "EVP_KDF_derive" } - // Expr getAlgorithmArg() { result = super.getArgument(3) } - // Expr getContextArg() { result = super.getArgument(0) } - // } - // class EVP_KDF_derive extends Crypto::KeyDerivationOperation { - // EVP_KDF_derive_FunctionCall instance; - // EVP_KDF_derive() { this = Crypto::TKeyDerivationOperation(instance) } - // override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) } - // EVP_KDF_derive_FunctionCall getCall() { result = instance } - // } - // /** - // * Key derivation algorithm nodes - // */ - // abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm { - // abstract Expr getInstance(); - // } - // /** - // * `EVP_KDF_fetch` returns a key derivation algorithm. - // */ - // class EVP_KDF_fetch_Call extends FunctionCall { - // EVP_KDF_fetch_Call() { this.getTarget().getName() = "EVP_KDF_fetch" } - // Expr getAlgorithmArg() { result = this.getArgument(1) } - // } - // class EVP_KDF_fetch_AlgorithmArg extends Crypto::KeyDerivationAlgorithmInstance instanceof Expr { - // EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) } - // } - // predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF", "PBKDF2"] } - // class KDFAlgorithmStringLiteral extends StringLiteral { - // KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) } - // } - // private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig { - // predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KDFAlgorithmStringLiteral } - // predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof EVP_KDF_fetch_AlgorithmArg } - // } - // module AlgorithmStringToFetchFlow = DataFlow::Global; - // predicate algorithmStringToKDFFetchArgFlow( - // string name, KDFAlgorithmStringLiteral origin, EVP_KDF_fetch_AlgorithmArg arg - // ) { - // origin.getValue().toUpperCase() = name and - // AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg)) - // } - // /** - // * HKDF key derivation algorithm. - // */ - // class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF { - // KDFAlgorithmStringLiteral origin; - // EVP_KDF_fetch_AlgorithmArg instance; - // HKDF() { - // this = Crypto::TKeyDerivationAlgorithm(instance) and - // algorithmStringToKDFFetchArgFlow("HKDF", origin, instance) - // } - // override string getRawAlgorithmName() { result = origin.getValue() } - // override Crypto::HashAlgorithm getHashAlgorithm() { none() } - // override Crypto::LocatableElement getOrigin(string name) { - // result = origin and name = origin.toString() - // } - // override Expr getInstance() { result = origin } - // } - // /** - // * PBKDF2 key derivation algorithm. - // */ - // class PBKDF2 extends KeyDerivationAlgorithm, Crypto::PBKDF2 { - // KDFAlgorithmStringLiteral origin; - // EVP_KDF_fetch_AlgorithmArg instance; - // PBKDF2() { - // this = Crypto::TKeyDerivationAlgorithm(instance) and - // algorithmStringToKDFFetchArgFlow("PBKDF2", origin, instance) - // } - // override string getRawAlgorithmName() { result = origin.getValue() } - // override string getIterationCount(Location location) { none() } // TODO - // override string getKeyLength(Location location) { none() } // TODO - // override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO - // override Crypto::LocatableElement getOrigin(string name) { - // result = origin and name = origin.toString() - // } - // override Expr getInstance() { result = instance } - // } - // /** - // * PKCS12KDF key derivation algorithm. - // */ - // class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF { - // KDFAlgorithmStringLiteral origin; - // EVP_KDF_fetch_AlgorithmArg instance; - // PKCS12KDF() { - // this = Crypto::TKeyDerivationAlgorithm(instance) and - // algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, instance) - // } - // override string getRawAlgorithmName() { result = origin.getValue() } - // override string getIterationCount(Location location) { none() } // TODO - // override string getIDByte(Location location) { none() } // TODO - // override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO - // override Crypto::LocatableElement getOrigin(string name) { - // result = origin and name = origin.toString() - // } - // override Expr getInstance() { result = instance } - // } + import experimental.Quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstances + import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers + import experimental.Quantum.OpenSSL.Operations.OpenSSLOperations } From c08525ad81b4bd600661e002bc1624fbc2024d66 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 2 May 2025 14:07:13 -0400 Subject: [PATCH 093/122] Additional cleanup --- cpp/ql/lib/experimental/Quantum/Language.qll | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index 7e1857cc68af..2d076a1323c0 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -112,12 +112,4 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig { module ArtifactUniversalFlow = DataFlow::Global; -// abstract class CipherOutputArtifact extends Crypto::KeyOperationOutputArtifactInstance { -// override predicate flowsTo(Crypto::FlowAwareElement other) { -// ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) -// } -// } -// // final override predicate flowsTo(FlowAwareElement other) { -// // Input::artifactOutputFlowsToGenericInput(this.getOutputNode(), other.getInputNode()) -// // } import OpenSSL.OpenSSL From 7481de75cbe4d0fa0180e32d1eeddae6a41415e8 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 2 May 2025 14:07:38 -0400 Subject: [PATCH 094/122] Updating the model to infer implicit cipher key sizes. --- .../codeql/cryptography/Model.qll | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index a7938b1d1ce7..8fcc304e9ef9 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -582,6 +582,28 @@ module CryptographyBase Input> { } } + predicate fixedImplicitCipherKeySize(TAlgorithm type, int size) { + type = TSymmetricCipher(DES()) and size = 56 + or + type = TSymmetricCipher(DESX()) and size = 184 + or + type = TSymmetricCipher(DoubleDES()) and size = 112 + or + type = TSymmetricCipher(TripleDES()) and size = 168 + or + type = TSymmetricCipher(CHACHA20()) and size = 256 + or + type = TSymmetricCipher(IDEA()) and size = 128 + or + type = TSymmetricCipher(KUZNYECHIK()) and size = 256 + or + type = TSymmetricCipher(MAGMA()) and size = 256 + or + type = TSymmetricCipher(SM4()) and size = 128 + or + type = TSymmetricCipher(SEED()) and size = 128 + } + bindingset[type] predicate symmetric_cipher_to_name_and_structure( TSymmetricCipherType type, string name, CipherStructureType s @@ -790,6 +812,10 @@ module CryptographyBase Input> { * If a specific key size is unknown, this predicate should be implemented as `none()`. * * If the algorithm accepts a range of key sizes without a particular one specified, this predicate should be implemented as `none()`. + * + * NOTE: if the algorithm has a single key size, the implicit key size does not need to be modeled. + * This will be automatically inferred and applied at the node level. + * See `fixedImplicitCipherKeySize`. */ abstract string getKeySizeFixed(); @@ -2178,7 +2204,14 @@ module CryptographyBase Input> { /** * Gets the key size variant of this algorithm in bits, e.g., 128 for "AES-128". */ - string getKeySizeFixed() { result = instance.asAlg().getKeySizeFixed() } // TODO: key sizes for known algorithms + string getKeySizeFixed() { + result = instance.asAlg().getKeySizeFixed() + or + exists(int size | + KeyOpAlg::fixedImplicitCipherKeySize(instance.asAlg().getAlgorithmType(), size) and + result = size.toString() + ) + } /** * Gets the key size generic source node. From 09d473674b060b748cfb0e1b65e4c6bd526169a0 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 2 May 2025 14:10:38 -0400 Subject: [PATCH 095/122] Working refactor for cipher, padding, block mode. Still haven't completed connecting padding to algorithm instances if through a set padding interface. --- .../AlgorithmInstances/AlgToAVCFlow.qll | 170 ++++++++ .../BlockAlgorithmInstance.qll | 76 ++++ .../CipherAlgorithmInstance.qll | 129 ++++++ .../KnownAlgorithmConstants.qll} | 401 ++++++++++++------ .../OpenSSLAlgorithmInstanceBase.qll | 6 + .../OpenSSLAlgorithmInstances.qll | 4 + .../PaddingAlgorithmInstance.qll | 167 ++++++++ .../CipherAlgorithmValueConsumer.qll | 39 ++ .../DirectAlgorithmValueConsumer.qll | 36 ++ .../OpenSSLAlgorithmValueConsumerBase.qll | 9 + .../OpenSSLAlgorithmValueConsumers.qll | 4 + .../PaddingAlgorithmValueConsumer.qll | 36 ++ .../Operations/EVPCipherInitializer.qll | 123 ++++++ .../OpenSSL/Operations/EVPCipherOperation.qll | 114 +++++ .../Operations/OpenSSLOperationBase.qll | 21 + .../OpenSSL/Operations/OpenSSLOperations.qll | 1 + 16 files changed, 1205 insertions(+), 131 deletions(-) create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll rename cpp/ql/lib/experimental/Quantum/OpenSSL/{OpenSSLKnownAlgorithmConstants.qll => AlgorithmInstances/KnownAlgorithmConstants.qll} (90%) create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll new file mode 100644 index 000000000000..25cdea6accf7 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll @@ -0,0 +1,170 @@ +import cpp +import semmle.code.cpp.dataflow.new.DataFlow +import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers // import all known alg value consummers + +/** + * Traces 'known algorithms' to AVCs, specifically + * algorithms that are in the set of known algorithm constants. + * Padding-specific consumers exist that have their own values that + * overlap with the known algorithm constants. + * Padding consumers (specific padding consumers) are excluded from the set of sinks. + */ +module KnownOpenSSLAlgorithmToAlgorithmValueConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof KnownOpenSSLAlgorithmConstant + } + + predicate isSink(DataFlow::Node sink) { + exists(OpenSSLAlgorithmValueConsumer c | + c.getInputNode() = sink and + not c instanceof PaddingAlgorithmValueConsumer + ) + } + + predicate isBarrier(DataFlow::Node node) { + // False positive reducer, don't flow out through argv + exists(VariableAccess va, Variable v | + v.getAnAccess() = va and va = node.asExpr() + or + va = node.asIndirectExpr() + | + v.getName().matches("%argv") + ) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + knownPassThroughStep(node1, node2) + } +} + +module KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow = + DataFlow::Global; + +module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof KnownOpenSSLAlgorithmConstant + } + + predicate isSink(DataFlow::Node sink) { + exists(PaddingAlgorithmValueConsumer c | c.getInputNode() = sink) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + knownPassThroughStep(node1, node2) + } +} + +module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow = + DataFlow::Global; + +class OpenSSLAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep { + OpenSSLAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) } + + override DataFlow::Node getOutput() { + exists(AlgorithmPassthroughCall c | c.getInNode() = this and c.getOutNode() = result) + } +} + +abstract class AlgorithmPassthroughCall extends Call { + abstract DataFlow::Node getInNode(); + + abstract DataFlow::Node getOutNode(); +} + +class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall { + DataFlow::Node inNode; + DataFlow::Node outNode; + + CopyAndDupAlgorithmPassthroughCall() { + // Flow out through any return or other argument of the same type + // Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed + // to be involved + // NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup + this.getTarget().getName().toLowerCase().matches(["%_dup%", "%_copy%"]) and + exists(Expr inArg, Type t | + inArg = this.getAnArgument() and t = inArg.getUnspecifiedType().stripType() + | + inNode.asIndirectExpr() = inArg and + ( + // Case 1: flow through another argument as an out arg of the same type + exists(Expr outArg | + outArg = this.getAnArgument() and + outArg != inArg and + outArg.getUnspecifiedType().stripType() = t + | + outNode.asDefiningArgument() = outArg + ) + or + // Case 2: flow through the return value if the result is the same as the intput type + exists(Expr outArg | outArg = this and outArg.getUnspecifiedType().stripType() = t | + outNode.asIndirectExpr() = outArg + ) + ) + ) + } + + override DataFlow::Node getInNode() { result = inNode } + + override DataFlow::Node getOutNode() { result = outNode } +} + +class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall { + DataFlow::Node inNode; + DataFlow::Node outNode; + + NIDToPointerPassthroughCall() { + this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and + inNode.asExpr() = this.getArgument(0) and + outNode.asExpr() = this + //outNode.asIndirectExpr() = this + } + + override DataFlow::Node getInNode() { result = inNode } + + override DataFlow::Node getOutNode() { result = outNode } +} + +class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall { + DataFlow::Node inNode; + DataFlow::Node outNode; + + PointerToPointerPassthroughCall() { + this.getTarget().getName() = "OBJ_txt2obj" and + inNode.asIndirectExpr() = this.getArgument(0) and + outNode.asIndirectExpr() = this + or + //outNode.asExpr() = this + this.getTarget().getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and + inNode.asIndirectExpr() = this.getArgument(2) and + outNode.asDefiningArgument() = this.getArgument(0) + } + + override DataFlow::Node getInNode() { result = inNode } + + override DataFlow::Node getOutNode() { result = outNode } +} + +class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall { + DataFlow::Node inNode; + DataFlow::Node outNode; + + PointerToNIDPassthroughCall() { + this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and + ( + inNode.asIndirectExpr() = this.getArgument(0) + or + inNode.asExpr() = this.getArgument(0) + ) and + outNode.asExpr() = this + } + + override DataFlow::Node getInNode() { result = inNode } + + override DataFlow::Node getOutNode() { result = outNode } +} + +// TODO: pkeys pass through EVP_PKEY_CTX_new and any similar variant +predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(AlgorithmPassthroughCall c | c.getInNode() = node1 and c.getOutNode() = node2) +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll new file mode 100644 index 000000000000..1e17c64f34ca --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll @@ -0,0 +1,76 @@ +import cpp +import experimental.Quantum.Language +import OpenSSLAlgorithmInstanceBase +import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer +import AlgToAVCFlow + +/** + * Given a `KnownOpenSSLBlockModeAlgorithmConstant`, converts this to a block family type. + * Does not bind if there is know mapping (no mapping to 'unknown' or 'other'). + */ +predicate knownOpenSSLConstantToBlockModeFamilyType( + KnownOpenSSLBlockModeAlgorithmConstant e, Crypto::TBlockCipherModeOfOperationType type +) { + exists(string name | + name = e.getNormalizedName() and + ( + name.matches("CBC") and type instanceof Crypto::CBC + or + name.matches("CFB%") and type instanceof Crypto::CFB + or + name.matches("CTR") and type instanceof Crypto::CTR + or + name.matches("GCM") and type instanceof Crypto::GCM + or + name.matches("OFB") and type instanceof Crypto::OFB + or + name.matches("XTS") and type instanceof Crypto::XTS + or + name.matches("CCM") and type instanceof Crypto::CCM + or + name.matches("GCM") and type instanceof Crypto::GCM + or + name.matches("CCM") and type instanceof Crypto::CCM + or + name.matches("ECB") and type instanceof Crypto::ECB + ) + ) +} + +class KnownOpenSSLBlockModeConstantAlgorithmInstance extends OpenSSLAlgorithmInstance, + Crypto::ModeOfOperationAlgorithmInstance instanceof KnownOpenSSLBlockModeAlgorithmConstant +{ + OpenSSLAlgorithmValueConsumer getterCall; + + KnownOpenSSLBlockModeConstantAlgorithmInstance() { + // Two possibilities: + // 1) The source is a literal and flows to a getter, then we know we have an instance + // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that + // Possibility 1: + this instanceof Literal and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a CipherGetterCall + sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a getter + KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) + ) + or + // Possibility 2: + this instanceof DirectAlgorithmValueConsumer and getterCall = this + } + + override Crypto::TBlockCipherModeOfOperationType getModeType() { + knownOpenSSLConstantToBlockModeFamilyType(this, result) + or + not knownOpenSSLConstantToBlockModeFamilyType(this, _) and result = Crypto::OtherMode() + } + + // NOTE: I'm not going to attempt to parse out the mode specific part, so returning + // the same as the raw name for now. + override string getRawModeAlgorithmName() { result = this.(Literal).getValue().toString() } + + override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll new file mode 100644 index 000000000000..03f2643d46d2 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll @@ -0,0 +1,129 @@ +import cpp +import experimental.Quantum.Language +import KnownAlgorithmConstants +import Crypto::KeyOpAlg as KeyOpAlg +import OpenSSLAlgorithmInstanceBase +import PaddingAlgorithmInstance +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +import AlgToAVCFlow +import BlockAlgorithmInstance + +/** + * Given a `KnownOpenSSLCipherAlgorithmConstant`, converts this to a cipher family type. + * Does not bind if there is know mapping (no mapping to 'unknown' or 'other'). + */ +predicate knownOpenSSLConstantToCipherFamilyType( + KnownOpenSSLCipherAlgorithmConstant e, Crypto::KeyOpAlg::TAlgorithm type +) { + exists(string name | + name = e.getNormalizedName() and + ( + name.matches("AES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::AES()) + or + name.matches("ARIA%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::ARIA()) + or + name.matches("BLOWFISH%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::BLOWFISH()) + or + name.matches("BF%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::BLOWFISH()) + or + name.matches("CAMELLIA%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CAMELLIA()) + or + name.matches("CHACHA20%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CHACHA20()) + or + name.matches("CAST5%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CAST5()) + or + name.matches("2DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DoubleDES()) + or + name.matches("3DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::TripleDES()) + or + name.matches("DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DES()) + or + name.matches("DESX%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DESX()) + or + name.matches("GOST%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::GOST()) + or + name.matches("IDEA%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::IDEA()) + or + name.matches("KUZNYECHIK%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::KUZNYECHIK()) + or + name.matches("MAGMA%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::MAGMA()) + or + name.matches("RC2%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC2()) + or + name.matches("RC4%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC4()) + or + name.matches("RC5%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC5()) + or + name.matches("RSA%") and type = KeyOpAlg::TAsymmetricCipher(KeyOpAlg::RSA()) + or + name.matches("SEED%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::SEED()) + or + name.matches("SM4%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::SM4()) + ) + ) +} + +class KnownOpenSSLCipherConstantAlgorithmInstance extends OpenSSLAlgorithmInstance, + Crypto::KeyOperationAlgorithmInstance instanceof KnownOpenSSLCipherAlgorithmConstant +{ + //OpenSSLAlgorithmInstance, + OpenSSLAlgorithmValueConsumer getterCall; + + KnownOpenSSLCipherConstantAlgorithmInstance() { + ( + // Two possibilities: + // 1) The source is a literal and flows to a getter, then we know we have an instance + // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that + // Possibility 1: + this instanceof Literal and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a CipherGetterCall + sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a getter + KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) + ) + or + // Possibility 2: + this instanceof DirectAlgorithmValueConsumer and getterCall = this + ) + } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { + // if there is a block mode associated with the same element, then that's the block mode + // note, if none are associated, we may need to parse if the cipher is a block cipher + // to determine if this is an unknown vs not relevant. + result = this + } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { + //TODO: the padding is either self, or it flows through getter ctx to a set padding call + // like EVP_PKEY_CTX_set_rsa_padding + result = this + // or trace through getter ctx to set padding + } + + override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } + + override string getKeySizeFixed() { + exists(int keySize | + this.(KnownOpenSSLCipherAlgorithmConstant).getExplicitKeySize() = keySize and + result = keySize.toString() + ) + } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + knownOpenSSLConstantToCipherFamilyType(this, result) + or + not knownOpenSSLConstantToCipherFamilyType(this, _) and + result = Crypto::KeyOpAlg::TUnknownKeyOperationAlgorithmType() + } + + override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + // TODO: trace to any key size initializer, symmetric and asymmetric + none() + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll similarity index 90% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll rename to cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll index 0d328e287f6a..80bd6b422da4 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLKnownAlgorithmConstants.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll @@ -1,5 +1,5 @@ import cpp -import LibraryDetector +import experimental.Quantum.OpenSSL.LibraryDetector class KnownOpenSSLAlgorithmConstant extends Expr { string normalizedName; @@ -16,6 +16,28 @@ class KnownOpenSSLAlgorithmConstant extends Expr { string getAlgType() { result = algType } } +class KnownOpenSSLCipherAlgorithmConstant extends KnownOpenSSLAlgorithmConstant { + KnownOpenSSLCipherAlgorithmConstant() { + this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("%encryption") + } + + int getExplicitKeySize() { + result = this.getNormalizedName().regexpCapture(".*-(\\d*)", 1).toInt() + } +} + +class KnownOpenSSLPaddingAlgorithmConstant extends KnownOpenSSLAlgorithmConstant { + KnownOpenSSLPaddingAlgorithmConstant() { + this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("%padding") + } +} + +class KnownOpenSSLBlockModeAlgorithmConstant extends KnownOpenSSLAlgorithmConstant { + KnownOpenSSLBlockModeAlgorithmConstant() { + this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("block_mode") + } +} + /** * Resolves a call to a 'direct algorithm getter', e.g., EVP_MD5() * This approach to fetching algorithms was used in OpenSSL 1.0.2. @@ -222,9 +244,9 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "gost2001" and nid = 811 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" or - name = "gost2012_256" and nid = 979 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + name = "gost2012_256" and nid = 979 and normalized = "GOST" and algType = "HASH" // TODO: Verify algorithm type or - name = "gost2012_512" and nid = 980 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" + name = "gost2012_512" and nid = 980 and normalized = "GOST" and algType = "HASH" // TODO: Verify algorithm type or name = "ed25519" and nid = 1087 and normalized = "ED25519" and algType = "ELLIPTIC_CURVE" or @@ -276,159 +298,222 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "sm3" and nid = 1143 and normalized = "SM3" and algType = "HASH" or - name = "aes-128-cbc" and nid = 419 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-cbc" and nid = 419 and normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-cbc" and nid = 419 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aes-128-ecb" and nid = 418 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-ecb" and nid = 418 and normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-ecb" and nid = 418 and normalized = "ECB" and algType = "BLOCK_MODE" or - name = "aes-192-cbc" and nid = 423 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-cbc" and nid = 423 and normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-cbc" and nid = 423 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aes-192-ecb" and nid = 422 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-ecb" and nid = 422 and normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-ecb" and nid = 422 and normalized = "ECB" and algType = "BLOCK_MODE" or - name = "aes-256-cbc" and nid = 427 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-cbc" and nid = 427 and normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-cbc" and nid = 427 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aes-256-ecb" and nid = 426 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-ecb" and nid = 426 and normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-ecb" and nid = 426 and normalized = "ECB" and algType = "BLOCK_MODE" or name = "aria-128-cbc" and nid = 1066 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aria-128-cbc" and nid = 1066 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-cbc" and + nid = 1066 and + normalized = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-cfb" and nid = 1067 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aria-128-cfb" and nid = 1067 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-cfb" and + nid = 1067 and + normalized = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-ctr" and nid = 1069 and normalized = "CTR" and algType = "BLOCK_MODE" or - name = "aria-128-ctr" and nid = 1069 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-ctr" and + nid = 1069 and + normalized = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-ecb" and nid = 1065 and normalized = "ECB" and algType = "BLOCK_MODE" or - name = "aria-128-ecb" and nid = 1065 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-ecb" and + nid = 1065 and + normalized = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-ofb" and nid = 1068 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "aria-128-ofb" and nid = 1068 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-ofb" and + nid = 1068 and + normalized = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-cfb1" and nid = 1080 and normalized = "CFB1" and algType = "BLOCK_MODE" or - name = "aria-128-cfb1" and nid = 1080 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-cfb1" and + nid = 1080 and + normalized = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" or - name = "aria-128-cfb8" and nid = 1083 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-cfb8" and + nid = 1083 and + normalized = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-cfb8" and nid = 1083 and normalized = "CFB8" and algType = "BLOCK_MODE" or name = "aria-192-cbc" and nid = 1071 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aria-192-cbc" and nid = 1071 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-cbc" and + nid = 1071 and + normalized = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-cfb" and nid = 1072 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aria-192-cfb" and nid = 1072 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-cfb" and + nid = 1072 and + normalized = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-ctr" and nid = 1074 and normalized = "CTR" and algType = "BLOCK_MODE" or - name = "aria-192-ctr" and nid = 1074 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-ctr" and + nid = 1074 and + normalized = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-ecb" and nid = 1070 and normalized = "ECB" and algType = "BLOCK_MODE" or - name = "aria-192-ecb" and nid = 1070 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-ecb" and + nid = 1070 and + normalized = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-ofb" and nid = 1073 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "aria-192-ofb" and nid = 1073 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-ofb" and + nid = 1073 and + normalized = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-cfb1" and nid = 1081 and normalized = "CFB1" and algType = "BLOCK_MODE" or - name = "aria-192-cfb1" and nid = 1081 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-cfb1" and + nid = 1081 and + normalized = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" or - name = "aria-192-cfb8" and nid = 1084 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-cfb8" and + nid = 1084 and + normalized = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-cfb8" and nid = 1084 and normalized = "CFB8" and algType = "BLOCK_MODE" or name = "aria-256-cbc" and nid = 1076 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aria-256-cbc" and nid = 1076 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-cbc" and + nid = 1076 and + normalized = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-cfb" and nid = 1077 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aria-256-cfb" and nid = 1077 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-cfb" and + nid = 1077 and + normalized = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-ctr" and nid = 1079 and normalized = "CTR" and algType = "BLOCK_MODE" or - name = "aria-256-ctr" and nid = 1079 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-ctr" and + nid = 1079 and + normalized = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-ecb" and nid = 1075 and normalized = "ECB" and algType = "BLOCK_MODE" or - name = "aria-256-ecb" and nid = 1075 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-ecb" and + nid = 1075 and + normalized = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-ofb" and nid = 1078 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "aria-256-ofb" and nid = 1078 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-ofb" and + nid = 1078 and + normalized = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-cfb1" and nid = 1082 and normalized = "CFB1" and algType = "BLOCK_MODE" or - name = "aria-256-cfb1" and nid = 1082 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-cfb1" and + nid = 1082 and + normalized = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" or - name = "aria-256-cfb8" and nid = 1085 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-cfb8" and + nid = 1085 and + normalized = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-cfb8" and nid = 1085 and normalized = "CFB8" and algType = "BLOCK_MODE" or name = "camellia-128-cbc" and nid = 751 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-cbc" and nid = 751 and normalized = "CBC" and algType = "BLOCK_MODE" or name = "camellia-128-ecb" and nid = 754 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-ecb" and nid = 754 and normalized = "ECB" and algType = "BLOCK_MODE" or name = "camellia-192-cbc" and nid = 752 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-cbc" and nid = 752 and normalized = "CBC" and algType = "BLOCK_MODE" or name = "camellia-192-ecb" and nid = 755 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-ecb" and nid = 755 and normalized = "ECB" and algType = "BLOCK_MODE" or name = "camellia-256-cbc" and nid = 753 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-cbc" and nid = 753 and normalized = "CBC" and algType = "BLOCK_MODE" or name = "camellia-256-ecb" and nid = 756 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-ecb" and nid = 756 and normalized = "ECB" and algType = "BLOCK_MODE" or name = "rc4" and nid = 5 and normalized = "RC4" and algType = "SYMMETRIC_ENCRYPTION" or - name = "rc4-40" and nid = 97 and normalized = "RC4" and algType = "SYMMETRIC_ENCRYPTION" + name = "rc4-40" and nid = 97 and normalized = "RC4-40" and algType = "SYMMETRIC_ENCRYPTION" or name = "des-ecb" and nid = 29 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" or @@ -510,7 +595,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "seed-ofb" and nid = 778 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "rc2-cbc" and nid = 37 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + name = "rc2-cbc" and nid = 37 and normalized = "RC2-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "rc2-cbc" and nid = 37 and normalized = "CBC" and algType = "BLOCK_MODE" or @@ -526,11 +611,11 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "rc2-ofb" and nid = 40 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "rc2-64-cbc" and nid = 166 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + name = "rc2-64-cbc" and nid = 166 and normalized = "RC2-64" and algType = "SYMMETRIC_ENCRYPTION" or name = "rc2-64-cbc" and nid = 166 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "rc2-40-cbc" and nid = 98 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + name = "rc2-40-cbc" and nid = 98 and normalized = "RC2-40" and algType = "SYMMETRIC_ENCRYPTION" or name = "rc2-40-cbc" and nid = 98 and normalized = "CBC" and algType = "BLOCK_MODE" or @@ -586,7 +671,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "sm4-ctr" and nid = 1139 and normalized = "CTR" and algType = "BLOCK_MODE" or - name = "aes-128-gcm" and nid = 895 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-gcm" and nid = 895 and normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-gcm" and nid = 895 and normalized = "GCM" and algType = "BLOCK_MODE" or @@ -604,32 +689,32 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "id-aes128-wrap" and nid = 788 and - normalized = "AES128" and + normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes192-wrap" and nid = 789 and - normalized = "AES192" and + normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes256-wrap" and nid = 790 and - normalized = "AES256" and + normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes128-wrap-pad" and nid = 897 and - normalized = "AES128" and + normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes192-wrap-pad" and nid = 900 and - normalized = "AES192" and + normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes256-wrap-pad" and nid = 903 and - normalized = "AES256" and + normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "chacha20" and nid = 1019 and normalized = "CHACHA20" and algType = "SYMMETRIC_ENCRYPTION" @@ -738,7 +823,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "pkcs5" and nid = 187 and normalized = "PKCS5" and algType = "KEY_DERIVATION" or - name = "aes-256-gcm" and nid = 901 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-gcm" and nid = 901 and normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-gcm" and nid = 901 and normalized = "GCM" and algType = "BLOCK_MODE" or @@ -775,53 +860,71 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "id-alg-dh-sig-hmac-sha1" and nid = 325 and normalized = "SHA1" and algType = "HASH" or - name = "aes-128-ofb" and nid = 420 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-ofb" and nid = 420 and normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-ofb" and nid = 420 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "aes-128-cfb" and nid = 421 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-cfb" and nid = 421 and normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-cfb" and nid = 421 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aes-192-ofb" and nid = 424 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-ofb" and nid = 424 and normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-ofb" and nid = 424 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "aes-192-cfb" and nid = 425 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-cfb" and nid = 425 and normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-cfb" and nid = 425 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aes-256-ofb" and nid = 428 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-ofb" and nid = 428 and normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-ofb" and nid = 428 and normalized = "OFB" and algType = "BLOCK_MODE" or - name = "aes-256-cfb" and nid = 429 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-cfb" and nid = 429 and normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-cfb" and nid = 429 and normalized = "CFB" and algType = "BLOCK_MODE" or name = "des-cdmf" and nid = 643 and normalized = "DES" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aes-128-cfb1" and nid = 650 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-cfb1" and + nid = 650 and + normalized = "AES-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-cfb1" and nid = 650 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aes-192-cfb1" and nid = 651 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-cfb1" and + nid = 651 and + normalized = "AES-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-cfb1" and nid = 651 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aes-256-cfb1" and nid = 652 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-cfb1" and + nid = 652 and + normalized = "AES-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-cfb1" and nid = 652 and normalized = "CFB" and algType = "BLOCK_MODE" or - name = "aes-128-cfb8" and nid = 653 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-cfb8" and + nid = 653 and + normalized = "AES-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-cfb8" and nid = 653 and normalized = "CFB8" and algType = "BLOCK_MODE" or - name = "aes-192-cfb8" and nid = 654 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-cfb8" and + nid = 654 and + normalized = "AES-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-cfb8" and nid = 654 and normalized = "CFB8" and algType = "BLOCK_MODE" or - name = "aes-256-cfb8" and nid = 655 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-cfb8" and + nid = 655 and + normalized = "AES-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-cfb8" and nid = 655 and normalized = "CFB8" and algType = "BLOCK_MODE" or @@ -851,84 +954,84 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "camellia-128-cfb" and nid = 757 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-cfb" and nid = 757 and normalized = "CFB" and algType = "BLOCK_MODE" or name = "camellia-192-cfb" and nid = 758 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-cfb" and nid = 758 and normalized = "CFB" and algType = "BLOCK_MODE" or name = "camellia-256-cfb" and nid = 759 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-cfb" and nid = 759 and normalized = "CFB" and algType = "BLOCK_MODE" or name = "camellia-128-cfb1" and nid = 760 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-cfb1" and nid = 760 and normalized = "CFB" and algType = "BLOCK_MODE" or name = "camellia-192-cfb1" and nid = 761 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-cfb1" and nid = 761 and normalized = "CFB" and algType = "BLOCK_MODE" or name = "camellia-256-cfb1" and nid = 762 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-cfb1" and nid = 762 and normalized = "CFB" and algType = "BLOCK_MODE" or name = "camellia-128-cfb8" and nid = 763 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-cfb8" and nid = 763 and normalized = "CFB8" and algType = "BLOCK_MODE" or name = "camellia-192-cfb8" and nid = 764 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-cfb8" and nid = 764 and normalized = "CFB8" and algType = "BLOCK_MODE" or name = "camellia-256-cfb8" and nid = 765 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-cfb8" and nid = 765 and normalized = "CFB8" and algType = "BLOCK_MODE" or name = "camellia-128-ofb" and nid = 766 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-ofb" and nid = 766 and normalized = "OFB" and algType = "BLOCK_MODE" or name = "camellia-192-ofb" and nid = 767 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-ofb" and nid = 767 and normalized = "OFB" and algType = "BLOCK_MODE" or name = "camellia-256-ofb" and nid = 768 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-ofb" and nid = 768 and normalized = "OFB" and algType = "BLOCK_MODE" @@ -956,56 +1059,56 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "gost2001cc" and nid = 851 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aes-128-ccm" and nid = 896 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-ccm" and nid = 896 and normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-ccm" and nid = 896 and normalized = "CCM" and algType = "BLOCK_MODE" or - name = "aes-192-gcm" and nid = 898 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-gcm" and nid = 898 and normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-gcm" and nid = 898 and normalized = "GCM" and algType = "BLOCK_MODE" or - name = "aes-192-ccm" and nid = 899 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-ccm" and nid = 899 and normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-ccm" and nid = 899 and normalized = "CCM" and algType = "BLOCK_MODE" or - name = "aes-256-ccm" and nid = 902 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-ccm" and nid = 902 and normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-ccm" and nid = 902 and normalized = "CCM" and algType = "BLOCK_MODE" or - name = "aes-128-ctr" and nid = 904 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-ctr" and nid = 904 and normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-ctr" and nid = 904 and normalized = "CTR" and algType = "BLOCK_MODE" or - name = "aes-192-ctr" and nid = 905 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-ctr" and nid = 905 and normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-ctr" and nid = 905 and normalized = "CTR" and algType = "BLOCK_MODE" or - name = "aes-256-ctr" and nid = 906 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-ctr" and nid = 906 and normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-ctr" and nid = 906 and normalized = "CTR" and algType = "BLOCK_MODE" or name = "id-camellia128-wrap" and nid = 907 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-camellia192-wrap" and nid = 908 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-camellia256-wrap" and nid = 909 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "mgf1" and nid = 911 and normalized = "MGF1" and algType = "HASH" or - name = "aes-128-xts" and nid = 913 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-xts" and nid = 913 and normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-xts" and nid = 913 and normalized = "XTS" and algType = "BLOCK_MODE" or - name = "aes-256-xts" and nid = 914 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-xts" and nid = 914 and normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-xts" and nid = 914 and normalized = "XTS" and algType = "BLOCK_MODE" or @@ -1017,7 +1120,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aes-128-cbc-hmac-sha1" and nid = 916 and - normalized = "AES128" and + normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-cbc-hmac-sha1" and nid = 916 and normalized = "CBC" and algType = "BLOCK_MODE" @@ -1026,14 +1129,14 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aes-192-cbc-hmac-sha1" and nid = 917 and - normalized = "AES192" and + normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-cbc-hmac-sha1" and nid = 917 and normalized = "CBC" and algType = "BLOCK_MODE" or name = "aes-256-cbc-hmac-sha1" and nid = 918 and - normalized = "AES256" and + normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-cbc-hmac-sha1" and nid = 918 and normalized = "CBC" and algType = "BLOCK_MODE" @@ -1042,7 +1145,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aes-128-cbc-hmac-sha256" and nid = 948 and - normalized = "AES128" and + normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-cbc-hmac-sha256" and nid = 948 and normalized = "CBC" and algType = "BLOCK_MODE" @@ -1051,7 +1154,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aes-192-cbc-hmac-sha256" and nid = 949 and - normalized = "AES192" and + normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-cbc-hmac-sha256" and nid = 949 and normalized = "CBC" and algType = "BLOCK_MODE" @@ -1060,93 +1163,93 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aes-256-cbc-hmac-sha256" and nid = 950 and - normalized = "AES256" and + normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-cbc-hmac-sha256" and nid = 950 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aes-128-ocb" and nid = 958 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-ocb" and nid = 958 and normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aes-192-ocb" and nid = 959 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-ocb" and nid = 959 and normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aes-256-ocb" and nid = 960 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-ocb" and nid = 960 and normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-gcm" and nid = 961 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-gcm" and nid = 961 and normalized = "GCM" and algType = "BLOCK_MODE" or name = "camellia-128-ccm" and nid = 962 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-ccm" and nid = 962 and normalized = "CCM" and algType = "BLOCK_MODE" or name = "camellia-128-ctr" and nid = 963 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-128-ctr" and nid = 963 and normalized = "CTR" and algType = "BLOCK_MODE" or name = "camellia-128-cmac" and nid = 964 and - normalized = "CAMELLIA128" and + normalized = "CAMELLIA-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-gcm" and nid = 965 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-gcm" and nid = 965 and normalized = "GCM" and algType = "BLOCK_MODE" or name = "camellia-192-ccm" and nid = 966 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-ccm" and nid = 966 and normalized = "CCM" and algType = "BLOCK_MODE" or name = "camellia-192-ctr" and nid = 967 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-192-ctr" and nid = 967 and normalized = "CTR" and algType = "BLOCK_MODE" or name = "camellia-192-cmac" and nid = 968 and - normalized = "CAMELLIA192" and + normalized = "CAMELLIA-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-gcm" and nid = 969 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-gcm" and nid = 969 and normalized = "GCM" and algType = "BLOCK_MODE" or name = "camellia-256-ccm" and nid = 970 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-ccm" and nid = 970 and normalized = "CCM" and algType = "BLOCK_MODE" or name = "camellia-256-ctr" and nid = 971 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "camellia-256-ctr" and nid = 971 and normalized = "CTR" and algType = "BLOCK_MODE" or name = "camellia-256-cmac" and nid = 972 and - normalized = "CAMELLIA256" and + normalized = "CAMELLIA-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-scrypt" and nid = 973 and normalized = "SCRYPT" and algType = "KEY_DERIVATION" @@ -1386,27 +1489,45 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aria-128-ccm" and nid = 1120 and normalized = "CCM" and algType = "BLOCK_MODE" or - name = "aria-128-ccm" and nid = 1120 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-ccm" and + nid = 1120 and + normalized = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-ccm" and nid = 1121 and normalized = "CCM" and algType = "BLOCK_MODE" or - name = "aria-192-ccm" and nid = 1121 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-ccm" and + nid = 1121 and + normalized = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-ccm" and nid = 1122 and normalized = "CCM" and algType = "BLOCK_MODE" or - name = "aria-256-ccm" and nid = 1122 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-ccm" and + nid = 1122 and + normalized = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-128-gcm" and nid = 1123 and normalized = "GCM" and algType = "BLOCK_MODE" or - name = "aria-128-gcm" and nid = 1123 and normalized = "ARIA128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-128-gcm" and + nid = 1123 and + normalized = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-192-gcm" and nid = 1124 and normalized = "GCM" and algType = "BLOCK_MODE" or - name = "aria-192-gcm" and nid = 1124 and normalized = "ARIA192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-192-gcm" and + nid = 1124 and + normalized = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aria-256-gcm" and nid = 1125 and normalized = "GCM" and algType = "BLOCK_MODE" or - name = "aria-256-gcm" and nid = 1125 and normalized = "ARIA256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aria-256-gcm" and + nid = 1125 and + normalized = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "sm4-cfb1" and nid = 1136 and normalized = "SM4" and algType = "SYMMETRIC_ENCRYPTION" or @@ -1505,15 +1626,24 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "magma-mac" and nid = 1192 and normalized = "MAGMA" and algType = "SYMMETRIC_ENCRYPTION" or - name = "aes-128-siv" and nid = 1198 and normalized = "AES128" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-128-siv" and + nid = 1198 and + normalized = "AES-128" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aes-128-siv" and nid = 1198 and normalized = "SIV" and algType = "BLOCK_MODE" or - name = "aes-192-siv" and nid = 1199 and normalized = "AES192" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-192-siv" and + nid = 1199 and + normalized = "AES-192" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aes-192-siv" and nid = 1199 and normalized = "SIV" and algType = "BLOCK_MODE" or - name = "aes-256-siv" and nid = 1200 and normalized = "AES256" and algType = "SYMMETRIC_ENCRYPTION" + name = "aes-256-siv" and + nid = 1200 and + normalized = "AES-256" and + algType = "SYMMETRIC_ENCRYPTION" or name = "aes-256-siv" and nid = 1200 and normalized = "SIV" and algType = "BLOCK_MODE" or @@ -2000,42 +2130,42 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "id-aes128-ccm" and nid = 896 and - normalized = "AES128" and + normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes128-ccm" and nid = 896 and normalized = "CCM" and algType = "BLOCK_MODE" or name = "id-aes128-gcm" and nid = 895 and - normalized = "AES128" and + normalized = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes128-gcm" and nid = 895 and normalized = "GCM" and algType = "BLOCK_MODE" or name = "id-aes192-ccm" and nid = 899 and - normalized = "AES192" and + normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes192-ccm" and nid = 899 and normalized = "CCM" and algType = "BLOCK_MODE" or name = "id-aes192-gcm" and nid = 898 and - normalized = "AES192" and + normalized = "AES-192" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes192-gcm" and nid = 898 and normalized = "GCM" and algType = "BLOCK_MODE" or name = "id-aes256-ccm" and nid = 902 and - normalized = "AES256" and + normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes256-ccm" and nid = 902 and normalized = "CCM" and algType = "BLOCK_MODE" or name = "id-aes256-gcm" and nid = 901 and - normalized = "AES256" and + normalized = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" or name = "id-aes256-gcm" and nid = 901 and normalized = "GCM" and algType = "BLOCK_MODE" @@ -2407,27 +2537,36 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "pbe-sha1-rc2-128" and nid = 148 and - normalized = "RC2" and + normalized = "RC2-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "pbe-sha1-rc2-40" and nid = 149 and normalized = "SHA1" and algType = "HASH" or - name = "pbe-sha1-rc2-40" and nid = 149 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + name = "pbe-sha1-rc2-40" and + nid = 149 and + normalized = "RC2-40" and + algType = "SYMMETRIC_ENCRYPTION" or name = "pbe-sha1-rc2-64" and nid = 68 and normalized = "SHA1" and algType = "HASH" or - name = "pbe-sha1-rc2-64" and nid = 68 and normalized = "RC2" and algType = "SYMMETRIC_ENCRYPTION" + name = "pbe-sha1-rc2-64" and + nid = 68 and + normalized = "RC2-64" and + algType = "SYMMETRIC_ENCRYPTION" or name = "pbe-sha1-rc4-128" and nid = 144 and normalized = "SHA1" and algType = "HASH" or name = "pbe-sha1-rc4-128" and nid = 144 and - normalized = "RC4" and + normalized = "RC4-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "pbe-sha1-rc4-40" and nid = 145 and normalized = "SHA1" and algType = "HASH" or - name = "pbe-sha1-rc4-40" and nid = 145 and normalized = "RC4" and algType = "SYMMETRIC_ENCRYPTION" + name = "pbe-sha1-rc4-40" and + nid = 145 and + normalized = "RC4-40" and + algType = "SYMMETRIC_ENCRYPTION" or name = "pbewithmd2anddes-cbc" and nid = 9 and @@ -2478,7 +2617,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "pbewithsha1and128bitrc2-cbc" and nid = 148 and - normalized = "RC2" and + normalized = "RC2-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "pbewithsha1and128bitrc2-cbc" and @@ -2490,7 +2629,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "pbewithsha1and128bitrc4" and nid = 144 and - normalized = "RC4" and + normalized = "RC4-128" and algType = "SYMMETRIC_ENCRYPTION" or name = "pbewithsha1and2-keytripledes-cbc" and @@ -2505,7 +2644,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "pbewithsha1and2-keytripledes-cbc" and nid = 147 and - normalized = "TRIPLEDES" and + normalized = "3DES" and algType = "SYMMETRIC_ENCRYPTION" or name = "pbewithsha1and3-keytripledes-cbc" and @@ -2520,14 +2659,14 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "pbewithsha1and3-keytripledes-cbc" and nid = 146 and - normalized = "TRIPLEDES" and + normalized = "3DES" and algType = "SYMMETRIC_ENCRYPTION" or name = "pbewithsha1and40bitrc2-cbc" and nid = 149 and normalized = "SHA1" and algType = "HASH" or name = "pbewithsha1and40bitrc2-cbc" and nid = 149 and - normalized = "RC2" and + normalized = "RC2-40" and algType = "SYMMETRIC_ENCRYPTION" or name = "pbewithsha1and40bitrc2-cbc" and @@ -2539,7 +2678,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "pbewithsha1and40bitrc4" and nid = 145 and - normalized = "RC4" and + normalized = "RC4-40" and algType = "SYMMETRIC_ENCRYPTION" or name = "pbewithsha1anddes-cbc" and nid = 170 and normalized = "SHA1" and algType = "HASH" diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll new file mode 100644 index 000000000000..6a206773bfb8 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll @@ -0,0 +1,6 @@ +import experimental.Quantum.Language +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase + +abstract class OpenSSLAlgorithmInstance extends Crypto::AlgorithmInstance { + abstract OpenSSLAlgorithmValueConsumer getAVC(); +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll new file mode 100644 index 000000000000..2a3f136f117a --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll @@ -0,0 +1,4 @@ +import OpenSSLAlgorithmInstanceBase +import CipherAlgorithmInstance +import PaddingAlgorithmInstance +import BlockAlgorithmInstance diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll new file mode 100644 index 000000000000..219289c7da08 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll @@ -0,0 +1,167 @@ +import cpp +import experimental.Quantum.Language +import OpenSSLAlgorithmInstanceBase +import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import AlgToAVCFlow +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer + +/** + * Given a `KnownOpenSSLPaddingAlgorithmConstant`, converts this to a padding family type. + * Does not bind if there is know mapping (no mapping to 'unknown' or 'other'). + */ +predicate knownOpenSSLConstantToPaddingFamilyType( + KnownOpenSSLPaddingAlgorithmConstant e, Crypto::TPaddingType type +) { + exists(string name | + name = e.getNormalizedName() and + ( + name.matches("OAEP") and type = Crypto::OAEP() + or + name.matches("PSS") and type = Crypto::PSS() + or + name.matches("PKCS7") and type = Crypto::PKCS7() + or + name.matches("PKCS1V15") and type = Crypto::PKCS1_v1_5() + ) + ) +} + +//abstract class OpenSSLPaddingAlgorithmInstance extends OpenSSLAlgorithmInstance, Crypto::PaddingAlgorithmInstance{} +// TODO: need to alter this to include known padding constants which don't have the +// same mechanics as those with known nids +class KnownOpenSSLPaddingConstantAlgorithmInstance extends OpenSSLAlgorithmInstance, + Crypto::PaddingAlgorithmInstance instanceof Expr +{ + OpenSSLAlgorithmValueConsumer getterCall; + boolean isPaddingSpecificConsumer; + + KnownOpenSSLPaddingConstantAlgorithmInstance() { + // three possibilities: + // 1) The source is a 'typical' literal and flows to a getter, then we know we have an instance + // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that + // 3) the source is a padding-specific literal flowing to a padding-specific consumer + // Possibility 1: + this instanceof Literal and + this instanceof KnownOpenSSLPaddingAlgorithmConstant and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a CipherGetterCall + sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a getter + KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) and + isPaddingSpecificConsumer = false + ) + or + // Possibility 2: + this instanceof DirectAlgorithmValueConsumer and + getterCall = this and + this instanceof KnownOpenSSLPaddingAlgorithmConstant and + isPaddingSpecificConsumer = false + or + // Possibility 3: + // from rsa.h in openssl: + // # define RSA_PKCS1_PADDING 1 + // # define RSA_NO_PADDING 3 + // # define RSA_PKCS1_OAEP_PADDING 4 + // # define RSA_X931_PADDING 5 + // /* EVP_PKEY_ only */ + // # define RSA_PKCS1_PSS_PADDING 6 + // # define RSA_PKCS1_WITH_TLS_PADDING 7 + // /* internal RSA_ only */ + // # define RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING 8 + this instanceof Literal and + this.getValue().toInt() in [0, 1, 3, 4, 5, 6, 7, 8] and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a CipherGetterCall + sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a padding-specific consumer + RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow::flow(src, sink) + ) and + isPaddingSpecificConsumer = true + } + + override string getRawPaddingAlgorithmName() { result = this.(Literal).getValue().toString() } + + override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall } + + override Crypto::TPaddingType getPaddingType() { + isPaddingSpecificConsumer = true and + ( + if this.(Literal).getValue().toInt() in [1, 7, 8] + then result = Crypto::PKCS1_v1_5() + else + if this.(Literal).getValue().toInt() = 3 + then result = Crypto::NoPadding() + else + if this.(Literal).getValue().toInt() = 4 + then result = Crypto::OAEP() + else + if this.(Literal).getValue().toInt() = 5 + then result = Crypto::ANSI_X9_23() + else + if this.(Literal).getValue().toInt() = 6 + then result = Crypto::PSS() + else result = Crypto::OtherPadding() + ) + or + isPaddingSpecificConsumer = false and + knownOpenSSLConstantToPaddingFamilyType(this, result) + } +} + +// // Values used for EVP_PKEY_CTX_set_rsa_padding, these are +// // not the same as 'typical' constants found in the set of known algorithm constants +// // they do not have an NID +// // TODO: what about setting the padding directly? +// class KnownRSAPaddingConstant extends OpenSSLPaddingAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof Literal +// { +// KnownRSAPaddingConstant() { +// // from rsa.h in openssl: +// // # define RSA_PKCS1_PADDING 1 +// // # define RSA_NO_PADDING 3 +// // # define RSA_PKCS1_OAEP_PADDING 4 +// // # define RSA_X931_PADDING 5 +// // /* EVP_PKEY_ only */ +// // # define RSA_PKCS1_PSS_PADDING 6 +// // # define RSA_PKCS1_WITH_TLS_PADDING 7 +// // /* internal RSA_ only */ +// // # define RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING 8 +// this instanceof Literal and +// this.getValue().toInt() in [0, 1, 3, 4, 5, 6, 7, 8] +// // TODO: trace to padding-specific consumers +// RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow +// } +// override string getRawPaddingAlgorithmName() { result = this.(Literal).getValue().toString() } +// override Crypto::TPaddingType getPaddingType() { +// if this.(Literal).getValue().toInt() in [1, 6, 7, 8] +// then result = Crypto::PKCS1_v1_5() +// else +// if this.(Literal).getValue().toInt() = 3 +// then result = Crypto::NoPadding() +// else +// if this.(Literal).getValue().toInt() = 4 +// then result = Crypto::OAEP() +// else +// if this.(Literal).getValue().toInt() = 5 +// then result = Crypto::ANSI_X9_23() +// else result = Crypto::OtherPadding() +// } +// } +class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance, + KnownOpenSSLPaddingConstantAlgorithmInstance +{ + OAEPPaddingAlgorithmInstance() { + this.(Crypto::PaddingAlgorithmInstance).getPaddingType() = Crypto::OAEP() + } + + override Crypto::HashAlgorithmInstance getOAEPEncodingHashAlgorithm() { + none() //TODO + } + + override Crypto::HashAlgorithmInstance getMGF1HashAlgorithm() { + none() //TODO + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll new file mode 100644 index 000000000000..19777bc06d1e --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll @@ -0,0 +1,39 @@ +import cpp +import experimental.Quantum.Language +import experimental.Quantum.OpenSSL.LibraryDetector +import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.Quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase +import OpenSSLAlgorithmValueConsumerBase + +abstract class CipherAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { } + +// https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html +class EVPCipherAlgorithmValueConsumer extends CipherAlgorithmValueConsumer { + DataFlow::Node valueArgNode; + DataFlow::Node resultNode; + + EVPCipherAlgorithmValueConsumer() { + resultNode.asExpr() = this and + isPossibleOpenSSLFunction(this.(Call).getTarget()) and + ( + this.(Call).getTarget().getName() in [ + "EVP_get_cipherbyname", "EVP_get_cipherbyobj", "EVP_get_cipherbynid" + ] and + valueArgNode.asExpr() = this.(Call).getArgument(0) + or + this.(Call).getTarget().getName() in ["EVP_CIPHER_fetch", "EVP_ASYM_CIPHER_fetch"] and + valueArgNode.asExpr() = this.(Call).getArgument(1) + ) + } + + override DataFlow::Node getResultNode() { result = resultNode } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result = valueArgNode } + + // override DataFlow::Node getInputNode() { result = valueArgNode } + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(OpenSSLAlgorithmInstance i | i.getAVC() = this and result = i) + //TODO: As a potential alternative, for OpenSSL only, add a generic source node for literals and only create flow (flowsTo) to + // OpenSSL AVCs... the unknown literal sources would have to be any literals not in the known set. + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll new file mode 100644 index 000000000000..cebe7a86a129 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll @@ -0,0 +1,36 @@ +import cpp +import experimental.Quantum.Language +import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase + +// TODO: can self referential to itself, which is also an algorithm (Known algorithm) +/** + * Cases like EVP_MD5(), + * there is no input, rather it directly gets an algorithm + * and returns it. + */ +class DirectAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { + DataFlow::Node resultNode; + Expr resultExpr; + + DirectAlgorithmValueConsumer() { + this instanceof KnownOpenSSLAlgorithmConstant and + this instanceof Call and + resultExpr = this and + resultNode.asExpr() = resultExpr + } + + /** + * These cases take in no explicit value (the value is implicit) + */ + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } + + override DataFlow::Node getResultNode() { result = resultNode } + + // override DataFlow::Node getOutputNode() { result = resultNode } + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + // Note: algorithm source definitions enforces that + // this class will be a known algorithm source + result = this + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll new file mode 100644 index 000000000000..dddcf14c7134 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll @@ -0,0 +1,9 @@ +import experimental.Quantum.Language +import semmle.code.cpp.dataflow.new.DataFlow + +abstract class OpenSSLAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof Call { + /** + * Returns the node representing the resulting algorithm + */ + abstract DataFlow::Node getResultNode(); +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll new file mode 100644 index 000000000000..46021269d71d --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll @@ -0,0 +1,4 @@ +import OpenSSLAlgorithmValueConsumerBase +import CipherAlgorithmValueConsumer +import DirectAlgorithmValueConsumer +import PaddingAlgorithmValueConsumer diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll new file mode 100644 index 000000000000..009b22cf1b86 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll @@ -0,0 +1,36 @@ +import cpp +import experimental.Quantum.Language +import experimental.Quantum.OpenSSL.LibraryDetector +import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.Quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase +import OpenSSLAlgorithmValueConsumerBase + +abstract class PaddingAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { } + +// https://docs.openssl.org/master/man7/EVP_ASYM_CIPHER-RSA/#rsa-asymmetric-cipher-parameters +// TODO: need to handle setting padding through EVP_PKEY_CTX_set_params, where modes like "OSSL_PKEY_RSA_PAD_MODE_OAEP" +// are set. +class EVP_PKEY_CTX_set_rsa_padding_AlgorithmValueConsumer extends PaddingAlgorithmValueConsumer { + DataFlow::Node valueArgNode; + DataFlow::Node resultNode; + + EVP_PKEY_CTX_set_rsa_padding_AlgorithmValueConsumer() { + resultNode.asExpr() = this and + isPossibleOpenSSLFunction(this.(Call).getTarget()) and + ( + this.(Call).getTarget().getName() in ["EVP_PKEY_CTX_set_rsa_padding"] and + valueArgNode.asExpr() = this.(Call).getArgument(1) + ) + } + + override DataFlow::Node getResultNode() { result = resultNode } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result = valueArgNode } + + // override DataFlow::Node getInputNode() { result = valueArgNode } + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(OpenSSLAlgorithmInstance i | i.getAVC() = this and result = i) + //TODO: As a potential alternative, for OpenSSL only, add a generic source node for literals and only create flow (flowsTo) to + // OpenSSL AVCs... the unknown literal sources would have to be any literals not in the known set. + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll new file mode 100644 index 000000000000..584fd18a64c2 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll @@ -0,0 +1,123 @@ +/** + * see: https://docs.openssl.org/master/man3/EVP_EncryptInit/ + * Models cipher initialization for EVP cipher operations. + */ + +import experimental.Quantum.Language +import experimental.Quantum.OpenSSL.CtxFlow as CTXFlow + +module EncValToInitEncArgConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] } + + predicate isSink(DataFlow::Node sink) { + exists(EVP_Cipher_Inititalizer initCall | sink.asExpr() = initCall.getOperataionSubtypeArg()) + } +} + +module EncValToInitEncArgFlow = DataFlow::Global; + +int getEncConfigValue(Expr e) { + exists(EVP_Cipher_Inititalizer initCall | e = initCall.getOperataionSubtypeArg()) and + exists(DataFlow::Node a, DataFlow::Node b | + EncValToInitEncArgFlow::flow(a, b) and b.asExpr() = e and result = a.asExpr().getValue().toInt() + ) +} + +bindingset[i] +Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) { + if i = 0 + then result instanceof Crypto::TEncryptMode + else + if i = 1 + then result instanceof Crypto::TDecryptMode + else result instanceof Crypto::TUnknownKeyOperationMode +} + +// TODO: need to add key consumer +abstract class EVP_Cipher_Inititalizer extends Call { + Expr getContextArg() { result = this.(Call).getArgument(0) } + + Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } + + abstract Expr getKeyArg(); + + abstract Expr getIVArg(); + + // abstract Crypto::CipherOperationSubtype getCipherOperationSubtype(); + abstract Expr getOperataionSubtypeArg(); + + Crypto::KeyOperationSubtype getCipherOperationSubtype() { + if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") + then result instanceof Crypto::TEncryptMode + else + if this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") + then result instanceof Crypto::TDecryptMode + else + if exists(getEncConfigValue(this.getOperataionSubtypeArg())) + then result = intToCipherOperationSubtype(getEncConfigValue(this.getOperataionSubtypeArg())) + else result instanceof Crypto::TUnknownKeyOperationMode + } +} + +abstract class EVP_EX_Initializer extends EVP_Cipher_Inititalizer { + override Expr getKeyArg() { result = this.(Call).getArgument(3) } + + override Expr getIVArg() { result = this.(Call).getArgument(4) } +} + +abstract class EVP_EX2_Initializer extends EVP_Cipher_Inititalizer { + override Expr getKeyArg() { result = this.(Call).getArgument(2) } + + override Expr getIVArg() { result = this.(Call).getArgument(3) } +} + +class EVP_Cipher_EX_Init_Call extends EVP_EX_Initializer { + EVP_Cipher_EX_Init_Call() { + this.(Call).getTarget().getName() in [ + "EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex" + ] + } + + override Expr getOperataionSubtypeArg() { + this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and + result = this.(Call).getArgument(5) + } +} + +class EVP_Cipher_EX2_or_Simple_Init_Call extends EVP_EX2_Initializer { + EVP_Cipher_EX2_or_Simple_Init_Call() { + this.(Call).getTarget().getName() in [ + "EVP_EncryptInit_ex2", "EVP_DecryptInit_ex2", "EVP_CipherInit_ex2", "EVP_EncryptInit", + "EVP_DecryptInit", "EVP_CipherInit" + ] + } + + override Expr getOperataionSubtypeArg() { + this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and + result = this.(Call).getArgument(4) + } +} + +class EVP_CipherInit_SKEY_Call extends EVP_EX2_Initializer { + EVP_CipherInit_SKEY_Call() { this.(Call).getTarget().getName() in ["EVP_CipherInit_SKEY"] } + + override Expr getOperataionSubtypeArg() { result = this.(Call).getArgument(5) } +} + +class EVPCipherInitializerAlgorithmArgument extends Expr { + EVPCipherInitializerAlgorithmArgument() { + exists(EVP_Cipher_Inititalizer initCall | this = initCall.getAlgorithmArg()) + } +} + +class EVPCipherInitializerKeyArgument extends Expr { + EVPCipherInitializerKeyArgument() { + exists(EVP_Cipher_Inititalizer initCall | this = initCall.getKeyArg()) + } +} + +class EVPCipherInitializerIVArgument extends Expr { + EVPCipherInitializerIVArgument() { + exists(EVP_Cipher_Inititalizer initCall | this = initCall.getIVArg()) + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll new file mode 100644 index 000000000000..56288e6acb75 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll @@ -0,0 +1,114 @@ +import experimental.Quantum.Language +import experimental.Quantum.OpenSSL.CtxFlow as CTXFlow +import EVPCipherInitializer +import OpenSSLOperationBase +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers + +// import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers +// import OpenSSLOperation +module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source) + } + + predicate isSink(DataFlow::Node sink) { + exists(EVP_Cipher_Operation c | c.getInitCall().getAlgorithmArg() = sink.asExpr()) + } +} + +module AlgGetterToAlgConsumerFlow = DataFlow::Global; + +// class EVPCipherOutput extends CipherOutputArtifact { +// EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) } +// override DataFlow::Node getOutputNode() { result.asDefiningArgument() = this } +// } +// +/** + * see: https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis + * Base configuration for all EVP cipher operations. + * NOTE: cannot extend instance of OpenSSLOperation, as we need to override + * elements of OpenSSLOperation (i.e., we are creating an instance) + */ +abstract class EVP_Cipher_Operation extends OpenSSLOperation, Crypto::KeyOperationInstance { + Expr getContextArg() { result = this.(Call).getArgument(0) } + + override Expr getOutputArg() { result = this.(Call).getArgument(1) } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + result instanceof Crypto::TEncryptMode and + this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") + or + result instanceof Crypto::TDecryptMode and + this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") + or + result = this.getInitCall().getCipherOperationSubtype() and + this.(Call).getTarget().getName().toLowerCase().matches("%cipher%") + } + + EVP_Cipher_Inititalizer getInitCall() { + CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { + this.getInitCall().getIVArg() = result.asExpr() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + this.getInitCall().getKeyArg() = result.asExpr() + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), + DataFlow::exprNode(this.getInitCall().getAlgorithmArg())) + } +} + +// abstract class EVP_Update_Call extends EVP_Cipher_Operation { } +abstract class EVP_Final_Call extends EVP_Cipher_Operation { + override Expr getInputArg() { none() } +} + +// TODO: only model Final (model final as operation and model update but not as an operation) +// Updates are multiple input consumers (most important) +// PUNT assuming update doesn't ouput, otherwise it outputs arifacts, but is not an operation +class EVP_Cipher_Call extends EVP_Cipher_Operation { + EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } + + override Expr getInputArg() { result = this.(Call).getArgument(2) } +} + +// class EVP_Encrypt_Decrypt_or_Cipher_Update_Call extends EVP_Update_Call { +// EVP_Encrypt_Decrypt_or_Cipher_Update_Call() { +// this.(Call).getTarget().getName() in [ +// "EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate" +// ] +// } +// override Expr getInputArg() { result = this.(Call).getArgument(3) } +// } +class EVP_Encrypt_Decrypt_or_Cipher_Final_Call extends EVP_Final_Call { + EVP_Encrypt_Decrypt_or_Cipher_Final_Call() { + this.(Call).getTarget().getName() in [ + "EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal", + "EVP_DecryptFinal", "EVP_CipherFinal" + ] + } +} + +class EVP_PKEY_Operation extends EVP_Cipher_Operation { + EVP_PKEY_Operation() { + this.(Call).getTarget().getName() in ["EVP_PKEY_decrypt", "EVP_PKEY_encrypt"] + } + + override Expr getInputArg() { result = this.(Call).getArgument(3) } + // TODO: how PKEY is initialized is different that symmetric cipher + // Consider making an entirely new class for this and specializing + // the get init call +} + +class EVPCipherInputArgument extends Expr { + EVPCipherInputArgument() { exists(EVP_Cipher_Operation op | op.getInputArg() = this) } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll new file mode 100644 index 000000000000..851d7a4b7e99 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -0,0 +1,21 @@ +import experimental.Quantum.Language + +abstract class OpenSSLOperation extends Crypto::OperationInstance instanceof Call { + abstract Expr getInputArg(); + + /** + * Can be an argument of a call or a return value of a function. + */ + abstract Expr getOutputArg(); + + DataFlow::Node getInputNode() { + // Assumed to be default to asExpr + result.asExpr() = this.getInputArg() + } + + DataFlow::Node getOutputNode() { + if exists(Call c | c.getAnArgument() = this) + then result.asDefiningArgument() = this + else result.asExpr() = this + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll new file mode 100644 index 000000000000..76a60b064e55 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll @@ -0,0 +1 @@ +import EVPCipherOperation From 94632931ba08b3cac96882de1d1e2aa315ffd69d Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 2 May 2025 14:11:10 -0400 Subject: [PATCH 096/122] Clean up --- .../OpenSSL/OpenSSLAlgorithmGetter.qll | 495 ------------------ 1 file changed, 495 deletions(-) delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll deleted file mode 100644 index b436f3b412a8..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll +++ /dev/null @@ -1,495 +0,0 @@ - -// import cpp -// import semmle.code.cpp.dataflow.new.DataFlow -// import LibraryDetector -// import OpenSSLKnownAlgorithmConstants -// import experimental.Quantum.Language -// class OpenSSLAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep { -// OpenSSLAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) } -// override DataFlow::Node getOutput() { -// exists(AlgorithmPassthroughCall c | c.getInNode() = this and c.getOutNode() = result) -// } -// } -// module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { -// predicate isSource(DataFlow::Node source) { -// exists(OpenSSLAlgorithmGetterCall c | c.getResultNode() = source) -// } -// predicate isSink(DataFlow::Node sink) { -// exists(Crypto::AlgorithmConsumer c | c.getInputNode() = sink) -// } -// } -// module AlgGetterToAlgConsumerFlow = DataFlow::Global; -// abstract class AlgorithmPassthroughCall extends Call { -// abstract DataFlow::Node getInNode(); -// abstract DataFlow::Node getOutNode(); -// } -// class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall { -// DataFlow::Node inNode; -// DataFlow::Node outNode; -// CopyAndDupAlgorithmPassthroughCall() { -// // Flow out through any return or other argument of the same type -// // Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed -// // to be involved -// // NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup -// this.getTarget().getName().toLowerCase().matches(["%_dup%", "%_copy%"]) and -// exists(Expr inArg, Type t | -// inArg = this.getAnArgument() and t = inArg.getUnspecifiedType().stripType() -// | -// inNode.asIndirectExpr() = inArg and -// ( -// // Case 1: flow through another argument as an out arg of the same type -// exists(Expr outArg | -// outArg = this.getAnArgument() and -// outArg != inArg and -// outArg.getUnspecifiedType().stripType() = t -// | -// outNode.asDefiningArgument() = outArg -// ) -// or -// // Case 2: flow through the return value if the result is the same as the intput type -// exists(Expr outArg | outArg = this and outArg.getUnspecifiedType().stripType() = t | -// outNode.asIndirectExpr() = outArg -// ) -// ) -// ) -// } -// override DataFlow::Node getInNode() { result = inNode } -// override DataFlow::Node getOutNode() { result = outNode } -// } -// class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall { -// DataFlow::Node inNode; -// DataFlow::Node outNode; -// NIDToPointerPassthroughCall() { -// this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and -// inNode.asExpr() = this.getArgument(0) and -// outNode.asExpr() = this -// //outNode.asIndirectExpr() = this -// } -// override DataFlow::Node getInNode() { result = inNode } -// override DataFlow::Node getOutNode() { result = outNode } -// } -// class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall { -// DataFlow::Node inNode; -// DataFlow::Node outNode; -// PointerToPointerPassthroughCall() { -// this.getTarget().getName() = "OBJ_txt2obj" and -// inNode.asIndirectExpr() = this.getArgument(0) and -// outNode.asIndirectExpr() = this -// or -// //outNode.asExpr() = this -// this.getTarget().getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and -// inNode.asIndirectExpr() = this.getArgument(2) and -// outNode.asDefiningArgument() = this.getArgument(0) -// } -// override DataFlow::Node getInNode() { result = inNode } -// override DataFlow::Node getOutNode() { result = outNode } -// } -// class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall { -// DataFlow::Node inNode; -// DataFlow::Node outNode; -// PointerToNIDPassthroughCall() { -// this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and -// ( -// inNode.asIndirectExpr() = this.getArgument(0) -// or -// inNode.asExpr() = this.getArgument(0) -// ) and -// outNode.asExpr() = this -// } -// override DataFlow::Node getInNode() { result = inNode } -// override DataFlow::Node getOutNode() { result = outNode } -// } -// predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) { -// exists(AlgorithmPassthroughCall c | c.getInNode() = node1 and c.getOutNode() = node2) -// } -// abstract class OpenSSLAlgorithmGetterCall extends Call { -// abstract DataFlow::Node getValueArgNode(); -// abstract DataFlow::Node getResultNode(); -// abstract Expr getValueArgExpr(); -// abstract Expr getResultExpr(); -// } -// module KnownOpenSSLAlgorithmToAlgorithmGetterConfig implements DataFlow::ConfigSig { -// predicate isSource(DataFlow::Node source) { -// source.asExpr() instanceof KnownOpenSSLAlgorithmConstant -// } -// predicate isSink(DataFlow::Node sink) { -// exists(OpenSSLAlgorithmGetterCall c | c.getValueArgNode() = sink) -// } -// predicate isBarrier(DataFlow::Node node) { -// // False positive reducer, don't flow out through argv -// exists(VariableAccess va, Variable v | -// v.getAnAccess() = va and va = node.asExpr() -// or -// va = node.asIndirectExpr() -// | -// v.getName().matches("%argv") -// ) -// } -// predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { -// knownPassThroughStep(node1, node2) -// } -// } -// module KnownOpenSSLAlgorithmToAlgorithmGetterFlow = -// DataFlow::Global; -// /** -// * Cases like EVP_MD5(), -// * there is no input, rather it directly gets an algorithm -// * and returns it. -// */ -// class DirectGetterCall extends OpenSSLAlgorithmGetterCall { -// DataFlow::Node resultNode; -// Expr resultExpr; -// DirectGetterCall() { -// this instanceof KnownOpenSSLAlgorithmConstant and -// this instanceof Call and -// resultExpr = this and -// resultNode.asExpr() = resultExpr -// } -// override DataFlow::Node getValueArgNode() { none() } -// override DataFlow::Node getResultNode() { result = resultNode } -// override Expr getValueArgExpr() { none() } -// override Expr getResultExpr() { result = resultExpr } -// } -// // https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html -// class EVPCipherGetterCall extends OpenSSLAlgorithmGetterCall { -// DataFlow::Node valueArgNode; -// DataFlow::Node resultNode; -// Expr valueArgExpr; -// Expr resultExpr; -// EVPCipherGetterCall() { -// resultExpr = this and -// resultNode.asExpr() = this and -// isPossibleOpenSSLFunction(this.getTarget()) and -// ( -// this.getTarget().getName() in ["EVP_get_cipherbyname", "EVP_get_cipherbyobj"] and -// valueArgExpr = this.getArgument(0) and -// valueArgNode.asExpr() = valueArgExpr -// or -// this.getTarget().getName() = "EVP_CIPHER_fetch" and -// valueArgExpr = this.getArgument(1) and -// valueArgNode.asExpr() = valueArgExpr -// or -// this.getTarget().getName() = "EVP_get_cipherbynid" and -// valueArgExpr = this.getArgument(0) and -// valueArgNode.asExpr() = valueArgExpr -// ) -// } -// override DataFlow::Node getValueArgNode() { result = valueArgNode } -// override DataFlow::Node getResultNode() { result = resultNode } -// override Expr getValueArgExpr() { result = valueArgExpr } -// override Expr getResultExpr() { result = resultExpr } -// } -// class EVPAsymCipherGetterCall extends OpenSSLAlgorithmGetterCall { -// DataFlow::Node valueArgNode; -// DataFlow::Node resultNode; -// Expr valueArgExpr; -// Expr resultExpr; -// EVPAsymCipherGetterCall() { -// isPossibleOpenSSLFunction(this.getTarget()) and -// resultExpr = this and -// resultNode.asExpr() = this and -// this.getTarget().getName() = "EVP_ASYM_CIPHER_fetch" and -// valueArgExpr = this.getArgument(1) and -// valueArgNode.asExpr() = valueArgExpr -// } -// override DataFlow::Node getValueArgNode() { result = valueArgNode } -// override DataFlow::Node getResultNode() { result = resultNode } -// override Expr getValueArgExpr() { result = valueArgExpr } -// override Expr getResultExpr() { result = resultExpr } -// } -// class EVPDigestGetterCall extends OpenSSLAlgorithmGetterCall { -// DataFlow::Node valueArgNode; -// DataFlow::Node resultNode; -// Expr valueArgExpr; -// Expr resultExpr; -// EVPDigestGetterCall() { -// resultExpr = this and -// resultNode.asExpr() = this and -// isPossibleOpenSSLFunction(this.getTarget()) and -// ( -// this.getTarget().getName() in [ -// "EVP_get_digestbyname", "EVP_get_digestbyobj", "EVP_get_digestbynid" -// ] and -// valueArgExpr = this.getArgument(0) and -// valueArgNode.asExpr() = valueArgExpr -// or -// this.getTarget().getName() = "EVP_MD_fetch" and -// valueArgExpr = this.getArgument(1) and -// valueArgNode.asExpr() = valueArgExpr -// ) -// } -// override DataFlow::Node getValueArgNode() { result = valueArgNode } -// override DataFlow::Node getResultNode() { result = resultNode } -// override Expr getValueArgExpr() { result = valueArgExpr } -// override Expr getResultExpr() { result = resultExpr } -// } -// class EVPKDFFetch extends OpenSSLAlgorithmGetterCall { -// DataFlow::Node valueArgNode; -// DataFlow::Node resultNode; -// Expr valueArgExpr; -// Expr resultExpr; -// EVPKDFFetch() { -// resultExpr = this and -// resultNode.asExpr() = this and -// isPossibleOpenSSLFunction(this.getTarget()) and -// this.getTarget().getName() in ["EVP_KDF_fetch"] and -// valueArgExpr = this.getArgument(1) and -// valueArgNode.asExpr() = valueArgExpr -// } -// override DataFlow::Node getValueArgNode() { result = valueArgNode } -// override DataFlow::Node getResultNode() { result = resultNode } -// override Expr getValueArgExpr() { result = valueArgExpr } -// override Expr getResultExpr() { result = resultExpr } -// } -// // /** -// // * Predicates/classes for identifying algorithm sinks. -// // * An Algorithm Sink is a function that takes an algorithm as an argument. -// // * In particular, any function that takes in an algorithm that until the call -// // * the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm) -// // */ -// // //TODO: enforce a hierarchy of AlgorithmSinkArgument, e.g., so I can get all Asymmetric SinkArguments that includes all the strictly RSA etc. -// // import cpp -// // // import experimental.cryptography.utils.OpenSSL.LibraryFunction -// // // import experimental.cryptography.CryptoAlgorithmNames -// // predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType) { arg.algType() = algType } -// // abstract class AlgorithmSinkArgument extends Expr { -// // AlgorithmSinkArgument() { -// // exists(Call c | c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget())) -// // } -// // /** -// // * Gets the function call in which the argument exists -// // */ -// // Call getSinkCall() { result.getAnArgument() = this } -// // abstract string algType(); -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html -// // predicate cipherAlgorithmSink(string funcName, int argInd) { -// // funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0 -// // or -// // funcName = "EVP_CIPHER_fetch" and argInd = 1 -// // } -// // class CipherAlgorithmSink extends AlgorithmSinkArgument { -// // CipherAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // cipherAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = getSymmetricEncryptionType() } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch -// // predicate macAlgorithmSink(string funcName, int argInd) { -// // (funcName = "EVP_MAC_fetch" and argInd = 1) -// // } -// // class MACAlgorithmSink extends AlgorithmSinkArgument { -// // MACAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // macAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = "TBD" } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch -// // predicate messageDigestAlgorithmSink(string funcName, int argInd) { -// // funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0 -// // or -// // funcName = "EVP_MD_fetch" and argInd = 1 -// // } -// // class MessageDigestAlgorithmSink extends AlgorithmSinkArgument { -// // MessageDigestAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // messageDigestAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = getHashType() } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch -// // // https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch -// // predicate keyExchangeAlgorithmSink(string funcName, int argInd) { -// // funcName = "EVP_KEYEXCH_fetch" and argInd = 1 -// // or -// // funcName = "EVP_KEM_fetch" and argInd = 1 -// // } -// // class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument { -// // KeyExchangeAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // keyExchangeAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = getKeyExchangeType() } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch -// // predicate keyManagementAlgorithmSink(string funcName, int argInd) { -// // funcName = "EVP_KEYMGMT_fetch" and argInd = 1 -// // } -// // class KeyManagementAlgorithmSink extends AlgorithmSinkArgument { -// // KeyManagementAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // keyManagementAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = "TBD" } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_KDF -// // predicate keyDerivationAlgorithmSink(string funcName, int argInd) { -// // funcName = "EVP_KDF_fetch" and argInd = 1 -// // } -// // class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument { -// // KeyDerivationAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // keyDerivationAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = getKeyDerivationType() } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch -// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id -// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html -// // predicate asymmetricCipherAlgorithmSink(string funcName, int argInd) { -// // funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1 -// // or -// // funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3 -// // // NOTE: other cases are handled by AsymmetricAlgorithmSink -// // } -// // class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument { -// // AsymmetricCipherAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // asymmetricCipherAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = "ASYMMETRIC_ENCRYPTION" } -// // } -// // class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { -// // AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() { -// // exists(Call c, string funcName | -// // funcName = c.getTarget().getName() and -// // this = c.getArgument(3) -// // | -// // funcName = "EVP_PKEY_Q_keygen" and -// // c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType -// // ) -// // } -// // override string algType() { result = "ASYMMETRIC_ENCRYPTION" } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch -// // predicate randomAlgorithmSink(string funcName, int argInd) { -// // funcName = "EVP_RAND_fetch" and argInd = 1 -// // } -// // class RandomAlgorithmSink extends AlgorithmSinkArgument { -// // RandomAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // randomAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = "TBD" } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch -// // predicate signatureAlgorithmSink(string funcName, int argInd) { -// // funcName = "EVP_SIGNATURE_fetch" and argInd = 1 -// // } -// // class SignatureAlgorithmSink extends AlgorithmSinkArgument { -// // SignatureAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // signatureAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = getSignatureType() } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html -// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html -// // predicate ellipticCurveAlgorithmSink(string funcName, int argInd) { -// // funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0 -// // or -// // funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2 -// // or -// // funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1 -// // } -// // class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument { -// // EllipticCurveAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // ellipticCurveAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = getEllipticCurveType() } -// // } -// // /** -// // * Special cased to address the fact that arg index 3 (zero offset based) is the curve name. -// // * ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve -// // */ -// // class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { -// // EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() { -// // exists(Call c, string funcName | -// // funcName = c.getTarget().getName() and -// // this = c.getArgument(3) -// // | -// // funcName = "EVP_PKEY_Q_keygen" and -// // c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and -// // c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType -// // ) -// // } -// // override string algType() { result = getEllipticCurveType() } -// // } -// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html -// // // https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html -// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html -// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html -// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html -// // // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html -// // predicate asymmetricAlgorithmSink(string funcName, int argInd) { -// // funcName = "EVP_PKEY_CTX_new_id" and argInd = 0 -// // or -// // funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1 -// // or -// // funcName in [ -// // "EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key" -// // ] and -// // argInd = 0 -// // or -// // funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1 -// // or -// // // special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs -// // // funcName = "EVP_PKEY_Q_keygen" and argInd = 2 -// // funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1 -// // // TODO consider void cases EVP_PKEY_new -// // } -// // class AsymmetricAlgorithmSink extends AlgorithmSinkArgument { -// // AsymmetricAlgorithmSink() { -// // exists(Call c, string funcName, int argInd | -// // funcName = c.getTarget().getName() and this = c.getArgument(argInd) -// // | -// // asymmetricAlgorithmSink(funcName, argInd) -// // ) -// // } -// // override string algType() { result = getAsymmetricType() } -// // } -// // class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument { -// // AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() { -// // exists(Call c, string funcName | -// // funcName = c.getTarget().getName() and -// // this = c.getArgument(2) -// // | -// // funcName = "EVP_PKEY_Q_keygen" and -// // not exists(c.getArgument(3)) -// // ) -// // } -// // override string algType() { result = getAsymmetricType() } -// // } From 0a0be41527ac3cbbdfa09d2ccb1722ed840b9696 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 2 May 2025 16:33:52 -0400 Subject: [PATCH 097/122] Intermediate progress towards getting hashing upgraded. Still need to handle the final and update mechanics, matching the JCA. Similarly need to update cipher to follow the JCA for update/final as well. --- .../OpenSSLAlgorithmInstances.qll | 1 + .../OpenSSLAlgorithmValueConsumers.qll | 1 + .../Quantum/OpenSSL/EVPCipherConsumers.qll | 25 ------ .../OpenSSL/EVPHashAlgorithmSource.qll | 81 ------------------ .../Quantum/OpenSSL/EVPHashConsumers.qll | 30 ------- .../Quantum/OpenSSL/EVPHashInitializer.qll | 25 ------ .../Quantum/OpenSSL/EVPHashOperation.qll | 83 ------------------- .../OpenSSL/Operations/EVPCipherOperation.qll | 10 ++- .../OpenSSL/Operations/OpenSSLOperations.qll | 2 + java/ql/lib/experimental/Quantum/JCA.qll | 6 +- .../codeql/cryptography/Model.qll | 27 +++++- 11 files changed, 37 insertions(+), 254 deletions(-) delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashInitializer.qll delete mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll index 2a3f136f117a..7a77a4c3e13e 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll @@ -2,3 +2,4 @@ import OpenSSLAlgorithmInstanceBase import CipherAlgorithmInstance import PaddingAlgorithmInstance import BlockAlgorithmInstance +import HashAlgorithmInstance diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll index 46021269d71d..0638595afb88 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll @@ -2,3 +2,4 @@ import OpenSSLAlgorithmValueConsumerBase import CipherAlgorithmValueConsumer import DirectAlgorithmValueConsumer import PaddingAlgorithmValueConsumer +import HashAlgorithmValueConsumer diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll deleted file mode 100644 index e2f5bc0e8380..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPCipherConsumers.qll +++ /dev/null @@ -1,25 +0,0 @@ -import EVPCipherInitializer -import EVPCipherOperation -import EVPCipherAlgorithmSource - -class EVP_Cipher_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVPCipherInitializerAlgorithmArgument -{ - override DataFlow::Node getInputNode() { result.asExpr() = this } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result.(KnownOpenSSLCipherConstantAlgorithmInstance).getConsumer() = this - } -} - -// //TODO: need a key consumer -// class EVP_Initializer_Key_Consumer extends Crypto::KeyConsumer instanceof EVP_Cipher_Inititalizer isntanceof InitializerKeyArgument{ -// } -class EVP_Cipher_Initializer_IV_Consumer extends Crypto::NonceArtifactConsumer instanceof EVPCipherInitializerIVArgument -{ - override DataFlow::Node getInputNode() { result.asExpr() = this } -} - -class EVP_Cipher_Input_Consumer extends Crypto::CipherInputConsumer instanceof EVPCipherInputArgument -{ - override DataFlow::Node getInputNode() { result.asExpr() = this } -} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll deleted file mode 100644 index d269658c8aab..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashAlgorithmSource.qll +++ /dev/null @@ -1,81 +0,0 @@ -import cpp -import experimental.Quantum.Language -import OpenSSLAlgorithmGetter - -predicate knownOpenSSLConstantToHashFamilyType(KnownOpenSSLAlgorithmConstant e, Crypto::THashType type) { - exists(string name | e.getAlgType().toLowerCase().matches("hash") | - name = e.getNormalizedName() and - ( - name.matches("BLAKE2B") and type instanceof Crypto::BLAKE2B - or - name.matches("BLAKE2S") and type instanceof Crypto::BLAKE2S - or - name.matches("GOST%") and type instanceof Crypto::GOSTHash - or - name.matches("MD2") and type instanceof Crypto::MD2 - or - name.matches("MD4") and type instanceof Crypto::MD4 - or - name.matches("MD5") and type instanceof Crypto::MD5 - or - name.matches("MDC2") and type instanceof Crypto::MDC2 - or - name.matches("POLY1305") and type instanceof Crypto::POLY1305 - or - name.matches(["SHA", "SHA1"]) and type instanceof Crypto::SHA1 - or - name.matches("SHA+%") and not name.matches(["SHA1", "SHA3-"]) and type instanceof Crypto::SHA2 - or - name.matches("SHA3-%") and type instanceof Crypto::SHA3 - or - name.matches(["SHAKE"]) and type instanceof Crypto::SHAKE - or - name.matches("SM3") and type instanceof Crypto::SM3 - or - name.matches("RIPEMD160") and type instanceof Crypto::RIPEMD160 - or - name.matches("WHIRLPOOL") and type instanceof Crypto::WHIRLPOOL - ) - ) -} - -class KnownOpenSSLHashConstantAlgorithmInstance extends Crypto::HashAlgorithmInstance instanceof KnownOpenSSLAlgorithmConstant -{ - OpenSSLAlgorithmGetterCall getterCall; - - KnownOpenSSLHashConstantAlgorithmInstance() { - // Not just any known value, but specifically a known hash - this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("hash") and - ( - // Two possibilities: - // 1) The source is a literal and flows to a getter, then we know we have an instance - // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that - // Possibility 1: - this instanceof Literal and - exists(DataFlow::Node src, DataFlow::Node sink | - // Sink is an argument to a CipherGetterCall - sink = getterCall.(OpenSSLAlgorithmGetterCall).getValueArgNode() and - // Source is `this` - src.asExpr() = this and - // This traces to a getter - KnownOpenSSLAlgorithmToAlgorithmGetterFlow::flow(src, sink) - ) - or - // Possibility 2: - this instanceof DirectGetterCall and getterCall = this - ) - } - - Crypto::AlgorithmConsumer getConsumer() { - AlgGetterToAlgConsumerFlow::flow(getterCall.getResultNode(), DataFlow::exprNode(result)) - } - - override Crypto::THashType getHashFamily() { - knownOpenSSLConstantToHashFamilyType(this, result) or - not knownOpenSSLConstantToHashFamilyType(this, _) and result = Crypto::OtherHashType() - } - - override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } - - // override int getHashSize() { none() } //TODO -} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll deleted file mode 100644 index 82bf331546f7..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashConsumers.qll +++ /dev/null @@ -1,30 +0,0 @@ -import EVPHashInitializer -import EVPHashOperation -import EVPHashAlgorithmSource - -class EVP_Digest_Initializer_Algorithm_Consumer extends Crypto::AlgorithmValueConsumer instanceof EVPDigestInitializerAlgorithmArgument -{ - override DataFlow::Node getInputNode() { result.asExpr() = this } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this - } -} - -class EVP_Q_Digest_Algorithm_Consumer extends Crypto::AlgorithmValueConsumer instanceof EVP_Q_Digest_Algorithm_Argument -{ - override DataFlow::Node getInputNode() { result.asExpr() = this } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this - } -} - -class EVP_Digest_Algorithm_Consumer extends Crypto::AlgorithmValueConsumer instanceof EVP_Digest_Algorithm_Argument -{ - override DataFlow::Node getInputNode() { result.asExpr() = this } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this - } -} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashInitializer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashInitializer.qll deleted file mode 100644 index 16a9234d68ca..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashInitializer.qll +++ /dev/null @@ -1,25 +0,0 @@ -import cpp - -abstract class EVP_Hash_Inititalizer extends Call { - Expr getContextArg() { result = this.(Call).getArgument(0) } - - abstract Expr getAlgorithmArg(); -} - -class EVP_DigestInit_Variant_Calls extends EVP_Hash_Inititalizer { - EVP_DigestInit_Variant_Calls() { - this.(Call).getTarget().getName() in [ - "EVP_DigestInit", "EVP_DigestInit_ex", "EVP_DigestInit_ex2" - ] - } - - override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } - -} - - -class EVPDigestInitializerAlgorithmArgument extends Expr { - EVPDigestInitializerAlgorithmArgument() { - exists(EVP_Hash_Inititalizer initCall | this = initCall.getAlgorithmArg()) - } -} \ No newline at end of file diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll deleted file mode 100644 index 7db90c4eccc0..000000000000 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/EVPHashOperation.qll +++ /dev/null @@ -1,83 +0,0 @@ -import experimental.Quantum.Language -import CtxFlow as CTXFlow -import LibraryDetector -import EVPHashInitializer -import EVPHashConsumers - -abstract class EVP_Hash_Operation extends Crypto::HashOperationInstance instanceof Call { - Expr getContextArg() { result = this.(Call).getArgument(0) } - - EVP_Hash_Inititalizer getInitCall() { - CTXFlow::ctxFlowsTo(result.getContextArg(), this.getContextArg()) - } -} - -//https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis -class EVP_Q_Digest_Operation extends EVP_Hash_Operation { - EVP_Q_Digest_Operation() { - this.(Call).getTarget().getName() = "EVP_Q_digest" and - isPossibleOpenSSLFunction(this.(Call).getTarget()) - } - - override Crypto::AlgorithmConsumer getAlgorithmConsumer() { this.(Call).getArgument(1) = result } - - override EVP_Hash_Inititalizer getInitCall() { - // This variant of digest does not use an init - // and even if it were used, the init would be ignored/undefined - none() - } -} - -class EVP_Q_Digest_Algorithm_Argument extends Expr { - EVP_Q_Digest_Algorithm_Argument() { - exists(EVP_Q_Digest_Operation op | this = op.(Call).getArgument(1)) - } -} - -class EVP_Digest_Operation extends EVP_Hash_Operation { - EVP_Digest_Operation() { - this.(Call).getTarget().getName() = "EVP_Digest" and - isPossibleOpenSSLFunction(this.(Call).getTarget()) - } - - // There is no context argument for this function - override Expr getContextArg() { none() } - - override Crypto::AlgorithmConsumer getAlgorithmConsumer() { this.(Call).getArgument(4) = result } - - override EVP_Hash_Inititalizer getInitCall() { - // This variant of digest does not use an init - // and even if it were used, the init would be ignored/undefined - none() - } -} - -class EVP_Digest_Algorithm_Argument extends Expr { - EVP_Digest_Algorithm_Argument() { - exists(EVP_Digest_Operation op | this = op.(Call).getArgument(4)) - } -} - -class EVP_DigestUpdate_Operation extends EVP_Hash_Operation { - EVP_DigestUpdate_Operation() { - this.(Call).getTarget().getName() = "EVP_DigestUpdate" and - isPossibleOpenSSLFunction(this.(Call).getTarget()) - } - - override Crypto::AlgorithmConsumer getAlgorithmConsumer() { - this.getInitCall().getAlgorithmArg() = result - } -} - -class EVP_DigestFinal_Variants_Operation extends EVP_Hash_Operation { - EVP_DigestFinal_Variants_Operation() { - this.(Call).getTarget().getName() in [ - "EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF" - ] and - isPossibleOpenSSLFunction(this.(Call).getTarget()) - } - - override Crypto::AlgorithmConsumer getAlgorithmConsumer() { - this.getInitCall().getAlgorithmArg() = result - } -} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll index 56288e6acb75..ae05798ea1f4 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll @@ -4,9 +4,7 @@ import EVPCipherInitializer import OpenSSLOperationBase import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers -// import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers -// import OpenSSLOperation -module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { +private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source) } @@ -16,8 +14,10 @@ module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { } } -module AlgGetterToAlgConsumerFlow = DataFlow::Global; +private module AlgGetterToAlgConsumerFlow = DataFlow::Global; +// import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers +// import OpenSSLOperation // class EVPCipherOutput extends CipherOutputArtifact { // EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) } // override DataFlow::Node getOutputNode() { result.asDefiningArgument() = this } @@ -81,6 +81,8 @@ class EVP_Cipher_Call extends EVP_Cipher_Operation { override Expr getInputArg() { result = this.(Call).getArgument(2) } } +// ******* TODO NEED to model UPDATE but not as the coree operation, rather a step towards final, +// see the JCA // class EVP_Encrypt_Decrypt_or_Cipher_Update_Call extends EVP_Update_Call { // EVP_Encrypt_Decrypt_or_Cipher_Update_Call() { // this.(Call).getTarget().getName() in [ diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll index 76a60b064e55..819e964878c3 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll @@ -1 +1,3 @@ +import OpenSSLOperationBase import EVPCipherOperation +import EVPHashOperation diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 6589cbbc9d5d..ceca0e45464b 100644 --- a/java/ql/lib/experimental/Quantum/JCA.qll +++ b/java/ql/lib/experimental/Quantum/JCA.qll @@ -386,7 +386,7 @@ module JCAModel { override Crypto::THashType getHashFamily() { result = hash_name_to_type_known(hashName, _) } - override int getDigestLength() { exists(hash_name_to_type_known(hashName, result)) } + override int getFixedDigestLength() { exists(hash_name_to_type_known(hashName, result)) } } class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance, @@ -829,7 +829,7 @@ module JCAModel { result = hash_name_to_type_known(this.getRawHashAlgorithmName(), _) } - override int getDigestLength() { + override int getFixedDigestLength() { exists(hash_name_to_type_known(this.getRawHashAlgorithmName(), result)) } } @@ -1237,7 +1237,7 @@ module JCAModel { result = hash_name_to_type_known(this.getRawHashAlgorithmName(), _) } - override int getDigestLength() { + override int getFixedDigestLength() { exists(hash_name_to_type_known(this.getRawHashAlgorithmName(), result)) } diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/cryptography/codeql/cryptography/Model.qll index 8fcc304e9ef9..69a4fd75acfa 100644 --- a/shared/cryptography/codeql/cryptography/Model.qll +++ b/shared/cryptography/codeql/cryptography/Model.qll @@ -983,9 +983,27 @@ module CryptographyBase Input> { abstract string getRawHashAlgorithmName(); /** - * Gets the length of the hash digest in bits. + * Gets the length of the hash digest in bits if it is not an implicit size + * and is not fixed by the algorithm. + * For example, SHA-256 has a fixed length of 256 bits. + * SHA-1 should not be modled with digest length as it is always 160 bits. + * Fixed length digests are modeled with `fixedImplicitDigestLength` and + * are used at the node level. */ - abstract int getDigestLength(); + abstract int getFixedDigestLength(); + } + + predicate fixedImplicitDigestLength(THashType type, int digestLength) { + type instanceof SHA1 and digestLength = 160 + or + type instanceof MD5 and + digestLength = 128 + or + type instanceof RIPEMD160 and + digestLength = 160 + or + type instanceof WHIRLPOOL and + digestLength = 512 // TODO: verify } abstract private class KeyCreationOperationInstance extends OperationInstance { @@ -2398,7 +2416,10 @@ module CryptographyBase Input> { override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashFamily(), result) } - int getDigestLength() { result = instance.asAlg().getDigestLength() } + int getDigestLength() { + result = instance.asAlg().getFixedDigestLength() or + fixedImplicitDigestLength(instance.asAlg().getHashFamily(), result) + } final override predicate properties(string key, string value, Location location) { super.properties(key, value, location) From 4042081539ac17b7a163f7a24ae76e907b0066d6 Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Fri, 2 May 2025 16:35:27 -0400 Subject: [PATCH 098/122] Missing files, should have been part of last commit. --- .../CipherAlgorithmInstance.qll | 35 ++-- .../HashAlgorithmInstance.qll | 83 +++++++++ .../KnownAlgorithmConstants.qll | 160 +++++++++++------- .../HashAlgorithmValueConsumer.qll | 35 ++++ .../OpenSSL/Operations/EVPHashInitializer.qll | 17 ++ .../OpenSSL/Operations/EVPHashOperation.qll | 117 +++++++++++++ 6 files changed, 365 insertions(+), 82 deletions(-) create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashInitializer.qll create mode 100644 cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll index 03f2643d46d2..0dd948c9fae2 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll @@ -66,28 +66,25 @@ predicate knownOpenSSLConstantToCipherFamilyType( class KnownOpenSSLCipherConstantAlgorithmInstance extends OpenSSLAlgorithmInstance, Crypto::KeyOperationAlgorithmInstance instanceof KnownOpenSSLCipherAlgorithmConstant { - //OpenSSLAlgorithmInstance, OpenSSLAlgorithmValueConsumer getterCall; KnownOpenSSLCipherConstantAlgorithmInstance() { - ( - // Two possibilities: - // 1) The source is a literal and flows to a getter, then we know we have an instance - // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that - // Possibility 1: - this instanceof Literal and - exists(DataFlow::Node src, DataFlow::Node sink | - // Sink is an argument to a CipherGetterCall - sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and - // Source is `this` - src.asExpr() = this and - // This traces to a getter - KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) - ) - or - // Possibility 2: - this instanceof DirectAlgorithmValueConsumer and getterCall = this + // Two possibilities: + // 1) The source is a literal and flows to a getter, then we know we have an instance + // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that + // Possibility 1: + this instanceof Literal and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a CipherGetterCall + sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a getter + KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) ) + or + // Possibility 2: + this instanceof DirectAlgorithmValueConsumer and getterCall = this } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { @@ -101,7 +98,7 @@ class KnownOpenSSLCipherConstantAlgorithmInstance extends OpenSSLAlgorithmInstan //TODO: the padding is either self, or it flows through getter ctx to a set padding call // like EVP_PKEY_CTX_set_rsa_padding result = this - // or trace through getter ctx to set padding + // TODO or trace through getter ctx to set padding } override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll new file mode 100644 index 000000000000..4b412da9b55b --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll @@ -0,0 +1,83 @@ +import cpp +import experimental.Quantum.Language +import KnownAlgorithmConstants +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +import AlgToAVCFlow + +predicate knownOpenSSLConstantToHashFamilyType( + KnownOpenSSLHashAlgorithmConstant e, Crypto::THashType type +) { + exists(string name | + name = e.getNormalizedName() and + ( + name.matches("BLAKE2B") and type instanceof Crypto::BLAKE2B + or + name.matches("BLAKE2S") and type instanceof Crypto::BLAKE2S + or + name.matches("GOST%") and type instanceof Crypto::GOSTHash + or + name.matches("MD2") and type instanceof Crypto::MD2 + or + name.matches("MD4") and type instanceof Crypto::MD4 + or + name.matches("MD5") and type instanceof Crypto::MD5 + or + name.matches("MDC2") and type instanceof Crypto::MDC2 + or + name.matches("POLY1305") and type instanceof Crypto::POLY1305 + or + name.matches(["SHA", "SHA1"]) and type instanceof Crypto::SHA1 + or + name.matches("SHA+%") and not name.matches(["SHA1", "SHA3-"]) and type instanceof Crypto::SHA2 + or + name.matches("SHA3-%") and type instanceof Crypto::SHA3 + or + name.matches(["SHAKE"]) and type instanceof Crypto::SHAKE + or + name.matches("SM3") and type instanceof Crypto::SM3 + or + name.matches("RIPEMD160") and type instanceof Crypto::RIPEMD160 + or + name.matches("WHIRLPOOL") and type instanceof Crypto::WHIRLPOOL + ) + ) +} + +class KnownOpenSSLHashConstantAlgorithmInstance extends OpenSSLAlgorithmInstance, + Crypto::HashAlgorithmInstance instanceof KnownOpenSSLHashAlgorithmConstant +{ + OpenSSLAlgorithmValueConsumer getterCall; + + KnownOpenSSLHashConstantAlgorithmInstance() { + // Two possibilities: + // 1) The source is a literal and flows to a getter, then we know we have an instance + // 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that + // Possibility 1: + this instanceof Literal and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a CipherGetterCall + sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a getter + KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) + ) + or + // Possibility 2: + this instanceof DirectAlgorithmValueConsumer and getterCall = this + } + + override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall } + + override Crypto::THashType getHashFamily() { + knownOpenSSLConstantToHashFamilyType(this, result) + or + not knownOpenSSLConstantToHashFamilyType(this, _) and result = Crypto::OtherHashType() + } + + override string getRawHashAlgorithmName() { result = this.(Literal).getValue().toString() } + + override int getFixedDigestLength() { + this.(KnownOpenSSLHashAlgorithmConstant).getExplicitDigestLength() = result + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll index 80bd6b422da4..a0f0107d0ac6 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll @@ -1,40 +1,69 @@ import cpp import experimental.Quantum.OpenSSL.LibraryDetector -class KnownOpenSSLAlgorithmConstant extends Expr { - string normalizedName; - string algType; +predicate resolveAlgorithmFromExpr(Expr e, string normalizedName, string algType) { + resolveAlgorithmFromCall(e, normalizedName, algType) + or + resolveAlgorithmFromLiteral(e, normalizedName, algType) +} - KnownOpenSSLAlgorithmConstant() { - resolveAlgorithmFromCall(this, normalizedName, algType) - or - resolveAlgorithmFromLiteral(this, normalizedName, algType) - } +class KnownOpenSSLAlgorithmConstant extends Expr { + KnownOpenSSLAlgorithmConstant() { resolveAlgorithmFromExpr(this, _, _) } - string getNormalizedName() { result = normalizedName } + string getNormalizedName() { resolveAlgorithmFromExpr(this, result, _) } - string getAlgType() { result = algType } + string getAlgType() { resolveAlgorithmFromExpr(this, _, result) } } class KnownOpenSSLCipherAlgorithmConstant extends KnownOpenSSLAlgorithmConstant { + string algType; + KnownOpenSSLCipherAlgorithmConstant() { - this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("%encryption") + resolveAlgorithmFromExpr(this, _, algType) and + algType.toLowerCase().matches("%encryption") } int getExplicitKeySize() { - result = this.getNormalizedName().regexpCapture(".*-(\\d*)", 1).toInt() + exists(string name | + name = this.getNormalizedName() and + resolveAlgorithmFromExpr(this, name, algType) and + result = name.regexpCapture(".*-(\\d*)", 1).toInt() + ) } } class KnownOpenSSLPaddingAlgorithmConstant extends KnownOpenSSLAlgorithmConstant { + string algType; + KnownOpenSSLPaddingAlgorithmConstant() { - this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("%padding") + resolveAlgorithmFromExpr(this, _, algType) and + algType.toLowerCase().matches("%padding") } } class KnownOpenSSLBlockModeAlgorithmConstant extends KnownOpenSSLAlgorithmConstant { + string algType; + KnownOpenSSLBlockModeAlgorithmConstant() { - this.(KnownOpenSSLAlgorithmConstant).getAlgType().toLowerCase().matches("block_mode") + resolveAlgorithmFromExpr(this, _, algType) and + algType.toLowerCase().matches("%block_mode") + } +} + +class KnownOpenSSLHashAlgorithmConstant extends KnownOpenSSLAlgorithmConstant { + string algType; + + KnownOpenSSLHashAlgorithmConstant() { + resolveAlgorithmFromExpr(this, _, algType) and + algType.toLowerCase().matches("%hash") + } + + int getExplicitDigestLength() { + exists(string name | + name = this.getNormalizedName() and + resolveAlgorithmFromExpr(this, name, "HASH") and + result = name.regexpCapture(".*-(\\d*)$", 1).toInt() + ) } } @@ -224,6 +253,11 @@ predicate defaultAliases(string target, string alias) { alias = "ssl3-sha1" and target = "sha1" } +predicate tbd(string normalized, string algType) { + knownOpenSSLAlgorithmLiteral(_, _, normalized, algType) and + algType = "HASH" +} + /** * Enumeration of all known crypto algorithms for openSSL * `name` is all lower case (caller's must ensure they pass in lower case) @@ -244,9 +278,9 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "gost2001" and nid = 811 and normalized = "GOST" and algType = "SYMMETRIC_ENCRYPTION" or - name = "gost2012_256" and nid = 979 and normalized = "GOST" and algType = "HASH" // TODO: Verify algorithm type + name = "gost2012_256" and nid = 979 and normalized = "GOST" and algType = "ELLIPTIC_CURVE" // TODO: Verify algorithm type or - name = "gost2012_512" and nid = 980 and normalized = "GOST" and algType = "HASH" // TODO: Verify algorithm type + name = "gost2012_512" and nid = 980 and normalized = "GOST" and algType = "ELLIPTIC_CURVE" // TODO: Verify algorithm type or name = "ed25519" and nid = 1087 and normalized = "ED25519" and algType = "ELLIPTIC_CURVE" or @@ -266,17 +300,17 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "md5" and nid = 4 and normalized = "MD5" and algType = "HASH" or - name = "sha224" and nid = 675 and normalized = "SHA224" and algType = "HASH" + name = "sha224" and nid = 675 and normalized = "SHA-224" and algType = "HASH" or - name = "sha256" and nid = 672 and normalized = "SHA256" and algType = "HASH" + name = "sha256" and nid = 672 and normalized = "SHA-256" and algType = "HASH" or - name = "sha384" and nid = 673 and normalized = "SHA384" and algType = "HASH" + name = "sha384" and nid = 673 and normalized = "SHA-384" and algType = "HASH" or - name = "sha512" and nid = 674 and normalized = "SHA512" and algType = "HASH" + name = "sha512" and nid = 674 and normalized = "SHA-512" and algType = "HASH" or - name = "sha512-224" and nid = 1094 and normalized = "SHA512224" and algType = "HASH" + name = "sha512-224" and nid = 1094 and normalized = "SHA-512-224" and algType = "HASH" or - name = "sha512-256" and nid = 1095 and normalized = "SHA512256" and algType = "HASH" + name = "sha512-256" and nid = 1095 and normalized = "SHA-512-256" and algType = "HASH" or name = "sha3-224" and nid = 1096 and normalized = "SHA3-224" and algType = "HASH" or @@ -286,9 +320,9 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "sha3-512" and nid = 1099 and normalized = "SHA3-512" and algType = "HASH" or - name = "shake128" and nid = 1100 and normalized = "SHAKE128" and algType = "HASH" + name = "shake128" and nid = 1100 and normalized = "SHAKE-128" and algType = "HASH" or - name = "shake256" and nid = 1101 and normalized = "SHAKE256" and algType = "HASH" + name = "shake256" and nid = 1101 and normalized = "SHAKE-256" and algType = "HASH" or name = "mdc2" and nid = 95 and normalized = "MDC2" and algType = "HASH" or @@ -1141,7 +1175,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aes-256-cbc-hmac-sha1" and nid = 918 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aes-128-cbc-hmac-sha256" and nid = 948 and normalized = "SHA256" and algType = "HASH" + name = "aes-128-cbc-hmac-sha256" and nid = 948 and normalized = "SHA-256" and algType = "HASH" or name = "aes-128-cbc-hmac-sha256" and nid = 948 and @@ -1150,7 +1184,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aes-128-cbc-hmac-sha256" and nid = 948 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aes-192-cbc-hmac-sha256" and nid = 949 and normalized = "SHA256" and algType = "HASH" + name = "aes-192-cbc-hmac-sha256" and nid = 949 and normalized = "SHA-256" and algType = "HASH" or name = "aes-192-cbc-hmac-sha256" and nid = 949 and @@ -1159,7 +1193,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "aes-192-cbc-hmac-sha256" and nid = 949 and normalized = "CBC" and algType = "BLOCK_MODE" or - name = "aes-256-cbc-hmac-sha256" and nid = 950 and normalized = "SHA256" and algType = "HASH" + name = "aes-256-cbc-hmac-sha256" and nid = 950 and normalized = "SHA-256" and algType = "HASH" or name = "aes-256-cbc-hmac-sha256" and nid = 950 and @@ -1389,11 +1423,11 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "id-dsa-with-sha384" and nid = 1106 and normalized = "DSA" and algType = "SIGNATURE" or - name = "id-dsa-with-sha384" and nid = 1106 and normalized = "SHA384" and algType = "HASH" + name = "id-dsa-with-sha384" and nid = 1106 and normalized = "SHA-384" and algType = "HASH" or name = "id-dsa-with-sha512" and nid = 1107 and normalized = "DSA" and algType = "SIGNATURE" or - name = "id-dsa-with-sha512" and nid = 1107 and normalized = "SHA512" and algType = "HASH" + name = "id-dsa-with-sha512" and nid = 1107 and normalized = "SHA-512" and algType = "HASH" or name = "id-dsa-with-sha3-224" and nid = 1108 and normalized = "DSA" and algType = "SIGNATURE" or @@ -1773,22 +1807,22 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "dhsinglepass-cofactordh-sha224kdf-scheme" and nid = 942 and - normalized = "SHA224" and + normalized = "SHA-224" and algType = "HASH" or name = "dhsinglepass-cofactordh-sha256kdf-scheme" and nid = 943 and - normalized = "SHA256" and + normalized = "SHA-256" and algType = "HASH" or name = "dhsinglepass-cofactordh-sha384kdf-scheme" and nid = 944 and - normalized = "SHA384" and + normalized = "SHA-384" and algType = "HASH" or name = "dhsinglepass-cofactordh-sha512kdf-scheme" and nid = 945 and - normalized = "SHA512" and + normalized = "SHA-512" and algType = "HASH" or name = "dhsinglepass-stddh-sha1kdf-scheme" and @@ -1798,22 +1832,22 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "dhsinglepass-stddh-sha224kdf-scheme" and nid = 937 and - normalized = "SHA224" and + normalized = "SHA-224" and algType = "HASH" or name = "dhsinglepass-stddh-sha256kdf-scheme" and nid = 938 and - normalized = "SHA256" and + normalized = "SHA-256" and algType = "HASH" or name = "dhsinglepass-stddh-sha384kdf-scheme" and nid = 939 and - normalized = "SHA384" and + normalized = "SHA-384" and algType = "HASH" or name = "dhsinglepass-stddh-sha512kdf-scheme" and nid = 940 and - normalized = "SHA512" and + normalized = "SHA-512" and algType = "HASH" or name = "dsa-old" and nid = 67 and normalized = "DSA" and algType = "SIGNATURE" @@ -1832,9 +1866,9 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "dsa_with_sha224" and nid = 802 and normalized = "DSA" and algType = "SIGNATURE" or - name = "dsa_with_sha224" and nid = 802 and normalized = "SHA224" and algType = "HASH" + name = "dsa_with_sha224" and nid = 802 and normalized = "SHA-224" and algType = "HASH" or - name = "dsa_with_sha256" and nid = 803 and normalized = "SHA256" and algType = "HASH" + name = "dsa_with_sha256" and nid = 803 and normalized = "SHA-256" and algType = "HASH" or name = "dsa_with_sha256" and nid = 803 and normalized = "DSA" and algType = "SIGNATURE" or @@ -1856,11 +1890,11 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "dsa_with_sha384" and nid = 1106 and normalized = "DSA" and algType = "SIGNATURE" or - name = "dsa_with_sha384" and nid = 1106 and normalized = "SHA384" and algType = "HASH" + name = "dsa_with_sha384" and nid = 1106 and normalized = "SHA-384" and algType = "HASH" or name = "dsa_with_sha512" and nid = 1107 and normalized = "DSA" and algType = "SIGNATURE" or - name = "dsa_with_sha512" and nid = 1107 and normalized = "SHA512" and algType = "HASH" + name = "dsa_with_sha512" and nid = 1107 and normalized = "SHA-512" and algType = "HASH" or name = "dsaencryption" and nid = 116 and normalized = "DSA" and algType = "SIGNATURE" or @@ -1906,19 +1940,19 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "ecdsa-with-sha1" and nid = 416 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "ecdsa-with-sha224" and nid = 793 and normalized = "SHA224" and algType = "HASH" + name = "ecdsa-with-sha224" and nid = 793 and normalized = "SHA-224" and algType = "HASH" or name = "ecdsa-with-sha224" and nid = 793 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "ecdsa-with-sha256" and nid = 794 and normalized = "SHA256" and algType = "HASH" + name = "ecdsa-with-sha256" and nid = 794 and normalized = "SHA-256" and algType = "HASH" or name = "ecdsa-with-sha256" and nid = 794 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "ecdsa-with-sha384" and nid = 795 and normalized = "SHA384" and algType = "HASH" + name = "ecdsa-with-sha384" and nid = 795 and normalized = "SHA-384" and algType = "HASH" or name = "ecdsa-with-sha384" and nid = 795 and normalized = "ECDSA" and algType = "SIGNATURE" or - name = "ecdsa-with-sha512" and nid = 796 and normalized = "SHA512" and algType = "HASH" + name = "ecdsa-with-sha512" and nid = 796 and normalized = "SHA-512" and algType = "HASH" or name = "ecdsa-with-sha512" and nid = 796 and normalized = "ECDSA" and algType = "SIGNATURE" or @@ -2114,17 +2148,17 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "hmacwithsha1" and nid = 163 and normalized = "SHA1" and algType = "HASH" or - name = "hmacwithsha224" and nid = 798 and normalized = "SHA224" and algType = "HASH" + name = "hmacwithsha224" and nid = 798 and normalized = "SHA-224" and algType = "HASH" or - name = "hmacwithsha256" and nid = 799 and normalized = "SHA256" and algType = "HASH" + name = "hmacwithsha256" and nid = 799 and normalized = "SHA-256" and algType = "HASH" or - name = "hmacwithsha384" and nid = 800 and normalized = "SHA384" and algType = "HASH" + name = "hmacwithsha384" and nid = 800 and normalized = "SHA-384" and algType = "HASH" or - name = "hmacwithsha512" and nid = 801 and normalized = "SHA512" and algType = "HASH" + name = "hmacwithsha512" and nid = 801 and normalized = "SHA-512" and algType = "HASH" or - name = "hmacwithsha512-224" and nid = 1193 and normalized = "SHA512224" and algType = "HASH" + name = "hmacwithsha512-224" and nid = 1193 and normalized = "SHA-512-224" and algType = "HASH" or - name = "hmacwithsha512-256" and nid = 1194 and normalized = "SHA512256" and algType = "HASH" + name = "hmacwithsha512-256" and nid = 1194 and normalized = "SHA-512-256" and algType = "HASH" or name = "hmacwithsm3" and nid = 1281 and normalized = "SM3" and algType = "HASH" or @@ -2765,11 +2799,11 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "rsa-sha224" and nid = 671 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha224" and nid = 671 and normalized = "SHA224" and algType = "HASH" + name = "rsa-sha224" and nid = 671 and normalized = "SHA-224" and algType = "HASH" or name = "rsa-sha256" and nid = 668 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha256" and nid = 668 and normalized = "SHA256" and algType = "HASH" + name = "rsa-sha256" and nid = 668 and normalized = "SHA-256" and algType = "HASH" or name = "rsa-sha3-224" and nid = 1116 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or @@ -2789,25 +2823,25 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "rsa-sha384" and nid = 669 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha384" and nid = 669 and normalized = "SHA384" and algType = "HASH" + name = "rsa-sha384" and nid = 669 and normalized = "SHA-384" and algType = "HASH" or name = "rsa-sha512" and nid = 670 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha512" and nid = 670 and normalized = "SHA512" and algType = "HASH" + name = "rsa-sha512" and nid = 670 and normalized = "SHA-512" and algType = "HASH" or name = "rsa-sha512/224" and nid = 1145 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha512/224" and nid = 1145 and normalized = "SHA512224" and algType = "HASH" + name = "rsa-sha512/224" and nid = 1145 and normalized = "SHA-512-224" and algType = "HASH" or name = "rsa-sha512/256" and nid = 1146 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "rsa-sha512/256" and nid = 1146 and normalized = "SHA512256" and algType = "HASH" + name = "rsa-sha512/256" and nid = 1146 and normalized = "SHA-512-256" and algType = "HASH" or name = "rsa-sm3" and nid = 1144 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or @@ -2859,21 +2893,21 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "sha224withrsaencryption" and nid = 671 and normalized = "SHA224" and algType = "HASH" + name = "sha224withrsaencryption" and nid = 671 and normalized = "SHA-224" and algType = "HASH" or name = "sha256withrsaencryption" and nid = 668 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "sha256withrsaencryption" and nid = 668 and normalized = "SHA256" and algType = "HASH" + name = "sha256withrsaencryption" and nid = 668 and normalized = "SHA-256" and algType = "HASH" or name = "sha384withrsaencryption" and nid = 669 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "sha384withrsaencryption" and nid = 669 and normalized = "SHA384" and algType = "HASH" + name = "sha384withrsaencryption" and nid = 669 and normalized = "SHA-384" and algType = "HASH" or name = "sha512-224withrsaencryption" and nid = 1145 and @@ -2882,7 +2916,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "sha512-224withrsaencryption" and nid = 1145 and - normalized = "SHA512224" and + normalized = "SHA-512-224" and algType = "HASH" or name = "sha512-256withrsaencryption" and @@ -2892,7 +2926,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "sha512-256withrsaencryption" and nid = 1146 and - normalized = "SHA512256" and + normalized = "SHA-512-256" and algType = "HASH" or name = "sha512withrsaencryption" and @@ -2900,7 +2934,7 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or - name = "sha512withrsaencryption" and nid = 670 and normalized = "SHA512" and algType = "HASH" + name = "sha512withrsaencryption" and nid = 670 and normalized = "SHA-512" and algType = "HASH" or name = "shawithrsaencryption" and nid = 42 and diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll new file mode 100644 index 000000000000..753bb356e7a7 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll @@ -0,0 +1,35 @@ +// import EVPHashInitializer +// import EVPHashOperation +// import EVPHashAlgorithmSource +import cpp +import experimental.Quantum.Language +import semmle.code.cpp.dataflow.new.DataFlow +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase +import experimental.Quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstances +import experimental.Quantum.OpenSSL.LibraryDetector + +abstract class HashAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { } + +/** + * EVP_Q_Digest directly consumes algorithm constant values + */ +class EVP_Q_Digest_Algorithm_Consumer extends OpenSSLAlgorithmValueConsumer { + EVP_Q_Digest_Algorithm_Consumer() { + isPossibleOpenSSLFunction(this.(Call).getTarget()) and + this.(Call).getTarget().getName() = "EVP_Q_digest" + } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { + result.asExpr() = this.(Call).getArgument(1) + } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(OpenSSLAlgorithmInstance i | i.getAVC() = this and result = i) + } + + override DataFlow::Node getResultNode() { + // EVP_Q_Digest directly consumes the algorithm constant value and performs the operation, there is no + // algorithm result + none() + } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashInitializer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashInitializer.qll new file mode 100644 index 000000000000..cd0dac94001a --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashInitializer.qll @@ -0,0 +1,17 @@ +import cpp + +abstract class EVP_Hash_Inititalizer extends Call { + Expr getContextArg() { result = this.(Call).getArgument(0) } + + abstract Expr getAlgorithmArg(); +} + +class EVP_DigestInit_Variant_Calls extends EVP_Hash_Inititalizer { + EVP_DigestInit_Variant_Calls() { + this.(Call).getTarget().getName() in [ + "EVP_DigestInit", "EVP_DigestInit_ex", "EVP_DigestInit_ex2" + ] + } + + override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } +} diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll new file mode 100644 index 000000000000..b7771e240a47 --- /dev/null +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll @@ -0,0 +1,117 @@ +/** + * https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis + */ + +import experimental.Quantum.Language +import experimental.Quantum.OpenSSL.CtxFlow as CTXFlow +import experimental.Quantum.OpenSSL.LibraryDetector +import OpenSSLOperationBase +import EVPHashInitializer +import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers + +// import EVPHashConsumers +abstract class EVP_Hash_Operation extends OpenSSLOperation, Crypto::HashOperationInstance { + Expr getContextArg() { result = this.(Call).getArgument(0) } + + EVP_Hash_Inititalizer getInitCall() { + CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) + } +} + +private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source) + } + + predicate isSink(DataFlow::Node sink) { + exists(EVP_Hash_Operation c | c.getInitCall().getAlgorithmArg() = sink.asExpr()) + } +} + +private module AlgGetterToAlgConsumerFlow = DataFlow::Global; + +//https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis +class EVP_Q_Digest_Operation extends EVP_Hash_Operation { + EVP_Q_Digest_Operation() { + this.(Call).getTarget().getName() = "EVP_Q_digest" and + isPossibleOpenSSLFunction(this.(Call).getTarget()) + } + + //override Crypto::AlgorithmConsumer getAlgorithmConsumer() { } + override EVP_Hash_Inititalizer getInitCall() { + // This variant of digest does not use an init + // and even if it were used, the init would be ignored/undefined + none() + } + + override Expr getOutputArg() { result = this.(Call).getArgument(5) } + + override Expr getInputArg() { result = this.(Call).getArgument(3) } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + // The operation is a direct algorithm consumer + // NOTE: the operation itself is already modeld as a value consumer, so we can + // simply return 'this', see modeled hash algorithm consuers for EVP_Q_Digest + this = result + } +} + +class EVP_Digest_Operation extends EVP_Hash_Operation { + EVP_Digest_Operation() { + this.(Call).getTarget().getName() = "EVP_Digest" and + isPossibleOpenSSLFunction(this.(Call).getTarget()) + } + + // There is no context argument for this function + override Expr getContextArg() { none() } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), + DataFlow::exprNode(this.(Call).getArgument(4))) + } + + override EVP_Hash_Inititalizer getInitCall() { + // This variant of digest does not use an init + // and even if it were used, the init would be ignored/undefined + none() + } + + override Expr getOutputArg() { result = this.(Call).getArgument(2) } + + override Expr getInputArg() { result = this.(Call).getArgument(0) } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() } +} +// // override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { +// // AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), +// // DataFlow::exprNode(this.getInitCall().getAlgorithmArg())) +// // } +// // ***** TODO *** complete modelinlg for hash operations, but have consideration for terminal and non-terminal (non intermedaite) steps +// // see the JCA. May need to update the cipher operations similarly +// // ALSO SEE cipher for how we currently model initialization of the algorithm through an init call +// class EVP_DigestUpdate_Operation extends EVP_Hash_Operation { +// EVP_DigestUpdate_Operation() { +// this.(Call).getTarget().getName() = "EVP_DigestUpdate" and +// isPossibleOpenSSLFunction(this.(Call).getTarget()) +// } +// override Crypto::AlgorithmConsumer getAlgorithmConsumer() { +// this.getInitCall().getAlgorithmArg() = result +// } +// } +// class EVP_DigestFinal_Variants_Operation extends EVP_Hash_Operation { +// EVP_DigestFinal_Variants_Operation() { +// this.(Call).getTarget().getName() in [ +// "EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF" +// ] and +// isPossibleOpenSSLFunction(this.(Call).getTarget()) +// } +// override Crypto::AlgorithmConsumer getAlgorithmConsumer() { +// this.getInitCall().getAlgorithmArg() = result +// } +// } From 56670c66f14d5b8e324baf1b661d3ee4292b9481 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 00:26:13 +0200 Subject: [PATCH 099/122] Revert Python changes and delete implementation --- python/ql/lib/experimental/Quantum/Base.qll | 250 ------------------ .../ql/lib/experimental/Quantum/Language.qll | 12 - .../experimental/Quantum/PycaCryptography.qll | 55 ---- python/ql/lib/semmle/python/Files.qll | 37 --- 4 files changed, 354 deletions(-) delete mode 100644 python/ql/lib/experimental/Quantum/Base.qll delete mode 100644 python/ql/lib/experimental/Quantum/Language.qll delete mode 100644 python/ql/lib/experimental/Quantum/PycaCryptography.qll diff --git a/python/ql/lib/experimental/Quantum/Base.qll b/python/ql/lib/experimental/Quantum/Base.qll deleted file mode 100644 index 9be24ca6efaa..000000000000 --- a/python/ql/lib/experimental/Quantum/Base.qll +++ /dev/null @@ -1,250 +0,0 @@ -/** - * A language-independent library for reasoning about cryptography. - */ - -import codeql.util.Location -import codeql.util.Option - -signature module InputSig { - class LocatableElement { - Location getLocation(); - } - - class UnknownLocation instanceof Location; -} - -module CryptographyBase Input> { - final class LocatableElement = Input::LocatableElement; - - final class UnknownLocation = Input::UnknownLocation; - - final class UnknownPropertyValue extends string { - UnknownPropertyValue() { this = "" } - } - - abstract class NodeBase instanceof LocatableElement { - /** - * Returns a string representation of this node, usually the name of the operation/algorithm/property. - */ - abstract string toString(); - - /** - * Returns the location of this node in the code. - */ - Location getLocation() { result = super.getLocation() } - - /** - * Gets the origin of this node, e.g., a string literal in source describing it. - */ - LocatableElement getOrigin(string value) { none() } - - /** - * Returns the child of this node with the given edge name. - * - * This predicate is used by derived classes to construct the graph of cryptographic operations. - */ - NodeBase getChild(string edgeName) { none() } - - /** - * Defines properties of this node by name and either a value or location or both. - * - * This predicate is used by derived classes to construct the graph of cryptographic operations. - */ - predicate properties(string key, string value, Location location) { - key = "origin" and location = this.getOrigin(value).getLocation() - } - - /** - * Returns the parent of this node. - */ - final NodeBase getAParent() { result.getChild(_) = this } - } - - class Asset = NodeBase; - - /** - * A cryptographic operation, such as hashing or encryption. - */ - abstract class Operation extends Asset { - /** - * Gets the algorithm associated with this operation. - */ - abstract Algorithm getAlgorithm(); - - /** - * Gets the name of this operation, e.g., "hash" or "encrypt". - */ - abstract string getOperationName(); - - final override string toString() { result = this.getOperationName() } - - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - edgeName = "uses" and - if exists(this.getAlgorithm()) then result = this.getAlgorithm() else result = this - } - } - - abstract class Algorithm extends Asset { - /** - * Gets the name of this algorithm, e.g., "AES" or "SHA". - */ - abstract string getAlgorithmName(); - - /** - * Gets the raw name of this algorithm from source (no parsing or formatting) - */ - abstract string getRawAlgorithmName(); - - final override string toString() { result = this.getAlgorithmName() } - } - - /** - * A hashing operation that processes data to generate a hash value. - * This operation takes an input message of arbitrary content and length and produces a fixed-size - * hash value as the output using a specified hashing algorithm. - */ - abstract class HashOperation extends Operation { - abstract override HashAlgorithm getAlgorithm(); - - override string getOperationName() { result = "HASH" } - } - - // Rule: no newtype representing a type of algorithm should be modelled with multiple interfaces - // - // Example: HKDF and PKCS12KDF are both key derivation algorithms. - // However, PKCS12KDF also has a property: the iteration count. - // - // If we have HKDF and PKCS12KDF under TKeyDerivationType, - // someone modelling a library might try to make a generic identification of both of those algorithms. - // - // They will therefore not use the specialized type for PKCS12KDF, - // meaning "from PKCS12KDF algo select algo" will have no results. - // - newtype THashType = - // We're saying by this that all of these have an identical interface / properties / edges - MD5() or - SHA1() or - SHA256() or - SHA512() or - OtherHashType() - - /** - * A hashing algorithm that transforms variable-length input into a fixed-size hash value. - */ - abstract class HashAlgorithm extends Algorithm { - final predicate hashTypeToNameMapping(THashType type, string name) { - type instanceof MD5 and name = "MD5" - or - type instanceof SHA1 and name = "SHA-1" - or - type instanceof SHA256 and name = "SHA-256" - or - type instanceof SHA512 and name = "SHA-512" - or - type instanceof OtherHashType and name = this.getRawAlgorithmName() - } - - abstract THashType getHashType(); - - override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) } - - } - - /** - * An operation that derives one or more keys from an input value. - */ - abstract class KeyDerivationOperation extends Operation { - override string getOperationName() { result = "KEY_DERIVATION" } - } - - /** - * An algorithm that derives one or more keys from an input value. - */ - abstract class KeyDerivationAlgorithm extends Algorithm { - abstract override string getAlgorithmName(); - } - - /** - * HKDF key derivation function - */ - abstract class HKDF extends KeyDerivationAlgorithm { - final override string getAlgorithmName() { result = "HKDF" } - - abstract HashAlgorithm getHashAlgorithm(); - - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - edgeName = "digest" and result = this.getHashAlgorithm() - } - } - - /** - * PKCS #12 key derivation function - */ - abstract class PKCS12KDF extends KeyDerivationAlgorithm { - final override string getAlgorithmName() { result = "PKCS12KDF" } - - abstract HashAlgorithm getHashAlgorithm(); - - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) - or - edgeName = "digest" and result = this.getHashAlgorithm() - } - } - - newtype TEllipticCurveFamilyType = - // We're saying by this that all of these have an identical interface / properties / edges - NIST() or - SEC() or - NUMS() or - PRIME() or - BRAINPOOL() or - CURVE25519() or - CURVE448() or - C2() or - SM2() or - ES() or - OtherEllipticCurveFamilyType() - - - /** - * Elliptic curve algorithm - */ - abstract class EllipticCurve extends Algorithm { - - - abstract string getKeySize(Location location); - - abstract TEllipticCurveFamilyType getCurveFamilyType(); - - override predicate properties(string key, string value, Location location) { - super.properties(key, value, location) - or - key = "key_size" and - if exists(this.getKeySize(location)) - then value = this.getKeySize(location) - else ( - value instanceof UnknownPropertyValue and location instanceof UnknownLocation - ) - // other properties, like field type are possible, but not modeled until considered necessary - } - - override string getAlgorithmName() { result = this.getRawAlgorithmName().toUpperCase()} - - /** - * Mandating that for Elliptic Curves specifically, users are responsible - * for providing as the 'raw' name, the official name of the algorithm. - * Casing doesn't matter, we will enforce further naming restrictions on - * `getAlgorithmName` by default. - * Rationale: elliptic curve names can have a lot of variation in their components - * (e.g., "secp256r1" vs "P-256"), trying to produce generalized set of properties - * is possible to capture all cases, but such modeling is likely not necessary. - * if all properties need to be captured, we can reassess how names are generated. - */ - override abstract string getRawAlgorithmName(); - } -} diff --git a/python/ql/lib/experimental/Quantum/Language.qll b/python/ql/lib/experimental/Quantum/Language.qll deleted file mode 100644 index 9abf3e7fa7e4..000000000000 --- a/python/ql/lib/experimental/Quantum/Language.qll +++ /dev/null @@ -1,12 +0,0 @@ -private import Base -private import python as Lang - -module CryptoInput implements InputSig { - class LocatableElement = Lang::Expr; - - class UnknownLocation = Lang::UnknownDefaultLocation; -} - -module Crypto = CryptographyBase; - -import PycaCryptography diff --git a/python/ql/lib/experimental/Quantum/PycaCryptography.qll b/python/ql/lib/experimental/Quantum/PycaCryptography.qll deleted file mode 100644 index 802ad23cf9d9..000000000000 --- a/python/ql/lib/experimental/Quantum/PycaCryptography.qll +++ /dev/null @@ -1,55 +0,0 @@ -import python -import semmle.python.ApiGraphs - -module PycaCryptographyModule { - import Language - - /** - * Gets a predefined curve class constructor call from - * `cryptography.hazmat.primitives.asymmetric.ec` - * https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/#elliptic-curves - */ - DataFlow::Node predefinedCurveClass(string rawName, string curveName, Crypto::TEllipticCurveFamilyType family, int keySize) { - // getACall since the typical case is to construct the curve with initialization values, - // not to pass the mode uninitialized - result = - API::moduleImport("cryptography") - .getMember("hazmat") - .getMember("primitives") - .getMember("asymmetric") - .getMember("ec") - .getMember(rawName) - .getACall() - and - curveName = rawName.toUpperCase() - and - curveName.matches("SEC%") and family instanceof Crypto::SEC - and - curveName.matches("BRAINPOOL%") and family instanceof Crypto::BRAINPOOL - and - // Enumerating all key sizes known in the API - // TODO: should we dynamically extract them through a regex? - keySize in [160, 163, 192, 224, 233, 256, 283, 320, 384, 409, 512, 571] - and - curveName.matches("%" + keySize + "%") - } - - - class EllipticCurve extends Crypto::EllipticCurve instanceof Expr{ - int keySize; - string rawName; - string curveName; - Crypto::TEllipticCurveFamilyType family; - EllipticCurve() { - this = predefinedCurveClass(rawName, curveName, family, keySize).asExpr() - } - - override string getRawAlgorithmName() { result = rawName } - override string getAlgorithmName() { result = curveName } - Crypto::TEllipticCurveFamilyType getFamily() { result = family } - - override string getKeySize(Location location) { - location = this and - result = keySize.toString() } - } -} diff --git a/python/ql/lib/semmle/python/Files.qll b/python/ql/lib/semmle/python/Files.qll index 67f21ad0b249..2da0dd61f885 100644 --- a/python/ql/lib/semmle/python/Files.qll +++ b/python/ql/lib/semmle/python/Files.qll @@ -368,40 +368,3 @@ class EncodingError extends SyntaxError { override string toString() { result = "Encoding Error" } } - - - -/** - * 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. - */ -class UnknownLocation extends 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. - */ -class UnknownDefaultLocation extends UnknownLocation { - UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) } -} - -/** - * A dummy location which is used when an expression doesn't have a - * location in the source code but needs to have a `Location` associated - * with it. - */ -class UnknownExprLocation extends UnknownLocation { - UnknownExprLocation() { locations_default(this, _, 0, 0, 0, 0) } -} - -/** - * A dummy location which is used when a statement doesn't have a location - * in the source code but needs to have a `Location` associated with it. - */ -class UnknownStmtLocation extends UnknownLocation { - UnknownStmtLocation() { locations_default(this, _, 0, 0, 0, 0) } -} From 314f1ff93f75b87a323a3e62358209fbec4fa49b Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 00:28:32 +0200 Subject: [PATCH 100/122] Refactor Java directory structure --- .../Quantum/{InventoryFilters => Analysis}/ArtifactReuse.qll | 0 .../experimental/Quantum/{ => Analysis}/InsecureNonceSource.ql | 0 .../{InventoryFilters => Analysis}/KnownWeakKDFIterationCount.ql | 0 .../Quantum/{InventoryFilters => Analysis}/ReusedNonce.ql | 0 .../{InventoryFilters => Analysis}/UnknownKDFIterationCount.ql | 0 java/ql/src/experimental/Quantum/{ => Examples}/BrokenCrypto.ql | 0 .../Quantum/{ => Examples}/InsecureOrUnknownNonceAtOperation.ql | 0 .../ql/src/experimental/Quantum/{ => Examples}/TestAESGCMNonce.ql | 0 java/ql/src/experimental/Quantum/{ => Examples}/TestCipher.ql | 0 java/ql/src/experimental/Quantum/{ => Examples}/TestCipherKey.ql | 0 java/ql/src/experimental/Quantum/{ => Examples}/TestHash.ql | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename java/ql/src/experimental/Quantum/{InventoryFilters => Analysis}/ArtifactReuse.qll (100%) rename java/ql/src/experimental/Quantum/{ => Analysis}/InsecureNonceSource.ql (100%) rename java/ql/src/experimental/Quantum/{InventoryFilters => Analysis}/KnownWeakKDFIterationCount.ql (100%) rename java/ql/src/experimental/Quantum/{InventoryFilters => Analysis}/ReusedNonce.ql (100%) rename java/ql/src/experimental/Quantum/{InventoryFilters => Analysis}/UnknownKDFIterationCount.ql (100%) rename java/ql/src/experimental/Quantum/{ => Examples}/BrokenCrypto.ql (100%) rename java/ql/src/experimental/Quantum/{ => Examples}/InsecureOrUnknownNonceAtOperation.ql (100%) rename java/ql/src/experimental/Quantum/{ => Examples}/TestAESGCMNonce.ql (100%) rename java/ql/src/experimental/Quantum/{ => Examples}/TestCipher.ql (100%) rename java/ql/src/experimental/Quantum/{ => Examples}/TestCipherKey.ql (100%) rename java/ql/src/experimental/Quantum/{ => Examples}/TestHash.ql (100%) diff --git a/java/ql/src/experimental/Quantum/InventoryFilters/ArtifactReuse.qll b/java/ql/src/experimental/Quantum/Analysis/ArtifactReuse.qll similarity index 100% rename from java/ql/src/experimental/Quantum/InventoryFilters/ArtifactReuse.qll rename to java/ql/src/experimental/Quantum/Analysis/ArtifactReuse.qll diff --git a/java/ql/src/experimental/Quantum/InsecureNonceSource.ql b/java/ql/src/experimental/Quantum/Analysis/InsecureNonceSource.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InsecureNonceSource.ql rename to java/ql/src/experimental/Quantum/Analysis/InsecureNonceSource.ql diff --git a/java/ql/src/experimental/Quantum/InventoryFilters/KnownWeakKDFIterationCount.ql b/java/ql/src/experimental/Quantum/Analysis/KnownWeakKDFIterationCount.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventoryFilters/KnownWeakKDFIterationCount.ql rename to java/ql/src/experimental/Quantum/Analysis/KnownWeakKDFIterationCount.ql diff --git a/java/ql/src/experimental/Quantum/InventoryFilters/ReusedNonce.ql b/java/ql/src/experimental/Quantum/Analysis/ReusedNonce.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventoryFilters/ReusedNonce.ql rename to java/ql/src/experimental/Quantum/Analysis/ReusedNonce.ql diff --git a/java/ql/src/experimental/Quantum/InventoryFilters/UnknownKDFIterationCount.ql b/java/ql/src/experimental/Quantum/Analysis/UnknownKDFIterationCount.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventoryFilters/UnknownKDFIterationCount.ql rename to java/ql/src/experimental/Quantum/Analysis/UnknownKDFIterationCount.ql diff --git a/java/ql/src/experimental/Quantum/BrokenCrypto.ql b/java/ql/src/experimental/Quantum/Examples/BrokenCrypto.ql similarity index 100% rename from java/ql/src/experimental/Quantum/BrokenCrypto.ql rename to java/ql/src/experimental/Quantum/Examples/BrokenCrypto.ql diff --git a/java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql b/java/ql/src/experimental/Quantum/Examples/InsecureOrUnknownNonceAtOperation.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InsecureOrUnknownNonceAtOperation.ql rename to java/ql/src/experimental/Quantum/Examples/InsecureOrUnknownNonceAtOperation.ql diff --git a/java/ql/src/experimental/Quantum/TestAESGCMNonce.ql b/java/ql/src/experimental/Quantum/Examples/TestAESGCMNonce.ql similarity index 100% rename from java/ql/src/experimental/Quantum/TestAESGCMNonce.ql rename to java/ql/src/experimental/Quantum/Examples/TestAESGCMNonce.ql diff --git a/java/ql/src/experimental/Quantum/TestCipher.ql b/java/ql/src/experimental/Quantum/Examples/TestCipher.ql similarity index 100% rename from java/ql/src/experimental/Quantum/TestCipher.ql rename to java/ql/src/experimental/Quantum/Examples/TestCipher.ql diff --git a/java/ql/src/experimental/Quantum/TestCipherKey.ql b/java/ql/src/experimental/Quantum/Examples/TestCipherKey.ql similarity index 100% rename from java/ql/src/experimental/Quantum/TestCipherKey.ql rename to java/ql/src/experimental/Quantum/Examples/TestCipherKey.ql diff --git a/java/ql/src/experimental/Quantum/TestHash.ql b/java/ql/src/experimental/Quantum/Examples/TestHash.ql similarity index 100% rename from java/ql/src/experimental/Quantum/TestHash.ql rename to java/ql/src/experimental/Quantum/Examples/TestHash.ql From c19291be8872ffa1ae559079c968d86fd75939d2 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 01:38:53 +0200 Subject: [PATCH 101/122] Refactor 'cryptography' and 'Quantum' to 'quantum' --- cpp/ql/lib/experimental/Quantum/Language.qll | 4 +- .../AlgorithmInstances/AlgToAVCFlow.qll | 4 +- .../BlockAlgorithmInstance.qll | 6 +- .../CipherAlgorithmInstance.qll | 4 +- .../HashAlgorithmInstance.qll | 4 +- .../KnownAlgorithmConstants.qll | 2 +- .../OpenSSLAlgorithmInstanceBase.qll | 4 +- .../PaddingAlgorithmInstance.qll | 6 +- .../CipherAlgorithmValueConsumer.qll | 8 +- .../DirectAlgorithmValueConsumer.qll | 6 +- .../HashAlgorithmValueConsumer.qll | 8 +- .../OpenSSLAlgorithmValueConsumerBase.qll | 2 +- .../PaddingAlgorithmValueConsumer.qll | 8 +- .../experimental/Quantum/OpenSSL/OpenSSL.qll | 8 +- .../Operations/EVPCipherInitializer.qll | 4 +- .../OpenSSL/Operations/EVPCipherOperation.qll | 8 +- .../OpenSSL/Operations/EVPHashOperation.qll | 8 +- .../Operations/OpenSSLOperationBase.qll | 2 +- .../experimental/Quantum/OpenSSL/Random.qll | 7 +- cpp/ql/lib/qlpack.yml | 2 +- .../experimental/Quantum/PrintCBOMGraph.ql | 2 +- java/ql/lib/experimental/Quantum/Language.qll | 2 +- java/ql/lib/qlpack.yml | 2 +- .../Quantum/Analysis/ArtifactReuse.qll | 2 +- .../Quantum/Analysis/InsecureNonceSource.ql | 2 +- .../Analysis/KnownWeakKDFIterationCount.ql | 2 +- .../Analysis/UnknownKDFIterationCount.ql | 2 +- .../Quantum/Examples/BrokenCrypto.ql | 102 +++++++++--------- .../InsecureOrUnknownNonceAtOperation.ql | 2 +- .../Quantum/Examples/TestAESGCMNonce.ql | 2 +- .../Quantum/Examples/TestCipher.ql | 2 +- .../Quantum/Examples/TestCipherKey.ql | 2 +- .../experimental/Quantum/Examples/TestHash.ql | 2 +- .../KnownAsymmetricAlgorithm.ql | 2 +- .../KnownAsymmetricCipherAlgorithm.ql | 2 +- .../KnownAsymmetricOperationAlgorithm.ql | 2 +- .../InventorySlices/KnownCipherAlgorithm.ql | 2 +- .../KnownEllipticCurveAlgorithm.ql | 2 +- .../InventorySlices/KnownHashingAlgorithm.ql | 2 +- .../InventorySlices/KnownHashingOperation.ql | 2 +- .../KnownHashingOperationAlgorithm.ql | 2 +- .../KnownKeyDerivationAlgorithm.ql | 2 +- .../KnownKeyDerivationOperation.ql | 2 +- .../KnownKeyDerivationOperationAlgorithm.ql | 5 +- .../KnownSymmetricCipherAlgorithm.ql | 2 +- .../LikelyCryptoAPIFunction.ql | 2 +- .../UnknownOperationAlgorithm.ql | 2 +- .../experimental/Quantum/PrintCBOMGraph.ql | 2 +- .../codeql/quantum}/Model.qll | 0 .../{cryptography => experimental}/qlpack.yml | 2 +- 50 files changed, 130 insertions(+), 136 deletions(-) rename shared/{cryptography/codeql/cryptography => experimental/codeql/quantum}/Model.qll (100%) rename shared/{cryptography => experimental}/qlpack.yml (81%) diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index 2d076a1323c0..c3e2e3ad55f4 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -1,6 +1,4 @@ -private import codeql.cryptography.Model -import semmle.code.cpp.ir.IR -import semmle.code.cpp.security.FlowSources as FlowSources +import codeql.quantum.Model import semmle.code.cpp.dataflow.new.DataFlow private import cpp as Lang diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll index 25cdea6accf7..72c3ffcfad44 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll @@ -1,7 +1,7 @@ import cpp import semmle.code.cpp.dataflow.new.DataFlow -import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers // import all known alg value consummers +import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers /** * Traces 'known algorithms' to AVCs, specifically diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll index 1e17c64f34ca..2566c1188a6c 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll @@ -1,8 +1,8 @@ import cpp -import experimental.Quantum.Language +import experimental.quantum.Language import OpenSSLAlgorithmInstanceBase -import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer +import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer import AlgToAVCFlow /** diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll index 0dd948c9fae2..7483572848eb 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll @@ -1,10 +1,10 @@ import cpp -import experimental.Quantum.Language +import experimental.quantum.Language import KnownAlgorithmConstants import Crypto::KeyOpAlg as KeyOpAlg import OpenSSLAlgorithmInstanceBase import PaddingAlgorithmInstance -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers import AlgToAVCFlow import BlockAlgorithmInstance diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll index 4b412da9b55b..985e36dbdd71 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll @@ -1,7 +1,7 @@ import cpp -import experimental.Quantum.Language +import experimental.quantum.Language import KnownAlgorithmConstants -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers import AlgToAVCFlow predicate knownOpenSSLConstantToHashFamilyType( diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll index a0f0107d0ac6..77caf0bb378c 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll @@ -1,5 +1,5 @@ import cpp -import experimental.Quantum.OpenSSL.LibraryDetector +import experimental.quantum.OpenSSL.LibraryDetector predicate resolveAlgorithmFromExpr(Expr e, string normalizedName, string algType) { resolveAlgorithmFromCall(e, normalizedName, algType) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll index 6a206773bfb8..dc49c139cf05 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll @@ -1,5 +1,5 @@ -import experimental.Quantum.Language -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase +import experimental.quantum.Language +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase abstract class OpenSSLAlgorithmInstance extends Crypto::AlgorithmInstance { abstract OpenSSLAlgorithmValueConsumer getAVC(); diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll index 219289c7da08..4fb4d0818697 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll @@ -1,9 +1,9 @@ import cpp -import experimental.Quantum.Language +import experimental.quantum.Language import OpenSSLAlgorithmInstanceBase -import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants import AlgToAVCFlow -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer /** * Given a `KnownOpenSSLPaddingAlgorithmConstant`, converts this to a padding family type. diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll index 19777bc06d1e..8fa65860b60c 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll @@ -1,8 +1,8 @@ import cpp -import experimental.Quantum.Language -import experimental.Quantum.OpenSSL.LibraryDetector -import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants -import experimental.Quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase +import experimental.quantum.Language +import experimental.quantum.OpenSSL.LibraryDetector +import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase import OpenSSLAlgorithmValueConsumerBase abstract class CipherAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll index cebe7a86a129..ffc9a7c3991e 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll @@ -1,7 +1,7 @@ import cpp -import experimental.Quantum.Language -import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase +import experimental.quantum.Language +import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase // TODO: can self referential to itself, which is also an algorithm (Known algorithm) /** diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll index 753bb356e7a7..b041b986754c 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll @@ -2,11 +2,11 @@ // import EVPHashOperation // import EVPHashAlgorithmSource import cpp -import experimental.Quantum.Language +import experimental.quantum.Language import semmle.code.cpp.dataflow.new.DataFlow -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase -import experimental.Quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstances -import experimental.Quantum.OpenSSL.LibraryDetector +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase +import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstances +import experimental.quantum.OpenSSL.LibraryDetector abstract class HashAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll index dddcf14c7134..3f6e2bd4dc89 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll @@ -1,4 +1,4 @@ -import experimental.Quantum.Language +import experimental.quantum.Language import semmle.code.cpp.dataflow.new.DataFlow abstract class OpenSSLAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof Call { diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll index 009b22cf1b86..3f7ce20d6b3a 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll @@ -1,8 +1,8 @@ import cpp -import experimental.Quantum.Language -import experimental.Quantum.OpenSSL.LibraryDetector -import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants -import experimental.Quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase +import experimental.quantum.Language +import experimental.quantum.OpenSSL.LibraryDetector +import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase import OpenSSLAlgorithmValueConsumerBase abstract class PaddingAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll index 6b28c4ee8e4b..f53812093c42 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll @@ -2,8 +2,8 @@ import cpp import semmle.code.cpp.dataflow.new.DataFlow module OpenSSLModel { - import experimental.Quantum.Language - import experimental.Quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstances - import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers - import experimental.Quantum.OpenSSL.Operations.OpenSSLOperations + import experimental.quantum.Language + import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstances + import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers + import experimental.quantum.OpenSSL.Operations.OpenSSLOperations } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll index 584fd18a64c2..fdf60ef757e8 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll @@ -3,8 +3,8 @@ * Models cipher initialization for EVP cipher operations. */ -import experimental.Quantum.Language -import experimental.Quantum.OpenSSL.CtxFlow as CTXFlow +import experimental.quantum.Language +import experimental.quantum.OpenSSL.CtxFlow as CTXFlow module EncValToInitEncArgConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll index ae05798ea1f4..45c7d41b029b 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll @@ -1,8 +1,8 @@ -import experimental.Quantum.Language -import experimental.Quantum.OpenSSL.CtxFlow as CTXFlow +import experimental.quantum.Language +import experimental.quantum.OpenSSL.CtxFlow as CTXFlow import EVPCipherInitializer import OpenSSLOperationBase -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { @@ -16,7 +16,7 @@ private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { private module AlgGetterToAlgConsumerFlow = DataFlow::Global; -// import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers +// import experimental.quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers // import OpenSSLOperation // class EVPCipherOutput extends CipherOutputArtifact { // EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) } diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll index b7771e240a47..45776b6668b3 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll @@ -2,12 +2,12 @@ * https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis */ -import experimental.Quantum.Language -import experimental.Quantum.OpenSSL.CtxFlow as CTXFlow -import experimental.Quantum.OpenSSL.LibraryDetector +import experimental.quantum.Language +import experimental.quantum.OpenSSL.CtxFlow as CTXFlow +import experimental.quantum.OpenSSL.LibraryDetector import OpenSSLOperationBase import EVPHashInitializer -import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers // import EVPHashConsumers abstract class EVP_Hash_Operation extends OpenSSLOperation, Crypto::HashOperationInstance { diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index 851d7a4b7e99..4798f5650a9b 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -1,4 +1,4 @@ -import experimental.Quantum.Language +import experimental.quantum.Language abstract class OpenSSLOperation extends Crypto::OperationInstance instanceof Call { abstract Expr getInputArg(); diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll index eceff8748743..e599ed82169b 100644 --- a/cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll +++ b/cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll @@ -1,6 +1,5 @@ import cpp -private import experimental.Quantum.Language -private import codeql.cryptography.Model +private import experimental.quantum.Language private import LibraryDetector private import semmle.code.cpp.dataflow.new.DataFlow @@ -15,7 +14,5 @@ class OpenSSLRandomNumberGeneratorInstance extends Crypto::RandomNumberGeneratio result.asDefiningArgument() = this.(Call).getArgument(0) } - override predicate flowsTo(Crypto::FlowAwareElement other) { - ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode()) - } + override string getGeneratorName() { result = this.(Call).getTarget().getName() } } diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index 6bb19968c824..335360caeb26 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -6,8 +6,8 @@ extractor: cpp library: true upgrades: upgrades dependencies: - codeql/cryptography: ${workspace} codeql/dataflow: ${workspace} + codeql/experimental: ${workspace} codeql/mad: ${workspace} codeql/rangeanalysis: ${workspace} codeql/ssa: ${workspace} diff --git a/cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql b/cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql index d9658105aebf..f741e3c9f947 100644 --- a/cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql +++ b/cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql @@ -6,7 +6,7 @@ * @id cpp/print-cbom-graph */ -import experimental.Quantum.Language +import experimental.quantum.Language query predicate nodes(Crypto::NodeBase node, string key, string value) { Crypto::nodes_graph_impl(node, key, value) diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 6f7dc88157b2..572d5716e02a 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -1,4 +1,4 @@ -private import codeql.cryptography.Model +private import codeql.quantum.Model private import java as Language private import semmle.code.java.security.InsecureRandomnessQuery private import semmle.code.java.security.RandomQuery diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 52fa4620b462..036218757a84 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -6,8 +6,8 @@ extractor: java library: true upgrades: upgrades dependencies: - codeql/cryptography: ${workspace} codeql/dataflow: ${workspace} + codeql/experimental: ${workspace} codeql/mad: ${workspace} codeql/rangeanalysis: ${workspace} codeql/regex: ${workspace} diff --git a/java/ql/src/experimental/Quantum/Analysis/ArtifactReuse.qll b/java/ql/src/experimental/Quantum/Analysis/ArtifactReuse.qll index de283f89775e..88598e615896 100644 --- a/java/ql/src/experimental/Quantum/Analysis/ArtifactReuse.qll +++ b/java/ql/src/experimental/Quantum/Analysis/ArtifactReuse.qll @@ -1,6 +1,6 @@ import java import semmle.code.java.dataflow.DataFlow -import experimental.Quantum.Language +import experimental.quantum.Language /** * Flow from any function that appears to return a value diff --git a/java/ql/src/experimental/Quantum/Analysis/InsecureNonceSource.ql b/java/ql/src/experimental/Quantum/Analysis/InsecureNonceSource.ql index 9c06884328bb..b2c6f919d5fe 100644 --- a/java/ql/src/experimental/Quantum/Analysis/InsecureNonceSource.ql +++ b/java/ql/src/experimental/Quantum/Analysis/InsecureNonceSource.ql @@ -8,7 +8,7 @@ * vulnerabilities such as replay attacks or key recovery. */ -import experimental.Quantum.Language +import experimental.quantum.Language predicate isInsecureNonceSource(Crypto::NonceArtifactNode n, Crypto::NodeBase src) { src = n.getSourceNode() and diff --git a/java/ql/src/experimental/Quantum/Analysis/KnownWeakKDFIterationCount.ql b/java/ql/src/experimental/Quantum/Analysis/KnownWeakKDFIterationCount.ql index 439295f74e36..701f3064e92a 100644 --- a/java/ql/src/experimental/Quantum/Analysis/KnownWeakKDFIterationCount.ql +++ b/java/ql/src/experimental/Quantum/Analysis/KnownWeakKDFIterationCount.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::KeyDerivationOperationNode op, Literal l where diff --git a/java/ql/src/experimental/Quantum/Analysis/UnknownKDFIterationCount.ql b/java/ql/src/experimental/Quantum/Analysis/UnknownKDFIterationCount.ql index 0c91e66d52bc..4ce404f01b08 100644 --- a/java/ql/src/experimental/Quantum/Analysis/UnknownKDFIterationCount.ql +++ b/java/ql/src/experimental/Quantum/Analysis/UnknownKDFIterationCount.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::KeyDerivationOperationNode op, Element e, string msg where diff --git a/java/ql/src/experimental/Quantum/Examples/BrokenCrypto.ql b/java/ql/src/experimental/Quantum/Examples/BrokenCrypto.ql index 020ac1b8925a..b4e9de9ac945 100644 --- a/java/ql/src/experimental/Quantum/Examples/BrokenCrypto.ql +++ b/java/ql/src/experimental/Quantum/Examples/BrokenCrypto.ql @@ -1,58 +1,55 @@ /** -* @name Use of a broken or risky cryptographic algorithm -* @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security. -* @kind problem -* @problem.severity warning -* @security-severity 7.5 -* @precision high -* @id java/weak-cryptographic-algorithm-new-model -* @tags security -* external/cwe/cwe-327 -* external/cwe/cwe-328 -*/ - - + * @name Use of a broken or risky cryptographic algorithm + * @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security. + * @kind problem + * @problem.severity warning + * @security-severity 7.5 + * @precision high + * @id java/weak-cryptographic-algorithm-new-model + * @tags security + * external/cwe/cwe-327 + * external/cwe/cwe-328 + */ //THIS QUERY IS A REPLICA OF: https://github.com/github/codeql/blob/main/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql //but uses the **NEW MODELLING** -import experimental.Quantum.Language - +import experimental.quantum.Language /** * Gets the name of an algorithm that is known to be insecure. */ string getAnInsecureAlgorithmName() { - result = - [ - "DES", "RC2", "RC4", "RC5", - // ARCFOUR is a variant of RC4 - "ARCFOUR", - // Encryption mode ECB like AES/ECB/NoPadding is vulnerable to replay and other attacks - "ECB", - // CBC mode of operation with PKCS#5 or PKCS#7 padding is vulnerable to padding oracle attacks - "AES/CBC/PKCS[57]Padding" - ] - } - - private string rankedInsecureAlgorithm(int i) { - result = rank[i](string s | s = getAnInsecureAlgorithmName()) - } - - private string insecureAlgorithmString(int i) { - i = 1 and result = rankedInsecureAlgorithm(i) - or - result = rankedInsecureAlgorithm(i) + "|" + insecureAlgorithmString(i - 1) - } - - /** - * Gets the regular expression used for matching strings that look like they - * contain an algorithm that is known to be insecure. - */ - string getInsecureAlgorithmRegex() { - result = algorithmRegex(insecureAlgorithmString(max(int i | exists(rankedInsecureAlgorithm(i))))) - } + result = + [ + "DES", "RC2", "RC4", "RC5", + // ARCFOUR is a variant of RC4 + "ARCFOUR", + // Encryption mode ECB like AES/ECB/NoPadding is vulnerable to replay and other attacks + "ECB", + // CBC mode of operation with PKCS#5 or PKCS#7 padding is vulnerable to padding oracle attacks + "AES/CBC/PKCS[57]Padding" + ] +} + +private string rankedInsecureAlgorithm(int i) { + result = rank[i](string s | s = getAnInsecureAlgorithmName()) +} + +private string insecureAlgorithmString(int i) { + i = 1 and result = rankedInsecureAlgorithm(i) + or + result = rankedInsecureAlgorithm(i) + "|" + insecureAlgorithmString(i - 1) +} + +/** + * Gets the regular expression used for matching strings that look like they + * contain an algorithm that is known to be insecure. + */ +string getInsecureAlgorithmRegex() { + result = algorithmRegex(insecureAlgorithmString(max(int i | exists(rankedInsecureAlgorithm(i))))) +} - bindingset[algorithmString] +bindingset[algorithmString] private string algorithmRegex(string algorithmString) { // Algorithms usually appear in names surrounded by characters that are not // alphabetical characters in the same case. This handles the upper and lower @@ -67,11 +64,12 @@ private string algorithmRegex(string algorithmString) { "((^|.*[A-Z]{2}|.*[^a-zA-Z])(" + algorithmString.toLowerCase() + ")([^a-z].*|$))" } -from Crypto::Algorithm alg -where alg.getAlgorithmName().regexpMatch(getInsecureAlgorithmRegex()) and -// Exclude RSA/ECB/.* ciphers. -not alg.getAlgorithmName().regexpMatch("RSA/ECB.*") and -// Exclude German and French sentences. -not alg.getAlgorithmName().regexpMatch(".*\\p{IsLowercase} des \\p{IsLetter}.*") +from Crypto::Algorithm alg +where + alg.getAlgorithmName().regexpMatch(getInsecureAlgorithmRegex()) and + // Exclude RSA/ECB/.* ciphers. + not alg.getAlgorithmName().regexpMatch("RSA/ECB.*") and + // Exclude German and French sentences. + not alg.getAlgorithmName().regexpMatch(".*\\p{IsLowercase} des \\p{IsLetter}.*") select alg, "Cryptographic algorithm $@ is weak and should not be used.", alg, -alg.getAlgorithmName() + alg.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/Examples/InsecureOrUnknownNonceAtOperation.ql b/java/ql/src/experimental/Quantum/Examples/InsecureOrUnknownNonceAtOperation.ql index a9eb70076a0a..fc2387fda374 100644 --- a/java/ql/src/experimental/Quantum/Examples/InsecureOrUnknownNonceAtOperation.ql +++ b/java/ql/src/experimental/Quantum/Examples/InsecureOrUnknownNonceAtOperation.ql @@ -4,7 +4,7 @@ * @kind problem */ -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::NonceArtifactNode n, Crypto::KeyOperationNode op, Crypto::FlowAwareElement src, string msg diff --git a/java/ql/src/experimental/Quantum/Examples/TestAESGCMNonce.ql b/java/ql/src/experimental/Quantum/Examples/TestAESGCMNonce.ql index 985527318ffa..096cfa822161 100644 --- a/java/ql/src/experimental/Quantum/Examples/TestAESGCMNonce.ql +++ b/java/ql/src/experimental/Quantum/Examples/TestAESGCMNonce.ql @@ -2,7 +2,7 @@ * @name "PQC Test" */ -import experimental.Quantum.Language +import experimental.quantum.Language class AESGCMAlgorithmNode extends Crypto::KeyOperationAlgorithmNode { AESGCMAlgorithmNode() { diff --git a/java/ql/src/experimental/Quantum/Examples/TestCipher.ql b/java/ql/src/experimental/Quantum/Examples/TestCipher.ql index 503d60039229..2b1d6ebcf06e 100644 --- a/java/ql/src/experimental/Quantum/Examples/TestCipher.ql +++ b/java/ql/src/experimental/Quantum/Examples/TestCipher.ql @@ -2,7 +2,7 @@ * @name "Key operation slice table demo query" */ -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::KeyOperationNode op, Crypto::KeyOperationAlgorithmNode a, diff --git a/java/ql/src/experimental/Quantum/Examples/TestCipherKey.ql b/java/ql/src/experimental/Quantum/Examples/TestCipherKey.ql index c489320528d0..9408aac7e3ba 100644 --- a/java/ql/src/experimental/Quantum/Examples/TestCipherKey.ql +++ b/java/ql/src/experimental/Quantum/Examples/TestCipherKey.ql @@ -2,7 +2,7 @@ * @name "PQC Test" */ -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::KeyOperationNode op, Crypto::CipherAlgorithmNode a, Crypto::KeyArtifactNode k where diff --git a/java/ql/src/experimental/Quantum/Examples/TestHash.ql b/java/ql/src/experimental/Quantum/Examples/TestHash.ql index 76ef6951a7e8..b319c95a62b1 100644 --- a/java/ql/src/experimental/Quantum/Examples/TestHash.ql +++ b/java/ql/src/experimental/Quantum/Examples/TestHash.ql @@ -2,7 +2,7 @@ * @name "Hash operation slice table demo query" */ -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::HashOperationNode op, Crypto::HashAlgorithmNode alg where alg = op.getAKnownAlgorithm() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql index 1e5a7c5bced1..7ae2a0f08880 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::AlgorithmNode a where Crypto::isKnownAsymmetricAlgorithm(a) diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql index 962a6b720159..c7242ed11c50 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::KeyOperationAlgorithmNode a where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::AsymmetricCipherAlgorithm diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql index 0900401b80d7..a14a0dfbaba7 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::OperationNode op, Crypto::AlgorithmNode a where a = op.getAKnownAlgorithm() and Crypto::isKnownAsymmetricAlgorithm(a) diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql index 4096fe16d29c..f126c3d9ae11 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language // TODO: should there be a cipher algorithm node? from Crypto::KeyOperationAlgorithmNode a diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql index 048bcd8182ce..c3f69d91cb70 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::EllipticCurveNode a select a, "Instance of elliptic curve algorithm " + a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql index 632872725e71..ed24b62364dd 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::HashAlgorithmNode a select a, "Instance of hashing algorithm " + a.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql index b3556393173d..23fc6235e802 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::HashOperationNode op select op, "Known hashing operation" diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql index ce0f4d37d4d1..8af3c09dd10e 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::OperationNode op, Crypto::HashAlgorithmNode a where a = op.getAKnownAlgorithm() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql index 584ffef0bbfa..e0970353a987 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::KeyDerivationAlgorithmNode alg select alg, "Known key derivation algorithm " + alg.getAlgorithmName() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql index 8c77b2aa984d..240a4ea3fc54 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::KeyDerivationOperationNode op select op, "Known key derivation operation" diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql index cf9a4e96f4da..9afbd7d7f2f7 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql @@ -1,11 +1,12 @@ /** * @name Detects operations where the algorithm applied is a known key derivation algorithm - * @id java/crypto_inventory_slices/operation_with_known_key_derivation_algorithm + * @id java/cryptography-inventory-slices/operation-known-key-derivation-algorithm + * @description This query identifies operations that utilize a known key derivation algorithm. * @kind problem */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::OperationNode op, Crypto::KeyDerivationAlgorithmNode a where a = op.getAKnownAlgorithm() diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql index 21949f1c8c6d..d8467305df97 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::KeyOperationAlgorithmNode a where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::SymmetricCipherAlgorithm diff --git a/java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql b/java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql index 0076c478dec9..d02524a7df37 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language from Callable f, Parameter p, Crypto::OperationNode op where diff --git a/java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql b/java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql index 61a27c75dac2..7559579d863f 100644 --- a/java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql +++ b/java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql @@ -5,7 +5,7 @@ */ import java -import experimental.Quantum.Language +import experimental.quantum.Language //TODO: can we have an unknown node concept? from Crypto::OperationNode op, Element e, string msg diff --git a/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql b/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql index 063cda564b60..bc79d5d4ce6a 100644 --- a/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql +++ b/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql @@ -6,7 +6,7 @@ * @id java/print-cbom-graph */ -import experimental.Quantum.Language +import experimental.quantum.Language query predicate nodes(Crypto::NodeBase node, string key, string value) { Crypto::nodes_graph_impl(node, key, value) diff --git a/shared/cryptography/codeql/cryptography/Model.qll b/shared/experimental/codeql/quantum/Model.qll similarity index 100% rename from shared/cryptography/codeql/cryptography/Model.qll rename to shared/experimental/codeql/quantum/Model.qll diff --git a/shared/cryptography/qlpack.yml b/shared/experimental/qlpack.yml similarity index 81% rename from shared/cryptography/qlpack.yml rename to shared/experimental/qlpack.yml index 768c64a0704e..2976c56ba492 100644 --- a/shared/cryptography/qlpack.yml +++ b/shared/experimental/qlpack.yml @@ -1,4 +1,4 @@ -name: codeql/cryptography +name: codeql/experimental version: 0.0.0-dev groups: shared library: true From ac72abd3a69d69d37701c0622f837460a2a115ed Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 02:35:09 +0200 Subject: [PATCH 102/122] Refactor directory structure (shared experimental) --- cpp/ql/lib/experimental/Quantum/Language.qll | 2 +- cpp/ql/src/experimental/Quantum/Test2.ql | 2 +- java/ql/lib/experimental/Quantum/Language.qll | 2 +- shared/experimental/codeql/{ => experimental}/quantum/Model.qll | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename shared/experimental/codeql/{ => experimental}/quantum/Model.qll (100%) diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/Quantum/Language.qll index c3e2e3ad55f4..176db9736adb 100644 --- a/cpp/ql/lib/experimental/Quantum/Language.qll +++ b/cpp/ql/lib/experimental/Quantum/Language.qll @@ -1,4 +1,4 @@ -import codeql.quantum.Model +import codeql.experimental.quantum.Model import semmle.code.cpp.dataflow.new.DataFlow private import cpp as Lang diff --git a/cpp/ql/src/experimental/Quantum/Test2.ql b/cpp/ql/src/experimental/Quantum/Test2.ql index 3f48f156a430..98a35d2a9e9f 100644 --- a/cpp/ql/src/experimental/Quantum/Test2.ql +++ b/cpp/ql/src/experimental/Quantum/Test2.ql @@ -2,7 +2,7 @@ * @name "PQC Test" */ -import experimental.Quantum.Language +import experimental.quantum.Language from Crypto::NodeBase node select node diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/Quantum/Language.qll index 572d5716e02a..eaf63aa5be25 100644 --- a/java/ql/lib/experimental/Quantum/Language.qll +++ b/java/ql/lib/experimental/Quantum/Language.qll @@ -1,4 +1,4 @@ -private import codeql.quantum.Model +private import codeql.experimental.quantum.Model private import java as Language private import semmle.code.java.security.InsecureRandomnessQuery private import semmle.code.java.security.RandomQuery diff --git a/shared/experimental/codeql/quantum/Model.qll b/shared/experimental/codeql/experimental/quantum/Model.qll similarity index 100% rename from shared/experimental/codeql/quantum/Model.qll rename to shared/experimental/codeql/experimental/quantum/Model.qll From 7339dd0077664ae41728488667df1f7368eef996 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 02:39:40 +0200 Subject: [PATCH 103/122] Rename "Quantum" to "quantum" in dir structure --- cpp/ql/lib/experimental/{Quantum => quantum}/Language.qll | 0 .../OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll | 0 .../OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll | 0 .../OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll | 0 .../OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll | 0 .../OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll | 0 .../OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll | 0 .../OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll | 0 .../OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll | 0 .../AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll | 0 .../AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll | 0 .../AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll | 0 .../AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll | 0 .../AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll | 0 .../AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll | 0 cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/CtxFlow.qll | 0 .../experimental/{Quantum => quantum}/OpenSSL/LibraryDetector.qll | 0 cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/OpenSSL.qll | 0 .../OpenSSL/Operations/EVPCipherInitializer.qll | 0 .../OpenSSL/Operations/EVPCipherOperation.qll | 0 .../OpenSSL/Operations/EVPHashInitializer.qll | 0 .../{Quantum => quantum}/OpenSSL/Operations/EVPHashOperation.qll | 0 .../OpenSSL/Operations/OpenSSLOperationBase.qll | 0 .../{Quantum => quantum}/OpenSSL/Operations/OpenSSLOperations.qll | 0 cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/Random.qll | 0 cpp/ql/src/experimental/{Quantum => quantum}/PrintCBOMGraph.ql | 0 cpp/ql/src/experimental/{Quantum => quantum}/Test2.ql | 0 java/ql/lib/experimental/{Quantum => quantum}/JCA.qll | 0 java/ql/lib/experimental/{Quantum => quantum}/Language.qll | 0 .../experimental/{Quantum => quantum}/Analysis/ArtifactReuse.qll | 0 .../{Quantum => quantum}/Analysis/InsecureNonceSource.ql | 0 .../{Quantum => quantum}/Analysis/KnownWeakKDFIterationCount.ql | 0 .../src/experimental/{Quantum => quantum}/Analysis/ReusedNonce.ql | 0 .../{Quantum => quantum}/Analysis/UnknownKDFIterationCount.ql | 0 .../experimental/{Quantum => quantum}/Examples/BrokenCrypto.ql | 0 .../Examples/InsecureOrUnknownNonceAtOperation.ql | 0 .../experimental/{Quantum => quantum}/Examples/TestAESGCMNonce.ql | 0 .../src/experimental/{Quantum => quantum}/Examples/TestCipher.ql | 0 .../experimental/{Quantum => quantum}/Examples/TestCipherKey.ql | 0 .../ql/src/experimental/{Quantum => quantum}/Examples/TestHash.ql | 0 .../InventorySlices/KnownAsymmetricAlgorithm.ql | 0 .../InventorySlices/KnownAsymmetricCipherAlgorithm.ql | 0 .../InventorySlices/KnownAsymmetricOperationAlgorithm.ql | 0 .../{Quantum => quantum}/InventorySlices/KnownCipherAlgorithm.ql | 0 .../InventorySlices/KnownEllipticCurveAlgorithm.ql | 0 .../{Quantum => quantum}/InventorySlices/KnownHashingAlgorithm.ql | 0 .../{Quantum => quantum}/InventorySlices/KnownHashingOperation.ql | 0 .../InventorySlices/KnownHashingOperationAlgorithm.ql | 0 .../InventorySlices/KnownKeyDerivationAlgorithm.ql | 0 .../InventorySlices/KnownKeyDerivationOperation.ql | 0 .../InventorySlices/KnownKeyDerivationOperationAlgorithm.ql | 0 .../InventorySlices/KnownSymmetricCipherAlgorithm.ql | 0 .../InventorySlices/LikelyCryptoAPIFunction.ql | 0 .../InventorySlices/UnknownOperationAlgorithm.ql | 0 java/ql/src/experimental/{Quantum => quantum}/PrintCBOMGraph.ql | 0 55 files changed, 0 insertions(+), 0 deletions(-) rename cpp/ql/lib/experimental/{Quantum => quantum}/Language.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/CtxFlow.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/LibraryDetector.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/OpenSSL.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/Operations/EVPCipherInitializer.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/Operations/EVPCipherOperation.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/Operations/EVPHashInitializer.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/Operations/EVPHashOperation.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/Operations/OpenSSLOperationBase.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/Operations/OpenSSLOperations.qll (100%) rename cpp/ql/lib/experimental/{Quantum => quantum}/OpenSSL/Random.qll (100%) rename cpp/ql/src/experimental/{Quantum => quantum}/PrintCBOMGraph.ql (100%) rename cpp/ql/src/experimental/{Quantum => quantum}/Test2.ql (100%) rename java/ql/lib/experimental/{Quantum => quantum}/JCA.qll (100%) rename java/ql/lib/experimental/{Quantum => quantum}/Language.qll (100%) rename java/ql/src/experimental/{Quantum => quantum}/Analysis/ArtifactReuse.qll (100%) rename java/ql/src/experimental/{Quantum => quantum}/Analysis/InsecureNonceSource.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/Analysis/KnownWeakKDFIterationCount.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/Analysis/ReusedNonce.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/Analysis/UnknownKDFIterationCount.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/Examples/BrokenCrypto.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/Examples/InsecureOrUnknownNonceAtOperation.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/Examples/TestAESGCMNonce.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/Examples/TestCipher.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/Examples/TestCipherKey.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/Examples/TestHash.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownAsymmetricAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownAsymmetricCipherAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownAsymmetricOperationAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownCipherAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownEllipticCurveAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownHashingAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownHashingOperation.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownHashingOperationAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownKeyDerivationAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownKeyDerivationOperation.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/KnownSymmetricCipherAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/LikelyCryptoAPIFunction.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/InventorySlices/UnknownOperationAlgorithm.ql (100%) rename java/ql/src/experimental/{Quantum => quantum}/PrintCBOMGraph.ql (100%) diff --git a/cpp/ql/lib/experimental/Quantum/Language.qll b/cpp/ql/lib/experimental/quantum/Language.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/Language.qll rename to cpp/ql/lib/experimental/quantum/Language.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/CipherAlgorithmValueConsumer.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/DirectAlgorithmValueConsumer.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PaddingAlgorithmValueConsumer.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/CtxFlow.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/LibraryDetector.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/LibraryDetector.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/LibraryDetector.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/LibraryDetector.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/OpenSSL.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSL.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/OpenSSL.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherInitializer.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPCipherOperation.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashInitializer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashInitializer.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/EVPHashOperation.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperationBase.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/Operations/OpenSSLOperations.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Random.qll similarity index 100% rename from cpp/ql/lib/experimental/Quantum/OpenSSL/Random.qll rename to cpp/ql/lib/experimental/quantum/OpenSSL/Random.qll diff --git a/cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql b/cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql similarity index 100% rename from cpp/ql/src/experimental/Quantum/PrintCBOMGraph.ql rename to cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql diff --git a/cpp/ql/src/experimental/Quantum/Test2.ql b/cpp/ql/src/experimental/quantum/Test2.ql similarity index 100% rename from cpp/ql/src/experimental/Quantum/Test2.ql rename to cpp/ql/src/experimental/quantum/Test2.ql diff --git a/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/quantum/JCA.qll similarity index 100% rename from java/ql/lib/experimental/Quantum/JCA.qll rename to java/ql/lib/experimental/quantum/JCA.qll diff --git a/java/ql/lib/experimental/Quantum/Language.qll b/java/ql/lib/experimental/quantum/Language.qll similarity index 100% rename from java/ql/lib/experimental/Quantum/Language.qll rename to java/ql/lib/experimental/quantum/Language.qll diff --git a/java/ql/src/experimental/Quantum/Analysis/ArtifactReuse.qll b/java/ql/src/experimental/quantum/Analysis/ArtifactReuse.qll similarity index 100% rename from java/ql/src/experimental/Quantum/Analysis/ArtifactReuse.qll rename to java/ql/src/experimental/quantum/Analysis/ArtifactReuse.qll diff --git a/java/ql/src/experimental/Quantum/Analysis/InsecureNonceSource.ql b/java/ql/src/experimental/quantum/Analysis/InsecureNonceSource.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Analysis/InsecureNonceSource.ql rename to java/ql/src/experimental/quantum/Analysis/InsecureNonceSource.ql diff --git a/java/ql/src/experimental/Quantum/Analysis/KnownWeakKDFIterationCount.ql b/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Analysis/KnownWeakKDFIterationCount.ql rename to java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql diff --git a/java/ql/src/experimental/Quantum/Analysis/ReusedNonce.ql b/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Analysis/ReusedNonce.ql rename to java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql diff --git a/java/ql/src/experimental/Quantum/Analysis/UnknownKDFIterationCount.ql b/java/ql/src/experimental/quantum/Analysis/UnknownKDFIterationCount.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Analysis/UnknownKDFIterationCount.ql rename to java/ql/src/experimental/quantum/Analysis/UnknownKDFIterationCount.ql diff --git a/java/ql/src/experimental/Quantum/Examples/BrokenCrypto.ql b/java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Examples/BrokenCrypto.ql rename to java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql diff --git a/java/ql/src/experimental/Quantum/Examples/InsecureOrUnknownNonceAtOperation.ql b/java/ql/src/experimental/quantum/Examples/InsecureOrUnknownNonceAtOperation.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Examples/InsecureOrUnknownNonceAtOperation.ql rename to java/ql/src/experimental/quantum/Examples/InsecureOrUnknownNonceAtOperation.ql diff --git a/java/ql/src/experimental/Quantum/Examples/TestAESGCMNonce.ql b/java/ql/src/experimental/quantum/Examples/TestAESGCMNonce.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Examples/TestAESGCMNonce.ql rename to java/ql/src/experimental/quantum/Examples/TestAESGCMNonce.ql diff --git a/java/ql/src/experimental/Quantum/Examples/TestCipher.ql b/java/ql/src/experimental/quantum/Examples/TestCipher.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Examples/TestCipher.ql rename to java/ql/src/experimental/quantum/Examples/TestCipher.ql diff --git a/java/ql/src/experimental/Quantum/Examples/TestCipherKey.ql b/java/ql/src/experimental/quantum/Examples/TestCipherKey.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Examples/TestCipherKey.ql rename to java/ql/src/experimental/quantum/Examples/TestCipherKey.ql diff --git a/java/ql/src/experimental/Quantum/Examples/TestHash.ql b/java/ql/src/experimental/quantum/Examples/TestHash.ql similarity index 100% rename from java/ql/src/experimental/Quantum/Examples/TestHash.ql rename to java/ql/src/experimental/quantum/Examples/TestHash.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownCipherAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownHashingAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperation.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownHashingOperationAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperation.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql b/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/LikelyCryptoAPIFunction.ql rename to java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql diff --git a/java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql similarity index 100% rename from java/ql/src/experimental/Quantum/InventorySlices/UnknownOperationAlgorithm.ql rename to java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql diff --git a/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql b/java/ql/src/experimental/quantum/PrintCBOMGraph.ql similarity index 100% rename from java/ql/src/experimental/Quantum/PrintCBOMGraph.ql rename to java/ql/src/experimental/quantum/PrintCBOMGraph.ql From 0066f74d3fe7f31665f78ae43faaf5b6a20d12c8 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 02:40:56 +0200 Subject: [PATCH 104/122] Delete development scripts --- misc/scripts/cryptography/cbom.sh | 8 -- misc/scripts/cryptography/generate_cbom.py | 117 --------------------- 2 files changed, 125 deletions(-) delete mode 100755 misc/scripts/cryptography/cbom.sh delete mode 100644 misc/scripts/cryptography/generate_cbom.py diff --git a/misc/scripts/cryptography/cbom.sh b/misc/scripts/cryptography/cbom.sh deleted file mode 100755 index ef558ccf432e..000000000000 --- a/misc/scripts/cryptography/cbom.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -CODEQL_PATH="/Users/nicolaswill/.local/share/gh/extensions/gh-codeql/dist/release/v2.20.4/codeql" -DATABASE_PATH="/Users/nicolaswill/pqc/gpt-crypto-test-cases/gpt_ai_gen_jca_test_cases_db" -QUERY_FILE="/Users/nicolaswill/pqc/codeql/java/ql/src/experimental/Quantum/PrintCBOMGraph.ql" -OUTPUT_DIR="graph_output" - -python3 generate_cbom.py -c "$CODEQL_PATH" -d "$DATABASE_PATH" -q "$QUERY_FILE" -o "$OUTPUT_DIR" diff --git a/misc/scripts/cryptography/generate_cbom.py b/misc/scripts/cryptography/generate_cbom.py deleted file mode 100644 index 6bd0d19712c5..000000000000 --- a/misc/scripts/cryptography/generate_cbom.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python3 - -import os -import re -import sys -import argparse -import subprocess -import xml.etree.ElementTree as ET - -def run_codeql_analysis(codeql_path, database_path, query_path, output_dir): - """Runs the CodeQL analysis and generates a DGML file.""" - os.makedirs(output_dir, exist_ok=True) - command = [ - codeql_path, "database", "analyze", database_path, query_path, - "--rerun", "--format=dgml", "--output", output_dir - ] - - print(f"Running CodeQL analysis: {' '.join(command)}") - result = subprocess.run(command, capture_output=True, text=True) - - if result.returncode == 0: - print("Analysis completed successfully.") - else: - print("Analysis failed.") - print(result.stderr) - sys.exit(1) - - return result.returncode - - -def convert_dgml_to_dot(dgml_file, dot_file): - """Converts the DGML file to DOT format using the exact original implementation.""" - print(f"Processing DGML file: {dgml_file}") - - # Read source DGML - with open(dgml_file, "r", encoding="utf-8") as f: - xml_content = f.read() - - root = ET.fromstring(xml_content) - - # Form dot element sequence - body_l = ["digraph cbom {", - "node [shape=box];" - ] - - # Process nodes - for node in root.find("{http://schemas.microsoft.com/vs/2009/dgml}Nodes"): - att = node.attrib - node_id = att['Id'] - label_parts = [] - for key, value in att.items(): - if key == 'Id': - continue - elif key == 'Label': - label_parts.append(value) - else: - label_parts.append(f"{key}={value}") - label = "\\n".join(label_parts) - # Escape forward slashes and double quotes - label = label.replace("/", "\\/") - label = label.replace("\"", "\\\"") - prop_l = [f'label="{label}"'] - node_s = f'nd_{node_id} [{", ".join(prop_l)}];' - body_l.append(node_s) - - # Process edges - for edge in root.find("{http://schemas.microsoft.com/vs/2009/dgml}Links"): - att = edge.attrib - edge_label = att.get("Label", "") - edge_label = edge_label.replace("/", "\\/") - edge_label = edge_label.replace("\"", "\\\"") - edge_s = 'nd_{} -> nd_{} [label="{}"];'.format( - att["Source"], att["Target"], edge_label) - body_l.append(edge_s) - - body_l.append("}") - - # Write DOT output - with open(dot_file, "w", encoding="utf-8") as f: - f.write("\n".join(body_l)) - - print(f"DGML file successfully converted to DOT format: {dot_file}") - - -def main(): - parser = argparse.ArgumentParser(description="Run CodeQL analysis and convert DGML to DOT.") - parser.add_argument("-c", "--codeql", required=True, help="Path to CodeQL CLI executable.") - parser.add_argument("-d", "--database", required=True, help="Path to the CodeQL database.") - parser.add_argument("-q", "--query", required=True, help="Path to the .ql query file.") - parser.add_argument("--queryid", required=True, help="Query ID for the analysis.") - parser.add_argument("-o", "--output", required=True, help="Output directory for analysis results.") - - args = parser.parse_args() - - # Run CodeQL analysis - run_codeql_analysis(args.codeql, args.database, args.query, args.output) - - # Locate DGML file - ALLOWED_QUERY_ID = re.compile(r'^[a-zA-Z0-9_\-]+$') - - if not ALLOWED_QUERY_ID.match(args.queryid): - print("Invalid query_id provided: '%s'. Allowed characters: letters, digits, '_', and '-'.", args.queryid) - sys.exit(1) - - dgml_file = os.path.join(args.output, "java", '{}.dgml'.format(args.queryid)) - dot_file = dgml_file.replace(".dgml", ".dot") - - if os.path.exists(dgml_file): - # Convert DGML to DOT - convert_dgml_to_dot(dgml_file, dot_file) - else: - print(f"No DGML file found in {args.output}.") - sys.exit(1) - - -if __name__ == "__main__": - main() From a7ebe4a51a249774a0d5a48ae671a240de2e5e5f Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 02:43:29 +0200 Subject: [PATCH 105/122] Fix typo in asymmetric inventory slice query id --- .../quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql index c7242ed11c50..b903e152a299 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql @@ -1,6 +1,6 @@ /** * @name Detects known asymmetric cipher algorithms - * @id java/crypto_inventory_slices/known_symmetric_cipher_algorithm + * @id java/crypto_inventory_slices/known_asymmetric_cipher_algorithm * @kind problem */ From e956d041dc9cc405df5cc67ddf839157e14d76ab Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 02:51:53 +0200 Subject: [PATCH 106/122] Format LibraryDetector.qll --- .../lib/experimental/quantum/OpenSSL/LibraryDetector.qll | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/LibraryDetector.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/LibraryDetector.qll index 3cd359ce58b3..5ff02cd95197 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/LibraryDetector.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/LibraryDetector.qll @@ -1,10 +1,7 @@ import cpp predicate isPossibleOpenSSLFunction(Function f) { - isPossibleOpenSSLLocation(f.getADeclarationLocation()) - } - -predicate isPossibleOpenSSLLocation(Location l){ - l.toString().toLowerCase().matches("%openssl%") + isPossibleOpenSSLLocation(f.getADeclarationLocation()) } - \ No newline at end of file + +predicate isPossibleOpenSSLLocation(Location l) { l.toString().toLowerCase().matches("%openssl%") } From 1d8a57e7da529e38708480d4bc0684723ff1443f Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 02:56:52 +0200 Subject: [PATCH 107/122] Fix EVP Cipher class, predicate, and comment typos --- .../Operations/EVPCipherInitializer.qll | 28 +++++++++---------- .../OpenSSL/Operations/EVPCipherOperation.qll | 6 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll index fdf60ef757e8..3e8607ef8ecb 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll @@ -10,14 +10,14 @@ module EncValToInitEncArgConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] } predicate isSink(DataFlow::Node sink) { - exists(EVP_Cipher_Inititalizer initCall | sink.asExpr() = initCall.getOperataionSubtypeArg()) + exists(EVP_Cipher_Initializer initCall | sink.asExpr() = initCall.getOperationSubtypeArg()) } } module EncValToInitEncArgFlow = DataFlow::Global; int getEncConfigValue(Expr e) { - exists(EVP_Cipher_Inititalizer initCall | e = initCall.getOperataionSubtypeArg()) and + exists(EVP_Cipher_Initializer initCall | e = initCall.getOperationSubtypeArg()) and exists(DataFlow::Node a, DataFlow::Node b | EncValToInitEncArgFlow::flow(a, b) and b.asExpr() = e and result = a.asExpr().getValue().toInt() ) @@ -34,7 +34,7 @@ Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) { } // TODO: need to add key consumer -abstract class EVP_Cipher_Inititalizer extends Call { +abstract class EVP_Cipher_Initializer extends Call { Expr getContextArg() { result = this.(Call).getArgument(0) } Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } @@ -44,7 +44,7 @@ abstract class EVP_Cipher_Inititalizer extends Call { abstract Expr getIVArg(); // abstract Crypto::CipherOperationSubtype getCipherOperationSubtype(); - abstract Expr getOperataionSubtypeArg(); + abstract Expr getOperationSubtypeArg(); Crypto::KeyOperationSubtype getCipherOperationSubtype() { if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") @@ -53,19 +53,19 @@ abstract class EVP_Cipher_Inititalizer extends Call { if this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") then result instanceof Crypto::TDecryptMode else - if exists(getEncConfigValue(this.getOperataionSubtypeArg())) - then result = intToCipherOperationSubtype(getEncConfigValue(this.getOperataionSubtypeArg())) + if exists(getEncConfigValue(this.getOperationSubtypeArg())) + then result = intToCipherOperationSubtype(getEncConfigValue(this.getOperationSubtypeArg())) else result instanceof Crypto::TUnknownKeyOperationMode } } -abstract class EVP_EX_Initializer extends EVP_Cipher_Inititalizer { +abstract class EVP_EX_Initializer extends EVP_Cipher_Initializer { override Expr getKeyArg() { result = this.(Call).getArgument(3) } override Expr getIVArg() { result = this.(Call).getArgument(4) } } -abstract class EVP_EX2_Initializer extends EVP_Cipher_Inititalizer { +abstract class EVP_EX2_Initializer extends EVP_Cipher_Initializer { override Expr getKeyArg() { result = this.(Call).getArgument(2) } override Expr getIVArg() { result = this.(Call).getArgument(3) } @@ -78,7 +78,7 @@ class EVP_Cipher_EX_Init_Call extends EVP_EX_Initializer { ] } - override Expr getOperataionSubtypeArg() { + override Expr getOperationSubtypeArg() { this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and result = this.(Call).getArgument(5) } @@ -92,7 +92,7 @@ class EVP_Cipher_EX2_or_Simple_Init_Call extends EVP_EX2_Initializer { ] } - override Expr getOperataionSubtypeArg() { + override Expr getOperationSubtypeArg() { this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and result = this.(Call).getArgument(4) } @@ -101,23 +101,23 @@ class EVP_Cipher_EX2_or_Simple_Init_Call extends EVP_EX2_Initializer { class EVP_CipherInit_SKEY_Call extends EVP_EX2_Initializer { EVP_CipherInit_SKEY_Call() { this.(Call).getTarget().getName() in ["EVP_CipherInit_SKEY"] } - override Expr getOperataionSubtypeArg() { result = this.(Call).getArgument(5) } + override Expr getOperationSubtypeArg() { result = this.(Call).getArgument(5) } } class EVPCipherInitializerAlgorithmArgument extends Expr { EVPCipherInitializerAlgorithmArgument() { - exists(EVP_Cipher_Inititalizer initCall | this = initCall.getAlgorithmArg()) + exists(EVP_Cipher_Initializer initCall | this = initCall.getAlgorithmArg()) } } class EVPCipherInitializerKeyArgument extends Expr { EVPCipherInitializerKeyArgument() { - exists(EVP_Cipher_Inititalizer initCall | this = initCall.getKeyArg()) + exists(EVP_Cipher_Initializer initCall | this = initCall.getKeyArg()) } } class EVPCipherInitializerIVArgument extends Expr { EVPCipherInitializerIVArgument() { - exists(EVP_Cipher_Inititalizer initCall | this = initCall.getIVArg()) + exists(EVP_Cipher_Initializer initCall | this = initCall.getIVArg()) } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll index 45c7d41b029b..b544079579af 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll @@ -45,7 +45,7 @@ abstract class EVP_Cipher_Operation extends OpenSSLOperation, Crypto::KeyOperati this.(Call).getTarget().getName().toLowerCase().matches("%cipher%") } - EVP_Cipher_Inititalizer getInitCall() { + EVP_Cipher_Initializer getInitCall() { CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) } @@ -74,14 +74,14 @@ abstract class EVP_Final_Call extends EVP_Cipher_Operation { // TODO: only model Final (model final as operation and model update but not as an operation) // Updates are multiple input consumers (most important) -// PUNT assuming update doesn't ouput, otherwise it outputs arifacts, but is not an operation +// TODO: assuming update doesn't ouput, otherwise it outputs artifacts, but is not an operation class EVP_Cipher_Call extends EVP_Cipher_Operation { EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } override Expr getInputArg() { result = this.(Call).getArgument(2) } } -// ******* TODO NEED to model UPDATE but not as the coree operation, rather a step towards final, +// ******* TODO: model UPDATE but not as the core operation, rather a step towards final // see the JCA // class EVP_Encrypt_Decrypt_or_Cipher_Update_Call extends EVP_Update_Call { // EVP_Encrypt_Decrypt_or_Cipher_Update_Call() { From 1135fbe950b146bca576d7921b528a1c3ec68c20 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 02:58:43 +0200 Subject: [PATCH 108/122] Fix EVP_Hash_Initializer typo --- .../quantum/OpenSSL/Operations/EVPHashInitializer.qll | 4 ++-- .../quantum/OpenSSL/Operations/EVPHashOperation.qll | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll index cd0dac94001a..46d414ece6ce 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll @@ -1,12 +1,12 @@ import cpp -abstract class EVP_Hash_Inititalizer extends Call { +abstract class EVP_Hash_Initializer extends Call { Expr getContextArg() { result = this.(Call).getArgument(0) } abstract Expr getAlgorithmArg(); } -class EVP_DigestInit_Variant_Calls extends EVP_Hash_Inititalizer { +class EVP_DigestInit_Variant_Calls extends EVP_Hash_Initializer { EVP_DigestInit_Variant_Calls() { this.(Call).getTarget().getName() in [ "EVP_DigestInit", "EVP_DigestInit_ex", "EVP_DigestInit_ex2" diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll index 45776b6668b3..a187b62a7bdd 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll @@ -13,7 +13,7 @@ import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValu abstract class EVP_Hash_Operation extends OpenSSLOperation, Crypto::HashOperationInstance { Expr getContextArg() { result = this.(Call).getArgument(0) } - EVP_Hash_Inititalizer getInitCall() { + EVP_Hash_Initializer getInitCall() { CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) } } @@ -38,7 +38,7 @@ class EVP_Q_Digest_Operation extends EVP_Hash_Operation { } //override Crypto::AlgorithmConsumer getAlgorithmConsumer() { } - override EVP_Hash_Inititalizer getInitCall() { + override EVP_Hash_Initializer getInitCall() { // This variant of digest does not use an init // and even if it were used, the init would be ignored/undefined none() @@ -74,7 +74,7 @@ class EVP_Digest_Operation extends EVP_Hash_Operation { DataFlow::exprNode(this.(Call).getArgument(4))) } - override EVP_Hash_Inititalizer getInitCall() { + override EVP_Hash_Initializer getInitCall() { // This variant of digest does not use an init // and even if it were used, the init would be ignored/undefined none() From 0c6e124b01a34c9ee6b286e174ae999a3d58e136 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 03:02:59 +0200 Subject: [PATCH 109/122] Delete development test query --- cpp/ql/src/experimental/quantum/Test2.ql | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 cpp/ql/src/experimental/quantum/Test2.ql diff --git a/cpp/ql/src/experimental/quantum/Test2.ql b/cpp/ql/src/experimental/quantum/Test2.ql deleted file mode 100644 index 98a35d2a9e9f..000000000000 --- a/cpp/ql/src/experimental/quantum/Test2.ql +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @name "PQC Test" - */ - -import experimental.quantum.Language - -from Crypto::NodeBase node -select node From 986c8e1aecbdc52aeca368d93c3958643ab99431 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 03:24:04 +0200 Subject: [PATCH 110/122] Change Java inventory slices to @kind table --- .../InsecureOrUnknownNonceAtOperation.ql | 70 ------------------- .../quantum/Examples/TestCipherKey.ql | 17 ----- .../KnownAsymmetricAlgorithm.ql | 2 +- .../KnownAsymmetricCipherAlgorithm.ql | 2 +- .../KnownAsymmetricOperationAlgorithm.ql | 2 +- .../InventorySlices/KnownCipherAlgorithm.ql | 2 +- .../KnownEllipticCurveAlgorithm.ql | 2 +- .../InventorySlices/KnownHashingAlgorithm.ql | 2 +- .../InventorySlices/KnownHashingOperation.ql | 2 +- .../KnownHashingOperationAlgorithm.ql | 2 +- .../KnownKeyDerivationAlgorithm.ql | 2 +- .../KnownKeyDerivationOperation.ql | 2 +- .../KnownKeyDerivationOperationAlgorithm.ql | 5 +- .../KnownSymmetricCipherAlgorithm.ql | 2 +- .../LikelyCryptoAPIFunction.ql | 2 +- .../UnknownOperationAlgorithm.ql | 2 +- 16 files changed, 15 insertions(+), 103 deletions(-) delete mode 100644 java/ql/src/experimental/quantum/Examples/InsecureOrUnknownNonceAtOperation.ql delete mode 100644 java/ql/src/experimental/quantum/Examples/TestCipherKey.ql diff --git a/java/ql/src/experimental/quantum/Examples/InsecureOrUnknownNonceAtOperation.ql b/java/ql/src/experimental/quantum/Examples/InsecureOrUnknownNonceAtOperation.ql deleted file mode 100644 index fc2387fda374..000000000000 --- a/java/ql/src/experimental/quantum/Examples/InsecureOrUnknownNonceAtOperation.ql +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @name Insecure or unknown nonce source at a cipher operation - * @id java/insecure-or-unknown-nonce-at-operation - * @kind problem - */ - -import experimental.quantum.Language - -from - Crypto::NonceArtifactNode n, Crypto::KeyOperationNode op, Crypto::FlowAwareElement src, string msg -where - op.getANonce() = n and - // Only encryption mode is relevant for insecure nonces, consder any 'unknown' subtype - // as possibly encryption. - ( - op.getKeyOperationSubtype() instanceof Crypto::EncryptionSubtype - or - op.getKeyOperationSubtype() instanceof Crypto::WrapSubtype - or - op.getKeyOperationSubtype() instanceof Crypto::UnwrapSubtype - ) and - ( - // Known sources cases that are not secure - src = n.getSourceElement() and - not src instanceof SecureRandomnessInstance and - msg = "Operation uses insecure nonce source $@" - or - // Totally unknown sources (unmodeled input sources) - not exists(n.getSourceElement()) and - msg = "Operation uses unknown nonce source" and - src = n.asElement() - ) -select n, msg, src, src.toString() -// variant using instances, does not yield the same results -// from Crypto::NonceArtifactConsumer n, Crypto::CipherOperationInstance op, Crypto::FlowAwareElement src, string msg -// where -// op.getNonceConsumer() = n and -// TODO: only perform the query on encryption -// ( -// // Known sources cases that are not secure -// src = n.getAKnownArtifactSource()and -// not src instanceof SecureRandomnessInstance and -// msg = "Operation uses insecure nonce source $@" -// or -// // Totally unknown sources (unmodeled input sources) -// // When this occurs set src to n, just to bind it, but the output message will not report any source -// not exists(n.getAKnownArtifactSource()) and msg = "Operation uses unknown nonce source" and src = n -// ) -// select n, msg, src, src.toString() -// NOTE: this will find all unknowns too, constants, and allocations, without needing to model them -// which is kinda nice, but accidental, since getSourceElement is not modeled for everything -// If users want to find constants or unallocated, they need to model those sources, and output the -// getSourceElement -// QUESTION: why isn't the source element a node? -// NOTE: when not all sources are modeled, if one source is secure, even if others do exist, you -// will see the nonce and operation are secure, regardless of potentially insecure IV sources -// resulting in False Negatives -// NOTE: need to have a query where the op has no Nonce -// // Ideal query -// from Crypto::NonceNode n, Crypto::CipherOperationNode op -// where -// n = op.getANonce() and -// // n = op.getAnUnknownNonce() -// not n.asElement() instanceof SecureRandomSource -// select op, "Operation uses insecure nonce source @", n, n.toString() -// from Crypto::Nonce n, Crypto::ArtifactLocatableElement nonceSrc -// where -// n.() = nonceSrc and -// not nonceSrc instanceof SecureRandomnessInstance -// select n, nonceSrc diff --git a/java/ql/src/experimental/quantum/Examples/TestCipherKey.ql b/java/ql/src/experimental/quantum/Examples/TestCipherKey.ql deleted file mode 100644 index 9408aac7e3ba..000000000000 --- a/java/ql/src/experimental/quantum/Examples/TestCipherKey.ql +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @name "PQC Test" - */ - -import experimental.quantum.Language - -from Crypto::KeyOperationNode op, Crypto::CipherAlgorithmNode a, Crypto::KeyArtifactNode k -where - a = op.getAKnownCipherAlgorithm() and - k = op.getAKey() -select op, op.getKeyOperationSubtype(), a, a.getRawAlgorithmName(), k, k.getSourceNode() -/* - * from Crypto::CipherOperationNode op - * where op.getLocation().getFile().getBaseName() = "AsymmetricEncryptionMacHybridCryptosystem.java" - * select op, op.getAKey().getSourceNode() - */ - diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql index 7ae2a0f08880..918be510b6c6 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects known asymmetric algorithms * @id java/crypto_inventory_slices/known_asymmetric_algorithm - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql index b903e152a299..b40910b10f4c 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects known asymmetric cipher algorithms * @id java/crypto_inventory_slices/known_asymmetric_cipher_algorithm - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql index a14a0dfbaba7..b3e0097ec4c4 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects operations where the algorithm applied is a known asymmetric algorithms * @id java/crypto_inventory_slices/known_asymmetric_operation_algorithm - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql index f126c3d9ae11..de80f2a6d1ae 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects known cipher algorithms * @id java/crypto_inventory_slices/known_cipher_algorithm - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql index c3f69d91cb70..dd204bfd54e4 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects known elliptic curve algorithms * @id java/crypto_inventory_slices/known_elliptic_curve_algorithm - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql index ed24b62364dd..267625437401 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects algorithms that are known hashing algorithms * @id java/crypto_inventory_slices/known_hashing_algorithm - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql index 23fc6235e802..2edfa6190eaa 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql @@ -1,7 +1,7 @@ /** * @name Detects uses of hashing operations (operations exlicitly for hashing only, irrespective of the algorithm used) * @id java/crypto_inventory_slices/known_hashing_operation - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql index 8af3c09dd10e..6ee2ce9ac4d4 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects operations where the algorithm applied is a known hashing algorithm * @id java/crypto_inventory_slices/operation_with_known_hashing_algorithm - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql index e0970353a987..28de1170d272 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects known key derivation algorithms * @id java/crypto_inventory_slices/known_key_derivation_algorithm - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql index 240a4ea3fc54..c0ee9c88b2b2 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql @@ -1,7 +1,7 @@ /** * @name Detects uses of key derivation operations (operations exlicitly for key derivation only, irrespective of the algorithm used) * @id java/crypto_inventory_slices/known_key_derivation_operation - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql index 9afbd7d7f2f7..db27630cafa6 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql @@ -1,8 +1,7 @@ /** * @name Detects operations where the algorithm applied is a known key derivation algorithm - * @id java/cryptography-inventory-slices/operation-known-key-derivation-algorithm - * @description This query identifies operations that utilize a known key derivation algorithm. - * @kind problem + * @id java/crypto_inventory_slices/operation_known_key_derivation_algorithm + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql index d8467305df97..6c5f9c636771 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects known symmetric cipher algorithms * @id java/crypto_inventory_slices/known_symmetric_cipher_algorithm - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql b/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql index d02524a7df37..cb16064667ac 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql @@ -1,7 +1,7 @@ /** * @name Detects functions that take in crypto configuration parameters but calls are not detected in source. * @id java/crypto_inventory_slices/likely_crypto_api_function - * @kind problem + * @kind table */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql index 7559579d863f..962e8945e812 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Detects operations where the algorithm applied is unknown * @id java/crypto_inventory_slices/unknown_operation_algorithm - * @kind problem + * @kind table */ import java From b558e844ffa902b6ab7b665c902ca3e1bbdaed1c Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 03:56:20 +0200 Subject: [PATCH 111/122] Update slice query metadata and output tables --- .../InventorySlices/KnownAsymmetricAlgorithm.ql | 9 ++++++--- .../InventorySlices/KnownAsymmetricCipherAlgorithm.ql | 9 ++++++--- .../KnownAsymmetricOperationAlgorithm.ql | 9 ++++++--- .../quantum/InventorySlices/KnownCipherAlgorithm.ql | 9 ++++++--- .../InventorySlices/KnownEllipticCurveAlgorithm.ql | 9 ++++++--- .../quantum/InventorySlices/KnownHashingAlgorithm.ql | 9 ++++++--- .../quantum/InventorySlices/KnownHashingOperation.ql | 9 ++++++--- .../InventorySlices/KnownHashingOperationAlgorithm.ql | 9 ++++++--- .../InventorySlices/KnownKeyDerivationAlgorithm.ql | 9 ++++++--- .../InventorySlices/KnownKeyDerivationOperation.ql | 9 ++++++--- .../KnownKeyDerivationOperationAlgorithm.ql | 9 ++++++--- .../InventorySlices/KnownSymmetricCipherAlgorithm.ql | 9 ++++++--- .../quantum/InventorySlices/LikelyCryptoAPIFunction.ql | 10 +++++++--- .../InventorySlices/UnknownOperationAlgorithm.ql | 10 +++++++--- 14 files changed, 86 insertions(+), 42 deletions(-) diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql index 918be510b6c6..a74da7d1acc3 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql @@ -1,7 +1,10 @@ /** - * @name Detects known asymmetric algorithms - * @id java/crypto_inventory_slices/known_asymmetric_algorithm + * @name Operations using known asymmetric cipher algorithms (slice) + * @description Outputs operations where the algorithm used is a known asymmetric cipher algorithm. + * @id java/quantum/slices/known-asymmetric-cipher-algorithm * @kind table + * @tags quantum + * experimental */ import java @@ -9,4 +12,4 @@ import experimental.quantum.Language from Crypto::AlgorithmNode a where Crypto::isKnownAsymmetricAlgorithm(a) -select a, "Instance of asymmetric algorithm " + a.getAlgorithmName() +select a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql index b40910b10f4c..69643d92cd24 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql @@ -1,7 +1,10 @@ /** - * @name Detects known asymmetric cipher algorithms - * @id java/crypto_inventory_slices/known_asymmetric_cipher_algorithm + * @name Known asymmetric cipher algorithms (slice) + * @description Outputs known asymmetric cipher algorithms. + * @id java/quantum/slices/known-asymmetric-cipher-algorithm * @kind table + * @tags quantum + * experimental */ import java @@ -9,4 +12,4 @@ import experimental.quantum.Language from Crypto::KeyOperationAlgorithmNode a where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::AsymmetricCipherAlgorithm -select a, "Instance of asymmetric cipher algorithm " + a.getAlgorithmName() +select a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql index b3e0097ec4c4..224ee1161fb0 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql @@ -1,7 +1,10 @@ /** - * @name Detects operations where the algorithm applied is a known asymmetric algorithms - * @id java/crypto_inventory_slices/known_asymmetric_operation_algorithm + * @name Operations using known asymmetric algorithms (slice) + * @description Outputs operations where the algorithm used is a known asymmetric algorithm. + * @id java/quantum/slices/known-asymmetric-operation-algorithm * @kind table + * @tags quantum + * experimental */ import java @@ -9,4 +12,4 @@ import experimental.quantum.Language from Crypto::OperationNode op, Crypto::AlgorithmNode a where a = op.getAKnownAlgorithm() and Crypto::isKnownAsymmetricAlgorithm(a) -select op, "Operation using asymmetric algorithm $@", a, a.getAlgorithmName() +select op, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql index de80f2a6d1ae..da3371a59b34 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql @@ -1,7 +1,10 @@ /** - * @name Detects known cipher algorithms - * @id java/crypto_inventory_slices/known_cipher_algorithm + * @name Known cipher algorithms (slice) + * @description Outputs known cipher algorithms. + * @id java/quantum/slices/known-cipher-algorithm * @kind table + * @tags quantum + * experimental */ import java @@ -12,4 +15,4 @@ from Crypto::KeyOperationAlgorithmNode a where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::AsymmetricCipherAlgorithm or a.getAlgorithmType() instanceof Crypto::KeyOpAlg::SymmetricCipherAlgorithm -select a, "Instance of cipher algorithm " + a.getAlgorithmName() +select a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql index dd204bfd54e4..ca72e2de2517 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql @@ -1,11 +1,14 @@ /** - * @name Detects known elliptic curve algorithms - * @id java/crypto_inventory_slices/known_elliptic_curve_algorithm + * @name Known elliptic curve algorithms (slice) + * @description Outputs known elliptic curve algorithms. + * @id java/quantum/slices/known-elliptic-curve-algorithm * @kind table + * @tags quantum + * experimental */ import java import experimental.quantum.Language from Crypto::EllipticCurveNode a -select a, "Instance of elliptic curve algorithm " + a.getAlgorithmName() +select a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql index 267625437401..042f3b3dc915 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql @@ -1,11 +1,14 @@ /** - * @name Detects algorithms that are known hashing algorithms - * @id java/crypto_inventory_slices/known_hashing_algorithm + * @name Known hashing algorithms (slice) + * @description Outputs known hashing algorithms. + * @id java/quantum/slices/known-hashing-algorithm * @kind table + * @tags quantum + * experimental */ import java import experimental.quantum.Language from Crypto::HashAlgorithmNode a -select a, "Instance of hashing algorithm " + a.getAlgorithmName() +select a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql index 2edfa6190eaa..b004fae7fbbb 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql @@ -1,11 +1,14 @@ /** - * @name Detects uses of hashing operations (operations exlicitly for hashing only, irrespective of the algorithm used) - * @id java/crypto_inventory_slices/known_hashing_operation + * @name Known hashing operations (slice) + * @description Outputs known hashing operations. + * @id java/quantum/slices/known-hashing-operation * @kind table + * @tags quantum + * experimental */ import java import experimental.quantum.Language from Crypto::HashOperationNode op -select op, "Known hashing operation" +select op diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql index 6ee2ce9ac4d4..38d197bb7dd8 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql @@ -1,7 +1,10 @@ /** - * @name Detects operations where the algorithm applied is a known hashing algorithm - * @id java/crypto_inventory_slices/operation_with_known_hashing_algorithm + * @name Operations using known hashing algorithms (slice) + * @description Outputs operations where the algorithm used is a known hashing algorithm. + * @id java/quantum/slices/operation-with-known-hashing-algorithm * @kind table + * @tags quantum + * experimental */ import java @@ -9,4 +12,4 @@ import experimental.quantum.Language from Crypto::OperationNode op, Crypto::HashAlgorithmNode a where a = op.getAKnownAlgorithm() -select op, "Operation using hashing algorithm $@", a, a.getAlgorithmName() +select op, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql index 28de1170d272..5a9744c966b0 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql @@ -1,11 +1,14 @@ /** - * @name Detects known key derivation algorithms - * @id java/crypto_inventory_slices/known_key_derivation_algorithm + * @name Known key derivation algorithms (slice) + * @description Outputs known key derivation algorithms. + * @id java/quantum/slices/known-key-derivation-algorithm * @kind table + * @tags quantum + * experimental */ import java import experimental.quantum.Language from Crypto::KeyDerivationAlgorithmNode alg -select alg, "Known key derivation algorithm " + alg.getAlgorithmName() +select alg, alg.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql index c0ee9c88b2b2..95623ec7a1e8 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql @@ -1,11 +1,14 @@ /** - * @name Detects uses of key derivation operations (operations exlicitly for key derivation only, irrespective of the algorithm used) - * @id java/crypto_inventory_slices/known_key_derivation_operation + * @name Known key derivation operations (slice) + * @description Outputs known key derivation operations. + * @id java/quantum/slices/known-key-derivation-operation * @kind table + * @tags quantum + * experimental */ import java import experimental.quantum.Language from Crypto::KeyDerivationOperationNode op -select op, "Known key derivation operation" +select op diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql index db27630cafa6..efbfd9ed8658 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql @@ -1,7 +1,10 @@ /** - * @name Detects operations where the algorithm applied is a known key derivation algorithm - * @id java/crypto_inventory_slices/operation_known_key_derivation_algorithm + * @name Operations using known key derivation algorithms (slice) + * @description Outputs operations where the algorithm used is a known key derivation algorithm. + * @id java/quantum/slices/operation-with-known-kdf-algorithm * @kind table + * @tags quantum + * experimental */ import java @@ -9,4 +12,4 @@ import experimental.quantum.Language from Crypto::OperationNode op, Crypto::KeyDerivationAlgorithmNode a where a = op.getAKnownAlgorithm() -select op, "Operation using key derivation algorithm $@", a, a.getAlgorithmName() +select op, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql index 6c5f9c636771..e4a8d3ff8679 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql @@ -1,7 +1,10 @@ /** - * @name Detects known symmetric cipher algorithms - * @id java/crypto_inventory_slices/known_symmetric_cipher_algorithm + * @name Known symmetric cipher algorithms (slice) + * @description Outputs known symmetric cipher algorithms. + * @id java/quantum/slices/known-symmetric-cipher-algorithm * @kind table + * @tags quantum + * experimental */ import java @@ -9,4 +12,4 @@ import experimental.quantum.Language from Crypto::KeyOperationAlgorithmNode a where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::SymmetricCipherAlgorithm -select a, "Instance of symmetric cipher algorithm " + a.getAlgorithmName() +select a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql b/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql index cb16064667ac..2b81cf22dba5 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql @@ -1,7 +1,11 @@ /** - * @name Detects functions that take in crypto configuration parameters but calls are not detected in source. - * @id java/crypto_inventory_slices/likely_crypto_api_function - * @kind table + * @name Likely crypto API function + * @description Detects functions that take in crypto configuration parameters but calls are not detected in source. + * @id java/quantum/slices/likely-crypto-api-function + * @kind problem + * @severity info + * @tags quantum + * experimental */ import java diff --git a/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql index 962e8945e812..8469924a8501 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql @@ -1,7 +1,11 @@ /** - * @name Detects operations where the algorithm applied is unknown - * @id java/crypto_inventory_slices/unknown_operation_algorithm - * @kind table + * @name Operations with unknown algorithm + * @description Outputs operations where the algorithm applied is unknown + * @id java/quantum/slices/operation-with-unknown-algorithm + * @kind problem + * @severity info + * @tags quantum + * experimental */ import java From b8c3b43cc478d5fc7ff38cc85d6ba30d468fedf6 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 03:58:06 +0200 Subject: [PATCH 112/122] Fix KnownAsymmetricAlgorithm query id --- .../quantum/InventorySlices/KnownAsymmetricAlgorithm.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql index a74da7d1acc3..b4d0b908bbe1 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Operations using known asymmetric cipher algorithms (slice) * @description Outputs operations where the algorithm used is a known asymmetric cipher algorithm. - * @id java/quantum/slices/known-asymmetric-cipher-algorithm + * @id java/quantum/slices/known-asymmetric-algorithm * @kind table * @tags quantum * experimental From e03f57da9b956b6e2953edaeaef55ca4a95d7f8c Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 04:03:10 +0200 Subject: [PATCH 113/122] Update type name in experimental BrokenCrypto.ql --- java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql b/java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql index b4e9de9ac945..7291ea554a58 100644 --- a/java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql +++ b/java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql @@ -64,7 +64,7 @@ private string algorithmRegex(string algorithmString) { "((^|.*[A-Z]{2}|.*[^a-zA-Z])(" + algorithmString.toLowerCase() + ")([^a-z].*|$))" } -from Crypto::Algorithm alg +from Crypto::AlgorithmNode alg where alg.getAlgorithmName().regexpMatch(getInsecureAlgorithmRegex()) and // Exclude RSA/ECB/.* ciphers. From a57f4a1022e7d72c92afcfdc92cdcdc0adc8ba55 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 04:13:57 +0200 Subject: [PATCH 114/122] Update Java analysis query metadata --- .../experimental/quantum/Analysis/InsecureNonceSource.ql | 8 +++++--- .../quantum/Analysis/KnownWeakKDFIterationCount.ql | 9 +++++++-- java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql | 9 +++++++-- .../quantum/Analysis/UnknownKDFIterationCount.ql | 9 +++++++-- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/java/ql/src/experimental/quantum/Analysis/InsecureNonceSource.ql b/java/ql/src/experimental/quantum/Analysis/InsecureNonceSource.ql index b2c6f919d5fe..f00621d4d2b6 100644 --- a/java/ql/src/experimental/quantum/Analysis/InsecureNonceSource.ql +++ b/java/ql/src/experimental/quantum/Analysis/InsecureNonceSource.ql @@ -1,11 +1,13 @@ /** * @name Insecure nonce at a cipher operation - * @id java/insecure-nonce + * @id java/quantum/insecure-nonce + * @description A nonce is generated from a source that is not secure. This can lead to + * vulnerabilities such as replay attacks or key recovery. * @kind problem * @problem.severity error * @precision high - * @description A nonce is generated from a source that is not secure. This can lead to - * vulnerabilities such as replay attacks or key recovery. + * @tags quantum + * experimental */ import experimental.quantum.Language diff --git a/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql b/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql index 701f3064e92a..d8d9d125070e 100644 --- a/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql +++ b/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql @@ -1,7 +1,12 @@ /** - * @name Detects known weak KDf iteration counts (less than 100k and the count is statically known) - * @id java/crypto_inventory_filters/known_weak_kdf_iteration_count + * @name Weak known key derivation function iteration count + * @description Detects key derivation operations with a known weak iteration count. + * @id java/quantum/weak-kdf-iteration-count * @kind problem + * @precision high + * @severity problem + * @tags quantum + * experimental */ import java diff --git a/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql b/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql index d8adccace557..b7b7c27ec6bf 100644 --- a/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql +++ b/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql @@ -1,7 +1,12 @@ /** - * @name Detects reuse of the same nonce in multiple operations - * @id java/crypto_inventory_filter/nonce_reuse + * @name Reuse of cryptographic nonce + * @description Reuse of nonce in cryptographic operations can lead to vulnerabilities. + * @id java/quantum/reused-nonce * @kind problem + * @precision medium + * @severity problem + * @tags quantum + * experimental */ import java diff --git a/java/ql/src/experimental/quantum/Analysis/UnknownKDFIterationCount.ql b/java/ql/src/experimental/quantum/Analysis/UnknownKDFIterationCount.ql index 4ce404f01b08..21bca11cc1af 100644 --- a/java/ql/src/experimental/quantum/Analysis/UnknownKDFIterationCount.ql +++ b/java/ql/src/experimental/quantum/Analysis/UnknownKDFIterationCount.ql @@ -1,7 +1,12 @@ /** - * @name Detects unknown KDf iteration counts - * @id java/crypto_inventory_filters/unknown_kdf_iteration_count + * @name Unknown key derivation function iteration count + * @description Detects key derivation operations with an unknown iteration count. + * @id java/quantum/unknown-kdf-iteration-count * @kind problem + * @precision medium + * @severity warning + * @tags quantum + * experimental */ import java From 529128cbdec2bc02f2afaf8be3c635ffe1e50278 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 04:20:49 +0200 Subject: [PATCH 115/122] Add problem.severity for java analysis queries --- .../experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql | 2 +- java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql b/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql index d8d9d125070e..2dd5b0b006be 100644 --- a/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql +++ b/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql @@ -3,8 +3,8 @@ * @description Detects key derivation operations with a known weak iteration count. * @id java/quantum/weak-kdf-iteration-count * @kind problem + * @problem.severity error * @precision high - * @severity problem * @tags quantum * experimental */ diff --git a/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql b/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql index b7b7c27ec6bf..ed2872bb67e1 100644 --- a/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql +++ b/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql @@ -3,8 +3,8 @@ * @description Reuse of nonce in cryptographic operations can lead to vulnerabilities. * @id java/quantum/reused-nonce * @kind problem + * @problem.severity error * @precision medium - * @severity problem * @tags quantum * experimental */ From c6077947a71c53815535f249f2ff5e0d20708cb9 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Thu, 8 May 2025 16:10:28 +0200 Subject: [PATCH 116/122] Update cpp and java not_included_in_qls.expected --- .../query-suite/not_included_in_qls.expected | 1 + .../query-suite/not_included_in_qls.expected | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/cpp/ql/integration-tests/query-suite/not_included_in_qls.expected b/cpp/ql/integration-tests/query-suite/not_included_in_qls.expected index 68b8c8b0ab46..20ffa7c40c0d 100644 --- a/cpp/ql/integration-tests/query-suite/not_included_in_qls.expected +++ b/cpp/ql/integration-tests/query-suite/not_included_in_qls.expected @@ -299,6 +299,7 @@ ql/cpp/ql/src/experimental/cryptography/inventory/new_models/SigningAlgorithms.q ql/cpp/ql/src/experimental/cryptography/inventory/new_models/SymmetricEncryptionAlgorithms.ql ql/cpp/ql/src/experimental/cryptography/inventory/new_models/SymmetricPaddingAlgorithms.ql ql/cpp/ql/src/experimental/cryptography/inventory/new_models/UnknownAsymmetricKeyGeneration.ql +ql/cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql ql/cpp/ql/src/external/examples/filters/BumpMetricBy10.ql ql/cpp/ql/src/external/examples/filters/EditDefectMessage.ql ql/cpp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql diff --git a/java/ql/integration-tests/java/query-suite/not_included_in_qls.expected b/java/ql/integration-tests/java/query-suite/not_included_in_qls.expected index f6e89eede03b..0fbc365c1343 100644 --- a/java/ql/integration-tests/java/query-suite/not_included_in_qls.expected +++ b/java/ql/integration-tests/java/query-suite/not_included_in_qls.expected @@ -253,6 +253,29 @@ ql/java/ql/src/experimental/Security/CWE/CWE-665/InsecureRmiJmxEnvironmentConfig ql/java/ql/src/experimental/Security/CWE/CWE-755/NFEAndroidDoS.ql ql/java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql ql/java/ql/src/experimental/Security/CWE/CWE-939/IncorrectURLVerification.ql +ql/java/ql/src/experimental/quantum/Analysis/InsecureNonceSource.ql +ql/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql +ql/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql +ql/java/ql/src/experimental/quantum/Analysis/UnknownKDFIterationCount.ql +ql/java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql +ql/java/ql/src/experimental/quantum/Examples/TestAESGCMNonce.ql +ql/java/ql/src/experimental/quantum/Examples/TestCipher.ql +ql/java/ql/src/experimental/quantum/Examples/TestHash.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql +ql/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql +ql/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql +ql/java/ql/src/experimental/quantum/PrintCBOMGraph.ql ql/java/ql/src/external/DuplicateAnonymous.ql ql/java/ql/src/external/DuplicateBlock.ql ql/java/ql/src/external/DuplicateMethod.ql From cd59ce5b0493df7eb5804627fcf1845ad58f3533 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Fri, 9 May 2025 14:36:12 +0200 Subject: [PATCH 117/122] Rename shared pack to quantum from experimental --- cpp/ql/lib/experimental/quantum/Language.qll | 12 ++++++------ cpp/ql/lib/qlpack.yml | 2 +- java/ql/lib/experimental/quantum/Language.qll | 2 +- java/ql/lib/qlpack.yml | 2 +- .../codeql/quantum/experimental}/Model.qll | 0 shared/{experimental => quantum}/qlpack.yml | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) rename shared/{experimental/codeql/experimental/quantum => quantum/codeql/quantum/experimental}/Model.qll (100%) rename shared/{experimental => quantum}/qlpack.yml (81%) diff --git a/cpp/ql/lib/experimental/quantum/Language.qll b/cpp/ql/lib/experimental/quantum/Language.qll index 176db9736adb..db9737cf152d 100644 --- a/cpp/ql/lib/experimental/quantum/Language.qll +++ b/cpp/ql/lib/experimental/quantum/Language.qll @@ -1,13 +1,13 @@ -import codeql.experimental.quantum.Model +private import cpp as Language import semmle.code.cpp.dataflow.new.DataFlow -private import cpp as Lang +import codeql.quantum.experimental.Model -module CryptoInput implements InputSig { +module CryptoInput implements InputSig { class DataFlowNode = DataFlow::Node; - class LocatableElement = Lang::Locatable; + class LocatableElement = Language::Locatable; - class UnknownLocation = Lang::UnknownDefaultLocation; + class UnknownLocation = Language::UnknownDefaultLocation; LocatableElement dfn_to_element(DataFlow::Node node) { result = node.asExpr() or @@ -26,7 +26,7 @@ module CryptoInput implements InputSig { } } -module Crypto = CryptographyBase; +module Crypto = CryptographyBase; module ArtifactFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index 335360caeb26..b7bf768c3f2e 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -7,8 +7,8 @@ library: true upgrades: upgrades dependencies: codeql/dataflow: ${workspace} - codeql/experimental: ${workspace} codeql/mad: ${workspace} + codeql/quantum: ${workspace} codeql/rangeanalysis: ${workspace} codeql/ssa: ${workspace} codeql/typeflow: ${workspace} diff --git a/java/ql/lib/experimental/quantum/Language.qll b/java/ql/lib/experimental/quantum/Language.qll index eaf63aa5be25..8e00f34f02b1 100644 --- a/java/ql/lib/experimental/quantum/Language.qll +++ b/java/ql/lib/experimental/quantum/Language.qll @@ -1,9 +1,9 @@ -private import codeql.experimental.quantum.Model private import java as Language private import semmle.code.java.security.InsecureRandomnessQuery private import semmle.code.java.security.RandomQuery private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.FlowSources +private import codeql.quantum.experimental.Model private class UnknownLocation extends Language::Location { UnknownLocation() { this.getFile().getAbsolutePath() = "" } diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 036218757a84..eeaa063e4738 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -7,8 +7,8 @@ library: true upgrades: upgrades dependencies: codeql/dataflow: ${workspace} - codeql/experimental: ${workspace} codeql/mad: ${workspace} + codeql/quantum: ${workspace} codeql/rangeanalysis: ${workspace} codeql/regex: ${workspace} codeql/threat-models: ${workspace} diff --git a/shared/experimental/codeql/experimental/quantum/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll similarity index 100% rename from shared/experimental/codeql/experimental/quantum/Model.qll rename to shared/quantum/codeql/quantum/experimental/Model.qll diff --git a/shared/experimental/qlpack.yml b/shared/quantum/qlpack.yml similarity index 81% rename from shared/experimental/qlpack.yml rename to shared/quantum/qlpack.yml index 2976c56ba492..8e34c19c1121 100644 --- a/shared/experimental/qlpack.yml +++ b/shared/quantum/qlpack.yml @@ -1,4 +1,4 @@ -name: codeql/experimental +name: codeql/quantum version: 0.0.0-dev groups: shared library: true From fbf3d7c195d38c3c07f114a302b44aced009eaf3 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Fri, 9 May 2025 14:36:55 +0200 Subject: [PATCH 118/122] Update CODEOWNERS to specify shared pack directory --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 318d9f2c6de7..96aa46df9495 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -17,6 +17,7 @@ # Experimental CodeQL cryptography **/experimental/quantum/ @github/ps-codeql +/shared/quantum/ @github/ps-codeql # CodeQL tools and associated docs /docs/codeql/codeql-cli/ @github/codeql-cli-reviewers From ab3f62eed171480c5b92b92bd959eeb2630c118c Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Mon, 12 May 2025 14:34:16 +0200 Subject: [PATCH 119/122] Add missing tags to PrintCBOMGraph.ql queries --- cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql | 2 ++ java/ql/src/experimental/quantum/PrintCBOMGraph.ql | 2 ++ 2 files changed, 4 insertions(+) diff --git a/cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql b/cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql index f741e3c9f947..32979da10659 100644 --- a/cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql +++ b/cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql @@ -4,6 +4,8 @@ * This query only supports DGML output, as CodeQL DOT output omits properties. * @kind graph * @id cpp/print-cbom-graph + * @tags quantum + * experimental */ import experimental.quantum.Language diff --git a/java/ql/src/experimental/quantum/PrintCBOMGraph.ql b/java/ql/src/experimental/quantum/PrintCBOMGraph.ql index bc79d5d4ce6a..f9c59432846e 100644 --- a/java/ql/src/experimental/quantum/PrintCBOMGraph.ql +++ b/java/ql/src/experimental/quantum/PrintCBOMGraph.ql @@ -4,6 +4,8 @@ * This query only supports DGML output, as CodeQL DOT output omits properties. * @kind graph * @id java/print-cbom-graph + * @tags quantum + * experimental */ import experimental.quantum.Language From 8f36624171dd2dca388e5707451c4e5a53d03f24 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Mon, 12 May 2025 14:37:44 +0200 Subject: [PATCH 120/122] Add AsymmetricAlgorithmNode, refactor and address feedback --- java/ql/lib/experimental/quantum/Language.qll | 32 +++--- .../KnownAsymmetricAlgorithm.ql | 3 +- .../KnownAsymmetricOperationAlgorithm.ql | 4 +- .../LikelyCryptoAPIFunction.ql | 2 +- .../codeql/quantum/experimental/Model.qll | 101 +++++++++++------- 5 files changed, 83 insertions(+), 59 deletions(-) diff --git a/java/ql/lib/experimental/quantum/Language.qll b/java/ql/lib/experimental/quantum/Language.qll index 8e00f34f02b1..864ca6dd9317 100644 --- a/java/ql/lib/experimental/quantum/Language.qll +++ b/java/ql/lib/experimental/quantum/Language.qll @@ -42,19 +42,15 @@ module CryptoInput implements InputSig { } } -/** - * Instantiate the model - */ +// Instantiate the `CryptographyBase` module module Crypto = CryptographyBase; -/** - * Definitions of various generic data sources - */ +// Definitions of various generic sources final class DefaultFlowSource = SourceNode; final class DefaultRemoteFlowSource = RemoteFlowSource; -class GenericUnreferencedParameterSource extends Crypto::GenericUnreferencedParameterSource { +private class GenericUnreferencedParameterSource extends Crypto::GenericUnreferencedParameterSource { GenericUnreferencedParameterSource() { exists(Parameter p | this = p and not exists(p.getAnArgument())) } @@ -68,7 +64,7 @@ class GenericUnreferencedParameterSource extends Crypto::GenericUnreferencedPara override string getAdditionalDescription() { result = this.toString() } } -class GenericLocalDataSource extends Crypto::GenericLocalDataSource { +private class GenericLocalDataSource extends Crypto::GenericLocalDataSource { GenericLocalDataSource() { any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this } @@ -82,7 +78,7 @@ class GenericLocalDataSource extends Crypto::GenericLocalDataSource { override string getAdditionalDescription() { result = this.toString() } } -class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { +private class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this } override DataFlow::Node getOutputNode() { result.asExpr() = this } @@ -94,7 +90,7 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource { override string getAdditionalDescription() { result = this.toString() } } -class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof Literal { +private class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof Literal { ConstantDataSource() { // TODO: this is an API specific workaround for JCA, as 'EC' is a constant that may be used // where typical algorithms are specified, but EC specifically means set up a @@ -114,14 +110,14 @@ class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceo } /** - * Random number generation, where each instance is modelled as the expression + * An instance of random number generation, modelled as the expression * tied to an output node (i.e., the result of the source of randomness) */ abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance { override DataFlow::Node getOutputNode() { result.asExpr() = this } } -class SecureRandomnessInstance extends RandomnessInstance { +private class SecureRandomnessInstance extends RandomnessInstance { RandomDataSource source; SecureRandomnessInstance() { @@ -132,7 +128,7 @@ class SecureRandomnessInstance extends RandomnessInstance { override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() } } -class InsecureRandomnessInstance extends RandomnessInstance { +private class InsecureRandomnessInstance extends RandomnessInstance { RandomDataSource source; InsecureRandomnessInstance() { @@ -143,7 +139,11 @@ class InsecureRandomnessInstance extends RandomnessInstance { } /** - * Artifact output to node input configuration + * 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(); @@ -151,8 +151,6 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node { final DataFlow::Node getInput() { result = this } } -module ArtifactFlow = DataFlow::Global; - /** * Generic data source to node input configuration */ @@ -214,5 +212,7 @@ module ArtifactFlowConfig implements DataFlow::ConfigSig { module GenericDataSourceFlow = TaintTracking::Global; +module ArtifactFlow = DataFlow::Global; + // Import library-specific modeling import JCA diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql index b4d0b908bbe1..e796cc9e1091 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql @@ -10,6 +10,5 @@ import java import experimental.quantum.Language -from Crypto::AlgorithmNode a -where Crypto::isKnownAsymmetricAlgorithm(a) +from Crypto::AsymmetricAlgorithmNode a select a, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql index 224ee1161fb0..98a8aa38a03e 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql @@ -10,6 +10,6 @@ import java import experimental.quantum.Language -from Crypto::OperationNode op, Crypto::AlgorithmNode a -where a = op.getAKnownAlgorithm() and Crypto::isKnownAsymmetricAlgorithm(a) +from Crypto::OperationNode op, Crypto::AsymmetricAlgorithmNode a +where a = op.getAKnownAlgorithm() select op, a.getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql b/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql index 2b81cf22dba5..14148354d0fc 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql @@ -1,6 +1,6 @@ /** * @name Likely crypto API function - * @description Detects functions that take in crypto configuration parameters but calls are not detected in source. + * @description Outputs functions that take in crypto configuration parameters but calls are not detected in source. * @id java/quantum/slices/likely-crypto-api-function * @kind problem * @severity info diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index 69a4fd75acfa..54691c306bd6 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -3,7 +3,6 @@ */ import codeql.util.Location -import codeql.util.Either signature module InputSig { class LocatableElement { @@ -36,10 +35,20 @@ module CryptographyBase Input> { final class DataFlowNode = Input::DataFlowNode; + /** + * A `ConsumerInputDataFlowNode` is a `DataFlowNode` that represents a consumer of data. + * + * This class is equivalent to `DataFlowNode` but facilitates binding to a `ConsumerElement`. + */ class ConsumerInputDataFlowNode extends DataFlowNode { ConsumerElement getConsumer() { result.getInputNode() = this } } + /** + * An `ArtifactOutputDataFlowNode` is a `DataFlowNode` that represents the source of a created artifact. + * + * This class is equivalent to `DataFlowNode` but facilitates binding to an `OutputArtifactInstance`. + */ class ArtifactOutputDataFlowNode extends DataFlowNode { OutputArtifactInstance getArtifact() { result.getOutputNode() = this } } @@ -51,19 +60,17 @@ module CryptographyBase Input> { bindingset[root] private string getPropertyAsGraphString(NodeBase node, string key, Location root) { result = - strictconcat(any(string value, Location location, string parsed | - node.properties(key, value, location) and - ( - if location = root or location instanceof UnknownLocation - then parsed = value - else - parsed = - "(" + value + "," + Input::locationToFileBaseNameAndLineNumberString(location) + - ")" - ) - | - parsed - ), "," + strictconcat(string value, Location location, string parsed | + node.properties(key, value, location) and + ( + if location = root or location instanceof UnknownLocation + then parsed = value + else + parsed = + "(" + value + "," + Input::locationToFileBaseNameAndLineNumberString(location) + ")" + ) + | + parsed, "," ) } @@ -154,7 +161,7 @@ module CryptographyBase Input> { * CROSS PRODUCT WARNING: Modeling any *other* element that is a `FlowAwareElement` to the same * instance in the database will result in every `FlowAwareElement` sharing the output flow. */ - abstract class KnownElement extends LocatableElement { + abstract private class KnownElement extends LocatableElement { final ConsumerElement getAConsumer() { result.getAKnownSource() = this } } @@ -297,6 +304,23 @@ module CryptographyBase Input> { final override ConsumerInputDataFlowNode getInputNode() { result = input } } + /** + * An `AlgorithmValueConsumer` (_AVC_) is an element that consumes a value specifying an algorithm. + * + * Example 1: + * `arg0` of `set_algorithm` (`x`) is the AVC for the `ctx.encrypt()` operation. + * ```cpp + * x = "RSA"; + * ctx.set_algorithm(x); + * ctx.encrypt(); + * ``` + * + * Example 2: + * `encrypt_with_rsa` is concurrently an an operation, an AVC, and an algorithm. + * ```cpp + * `encrypt_with_rsa();` + * ``` + */ abstract class AlgorithmValueConsumer extends ConsumerElement { /** * DO NOT USE. @@ -324,8 +348,8 @@ module CryptographyBase Input> { * to the artifact it receives, thereby becoming the definitive contextual source for that artifact. * * Architectural Implications: - * * By directly coupling a consumer with the node that receives an artifact, - * the data flow is fully transparent with the consumer itself serving only as a transparent node. + * * By directly coupling a consumer with the node that receives an artifact, no modeling considerations have to be made + * to provide an interface for identifying the source via the consumer data-flow mechanisms. * * An artifact's properties (such as being a nonce) are not necessarily inherent; they are determined by the context in which the artifact is consumed. * The consumer node is therefore essential in defining these properties for inputs. * * This approach reduces ambiguity by avoiding separate notions of "artifact source" and "consumer", as the node itself encapsulates both roles. @@ -347,7 +371,7 @@ module CryptographyBase Input> { * A `NonceArtifactConsumer` is always the `NonceArtifactInstance` itself, since data only becomes (i.e., is determined to be) * a `NonceArtifactInstance` when it is consumed in a context that expects a nonce (e.g., an argument expecting nonce data). * - * In this case, the artifact (nonce) is fully defined by the context in which it is consumed, and the consumer embodies + * In this case, the artifact (nonce) is fully defined by the context in which it is consumed, and the consumer embodies * that identity without the need for additional differentiation. Without the context a consumer provides, that data could * otherwise be any other type of artifact or even simply random data. * @@ -604,7 +628,6 @@ module CryptographyBase Input> { type = TSymmetricCipher(SEED()) and size = 128 } - bindingset[type] predicate symmetric_cipher_to_name_and_structure( TSymmetricCipherType type, string name, CipherStructureType s ) { @@ -651,7 +674,6 @@ module CryptographyBase Input> { s = UnknownCipherStructureType() } - bindingset[type] predicate type_to_name(Algorithm type, string name) { // Symmetric cipher algorithm symmetric_cipher_to_name_and_structure(type.(SymmetricCipherAlgorithm).getType(), name, _) @@ -1551,6 +1573,20 @@ module CryptographyBase Input> { string toString() { result = super.getAlgorithmName() } } + /** + * The subset of algorithm nodes that are known asymmetric algorithm. + * + * Note: This is not an independent top-level node type. + */ + class AsymmetricAlgorithmNode extends TKeyCreationCandidateAlgorithm instanceof AlgorithmNode { + AsymmetricAlgorithmNode() { + this instanceof EllipticCurveNode or + this.(KeyOperationAlgorithmNode).isAsymmetric() + } + + string toString() { result = super.getAlgorithmName() } + } + /** * A cryptographic key, such as a symmetric key or asymmetric key pair. */ @@ -1709,7 +1745,6 @@ module CryptographyBase Input> { TMACType getMACType() { result = instance.asAlg().getMACType() } - bindingset[type] final private predicate macToNameMapping(TMACType type, string name) { type instanceof THMAC and name = "HMAC" @@ -2102,7 +2137,6 @@ module CryptographyBase Input> { */ TBlockCipherModeOfOperationType getModeType() { result = instance.getModeType() } - bindingset[type] final private predicate modeToNameMapping(TBlockCipherModeOfOperationType type, string name) { type = ECB() and name = "ECB" or @@ -2148,7 +2182,6 @@ module CryptographyBase Input> { TPaddingType getPaddingType() { result = instance.getPaddingType() } - bindingset[type] final private predicate paddingToNameMapping(TPaddingType type, string name) { type = ANSI_X9_23() and name = "ANSI_X9_23" or @@ -2454,9 +2487,9 @@ module CryptographyBase Input> { // ALL BRAINPOOL CURVES keySize in [160, 192, 224, 256, 320, 384, 512] and ( - curveName = "BRAINPOOLP" + keySize.toString() + "R1" + curveName = "BRAINPOOLP" + keySize + "R1" or - curveName = "BRAINPOOLP" + keySize.toString() + "T1" + curveName = "BRAINPOOLP" + keySize + "T1" ) } @@ -2464,8 +2497,8 @@ module CryptographyBase Input> { // ALL SEC CURVES keySize in [112, 113, 128, 131, 160, 163, 192, 193, 224, 233, 239, 256, 283, 384, 409, 521, 571] and exists(string suff | suff in ["R1", "R2", "K1"] | - curveName = "SECT" + keySize.toString() + suff or - curveName = "SECP" + keySize.toString() + suff + curveName = "SECT" + keySize + suff or + curveName = "SECP" + keySize + suff ) } @@ -2475,22 +2508,20 @@ module CryptographyBase Input> { exists(string pre, string suff | pre in ["PNB", "ONB", "TNB"] and suff in ["V1", "V2", "V3", "V4", "V5", "W1", "R1"] | - curveName = "C2" + pre + keySize.toString() + suff + curveName = "C2" + pre + keySize + suff ) } private predicate isPrimeCurve(string curveName, int keySize) { // ALL PRIME CURVES keySize in [192, 239, 256] and - exists(string suff | suff in ["V1", "V2", "V3"] | - curveName = "PRIME" + keySize.toString() + suff - ) + exists(string suff | suff in ["V1", "V2", "V3"] | curveName = "PRIME" + keySize + suff) } private predicate isNumsCurve(string curveName, int keySize) { // ALL NUMS CURVES keySize in [256, 384, 512] and - exists(string suff | suff = "T1" | curveName = "NUMSP" + keySize.toString() + suff) + exists(string suff | suff = "T1" | curveName = "NUMSP" + keySize + suff) } /** @@ -2587,10 +2618,4 @@ module CryptographyBase Input> { location = this.getLocation() } } - - predicate isKnownAsymmetricAlgorithm(AlgorithmNode node) { - node instanceof EllipticCurveNode - or - node instanceof KeyOperationAlgorithmNode and node.(KeyOperationAlgorithmNode).isAsymmetric() - } } From c66ec6333327b3430a02e166dce5fec7e5a40f88 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Mon, 12 May 2025 14:48:58 +0200 Subject: [PATCH 121/122] Fix query compilation errors --- java/ql/lib/experimental/quantum/Language.qll | 4 ++-- .../quantum/InventorySlices/KnownAsymmetricAlgorithm.ql | 2 +- .../InventorySlices/KnownAsymmetricOperationAlgorithm.ql | 2 +- shared/quantum/codeql/quantum/experimental/Model.qll | 4 +++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/java/ql/lib/experimental/quantum/Language.qll b/java/ql/lib/experimental/quantum/Language.qll index 864ca6dd9317..59164901c10c 100644 --- a/java/ql/lib/experimental/quantum/Language.qll +++ b/java/ql/lib/experimental/quantum/Language.qll @@ -117,7 +117,7 @@ abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance override DataFlow::Node getOutputNode() { result.asExpr() = this } } -private class SecureRandomnessInstance extends RandomnessInstance { +class SecureRandomnessInstance extends RandomnessInstance { RandomDataSource source; SecureRandomnessInstance() { @@ -128,7 +128,7 @@ private class SecureRandomnessInstance extends RandomnessInstance { override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() } } -private class InsecureRandomnessInstance extends RandomnessInstance { +class InsecureRandomnessInstance extends RandomnessInstance { RandomDataSource source; InsecureRandomnessInstance() { diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql index e796cc9e1091..dbdc832c70b5 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql @@ -11,4 +11,4 @@ import java import experimental.quantum.Language from Crypto::AsymmetricAlgorithmNode a -select a, a.getAlgorithmName() +select a, a.asAlgorithmNode().getAlgorithmName() diff --git a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql index 98a8aa38a03e..060b7df99a94 100644 --- a/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql @@ -12,4 +12,4 @@ import experimental.quantum.Language from Crypto::OperationNode op, Crypto::AsymmetricAlgorithmNode a where a = op.getAKnownAlgorithm() -select op, a.getAlgorithmName() +select op, a.asAlgorithmNode().getAlgorithmName() diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index 54691c306bd6..1a192e84778f 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -1584,7 +1584,9 @@ module CryptographyBase Input> { this.(KeyOperationAlgorithmNode).isAsymmetric() } - string toString() { result = super.getAlgorithmName() } + string toString() { result = super.toString() } + + AlgorithmNode asAlgorithmNode() { result = this } } /** From 5334e907170ec18ef4b2a538dea4dbf55d8d3a47 Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Mon, 12 May 2025 14:58:04 +0200 Subject: [PATCH 122/122] Make FlowAwareElement private --- shared/quantum/codeql/quantum/experimental/Model.qll | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index 1a192e84778f..a87aee2e69c4 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -130,7 +130,7 @@ module CryptographyBase Input> { /** * An element that is flow-aware, i.e., it has an input and output node implicitly used for data flow analysis. */ - abstract class FlowAwareElement extends LocatableElement { + abstract private class FlowAwareElementImpl extends LocatableElement { /** * Gets the output node for this element, which should usually be the same as `this`. */ @@ -155,6 +155,8 @@ module CryptographyBase Input> { abstract predicate flowsTo(FlowAwareElement other); } + final class FlowAwareElement = FlowAwareElementImpl; + /** * An element that represents a _known_ cryptographic asset with a determinable value OR an artifact. * @@ -187,7 +189,7 @@ module CryptographyBase Input> { * 1. A value (e.g., a string or integer literal) *or* * 1. An input for which a value cannot be determined (e.g., `argv`, file system reads, and web request headers) */ - abstract class GenericSourceInstance extends FlowAwareElement { + abstract class GenericSourceInstance extends FlowAwareElementImpl { final override ConsumerInputDataFlowNode getInputNode() { none() } abstract string getInternalType(); @@ -257,7 +259,7 @@ module CryptographyBase Input> { * * A consumer can consume multiple instances and types of assets at once, e.g., both a `PaddingAlgorithm` and `CipherAlgorithm`. */ - abstract private class ConsumerElement extends FlowAwareElement { + abstract private class ConsumerElement extends FlowAwareElementImpl { abstract KnownElement getAKnownSource(); override predicate flowsTo(FlowAwareElement other) { none() } @@ -337,7 +339,7 @@ module CryptographyBase Input> { /** * An element that represents a _known_ cryptographic artifact. */ - abstract class ArtifactInstance extends KnownElement, FlowAwareElement { + abstract class ArtifactInstance extends KnownElement, FlowAwareElementImpl { abstract predicate isConsumerArtifact(); // whether this is an input artifact defined by its consumer }