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 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/cpp/ql/lib/experimental/quantum/Language.qll b/cpp/ql/lib/experimental/quantum/Language.qll new file mode 100644 index 000000000000..db9737cf152d --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/Language.qll @@ -0,0 +1,113 @@ +private import cpp as Language +import semmle.code.cpp.dataflow.new.DataFlow +import codeql.quantum.experimental.Model + +module CryptoInput implements InputSig { + class DataFlowNode = DataFlow::Node; + + class LocatableElement = Language::Locatable; + + class UnknownLocation = Language::UnknownDefaultLocation; + + LocatableElement dfn_to_element(DataFlow::Node node) { + result = node.asExpr() or + 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 + */ +abstract class AdditionalFlowInputStep extends DataFlow::Node { + abstract DataFlow::Node getOutput(); + + final DataFlow::Node getInput() { result = this } +} + +/** + * Generic data source to node input configuration + */ +module GenericDataSourceFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::GenericSourceInstance i).getOutputNode() + } + + predicate isSink(DataFlow::Node sink) { + sink = any(Crypto::FlowAwareElement other).getInputNode() + } + + predicate isBarrierOut(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getInputNode() + } + + predicate isBarrierIn(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getOutputNode() + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node1.(AdditionalFlowInputStep).getOutput() = node2 + } +} + +module ArtifactUniversalFlowConfig 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 ArtifactUniversalFlow = DataFlow::Global; + +import OpenSSL.OpenSSL 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..72c3ffcfad44 --- /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 + +/** + * 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..2566c1188a6c --- /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..7483572848eb --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll @@ -0,0 +1,126 @@ +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 +{ + 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 + // TODO 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/AlgorithmInstances/HashAlgorithmInstance.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll new file mode 100644 index 000000000000..985e36dbdd71 --- /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 new file mode 100644 index 000000000000..77caf0bb378c --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll @@ -0,0 +1,2962 @@ +import cpp +import experimental.quantum.OpenSSL.LibraryDetector + +predicate resolveAlgorithmFromExpr(Expr e, string normalizedName, string algType) { + resolveAlgorithmFromCall(e, normalizedName, algType) + or + resolveAlgorithmFromLiteral(e, normalizedName, algType) +} + +class KnownOpenSSLAlgorithmConstant extends Expr { + KnownOpenSSLAlgorithmConstant() { resolveAlgorithmFromExpr(this, _, _) } + + string getNormalizedName() { resolveAlgorithmFromExpr(this, result, _) } + + string getAlgType() { resolveAlgorithmFromExpr(this, _, result) } +} + +class KnownOpenSSLCipherAlgorithmConstant extends KnownOpenSSLAlgorithmConstant { + string algType; + + KnownOpenSSLCipherAlgorithmConstant() { + resolveAlgorithmFromExpr(this, _, algType) and + algType.toLowerCase().matches("%encryption") + } + + int getExplicitKeySize() { + 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() { + resolveAlgorithmFromExpr(this, _, algType) and + algType.toLowerCase().matches("%padding") + } +} + +class KnownOpenSSLBlockModeAlgorithmConstant extends KnownOpenSSLAlgorithmConstant { + string algType; + + KnownOpenSSLBlockModeAlgorithmConstant() { + 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() + ) + } +} + +/** + * 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 + * 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 knownOpenSSLAlgorithmLiteral(_, nid, normalized, algType) + ) + or + exists(string name | + name = resolveAlgorithmAlias(e.getValue()) and + knownOpenSSLAlgorithmLiteral(name, _, normalized, algType) + ) +} + +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 + knownOpenSSLAlgorithmLiteral(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 = "dss" and target = "dsa" + or + alias = "dss1" and target = "dsaWithSHA1" + 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" +} + +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) + * `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 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" + 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 = "ELLIPTIC_CURVE" // TODO: Verify algorithm type + or + 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 + 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 = "SHA-224" and algType = "HASH" + or + name = "sha256" and nid = 672 and normalized = "SHA-256" and algType = "HASH" + or + name = "sha384" and nid = 673 and normalized = "SHA-384" and algType = "HASH" + or + name = "sha512" and nid = 674 and normalized = "SHA-512" and algType = "HASH" + or + name = "sha512-224" and nid = 1094 and normalized = "SHA-512-224" and algType = "HASH" + or + 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 + name = "sha3-256" and nid = 1097 and normalized = "SHA3-256" and algType = "HASH" + or + name = "sha3-384" and nid = 1098 and normalized = "SHA3-384" and algType = "HASH" + or + name = "sha3-512" and nid = 1099 and normalized = "SHA3-512" and algType = "HASH" + or + name = "shake128" and nid = 1100 and normalized = "SHAKE-128" and algType = "HASH" + or + 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 + 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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "ARIA-128" and + algType = "SYMMETRIC_ENCRYPTION" + or + 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 = "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 = "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 = "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 = "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 = "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 = "ARIA-192" and + algType = "SYMMETRIC_ENCRYPTION" + or + 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 = "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 = "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 = "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 = "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 = "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 = "ARIA-256" and + algType = "SYMMETRIC_ENCRYPTION" + or + 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 = "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 = "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 = "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 = "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 = "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 = "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-40" 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-128" 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-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-40" 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 = "AES-128" 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 = "AES-128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes192-wrap" and + nid = 789 and + normalized = "AES-192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes256-wrap" and + nid = 790 and + normalized = "AES-256" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes128-wrap-pad" and + nid = 897 and + normalized = "AES-128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes192-wrap-pad" and + nid = 900 and + normalized = "AES-192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-aes256-wrap-pad" and + nid = 903 and + normalized = "AES-256" 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 = "AES-256" 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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "AES-256" 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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "CAMELLIA-256" 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 = "HASH" + or + 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 + 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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "CAMELLIA-128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-camellia192-wrap" and + nid = 908 and + normalized = "CAMELLIA-192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "id-camellia256-wrap" and + nid = 909 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 = "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 = "AES-256" 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 = "AES-128" 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 = "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 = "AES-256" 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 = "SHA-256" and algType = "HASH" + or + name = "aes-128-cbc-hmac-sha256" and + nid = 948 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" + or + 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 + normalized = "AES-192" 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 = "SHA-256" and algType = "HASH" + or + name = "aes-256-cbc-hmac-sha256" and + nid = 950 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 = "AES-128" and algType = "SYMMETRIC_ENCRYPTION" + or + 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 = "AES-256" and algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-128-gcm" and + nid = 961 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 = "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 = "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 = "CAMELLIA-128" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-192-gcm" and + nid = 965 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 = "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 = "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 = "CAMELLIA-192" and + algType = "SYMMETRIC_ENCRYPTION" + or + name = "camellia-256-gcm" and + nid = 969 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 = "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 = "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 = "CAMELLIA-256" 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 = "HASH" + or + 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 + 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 = "SHA3-224" and algType = "HASH" + or + name = "hmac-sha3-256" and nid = 1103 and normalized = "SHA3-256" and algType = "HASH" + or + name = "hmac-sha3-384" and nid = 1104 and normalized = "SHA3-384" and algType = "HASH" + or + 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 + 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 = "SHA-512" 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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "SHA3-512" 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 = "SHA3-224" 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 = "SHA3-256" 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 = "SHA3-384" 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 = "SHA3-512" 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-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 = "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 = "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 = "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 = "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 = "ARIA-256" 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 = "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 = "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 = "AES-256" 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 = "SHA-224" and + algType = "HASH" + or + name = "dhsinglepass-cofactordh-sha256kdf-scheme" and + nid = 943 and + normalized = "SHA-256" and + algType = "HASH" + or + name = "dhsinglepass-cofactordh-sha384kdf-scheme" and + nid = 944 and + normalized = "SHA-384" and + algType = "HASH" + or + name = "dhsinglepass-cofactordh-sha512kdf-scheme" and + nid = 945 and + normalized = "SHA-512" 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 = "SHA-224" and + algType = "HASH" + or + name = "dhsinglepass-stddh-sha256kdf-scheme" and + nid = 938 and + normalized = "SHA-256" and + algType = "HASH" + or + name = "dhsinglepass-stddh-sha384kdf-scheme" and + nid = 939 and + normalized = "SHA-384" and + algType = "HASH" + or + name = "dhsinglepass-stddh-sha512kdf-scheme" and + nid = 940 and + normalized = "SHA-512" 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 = "SHA-224" and algType = "HASH" + or + 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 + 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 = "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 = "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 = "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 = "SHA3-512" 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 = "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 = "SHA-512" 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 = "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 = "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 = "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 = "SHA-512" 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 = "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 = "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 = "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 = "SHA3-512" 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 = "SHA-224" and algType = "HASH" + or + name = "hmacwithsha256" and nid = 799 and normalized = "SHA-256" and algType = "HASH" + or + name = "hmacwithsha384" and nid = 800 and normalized = "SHA-384" and algType = "HASH" + or + name = "hmacwithsha512" and nid = 801 and normalized = "SHA-512" and algType = "HASH" + or + name = "hmacwithsha512-224" and nid = 1193 and normalized = "SHA-512-224" and algType = "HASH" + or + 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 + name = "id-aes128-ccm" and + nid = 896 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 = "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 = "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 = "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 = "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 = "AES-256" 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 = "SHA3-224" and algType = "HASH" + or + name = "id-hmacwithsha3-256" and nid = 1103 and normalized = "SHA3-256" and algType = "HASH" + or + name = "id-hmacwithsha3-384" and nid = 1104 and normalized = "SHA3-384" and algType = "HASH" + or + 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 + 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 = "DES" 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-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-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-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-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-40" 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-128" 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-128" 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 = "3DES" 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 = "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-40" 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-40" 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 = "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 = "SHA-256" 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 = "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 = "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 = "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 = "SHA3-512" 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 = "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 = "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 = "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 = "SHA-512-256" 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 = "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 = "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 = "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 = "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 = "SHA-384" 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 = "SHA-512-224" 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 = "SHA-512-256" and + algType = "HASH" + or + name = "sha512withrsaencryption" and + nid = 670 and + normalized = "RSA" and + algType = "ASYMMETRIC_ENCRYPTION" + or + name = "sha512withrsaencryption" and nid = 670 and normalized = "SHA-512" 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/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstanceBase.qll new file mode 100644 index 000000000000..dc49c139cf05 --- /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..7a77a4c3e13e --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll @@ -0,0 +1,5 @@ +import OpenSSLAlgorithmInstanceBase +import CipherAlgorithmInstance +import PaddingAlgorithmInstance +import BlockAlgorithmInstance +import HashAlgorithmInstance 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..4fb4d0818697 --- /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..8fa65860b60c --- /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..ffc9a7c3991e --- /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/HashAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/HashAlgorithmValueConsumer.qll new file mode 100644 index 000000000000..b041b986754c --- /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/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumerBase.qll new file mode 100644 index 000000000000..3f6e2bd4dc89 --- /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..0638595afb88 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll @@ -0,0 +1,5 @@ +import OpenSSLAlgorithmValueConsumerBase +import CipherAlgorithmValueConsumer +import DirectAlgorithmValueConsumer +import PaddingAlgorithmValueConsumer +import HashAlgorithmValueConsumer 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..3f7ce20d6b3a --- /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/CtxFlow.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll new file mode 100644 index 000000000000..88e4a1c378b0 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll @@ -0,0 +1,99 @@ +//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. + * + * 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 { + 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 ctxArgFlowsToCtxArg(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/LibraryDetector.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/LibraryDetector.qll new file mode 100644 index 000000000000..5ff02cd95197 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/LibraryDetector.qll @@ -0,0 +1,7 @@ +import cpp + +predicate isPossibleOpenSSLFunction(Function f) { + isPossibleOpenSSLLocation(f.getADeclarationLocation()) +} + +predicate isPossibleOpenSSLLocation(Location l) { l.toString().toLowerCase().matches("%openssl%") } 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..f53812093c42 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/OpenSSL.qll @@ -0,0 +1,9 @@ +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 +} 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..3e8607ef8ecb --- /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_Initializer initCall | sink.asExpr() = initCall.getOperationSubtypeArg()) + } +} + +module EncValToInitEncArgFlow = DataFlow::Global; + +int getEncConfigValue(Expr e) { + 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() + ) +} + +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_Initializer 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 getOperationSubtypeArg(); + + 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.getOperationSubtypeArg())) + then result = intToCipherOperationSubtype(getEncConfigValue(this.getOperationSubtypeArg())) + else result instanceof Crypto::TUnknownKeyOperationMode + } +} + +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_Initializer { + 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 getOperationSubtypeArg() { + 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 getOperationSubtypeArg() { + 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 getOperationSubtypeArg() { result = this.(Call).getArgument(5) } +} + +class EVPCipherInitializerAlgorithmArgument extends Expr { + EVPCipherInitializerAlgorithmArgument() { + exists(EVP_Cipher_Initializer initCall | this = initCall.getAlgorithmArg()) + } +} + +class EVPCipherInitializerKeyArgument extends Expr { + EVPCipherInitializerKeyArgument() { + exists(EVP_Cipher_Initializer initCall | this = initCall.getKeyArg()) + } +} + +class EVPCipherInitializerIVArgument extends Expr { + EVPCipherInitializerIVArgument() { + 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 new file mode 100644 index 000000000000..b544079579af --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll @@ -0,0 +1,116 @@ +import experimental.quantum.Language +import experimental.quantum.OpenSSL.CtxFlow as CTXFlow +import EVPCipherInitializer +import OpenSSLOperationBase +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers + +private 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()) + } +} + +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 } +// } +// +/** + * 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_Initializer 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) +// 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: 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() { +// 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/EVPHashInitializer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll new file mode 100644 index 000000000000..46d414ece6ce --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll @@ -0,0 +1,17 @@ +import cpp + +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_Initializer { + 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..a187b62a7bdd --- /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_Initializer 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_Initializer 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_Initializer 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 +// } +// } 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..4798f5650a9b --- /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..819e964878c3 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll @@ -0,0 +1,3 @@ +import OpenSSLOperationBase +import EVPCipherOperation +import EVPHashOperation 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..e599ed82169b --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Random.qll @@ -0,0 +1,18 @@ +import cpp +private import experimental.quantum.Language +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 string getGeneratorName() { result = this.(Call).getTarget().getName() } +} diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index 109eb3bf0cc1..b7bf768c3f2e 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -8,6 +8,7 @@ upgrades: upgrades dependencies: codeql/dataflow: ${workspace} codeql/mad: ${workspace} + codeql/quantum: ${workspace} codeql/rangeanalysis: ${workspace} codeql/ssa: ${workspace} codeql/typeflow: ${workspace} diff --git a/cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql b/cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql new file mode 100644 index 000000000000..f741e3c9f947 --- /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/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 diff --git a/java/ql/lib/experimental/quantum/JCA.qll b/java/ql/lib/experimental/quantum/JCA.qll new file mode 100644 index 000000000000..ceca0e45464b --- /dev/null +++ b/java/ql/lib/experimental/quantum/JCA.qll @@ -0,0 +1,1618 @@ +import java +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.controlflow.Dominance + +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) { + algo.toUpperCase() + .matches([ + "AES", "AESWrap", "AESWrapPad", "ARCFOUR", "Blowfish", "ChaCha20", "ChaCha20-Poly1305", + "DES", "DESede", "DESedeWrap", "ECIES", "PBEWith%", "RC2", "RC4", "RC5", "RSA" + ].toUpperCase()) + } + + // TODO: Verify that the CFB% case works correctly + bindingset[mode] + predicate cipher_modes(string mode) { + mode.toUpperCase() + .matches([ + "NONE", "CBC", "CCM", "CFB", "CFB%", "CTR", "CTS", "ECB", "GCM", "KW", "KWP", "OFB", + "OFB%", "PCBC" + ].toUpperCase()) + } + + // TODO: Verify that the OAEPWith% case works correctly + bindingset[padding] + predicate cipher_padding(string padding) { + padding + .toUpperCase() + .matches([ + "NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith%", "PKCS1Padding", + "PKCS5Padding", "SSL3Padding" + ].toUpperCase()) + } + + bindingset[hash] + predicate hash_names(string hash) { + hash.toUpperCase() + .matches(["SHA-%", "SHA3-%", "BLAKE2b%", "BLAKE2s%", "MD5", "RIPEMD160", "Whirlpool"] + .toUpperCase()) + } + + bindingset[kdf] + predicate kdf_names(string kdf) { + kdf.toUpperCase().matches(["PBKDF2With%", "PBEWith%"].toUpperCase()) + } + + bindingset[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) { + name.toUpperCase().matches(["DH", "EDH", "ECDH", "X25519", "X448"].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) + // TODO: add additional + } + + bindingset[name] + 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 + 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 + } + + 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 + */ + 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 MethodCall { + CipherGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") + } + + Expr getAlgorithmArg() { result = this.getArgument(0) } + + Expr getProviderArg() { result = this.getArgument(1) } + } + + // TODO: handle key artifact produced by unwrap + private class CipherOperationCall extends MethodCall { + CipherOperationCall() { + this.getMethod() + .hasQualifiedName("javax.crypto", "Cipher", ["update", "doFinal", "wrap", "unwrap"]) + } + + predicate isIntermediate() { this.getMethod().getName() = "update" } + + Expr getInput() { result = this.getArgument(0) } + + Expr getOutput() { + exists(int outputIndex | this.getMethod().getParameter(outputIndex).getName() = "output" | + result = this.getArgument(outputIndex) + ) + or + this.getMethod().getReturnType().hasName("byte[]") and result = this + } + + DataFlow::Node getMessageArg() { result.asExpr() = this.getInput() } + } + + /** + * Data-flow configuration modelling flow from a cipher string literal to a cipher algorithm consumer. + */ + private module CipherAlgorithmStringToCipherConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral } + + predicate isSink(DataFlow::Node sink) { + sink = any(CipherAlgorithmValueConsumer call).getInputNode() + } + } + + module CipherAlgorithmStringToFetchFlow = + TaintTracking::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 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() } + + 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 CipherStringLiteralAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof CipherStringLiteral + { + CipherAlgorithmValueConsumer consumer; + + CipherStringLiteralAlgorithmInstance() { + CipherAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + } + + CipherAlgorithmValueConsumer getConsumer() { result = consumer } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { + result = this // TODO: provider defaults + } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { + result = this // TODO: provider defaults + } + + override string getRawAlgorithmName() { result = super.getValue() } + + 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 + } + + override string getKeySizeFixed() { + none() // TODO: implement to handle variants such as AES-128 + } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + } + + 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) + ) + } + + 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 getRawHashAlgorithmName() { result = super.getPadding() } + + override Crypto::THashType getHashFamily() { result = hash_name_to_type_known(hashName, _) } + + override int getFixedDigestLength() { exists(hash_name_to_type_known(hashName, result)) } + } + + class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance, + CipherStringLiteralPaddingAlgorithmInstance + { + override Crypto::HashAlgorithmInstance getOAEPEncodingHashAlgorithm() { result = this } + + override Crypto::HashAlgorithmInstance getMGF1HashAlgorithm() { none() } // TODO + } + + /** + * The cipher algorithm argument to a `CipherGetInstanceCall`. + * + * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`. + */ + class CipherGetInstanceAlgorithmArg extends CipherAlgorithmValueConsumer instanceof Expr { + CipherGetInstanceCall call; + + CipherGetInstanceAlgorithmArg() { this = call.getAlgorithmArg() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this + } + } + + /** + * 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().toUpperCase() in [ + "ENCRYPT_MODE", "DECRYPT_MODE", "WRAP_MODE", "UNWRAP_MODE" + ] + } + } + + signature class GetInstanceCallSig instanceof MethodCall; + + signature class InitCallSig instanceof MethodCall; + + signature class UseCallSig instanceof MethodCall { + /** + * Holds if the use is not a final use, such as an `update()` call before `doFinal()` + */ + predicate isIntermediate(); + } + + /** + * 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> + { + 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 + } + } + + class UninitializedFlowState extends InitFlowState, TUninitialized { } + + class InitializedFlowState extends InitFlowState, TInitialized { + Init call; + DataFlow::Node node1; + DataFlow::Node node2; + + InitializedFlowState() { + this = TInitialized(call) and + node2.asExpr() = call.(MethodCall).getQualifier() and + DataFlow::localFlowStep(node1, node2) and + node1 != node2 + } + + Init getInitCall() { result = call } + + DataFlow::Node getFstNode() { result = node1 } + + DataFlow::Node getSndNode() { result = node2 } + } + + 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 + } + + Use getUseCall() { result = call } + + DataFlow::Node getFstNode() { result = node1 } + + DataFlow::Node getSndNode() { result = node2 } + } + + module GetInstanceToInitToUseConfig implements DataFlow::StateConfigSig { + class FlowState = InitFlowState; + + 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) + } + + 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 + ) { + src.getNode().asExpr() = result.(MethodCall).getQualifier() and + sink.getNode().asExpr() = use.(MethodCall).getQualifier() and + GetInstanceToInitToUseFlow::flowPath(src, sink) + } + + 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 CipherFlowAnalysisImpl = + GetInstanceInitUseFlowAnalysis; + + module CipherFlow = CipherFlowAnalysisImpl::GetInstanceToInitToUseFlow; + + 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, _, _) + } + + CipherInitCall getInitCall() { result = CipherFlowAnalysisImpl::getInitFromUse(this, _, _) } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { + result.asExpr() = this.getInitCall().getNonceArg() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result = super.getMessageArg() or + result = CipherFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getMessageArg() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + result.asExpr() = this.getInitCall().getKeyArg() + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result = this.getInstantiationCall().getAlgorithmArg() + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = super.getOutput() + } + } + + /** + * Initialization vectors and other nonce artifacts + */ + abstract class NonceParameterInstantiation extends ClassInstanceExpr { + DataFlow::Node getOutputNode() { result.asExpr() = this } + + abstract DataFlow::Node getInputNode(); + } + + class IvParameterSpecInstance extends NonceParameterInstantiation { + IvParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("javax.crypto.spec", "IvParameterSpec") + } + + override DataFlow::Node getInputNode() { + result.asExpr() = this.(ClassInstanceExpr).getArgument(0) + } + } + + // TODO: this also specifies the tag length for GCM + class GCMParameterSpecInstance extends NonceParameterInstantiation { + GCMParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("javax.crypto.spec", "GCMParameterSpec") + } + + override DataFlow::Node getInputNode() { + result.asExpr() = this.(ClassInstanceExpr).getArgument(1) + } + } + + class IvParameterSpecGetIvCall extends MethodCall { + IvParameterSpecGetIvCall() { + this.getMethod().hasQualifiedName("javax.crypto.spec", "IvParameterSpec", "getIV") + } + } + + 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() } + + DataFlow::Node getOutputNode() { result.asExpr() = this } + } + + 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() + ) + or + exists(KeyAdditionalFlowSteps call | + node1 = call.getInputNode() and + node2 = call.getOutputNode() + ) + } + + class ArtifactAdditionalFlowStep extends AdditionalFlowInputStep { + DataFlow::Node output; + + ArtifactAdditionalFlowStep() { additionalFlowSteps(this, output) } + + override DataFlow::Node getOutput() { result = output } + } + + /** + * 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 + } + + predicate isSink(DataFlow::Node sink) { + exists(CipherInitCall c | c.getModeArg() = sink.asExpr()) + } + } + + module JavaxCipherModeAccessToInitFlow = DataFlow::Global; + + private predicate cipher_mode_str_to_cipher_mode_known( + string mode, Crypto::KeyOperationSubtype cipher_mode + ) { + mode = "ENCRYPT_MODE" and cipher_mode = Crypto::TEncryptMode() + or + mode = "WRAP_MODE" and cipher_mode = Crypto::TWrapMode() + or + mode = "DECRYPT_MODE" and cipher_mode = Crypto::TDecryptMode() + or + mode = "UNWRAP_MODE" and cipher_mode = Crypto::TUnwrapMode() + } + + class CipherInitCall extends MethodCall { + CipherInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "init") } + + /** + * 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) } + + JavaxCryptoCipherOperationModeAccess getModeOrigin() { + exists(DataFlow::Node src, DataFlow::Node sink | + JavaxCipherModeAccessToInitFlow::flow(src, sink) and + src.asExpr() = result and + this.getModeArg() = sink.asExpr() + ) + } + + 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 = Crypto::TUnknownKeyOperationMode() + } + + Expr getKeyArg() { + result = this.getArgument(1) and this.getMethod().getParameterType(1).hasName("Key") + } + + Expr getNonceArg() { + result = this.getArgument(2) and + this.getMethod().getParameterType(2).hasName("AlgorithmParameterSpec") + } + } + + class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer { + CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + } + + /* + * 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(HashAlgorithmValueConsumer consumer | sink = consumer.getInputNode()) + } + } + + module KnownHashAlgorithmLiteralToMessageDigestFlow = + DataFlow::Global; + + class KnownHashAlgorithm extends Crypto::HashAlgorithmInstance instanceof StringLiteral { + HashAlgorithmValueConsumer consumer; + + KnownHashAlgorithm() { + hash_names(this.getValue()) and + KnownHashAlgorithmLiteralToMessageDigestFlow::flow(DataFlow::exprNode(this), + consumer.getInputNode()) + } + + HashAlgorithmValueConsumer getConsumer() { result = consumer } + + override string getRawHashAlgorithmName() { result = this.(StringLiteral).getValue() } + + override Crypto::THashType getHashFamily() { + result = hash_name_to_type_known(this.getRawHashAlgorithmName(), _) + } + + override int getFixedDigestLength() { + exists(hash_name_to_type_known(this.getRawHashAlgorithmName(), result)) + } + } + + module MessageDigestFlowAnalysisImpl = + GetInstanceInitUseFlowAnalysis; + + class MessageDigestGetInstanceAlgorithmValueConsumer extends HashAlgorithmValueConsumer { + MessageDigestGetInstanceCall call; + + MessageDigestGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() } + + MessageDigestGetInstanceCall getInstantiationCall() { result = call } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(KnownHashAlgorithm l | l.getConsumer() = this and result = l) + } + } + + class MessageDigestGetInstanceCall extends MethodCall { + MessageDigestGetInstanceCall() { + this.getCallee().hasQualifiedName("java.security", "MessageDigest", "getInstance") + } + + Expr getAlgorithmArg() { result = this.getArgument(0) } + } + + class DigestCall extends MethodCall { + 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 + 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 DigestHashOperation extends Crypto::HashOperationInstance instanceof DigestCall { + DigestHashOperation() { not super.isIntermediate() } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = super.getDigestArtifactOutput() + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + MessageDigestFlowAnalysisImpl::getInstantiationFromUse(this, _, _) = + result.(MessageDigestGetInstanceAlgorithmValueConsumer).getInstantiationCall() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = super.getInputArg() or + result.asExpr() = + MessageDigestFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getInputArg() + } + } + + /* + * Key Generation + */ + + module KeyGeneratorFlowAnalysisImpl = + GetInstanceInitUseFlowAnalysis; + + module KeyGeneratorFlow = KeyGeneratorFlowAnalysisImpl::GetInstanceToInitToUseFlow; + + abstract class KeyGeneratorParameterSpecClassInstanceExpr extends ClassInstanceExpr { + KeyGeneratorInitCall getAnInitCallUse() { + exists(DataFlow::Node sink | + KeyGeneratorParameterSpecToInitializeFlow::flow(DataFlow::exprNode(this), sink) and + result.getAlgorithmParameterSpecArg() = sink.asExpr() + ) + } + } + + class DHGenParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { + DHGenParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") + } + + Expr getPrimeSizeArg() { result = this.getArgument(0) } + + Expr getExponentSizeArg() { result = this.getArgument(1) } + } + + class DSAParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { + DSAParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("java.security.spec", "DSAParameterSpec") + } + + Expr getPArg() { result = this.getArgument(0) } + + Expr getQArg() { result = this.getArgument(1) } + + Expr getSeedLenArg() { result = this.getArgument(2) } + } + + class ECGenParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { + ECGenParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("java.security.spec", "ECGenParameterSpec") + } + + Expr getCurveNameArg() { result = this.getArgument(0) } + + Expr getRandomNumberGeneratorArg() { result = this.getArgument(1) } + } + + class RSAGenParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { + RSAGenParameterSpecInstance() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("java.security.spec", "RSAGenParameterSpec") + } + + Expr getKeySizeArg() { result = this.getArgument(0) } + } + + private module KeyGeneratorParameterSpecToInitializeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + src.asExpr() instanceof KeyGeneratorParameterSpecClassInstanceExpr + } + + predicate isSink(DataFlow::Node sink) { + exists(KeyGeneratorInitCall c | + c.getKeyType() = Crypto::TAsymmetricKeyType() and + c.getArgument(0) = sink.asExpr() + ) + } + } + + module KeyGeneratorParameterSpecToInitializeFlow = + DataFlow::Global; + + class ECGenParameterSpecClassInstanceExpr extends KeyGeneratorParameterSpecClassInstanceExpr { + ECGenParameterSpecClassInstanceExpr() { + this.(ClassInstanceExpr) + .getConstructedType() + .hasQualifiedName("java.security.spec", "ECGenParameterSpec") + } + + Expr getAlgorithmArg() { result = this.getArgument(0) } + } + + class KeyGenerationAlgorithmValueConsumer extends CipherAlgorithmValueConsumer, + KeyAgreementAlgorithmValueConsumer, EllipticCurveAlgorithmValueConsumer instanceof Expr + { + KeyGeneratorGetInstanceCall instantiationCall; + + 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. + // 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 + exists(KeyGeneratorInitCall initCall, ECGenParameterSpecClassInstanceExpr spec | + KeyGeneratorFlow::flow(DataFlow::exprNode(instantiationCall), + DataFlow::exprNode(initCall.getQualifier())) and + spec.getAnInitCallUse() = initCall and + spec.getAlgorithmArg() = this + ) + } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this or + result.(KeyAgreementStringLiteralAlgorithmInstance).getConsumer() = this or + result.(EllipticCurveStringLiteralInstance).getConsumer() = this + } + + KeyGeneratorGetInstanceCall getInstantiationCall() { result = instantiationCall } + } + + // 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") + } + + Expr getAlgorithmArg() { result = super.getArgument(0) } + } + + class KeyGeneratorInitCall extends MethodCall { + Crypto::TKeyArtifactType keyType; + + 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() + } + + Crypto::TKeyArtifactType getKeyType() { result = keyType } + + Expr getAlgorithmParameterSpecArg() { + result = this.getArgument(0) and + this.getMethod().getParameterType(0).hasName("AlgorithmParameterSpec") + } + + Expr getKeySizeArg() { + result = this.getArgument(0) and + this.getMethod().getParameterType(0) instanceof IntegralType + } + + Expr getRandomnessSourceArg() { + exists(int index | + this.getMethod().getParameterType(index).hasName("SecureRandom") and + result = this.getArgument(index) + ) + } + } + + 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", "genKeyPair"]) and + type instanceof Crypto::TAsymmetricKeyType + } + + predicate isIntermediate() { none() } + + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result.asExpr() = this } + + override Crypto::KeyArtifactType getOutputKeyType() { result = type } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + KeyGeneratorFlowAnalysisImpl::getInstantiationFromUse(this, _, _) = + result.(KeyGenerationAlgorithmValueConsumer).getInstantiationCall() + } + + Crypto::AlgorithmInstance getAKnownAlgorithm() { + result = this.getAnAlgorithmValueConsumer().getAKnownAlgorithmSource() + } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + KeyGeneratorFlowAnalysisImpl::getInitFromUse(this, _, _).getKeySizeArg() = result.asExpr() + } + + 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) + */ + + class KeySpecInstantiation extends ClassInstanceExpr { + KeySpecInstantiation() { + this.getConstructedType() + .hasQualifiedName("javax.crypto.spec", + ["PBEKeySpec", "SecretKeySpec", "PBEKeySpec", "DESedeKeySpec"]) + } + + Expr getPasswordArg() { result = this.getArgument(0) } + } + + class PBEKeySpecInstantiation extends KeySpecInstantiation { + PBEKeySpecInstantiation() { this.getConstructedType().hasName("PBEKeySpec") } + + Expr getSaltArg() { result = this.getArgument(1) } + + Expr getIterationCountArg() { result = this.getArgument(2) } + + Expr getKeyLengthArg() { result = this.getArgument(3) } + } + + module KeySpecInstantiationToGenerateSecretFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + exists(KeySpecInstantiation 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 KeySpecInstantiationToGenerateSecretFlow = + DataFlow::Global; + + module KDFAlgorithmStringToGetInstanceFlow = + DataFlow::Global; + + class DUMMY_UNUSED_METHODCALL extends MethodCall { + DUMMY_UNUSED_METHODCALL() { none() } + } + + module SecretKeyFactoryFlowAnalysisImpl = + GetInstanceInitUseFlowAnalysis; + + module SecretKeyFactoryFlow = SecretKeyFactoryFlowAnalysisImpl::GetInstanceToInitToUseFlow; + + class SecretKeyFactoryGetInstanceCall extends MethodCall { + SecretKeyFactoryGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "SecretKeyFactory", "getInstance") + } + + Expr getAlgorithmArg() { result = this.getArgument(0) } + } + + 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 + { + 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_type_known(this.getRawHashAlgorithmName(), _) + } + + override int getFixedDigestLength() { + exists(hash_name_to_type_known(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 KeyDerivationOperationCall extends Crypto::KeyDerivationOperationInstance instanceof SecretKeyFactoryGenerateSecretCall + { + KeyDerivationOperationCall() { not super.isIntermediate() } + + KeySpecInstantiation getKeySpecInstantiation() { + KeySpecInstantiationToGenerateSecretFlow::flow(DataFlow::exprNode(result), + DataFlow::exprNode(super.getKeySpecArg())) + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result.(SecretKeyFactoryKDFAlgorithmValueConsumer).getInstantiation() = + SecretKeyFactoryFlowAnalysisImpl::getInstantiationFromUse(this, _, _) + } + + override Crypto::ConsumerInputDataFlowNode getSaltConsumer() { + result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getSaltArg() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = this.getKeySpecInstantiation().getPasswordArg() + } + + override Crypto::ConsumerInputDataFlowNode getIterationCountConsumer() { + result.asExpr() = + this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getIterationCountArg() + } + + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result.asExpr() = this } + + override Crypto::ConsumerInputDataFlowNode getOutputKeySizeConsumer() { + result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getKeyLengthArg() + } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getKeyLengthArg() + } + + override string getKeySizeFixed() { none() } + + override string getOutputKeySizeFixed() { none() } + + 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 string literal to a key agreement algorithm consumer. + */ + private module KeyAgreementAlgorithmStringToConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeyAgreementStringLiteral } + + predicate isSink(DataFlow::Node sink) { + sink = any(KeyAgreementAlgorithmValueConsumer consumer).getInputNode() + } + } + + module KeyAgreementAlgorithmStringToConsumerFlow = + TaintTracking::Global; + + class KeyAgreementInitCall extends MethodCall { + KeyAgreementInitCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "init") + } + + Expr getServerKeyArg() { result = this.getArgument(0) } + } + + class KeyAgreementGetInstanceCall extends MethodCall { + KeyAgreementGetInstanceCall() { + this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "getInstance") + } + + Expr getAlgorithmArg() { result = super.getArgument(0) } + } + + private class KeyAgreementGetInstanceAlgorithmArgValueConsumer extends KeyAgreementAlgorithmValueConsumer + { + KeyAgreementGetInstanceCall 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 + { + KeyAgreementAlgorithmValueConsumer consumer; + + KeyAgreementStringLiteralAlgorithmInstance() { + KeyAgreementAlgorithmStringToConsumerFlow::flow(DataFlow::exprNode(this), + consumer.getInputNode()) + } + + 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() + } + + 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 } + } + + /* + * 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; + + 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) { + 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(EllipticCurveAlgorithmValueConsumer consumer | sink = consumer.getInputNode()) + } + } + + module EllipticCurveStringToConsumerFlow = DataFlow::Global; + + class EllipticCurveStringLiteral extends StringLiteral { + EllipticCurveStringLiteral() { elliptic_curve_names(this.getValue()) } + } + + class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance instanceof EllipticCurveStringLiteral + { + EllipticCurveAlgorithmValueConsumer consumer; + + EllipticCurveStringLiteralInstance() { + EllipticCurveStringToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) + } + + 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 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 new file mode 100644 index 000000000000..8e00f34f02b1 --- /dev/null +++ b/java/ql/lib/experimental/quantum/Language.qll @@ -0,0 +1,218 @@ +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() = "" } +} + +/** + * A dummy location which is used when something doesn't have a location in + * the source code but needs to have a `Location` associated with it. There + * may be several distinct kinds of unknown locations. For example: one for + * expressions, one for statements and one for other program elements. + */ +private class UnknownDefaultLocation extends UnknownLocation { + UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) } +} + +module CryptoInput implements InputSig { + class DataFlowNode = DataFlow::Node; + + class LocatableElement = Language::Element; + + 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() + } + + predicate artifactOutputFlowsToGenericInput( + DataFlow::Node artifactOutput, DataFlow::Node otherInput + ) { + ArtifactFlow::flow(artifactOutput, otherInput) + } +} + +/** + * Instantiate the model + */ +module Crypto = CryptographyBase; + +/** + * Definitions of various generic data sources + */ +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) { + GenericDataSourceFlow::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 + } + + override DataFlow::Node getOutputNode() { result.asExpr() = this } + + override predicate flowsTo(Crypto::FlowAwareElement other) { + GenericDataSourceFlow::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) { + GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode()) + } + + override string getAdditionalDescription() { result = this.toString() } +} + +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.getValue() != "EC" + } + + override DataFlow::Node getOutputNode() { result.asExpr() = this } + + override predicate flowsTo(Crypto::FlowAwareElement other) { + // TODO: separate config to avoid blowing up data-flow analysis + GenericDataSourceFlow::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) + */ +abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance { + override DataFlow::Node getOutputNode() { result.asExpr() = this } +} + +class SecureRandomnessInstance extends RandomnessInstance { + RandomDataSource source; + + SecureRandomnessInstance() { + this = source.getOutput() and + source.getSourceOfRandomness() instanceof SecureRandomNumberGenerator + } + + override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() } +} + +class InsecureRandomnessInstance extends RandomnessInstance { + RandomDataSource source; + + InsecureRandomnessInstance() { + any(InsecureRandomnessSource src).asExpr() = this and source.getOutput() = this + } + + override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() } +} + +/** + * Artifact output to node input configuration + */ +abstract class AdditionalFlowInputStep extends DataFlow::Node { + abstract DataFlow::Node getOutput(); + + final DataFlow::Node getInput() { result = this } +} + +module ArtifactFlow = DataFlow::Global; + +/** + * Generic data source to node input configuration + */ +module GenericDataSourceFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::GenericSourceInstance i).getOutputNode() + } + + predicate isSink(DataFlow::Node sink) { + sink = any(Crypto::FlowAwareElement other).getInputNode() + } + + predicate isBarrierOut(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getInputNode() + } + + predicate isBarrierIn(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getOutputNode() + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node1.(AdditionalFlowInputStep).getOutput() = node2 + or + exists(MethodCall m | + m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and + node1.asExpr() = m.getQualifier() and + node2.asExpr() = m + ) + } +} + +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 + or + exists(MethodCall m | + m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and + node1.asExpr() = m.getQualifier() and + node2.asExpr() = m + ) + } +} + +module GenericDataSourceFlow = TaintTracking::Global; + +// Import library-specific modeling +import JCA diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 1acaa5702377..eeaa063e4738 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -8,6 +8,7 @@ upgrades: upgrades dependencies: codeql/dataflow: ${workspace} codeql/mad: ${workspace} + codeql/quantum: ${workspace} codeql/rangeanalysis: ${workspace} codeql/regex: ${workspace} codeql/threat-models: ${workspace} diff --git a/java/ql/src/experimental/quantum/Analysis/ArtifactReuse.qll b/java/ql/src/experimental/quantum/Analysis/ArtifactReuse.qll new file mode 100644 index 000000000000..88598e615896 --- /dev/null +++ b/java/ql/src/experimental/quantum/Analysis/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/Analysis/InsecureNonceSource.ql b/java/ql/src/experimental/quantum/Analysis/InsecureNonceSource.ql new file mode 100644 index 000000000000..f00621d4d2b6 --- /dev/null +++ b/java/ql/src/experimental/quantum/Analysis/InsecureNonceSource.ql @@ -0,0 +1,22 @@ +/** + * @name Insecure nonce at a cipher operation + * @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 + * @tags quantum + * experimental + */ + +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/Analysis/KnownWeakKDFIterationCount.ql b/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql new file mode 100644 index 000000000000..2dd5b0b006be --- /dev/null +++ b/java/ql/src/experimental/quantum/Analysis/KnownWeakKDFIterationCount.ql @@ -0,0 +1,20 @@ +/** + * @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 + * @problem.severity error + * @precision high + * @tags quantum + * experimental + */ + +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/Analysis/ReusedNonce.ql b/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql new file mode 100644 index 000000000000..ed2872bb67e1 --- /dev/null +++ b/java/ql/src/experimental/quantum/Analysis/ReusedNonce.ql @@ -0,0 +1,17 @@ +/** + * @name Reuse of cryptographic nonce + * @description Reuse of nonce in cryptographic operations can lead to vulnerabilities. + * @id java/quantum/reused-nonce + * @kind problem + * @problem.severity error + * @precision medium + * @tags quantum + * experimental + */ + +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/Analysis/UnknownKDFIterationCount.ql b/java/ql/src/experimental/quantum/Analysis/UnknownKDFIterationCount.ql new file mode 100644 index 000000000000..21bca11cc1af --- /dev/null +++ b/java/ql/src/experimental/quantum/Analysis/UnknownKDFIterationCount.ql @@ -0,0 +1,24 @@ +/** + * @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 +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() diff --git a/java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql b/java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql new file mode 100644 index 000000000000..7291ea554a58 --- /dev/null +++ b/java/ql/src/experimental/quantum/Examples/BrokenCrypto.ql @@ -0,0 +1,75 @@ +/** + * @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::AlgorithmNode 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() diff --git a/java/ql/src/experimental/quantum/Examples/TestAESGCMNonce.ql b/java/ql/src/experimental/quantum/Examples/TestAESGCMNonce.ql new file mode 100644 index 000000000000..096cfa822161 --- /dev/null +++ b/java/ql/src/experimental/quantum/Examples/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/Examples/TestCipher.ql b/java/ql/src/experimental/quantum/Examples/TestCipher.ql new file mode 100644 index 000000000000..2b1d6ebcf06e --- /dev/null +++ b/java/ql/src/experimental/quantum/Examples/TestCipher.ql @@ -0,0 +1,18 @@ +/** + * @name "Key operation slice table demo query" + */ + +import experimental.quantum.Language + +from + Crypto::KeyOperationNode op, Crypto::KeyOperationAlgorithmNode a, + Crypto::ModeOfOperationAlgorithmNode m, Crypto::PaddingAlgorithmNode p, + Crypto::NonceArtifactNode nonce, Crypto::KeyArtifactNode k +where + a = op.getAKnownAlgorithm() and + m = a.getModeOfOperation() and + p = a.getPaddingAlgorithm() and + nonce = op.getANonce() and + k = op.getAKey() +select op, op.getKeyOperationSubtype(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), p, + p.getRawAlgorithmName(), nonce, k diff --git a/java/ql/src/experimental/quantum/Examples/TestHash.ql b/java/ql/src/experimental/quantum/Examples/TestHash.ql new file mode 100644 index 000000000000..b319c95a62b1 --- /dev/null +++ b/java/ql/src/experimental/quantum/Examples/TestHash.ql @@ -0,0 +1,9 @@ +/** + * @name "Hash operation slice table demo query" + */ + +import experimental.quantum.Language + +from Crypto::HashOperationNode op, Crypto::HashAlgorithmNode alg +where alg = op.getAKnownAlgorithm() +select op, op.getDigest(), alg, alg.getRawAlgorithmName() 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..b4d0b908bbe1 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricAlgorithm.ql @@ -0,0 +1,15 @@ +/** + * @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-algorithm + * @kind table + * @tags quantum + * experimental + */ + +import java +import experimental.quantum.Language + +from Crypto::AlgorithmNode a +where Crypto::isKnownAsymmetricAlgorithm(a) +select a, a.getAlgorithmName() 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..69643d92cd24 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricCipherAlgorithm.ql @@ -0,0 +1,15 @@ +/** + * @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 +import experimental.quantum.Language + +from Crypto::KeyOperationAlgorithmNode a +where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::AsymmetricCipherAlgorithm +select a, 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..224ee1161fb0 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownAsymmetricOperationAlgorithm.ql @@ -0,0 +1,15 @@ +/** + * @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 +import experimental.quantum.Language + +from Crypto::OperationNode op, Crypto::AlgorithmNode a +where a = op.getAKnownAlgorithm() and Crypto::isKnownAsymmetricAlgorithm(a) +select op, 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..da3371a59b34 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownCipherAlgorithm.ql @@ -0,0 +1,18 @@ +/** + * @name Known cipher algorithms (slice) + * @description Outputs known cipher algorithms. + * @id java/quantum/slices/known-cipher-algorithm + * @kind table + * @tags quantum + * experimental + */ + +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, 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..ca72e2de2517 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownEllipticCurveAlgorithm.ql @@ -0,0 +1,14 @@ +/** + * @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, 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..042f3b3dc915 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingAlgorithm.ql @@ -0,0 +1,14 @@ +/** + * @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, 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..b004fae7fbbb --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperation.ql @@ -0,0 +1,14 @@ +/** + * @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 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..38d197bb7dd8 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownHashingOperationAlgorithm.ql @@ -0,0 +1,15 @@ +/** + * @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 +import experimental.quantum.Language + +from Crypto::OperationNode op, Crypto::HashAlgorithmNode a +where a = op.getAKnownAlgorithm() +select op, 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..5a9744c966b0 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationAlgorithm.ql @@ -0,0 +1,14 @@ +/** + * @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, 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..95623ec7a1e8 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperation.ql @@ -0,0 +1,14 @@ +/** + * @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 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..efbfd9ed8658 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownKeyDerivationOperationAlgorithm.ql @@ -0,0 +1,15 @@ +/** + * @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 +import experimental.quantum.Language + +from Crypto::OperationNode op, Crypto::KeyDerivationAlgorithmNode a +where a = op.getAKnownAlgorithm() +select op, 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..e4a8d3ff8679 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/KnownSymmetricCipherAlgorithm.ql @@ -0,0 +1,15 @@ +/** + * @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 +import experimental.quantum.Language + +from Crypto::KeyOperationAlgorithmNode a +where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::SymmetricCipherAlgorithm +select 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..2b81cf22dba5 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/LikelyCryptoAPIFunction.ql @@ -0,0 +1,20 @@ +/** + * @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 +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..8469924a8501 --- /dev/null +++ b/java/ql/src/experimental/quantum/InventorySlices/UnknownOperationAlgorithm.ql @@ -0,0 +1,26 @@ +/** + * @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 +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() diff --git a/java/ql/src/experimental/quantum/PrintCBOMGraph.ql b/java/ql/src/experimental/quantum/PrintCBOMGraph.ql new file mode 100644 index 000000000000..bc79d5d4ce6a --- /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/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll new file mode 100644 index 000000000000..69a4fd75acfa --- /dev/null +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -0,0 +1,2596 @@ +/** + * A language-independent library for reasoning about cryptography. + */ + +import codeql.util.Location +import codeql.util.Either + +signature module InputSig { + class LocatableElement { + Location getLocation(); + + string toString(); + } + + class DataFlowNode { + Location getLocation(); + + string toString(); + } + + class UnknownLocation instanceof Location; + + string locationToFileBaseNameAndLineNumberString(Location location); + + LocatableElement dfn_to_element(DataFlowNode node); + + predicate artifactOutputFlowsToGenericInput( + DataFlowNode artifactOutput, DataFlowNode otherFlowAwareInput + ); +} + +module CryptographyBase Input> { + final class LocatableElement = Input::LocatableElement; + + final class UnknownLocation = Input::UnknownLocation; + + final class DataFlowNode = Input::DataFlowNode; + + class ConsumerInputDataFlowNode extends DataFlowNode { + ConsumerElement getConsumer() { result.getInputNode() = this } + } + + class ArtifactOutputDataFlowNode extends DataFlowNode { + OutputArtifactInstance getArtifact() { result.getOutputNode() = this } + } + + final class UnknownPropertyValue extends string { + 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 + "," + Input::locationToFileBaseNameAndLineNumberString(location) + + ")" + ) + | + parsed + ), "," + ) + } + + 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 isPassthroughNodeWithSource(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 = Input::locationToFileBaseNameAndLineNumberString(node.getLocation()) // 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) { + key = "semmle.label" 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 + } + + /** + * 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 { + /** + * Gets the output node for this element, which should usually be the same as `this`. + */ + abstract DataFlowNode getOutputNode(); + + /** + * 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 ConsumerInputDataFlowNode getInputNode(); + + /** + * 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 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. + */ + abstract predicate flowsTo(FlowAwareElement other); + } + + /** + * An element that represents a _known_ cryptographic asset with a determinable value OR an artifact. + * + * 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 } + } + + /** + * An element that represents a _known_ cryptographic operation. + */ + abstract class OperationInstance extends KnownElement { + /** + * Gets the consumers of algorithm values associated with this operation. + */ + abstract AlgorithmValueConsumer getAnAlgorithmValueConsumer(); + } + + /** + * An element that represents a _known_ cryptographic algorithm. + */ + abstract class AlgorithmInstance extends KnownElement { } + + /** + * 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 GenericSourceInstance extends FlowAwareElement { + final override ConsumerInputDataFlowNode getInputNode() { none() } + + abstract string getInternalType(); + + string getAdditionalDescription() { none() } + } + + /** + * 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" } + } + + /** + * 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? + } + + /** + * 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? + } + + /** + * 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? + } + + /** + * 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? + } + + /** + * 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 `GenericSourceInstance` 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 { + abstract KnownElement getAKnownSource(); + + override predicate flowsTo(FlowAwareElement other) { none() } + + override DataFlowNode getOutputNode() { none() } + + final GenericSourceInstance getAGenericSource() { + result.flowsTo(this) and not result = this.getAKnownSource() + } + + final GenericSourceNode getAGenericSourceNode() { + result.asElement() = this.getAGenericSource() + } + + final NodeBase getAKnownSourceNode() { result.asElement() = this.getAKnownSource() } + + final LocatableElement getASource() { + 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. + * Model `getAKnownAlgorithmSource()` instead, which is equivalent but correctly typed. + */ + final override KnownElement getAKnownSource() { result = this.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 + } + + /** + * 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. + * + * 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 { + /** + * Use `getAKnownArtifactSource() instead. The behaviour of these two predicates is equivalent. + */ + final override KnownElement getAKnownSource() { result = this.getAKnownArtifactSource() } + + final ArtifactInstance getAKnownArtifactSource() { result.flowsTo(this) } + } + + /** + * 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. + * + * 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 { + final override predicate isConsumerArtifact() { any() } + } + + final private class NonceArtifactConsumer extends ArtifactConsumerAndInstance { + ConsumerInputDataFlowNode inputNode; + + NonceArtifactConsumer() { + exists(KeyOperationInstance op | inputNode = op.getNonceConsumer()) and + this = Input::dfn_to_element(inputNode) + } + + final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } + } + + final private class MessageArtifactConsumer extends ArtifactConsumerAndInstance { + ConsumerInputDataFlowNode inputNode; + + MessageArtifactConsumer() { + ( + exists(KeyOperationInstance op | inputNode = op.getInputConsumer()) + or + 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) + } + + final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } + } + + final private class SaltArtifactConsumer extends ArtifactConsumerAndInstance { + ConsumerInputDataFlowNode inputNode; + + SaltArtifactConsumer() { + exists(KeyDerivationOperationInstance op | inputNode = op.getSaltConsumer()) and + this = Input::dfn_to_element(inputNode) + } + + final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } + } + + /** + * 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() } + + override ConsumerInputDataFlowNode getInputNode() { none() } + + final override predicate flowsTo(FlowAwareElement other) { + Input::artifactOutputFlowsToGenericInput(this.getOutputNode(), other.getInputNode()) + } + } + + /** + * An artifact representing a random number generator's output. + */ + abstract class RandomNumberGenerationInstance extends OutputArtifactInstance { + abstract string getGeneratorName(); + } + + /** + * 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() } + } + + /** + * 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. + */ + 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 + 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 private class KeyArtifactInstance extends ArtifactInstance { + abstract KeyArtifactType getKeyType(); + } + + 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() } + } + + final class KeyArtifactConsumer extends KeyArtifactInstance, ArtifactConsumerAndInstance { + ConsumerInputDataFlowNode inputNode; + + // TODO: key type hint? e.g. hint: private || public + KeyArtifactConsumer() { + ( + 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) + } + + 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 } + } + + /** + * 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. + */ + module KeyOpAlg { + /** + * An algorithm used in key operations. + */ + 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() + } + } + + 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 + ) { + 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 } + } + + class AsymmetricCipherAlgorithm extends Algorithm, TAsymmetricCipher { + TAsymmetricCipherType type; + + AsymmetricCipherAlgorithm() { this = TAsymmetricCipher(type) } + + TAsymmetricCipherType 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 subtype of this key operation, distinguishing operations such as + * encryption, decryption, signing, verification, key wrapping, and key unwrapping. + */ + abstract KeyOperationSubtype getKeyOperationSubtype(); + + /** + * 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 getKeyConsumer(); + + /** + * 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 getNonceConsumer(); + + /** + * 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 semantically meaningful output should result in an artifact. + * 2. Discarded or transient intermediate values should not be artifacts. + */ + abstract ArtifactOutputDataFlowNode getOutputArtifact(); + } + + /** + * 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 algorithm name as provided in source, e.g., "AES/CBC/PKCS7Padding". + * This name is not parsed or normalized. + */ + abstract string getRawAlgorithmName(); + + /** + * 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 KeyOpAlg::Algorithm getAlgorithmType(); + + /** + * Gets the mode of operation, such as "CBC", "GCM", or "ECB". + * + * 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, such as "PKCS7", "OAEP", or "NoPadding". + * + * 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()`. + * + * 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(); + + /** + * 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 = + 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". + * + * 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 TBlockCipherModeOfOperationType 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 PaddingAlgorithmInstance extends AlgorithmInstance { + /** + * 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 OAEPPaddingAlgorithmInstance extends PaddingAlgorithmInstance { + OAEPPaddingAlgorithmInstance() { this.getPaddingType() instanceof OAEP } + + /** + * Gets the hash algorithm used in this padding scheme. + */ + 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 + TCMAC() 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 ConsumerInputDataFlowNode getMessageConsumer(); + + /** + * Gets the key used in this operation. + */ + abstract ConsumerInputDataFlowNode 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 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 getRawEllipticCurveName(); + + abstract TEllipticCurveType getEllipticCurveType(); + + abstract string getKeySize(); + + /** + * 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. + */ + string getParsedEllipticCurveName() { result = this.getRawEllipticCurveName() } + } + + abstract class HashOperationInstance extends OperationInstance { + abstract ArtifactOutputDataFlowNode getOutputArtifact(); + + abstract ConsumerInputDataFlowNode getInputConsumer(); + } + + abstract class HashAlgorithmInstance extends AlgorithmInstance { + /** + * 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 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 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 { + abstract string getKeyCreationTypeDescription(); + + /** + * Gets the key artifact produced by this operation. + */ + abstract ArtifactOutputDataFlowNode getOutputKeyArtifact(); + + /** + * Gets the key artifact type produced. + */ + abstract KeyArtifactType getOutputKeyType(); + + // Defaults or fixed values + string getKeySizeFixed() { none() } + + // 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 + string getIterationCountFixed() { none() } + + string getOutputKeySizeFixed() { none() } + + // 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 + SCRYPT() 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 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" } + } + + abstract class KeyLoadOperationInstance extends KeyCreationOperationInstance { + 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(); + + /** + * 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; + + 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> + { + class Union extends LocatableElement { + Union() { + // Either an AlgorithmInstance + 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(GenericSourceInstance src | + src = this.(AlgorithmValueConsumer).getAGenericSource() and src != this + ) + } + + Alg asAlg() { result = this } + + AlgorithmValueConsumer asAVC() { result = this and not this instanceof Alg } + } + } + + private predicate isHashAVC(AlgorithmValueConsumer avc) { + exists(HashOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) or + exists(HMACAlgorithmInstance alg | avc = alg.getAConsumer()) + } + + private predicate isKeyOperationAlgorithmAVC(AlgorithmValueConsumer avc) { + exists(KeyOperationInstance op | op.getAnAlgorithmValueConsumer() = avc) + } + + private predicate isMACAVC(AlgorithmValueConsumer 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) + } + + 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 + ) + } + + final private class KeyOperationAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; + + final private class HashAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; + + final private class MACAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; + + final private class KeyDerivationAlgorithmInstanceOrValueConsumer = + AlgorithmInstanceOrValueConsumer::Union; + + 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(HashOutputArtifactInstance 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 + TSaltInput(SaltArtifactConsumer e) or + TRandomNumberGeneration(RandomNumberGenerationInstance e) { e.flowsTo(_) } or + // 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 + 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(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` + // For example, a string literal "AES" could be a generic constant but also an algorithm instance. + // + // Therefore, only create generic nodes tied to instances which are not also a `KnownElement`... + not e instanceof KnownElement and + // ... and that flow to other elements + 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. + * + * 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 { + /** + * Returns a string representation of this node. + */ + string toString() { result = this.getInternalType() } + + /** + * 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. + */ + Location getLocation() { result = this.asElement().getLocation() } + + /** + * Returns the child of this node with the given edge name. + * + * 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 overriden by derived classes to construct the graph of cryptographic operations. + */ + 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(); + + /** + * If this predicate holds, this node should be excluded from the graph. + */ + predicate isExcludedFromGraph() { none() } + } + + /** + * A generic source node is a source of data that is not resolvable to a specific asset. + */ + final class GenericSourceNode extends NodeBase, TGenericSourceNode { + GenericSourceInstance 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() + } + + override predicate isExcludedFromGraph() { + // 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 isCandidateAlgorithmNode(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 `isCandidateAlgorithmNode`. + */ + AlgorithmNode getAKnownAlgorithm() { + result = + this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() and + this.isCandidateAlgorithmNode(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. + */ + abstract class ArtifactNode extends NodeBase { + /** + * Gets the `ArtifactNode` or `GenericSourceNode` node that is the data source for this artifact. + */ + 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. + * + * This predicate is equivalent to `getSourceArtifact().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" } // TODO: revisit why this exists + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] + 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 input. + */ + final class NonceArtifactNode extends ArtifactNode, TNonceInput { + NonceArtifactConsumer instance; + + NonceArtifactNode() { this = TNonceInput(instance) } + + final override string getInternalType() { result = "Nonce" } + + override LocatableElement asElement() { result = instance } + } + + /** + * A message or plaintext/ciphertext input. + */ + final class MessageArtifactNode extends ArtifactNode, TMessageInput { + MessageArtifactConsumer instance; + + MessageArtifactNode() { this = TMessageInput(instance) } + + final override string getInternalType() { result = "Message" } + + override LocatableElement asElement() { result = instance } + } + + /** + * 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 } + } + + /** + * The base class for output nodes from key operations. + * + * This class represents the output of key generation, key derivation, encryption, decryption, signing, and verification. + */ + class KeyOperationOutputNode extends ArtifactNode, TKeyOperationOutput { + KeyOperationOutputArtifactInstance instance; + + KeyOperationOutputNode() { this = TKeyOperationOutput(instance) } + + final override string getInternalType() { result = "KeyOperationOutput" } + + override LocatableElement asElement() { result = instance } + + override string getSourceNodeRelationship() { none() } + } + + /** + * A source of random number generation. + */ + final class RandomNumberGenerationNode extends ArtifactNode, TRandomNumberGeneration { + RandomNumberGenerationInstance instance; + + RandomNumberGenerationNode() { this = TRandomNumberGeneration(instance) } + + final override string getInternalType() { result = "RandomNumberGeneration" } + + 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() + } + } + + /** + * 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. + */ + final class KeyArtifactNode extends ArtifactNode, TKey { + KeyArtifactInstance instance; + + KeyArtifactNode() { this = TKey(instance) } + + final override string getInternalType() { result = "Key" } + + override LocatableElement asElement() { result = instance } + + NodeBase getAKnownAlgorithmOrGenericSourceNode() { + result = this.getAKnownAlgorithm() or + result = + instance + .(KeyCreationOperationInstance) + .getAnAlgorithmValueConsumer() + .getAGenericSourceNode() + } + + KeyCreationCandidateAlgorithmNode getAKnownAlgorithm() { + result = + instance.(KeyCreationOperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [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 + ) + } + + 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() + } + + override string getSourceNodeRelationship() { + instance.isConsumerArtifact() and + result = "Source" + } + } + + /** + * A digest artifact produced by a hash operation. + */ + final class DigestArtifactNode extends ArtifactNode, TDigest { + HashOutputArtifactInstance instance; + + DigestArtifactNode() { this = TDigest(instance) } + + final override string getInternalType() { result = "Digest" } + + 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; + + 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. + */ + final class MACOperationNode extends OperationNode, TMACOperation { + MACOperationInstance instance; + + MACOperationNode() { this = TMACOperation(instance) } + + final override string getInternalType() { result = "MACOperation" } + + override LocatableElement asElement() { result = instance } + + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { + node instanceof MACAlgorithmNode + } + + MessageArtifactNode getAMessage() { + result.asElement() = instance.getMessageConsumer().getConsumer() + } + + KeyArtifactNode getAKey() { result.asElement() = instance.getKeyConsumer().getConsumer() } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + 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" + } + + 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 + } + } + + 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() + } + + 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 + } + } + + 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; + + KeyGenerationOperationNode() { keyGenInstance = instance } + + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { + node instanceof KeyCreationCandidateAlgorithmNode + } + + override NodeBase getChild(string key) { + result = super.getChild(key) + or + // [ALWAYS_KNOWN] + key = "Output" and + result = this.getOutputKeyArtifact() + } + } + + class KeyDerivationOperationNode extends KeyCreationOperationNode { + KeyDerivationOperationInstance kdfInstance; + + KeyDerivationOperationNode() { kdfInstance = instance } + + 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 isCandidateAlgorithmNode(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 = "DefaultIterations" and + value = kdfInstance.getIterationCountFixed() and + location = this.getLocation() + or + // [ONLY_KNOWN] + 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) + } + } + + class KeyDerivationAlgorithmNode extends AlgorithmNode, TKeyDerivationAlgorithm { + KeyDerivationAlgorithmInstanceOrValueConsumer instance; + + KeyDerivationAlgorithmNode() { this = TKeyDerivationAlgorithm(instance) } + + final override string getInternalType() { result = "KeyDerivationAlgorithm" } + + override LocatableElement asElement() { result = instance } + + final override string getRawAlgorithmName() { + result = instance.asAlg().getRawKDFAlgorithmName() + } + + override string getAlgorithmName() { result = this.getRawAlgorithmName() } // TODO: standardize? + } + + /** + * 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 + // [KNOWN_OR_UNKNOWN] + key = "PRF" and + if exists(this.getHMACAlgorithm()) then result = this.getHMACAlgorithm() else result = this + } + } + + /** + * scrypt key derivation function + */ + class ScryptAlgorithmNode extends KeyDerivationAlgorithmNode { + ScryptAlgorithmInstance scryptInstance; + + ScryptAlgorithmNode() { scryptInstance = instance.asAlg() } + + /** + * Gets the iteration count (`N`) argument + */ + GenericSourceNode get_N() { none() } // TODO + + /** + * Gets the block size (`r`) argument + */ + GenericSourceNode get_r() { none() } // TODO + + /** + * Gets the parallelization factor (`p`) argument + */ + GenericSourceNode get_p() { none() } // TODO + } + + /** + * 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() + + /** + * 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() + } + } + + /** + * 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. + */ + class KeyOperationNode extends OperationNode, TKeyOperation { + KeyOperationInstance instance; + + KeyOperationNode() { this = TKeyOperation(instance) } + + final KeyOperationSubtype getKeyOperationSubtype() { + result = instance.getKeyOperationSubtype() + } + + override LocatableElement asElement() { result = instance } + + override string getInternalType() { result = "KeyOperation" } + + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { + node instanceof KeyOperationAlgorithmNode + } + + NonceArtifactNode getANonce() { result.asElement() = instance.getNonceConsumer().getConsumer() } + + MessageArtifactNode getAnInputArtifact() { + result.asElement() = instance.getInputConsumer().getConsumer() + } + + 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] - 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 = "Input" and + if exists(this.getAnInputArtifact()) + then result = this.getAnInputArtifact() + else result = this + or + // [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 + or + // [KNOWN_OR_UNKNOWN] + key = "Key" and + if exists(this.getAKey()) then result = this.getAKey() else result = this + } + } + + class CipherOperationNode extends KeyOperationNode { + string nodeName; + + CipherOperationNode() { + this.getKeyOperationSubtype() = TEncryptMode() and nodeName = "EncryptOperation" + or + 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 + */ + 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 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`. + */ + TBlockCipherModeOfOperationType getModeType() { result = instance.getModeType() } + + bindingset[type] + final private predicate modeToNameMapping(TBlockCipherModeOfOperationType type, string name) { + type = ECB() and name = "ECB" + or + type = CBC() and name = "CBC" + or + type = GCM() and name = "GCM" + or + type = CTR() and name = "CTR" + or + type = XTS() and name = "XTS" + or + type = CCM() and name = "CCM" + or + type = SIV() and name = "SIV" + or + type = OCB() and name = "OCB" + or + type = CFB() and name = "CFB" + or + type = OFB() and name = "OFB" + } + + override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) } + } + + 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 + 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] + final private predicate paddingToNameMapping(TPaddingType type, string name) { + type = ANSI_X9_23() and name = "ANSI_X9_23" + or + type = NoPadding() and name = "NoPadding" + or + type = OAEP() and name = "OAEP" + or + type = PKCS1_v1_5() and name = "PKCS1_v1_5" + or + type = PKCS7() and name = "PKCS7" + or + type = PSS() and name = "PSS" + } + + override string getAlgorithmName() { this.paddingToNameMapping(this.getPaddingType(), result) } + + override string getRawAlgorithmName() { result = instance.getRawPaddingAlgorithmName() } + } + + class OAEPPaddingAlgorithmNode extends PaddingAlgorithmNode { + override OAEPPaddingAlgorithmInstance instance; + + OAEPPaddingAlgorithmNode() { this = TPaddingAlgorithm(instance) } + + HashAlgorithmNode getOAEPEncodingHashAlgorithm() { + result.asElement() = instance.getOAEPEncodingHashAlgorithm() + } + + HashAlgorithmNode getMGF1HashAlgorithm() { + result.asElement() = instance.getMGF1HashAlgorithm() + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] + edgeName = "MD" and + if exists(this.getOAEPEncodingHashAlgorithm()) + then result = this.getOAEPEncodingHashAlgorithm() + else result = this + or + // [KNOWN_OR_UNKNOWN] + edgeName = "MGF1Hash" and + if exists(this.getMGF1HashAlgorithm()) + then result = this.getMGF1HashAlgorithm() + else result = this + } + } + + class KeyOperationAlgorithmNode extends AlgorithmNode, TKeyOperationAlgorithm { + KeyOperationAlgorithmInstanceOrValueConsumer instance; + + KeyOperationAlgorithmNode() { this = TKeyOperationAlgorithm(instance) } + + override LocatableElement asElement() { result = instance } + + override string getInternalType() { result = "KeyOperationAlgorithm" } + + final KeyOpAlg::CipherStructureType getSymmetricCipherStructure() { + KeyOpAlg::symmetric_cipher_to_name_and_structure(this.getAlgorithmType() + .(KeyOpAlg::SymmetricCipherAlgorithm) + .getType(), _, result) + } + + final override string getAlgorithmName() { + KeyOpAlg::type_to_name(this.getAlgorithmType(), result) + } + + final override string getRawAlgorithmName() { result = instance.asAlg().getRawAlgorithmName() } + + /** + * Gets the key size variant of this algorithm in bits, e.g., 128 for "AES-128". + */ + 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. + */ + GenericSourceNode getKeySize() { + result = instance.asAlg().getKeySizeConsumer().getConsumer().getAGenericSourceNode() + } + + /** + * Gets the type of this key operation algorithm, e.g., "SymmetricEncryption(_)" or "" + */ + 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". + */ + ModeOfOperationAlgorithmNode getModeOfOperation() { + result.asElement() = instance.asAlg().getModeOfOperationAlgorithm() + } + + /** + * Gets the padding scheme of this cipher, e.g., "PKCS7" or "NoPadding". + */ + PaddingAlgorithmNode getPaddingAlgorithm() { + result.asElement() = instance.asAlg().getPaddingAlgorithm() + } + + override NodeBase getChild(string edgeName) { + result = super.getChild(edgeName) + or + // [KNOWN_OR_UNKNOWN] - but only if not suppressed + edgeName = "Mode" and + ( + if exists(this.getModeOfOperation()) + then result = this.getModeOfOperation() + else result = this + ) and + instance.asAlg().shouldHaveModeOfOperation() + or + // [KNOWN_OR_UNKNOWN] - but only if not suppressed + edgeName = "Padding" and + ( + if exists(this.getPaddingAlgorithm()) + then result = this.getPaddingAlgorithm() + else result = this + ) and + instance.asAlg().shouldHavePaddingScheme() + } + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [ONLY_KNOWN] - only if symmetric, unknown case is handled in `toString` + key = "Structure" and + this.getSymmetricCipherStructure().toString() = value and + location = this.getLocation() + or + // [ONLY_KNOWN] + key = "KeySize" and + ( + value = this.getKeySizeFixed() and + location = this.getLocation() + or + node_as_property(this.getKeySize(), value, location) + ) + } + } + + /** + * 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. + */ + class HashOperationNode extends OperationNode, THashOperation { + HashOperationInstance instance; + + HashOperationNode() { this = THashOperation(instance) } + + override string getInternalType() { result = "HashOperation" } + + override LocatableElement asElement() { result = instance } + + override predicate isCandidateAlgorithmNode(AlgorithmNode node) { + node instanceof HashAlgorithmNode + } + + MessageArtifactNode getInputArtifact() { + result.asElement() = instance.getInputConsumer().getConsumer() + } + + /** + * Gets the output digest node + */ + DigestArtifactNode getDigest() { + result.asElement() = instance.getOutputArtifact().getArtifact() + } + + override NodeBase getChild(string key) { + result = super.getChild(key) + or + // [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 + } + } + + newtype THashType = + BLAKE2B() or + BLAKE2S() or + GOSTHash() or + MD2() or + MD4() or + MD5() or + MDC2() or + POLY1305() or + SHA1() or + SHA2() or + SHA3() or + SHAKE() or + SM3() or + RIPEMD160() or + WHIRLPOOL() or + OtherHashType() + + /** + * A hashing algorithm that transforms variable-length input into a fixed-size hash value. + */ + 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().getRawHashAlgorithmName() } + + final private predicate hashTypeToNameMapping(THashType type, string name) { + type = BLAKE2B() and name = "BLAKE2B" + or + type = BLAKE2S() and name = "BLAKE2S" + or + type = RIPEMD160() and name = "RIPEMD160" + or + type = MD2() and name = "MD2" + or + type = MD4() and name = "MD4" + or + type = MD5() and name = "MD5" + or + type = POLY1305() and name = "POLY1305" + or + type = SHA1() and name = "SHA1" + or + type = SHA2() and name = "SHA2" + or + type = SHA3() and name = "SHA3" + or + type = SHAKE() and name = "SHAKE" + or + type = SM3() and name = "SM3" + or + type = WHIRLPOOL() and name = "WHIRLPOOL" + } + + /** + * 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. + */ + THashType getHashFamily() { result = instance.asAlg().getHashFamily() } + + override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashFamily(), result) } + + 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) + or + // [KNOWN_OR_UNKNOWN] + key = "DigestSize" and + if exists(this.getDigestLength()) + then value = this.getDigestLength().toString() and location = this.getLocation() + else ( + value instanceof UnknownPropertyValue and location instanceof UnknownLocation + ) + } + } + + /** + * Elliptic curve algorithms + */ + newtype TEllipticCurveType = + NIST() or + SEC() or + NUMS() or + PRIME() or + BRAINPOOL() or + CURVE25519() or + CURVE448() or + C2() or + SM2() or + ES() or + OtherEllipticCurveType() + + 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 = "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) { + ellipticCurveNameToKeySizeAndFamilyMapping(curveName, _, _) + } + + /** + * 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 ellipticCurveNameToKeySizeAndFamilyMapping( + 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 + // 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() + ) + } + + final class EllipticCurveNode extends AlgorithmNode, TEllipticCurve { + EllipticCurveInstanceOrValueConsumer instance; + + EllipticCurveNode() { this = TEllipticCurve(instance) } + + override string getInternalType() { result = "EllipticCurve" } + + override LocatableElement asElement() { result = instance } + + final override string getRawAlgorithmName() { + result = instance.asAlg().getRawEllipticCurveName() + } + + /* + * 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 getEllipticCurveType() { result = instance.asAlg().getEllipticCurveType() } + + override predicate properties(string key, string value, Location location) { + super.properties(key, value, location) + or + // [ONLY_KNOWN] + key = "KeySize" and + value = instance.asAlg().getKeySize() and + location = this.getLocation() + or + // [KNOWN_OR_UNKNOWN] + key = "ParsedName" and + value = instance.asAlg().getParsedEllipticCurveName() and + location = this.getLocation() + } + } + + predicate isKnownAsymmetricAlgorithm(AlgorithmNode node) { + node instanceof EllipticCurveNode + or + node instanceof KeyOperationAlgorithmNode and node.(KeyOperationAlgorithmNode).isAsymmetric() + } +} diff --git a/shared/quantum/qlpack.yml b/shared/quantum/qlpack.yml new file mode 100644 index 000000000000..8e34c19c1121 --- /dev/null +++ b/shared/quantum/qlpack.yml @@ -0,0 +1,7 @@ +name: codeql/quantum +version: 0.0.0-dev +groups: shared +library: true +dependencies: + codeql/util: ${workspace} +warnOnImplicitThis: true \ No newline at end of file