From 874e3b5e063bdd403264eea80e367d6a2f4eccdc Mon Sep 17 00:00:00 2001 From: Nicolas Will Date: Wed, 12 Feb 2025 17:58:15 +0100 Subject: [PATCH] Modify model to use newtypes, expand modeling --- cpp/ql/lib/experimental/Quantum/OpenSSL.qll | 208 ++++++-- java/ql/lib/experimental/Quantum/JCA.qll | 161 +++--- java/ql/src/experimental/Quantum/Test.ql | 7 +- .../codeql/cryptography/Model.qll | 467 ++++++++++++++++-- 4 files changed, 676 insertions(+), 167 deletions(-) diff --git a/cpp/ql/lib/experimental/Quantum/OpenSSL.qll b/cpp/ql/lib/experimental/Quantum/OpenSSL.qll index bf38e5c0ab5..cb19092f3fd 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 } - class SHA1Algo extends Crypto::HashAlgorithm instanceof MacroAccess { - SHA1Algo() { this.getMacro().getName() = "SN_sha1" } - - override string getRawAlgorithmName() { result = "SN_sha1" } - - override Crypto::THashType getHashType() { result instanceof Crypto::SHA1 } + 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() + ) + } + + 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() 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 EVP_KDF_fetch_AlgorithmArg extends Expr { + EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) } + } - class KDFAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLiteral { + predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF", "PBKDF2"] } + + class KDFAlgorithmStringLiteral extends 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()) - } + 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 Crypto::NodeBase getOrigin(string name) { + 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/java/ql/lib/experimental/Quantum/JCA.qll b/java/ql/lib/experimental/Quantum/JCA.qll index 44a753bb7db..807dec96ba4 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 } - class ModeOfOperationStringLiteral extends StringLiteral { - ModeOfOperationStringLiteral() { cipher_modes(this.(StringLiteral).getValue().splitAt("/")) } - - string getRawAlgorithmName() { result = this.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 - } - - override Crypto::TModeOperation getModeType() { - this.modeToNameMapping(result, this.getRawAlgorithmName()) - } - - override Crypto::LocatableElement getOrigin(string name) { - result = mode and name = mode.toString() + predicate isSink(DataFlow::Node sink) { + exists(CipherGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) } } - abstract class CipherAlgorithmPadding extends Crypto::NodeBase { - string getValue() { result = "" } + module AlgorithmStringToFetchFlow = DataFlow::Global; + + class CipherGetInstanceAlgorithmArg extends Expr { + CipherGetInstanceAlgorithmArg() { + exists(CipherGetInstanceCall call | this = call.getArgument(0)) + } + + StringLiteral getOrigin() { + AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), DataFlow::exprNode(this)) + } + } + + class ModeStringLiteral extends Crypto::ModeOfOperation { + CipherStringLiteral instance; + + ModeStringLiteral() { + this = Crypto::TModeOfOperationAlgorithm(instance) and + exists(instance.getMode()) and + instance = any(CipherGetInstanceAlgorithmArg call).getOrigin() + } + + override Location getLocation() { result = instance.getLocation() } + + override string getRawAlgorithmName() { result = instance.getMode() } + + predicate modeToNameMapping(Crypto::TModeOperationType type, string name) { + super.modeToNameMapping(type, name) + } + + override Crypto::TModeOperationType getModeType() { + this.modeToNameMapping(result, instance.getMode().toUpperCase()) + } + + 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; - AESAlgo() { algorithmStringToCipherInstanceArgFlow("AES", alg, this) } + CipherAlgorithm() { + this = Crypto::TSymmetricAlgorithm(instance) and + instance.getOrigin() = origin + } + + 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 = origin.getValue() } - override string getRawAlgorithmName() { result = alg.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 f35336a846f..f3ae23a2cca 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 ad117674dff..dd570a89ef6 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 SHA256 and name = "SHA-256" + type instanceof SHA2 and name = "SHA2" or - type instanceof SHA512 and name = "SHA-512" + 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 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() } + } + + /** + * 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 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 HKDF extends KeyDerivationAlgorithm { + 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 + ) + ) } } /** - * PKCS #12 key derivation function + * PKCS12KDF key derivation function */ - abstract class PKCS12KDF extends KeyDerivationAlgorithm { - final override string getAlgorithmName() { result = "PKCS12KDF" } + abstract class PKCS12KDF extends KeyDerivationWithDigestParameter { + override string getAlgorithmName() { result = "PKCS12KDF" } - abstract HashAlgorithm getHashAlgorithm(); + /** + * Gets the iteration count of this key derivation algorithm. + */ + abstract string getIterationCount(Location location); - override NodeBase getChild(string edgeName) { - result = super.getChild(edgeName) + /** + * 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 - 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 = "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(); } }