diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/MACAlgorithmInstance.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/MACAlgorithmInstance.qll index f12bad03d469..97b183b7e7d3 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/MACAlgorithmInstance.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/MACAlgorithmInstance.qll @@ -54,13 +54,15 @@ class KnownOpenSslHMacConstantAlgorithmInstance extends Crypto::HmacAlgorithmIns then // ASSUMPTION: if there is an explicit hash algorithm, it is already modeled // and we can simply grab that model's AVC - exists(OpenSslAlgorithmInstance inst | inst.getAvc() = result and inst = this) + this.(OpenSslAlgorithmInstance).getAvc() = result else - // ASSUMPTION: If no explicit algorithm is given, then it is assumed to be configured by - // a signature operation - exists(Crypto::SignatureOperationInstance s | - s.getHashAlgorithmValueConsumer() = result and - s.getAnAlgorithmValueConsumer() = this.getAvc() + // ASSUMPTION: If no explicit algorithm is given, then find + // where the current AVC traces to a HashAlgorithmIO consuming operation step. + // TODO: need to consider getting reset values, tracing down to the first set for now + exists(OperationStep s, AvcContextCreationStep avc | + avc = this.getAvc() and + avc.flowsToOperationStep(s) and + s.getAlgorithmValueConsumerForInput(HashAlgorithmIO()) = result ) } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll deleted file mode 100644 index 63ec3e181325..000000000000 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll +++ /dev/null @@ -1,221 +0,0 @@ -//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 - -/** - * An openSSL CTX type, which is type for which the stripped underlying type - * matches the pattern 'evp_%ctx_%st'. - * This includes types like: - * - EVP_CIPHER_CTX - * - EVP_MD_CTX - * - EVP_PKEY_CTX - */ -class CtxType extends Type { - CtxType() { - // It is possible for users to use the underlying type of the CTX variables - // these have a name matching 'evp_%ctx_%st - this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st") - or - // In principal the above check should be sufficient, but in case of build mode none issues - // i.e., if a typedef cannot be resolved, - // or issues with properly stubbing test cases, we also explicitly check for the wrapping type defs - // i.e., patterns matching 'EVP_%_CTX' - exists(Type base | base = this or base = this.(DerivedType).getBaseType() | - base.getName().matches("EVP_%_CTX") - ) - } -} - -/** - * A pointer to a CtxType - */ -class CtxPointerExpr extends Expr { - CtxPointerExpr() { - this.getType() instanceof CtxType and - this.getType() instanceof PointerType - } -} - -/** - * A call argument of type CtxPointerExpr. - */ -class CtxPointerArgument extends CtxPointerExpr { - CtxPointerArgument() { exists(Call c | c.getAnArgument() = this) } - - Call getCall() { result.getAnArgument() = this } -} - -/** - * A call returning a CtxPointerExpr. - */ -private class CtxPointerReturn extends CtxPointerExpr instanceof Call { - Call getCall() { result = this } -} - -/** - * A call whose target contains 'free' or 'reset' and has an argument of type - * CtxPointerArgument. - */ -private class CtxClearCall extends Call { - CtxClearCall() { - this.getTarget().getName().toLowerCase().matches(["%free%", "%reset%"]) and - this.getAnArgument() instanceof CtxPointerArgument - } -} - -abstract private class CtxPassThroughCall extends Call { - abstract DataFlow::Node getNode1(); - - abstract DataFlow::Node getNode2(); -} - -/** - * A call whose target contains 'copy' and has an argument of type - * CtxPointerArgument. - */ -private class CtxCopyOutArgCall extends CtxPassThroughCall { - DataFlow::Node n1; - DataFlow::Node n2; - - CtxCopyOutArgCall() { - this.getTarget().getName().toLowerCase().matches("%copy%") and - n1.asExpr() = this.getAnArgument() and - n1.getType() instanceof CtxType and - n2.asDefiningArgument() = this.getAnArgument() and - n2.getType() instanceof CtxType and - n1.asDefiningArgument() != n2.asExpr() - } - - override DataFlow::Node getNode1() { result = n1 } - - override DataFlow::Node getNode2() { result = n2 } -} - -/** - * A call whose target contains 'dup' and has an argument of type - * CtxPointerArgument. - */ -private class CtxCopyReturnCall extends CtxPassThroughCall, CtxPointerExpr { - DataFlow::Node n1; - - CtxCopyReturnCall() { - this.getTarget().getName().toLowerCase().matches("%dup%") and - n1.asExpr() = this.getAnArgument() and - n1.getType() instanceof CtxType - } - - override DataFlow::Node getNode1() { result = n1 } - - override DataFlow::Node getNode2() { result.asExpr() = this } -} - -/** - * A call to `EVP_PKEY_paramgen` acts as a kind of pass through. - * It's output pkey is eventually used in a new operation generating - * a fresh context pointer (e.g., `EVP_PKEY_CTX_new`). - * It is easier to model this as a pass through - * than to model the flow from the paramgen to the new key generation. - */ -private class CtxParamGenCall extends CtxPassThroughCall { - DataFlow::Node n1; - DataFlow::Node n2; - - CtxParamGenCall() { - this.getTarget().getName() = "EVP_PKEY_paramgen" and - n1.asExpr() = this.getArgument(0) and - ( - n2.asExpr() = this.getArgument(1) - or - n2.asDefiningArgument() = this.getArgument(1) - ) - } - - override DataFlow::Node getNode1() { result = n1 } - - override DataFlow::Node getNode2() { result = n2 } -} - -/** - * If the current node gets is an argument to a function - * that returns a pointer type, immediately flow through. - * NOTE: this passthrough is required if we allow - * intermediate steps to go into variables that are not a CTX type. - * See for example `CtxParamGenCall`. - */ -private class CallArgToCtxRet extends CtxPassThroughCall, CtxPointerExpr { - DataFlow::Node n1; - DataFlow::Node n2; - - CallArgToCtxRet() { - this.getAnArgument() = n1.asExpr() and - n2.asExpr() = this - } - - override DataFlow::Node getNode1() { result = n1 } - - override DataFlow::Node getNode2() { result = n2 } -} - -/** - * A source Ctx of interest is any argument or return of type CtxPointerExpr. - */ -class CtxPointerSource extends CtxPointerExpr { - CtxPointerSource() { - this instanceof CtxPointerReturn or - this instanceof CtxPointerArgument - } - - DataFlow::Node asNode() { - result.asExpr() = this - or - result.asDefiningArgument() = this - } -} - -/** - * Flow from any CtxPointerSource to other CtxPointerSource. - */ -module OpenSslCtxSourceToSourceFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { exists(CtxPointerSource s | s.asNode() = source) } - - predicate isSink(DataFlow::Node sink) { exists(CtxPointerSource s | s.asNode() = sink) } - - predicate isBarrier(DataFlow::Node node) { - exists(CtxClearCall c | c.getAnArgument() = node.asExpr()) - } - - predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2) - } -} - -module OpenSslCtxSourceToArgumentFlow = DataFlow::Global; - -/** - * Holds if there is a context flow from the source to the sink. - */ -predicate ctxSrcToSrcFlow(CtxPointerSource source, CtxPointerSource sink) { - exists(DataFlow::Node a, DataFlow::Node b | - OpenSslCtxSourceToArgumentFlow::flow(a, b) and - a = source.asNode() and - b = sink.asNode() - ) -} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/CipherOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/CipherOperation.qll new file mode 100644 index 000000000000..44e30ddf9fc9 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/CipherOperation.qll @@ -0,0 +1,273 @@ +import experimental.quantum.Language +private import OpenSSLOperationBase +private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +import EVPPKeyCtxInitializer + +/** + * A base class for all EVP cipher operations. + */ +abstract class EvpCipherInitializer extends OperationStep { + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and + type = PrimaryAlgorithmIO() and + // Constants that are not equal to zero or + // non-constants (e.g., variable accesses, which require data-flow to determine the value) + // A zero (null) value typically indicates use of this operation step to initialize + // other out parameters in a multi-step initialization. + (exists(result.asExpr().getValue()) implies result.asExpr().getValue().toInt() != 0) + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override OperationStepType getStepType() { result = InitializerStep() } +} + +/** + * A base class for EVP cipher/decrypt/encrypt 'ex' operations. + */ +abstract class EvpEXInitializer extends EvpCipherInitializer { + override DataFlow::Node getInput(IOType type) { + result = super.getInput(type) + or + ( + // Constants that are not equal to zero or + // non-constants (e.g., variable accesses, which require data-flow to determine the value) + // A zero (null) value typically indicates use of this operation step to initialize + // other out parameters in a multi-step initialization. + result.asExpr() = this.getArgument(3) and type = KeyIO() + or + result.asExpr() = this.getArgument(4) and type = IVorNonceIO() + ) and + (exists(result.asExpr().getValue()) implies result.asExpr().getValue().toInt() != 0) + } +} + +/** + * A base class for EVP cipher/decrypt/encrypt 'ex2' operations. + */ +abstract class EvpEX2Initializer extends EvpCipherInitializer { + override DataFlow::Node getInput(IOType type) { + result = super.getInput(type) + or + result.asExpr() = this.getArgument(2) and type = KeyIO() + or + result.asExpr() = this.getArgument(3) and type = IVorNonceIO() + } +} + +/** + * A Call to an EVP Cipher/Encrypt/Decrypt initialization operation. + */ +class EvpCipherEXInitCall extends EvpEXInitializer { + EvpCipherEXInitCall() { + this.getTarget().getName() in ["EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex"] + } + + override DataFlow::Node getInput(IOType type) { + result = super.getInput(type) + or + // NOTE: for EncryptInit and DecryptInit there is no subtype arg + // the subtype is determined automatically by the initializer based on the operation name + this.getTarget().getName().toLowerCase().matches("%cipherinit%") and + result.asExpr() = this.getArgument(5) and + type = KeyOperationSubtypeIO() + } +} + +class Evp_Cipher_EX2_or_Simple_Init_Call extends EvpEX2Initializer { + Evp_Cipher_EX2_or_Simple_Init_Call() { + this.getTarget().getName() in [ + "EVP_EncryptInit_ex2", "EVP_DecryptInit_ex2", "EVP_CipherInit_ex2", "EVP_EncryptInit", + "EVP_DecryptInit", "EVP_CipherInit" + ] + } + + override DataFlow::Node getInput(IOType type) { + result = super.getInput(type) + or + this.getTarget().getName().toLowerCase().matches("%cipherinit%") and + result.asExpr() = this.getArgument(4) and + type = KeyOperationSubtypeIO() + } +} + +/** + * A call to EVP_Pkey_encrypt_init, EVP_Pkey_decrypt_init, or their 'ex' variants. + */ +class EvpPkeyEncryptDecryptInit extends OperationStep { + EvpPkeyEncryptDecryptInit() { + this.getTarget().getName() in [ + "EVP_PKEY_encrypt_init", "EVP_PKEY_encrypt_init_ex", "EVP_PKEY_decrypt_init", + "EVP_PKEY_decrypt_init_ex" + ] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = OsslParamIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override OperationStepType getStepType() { result = InitializerStep() } +} + +class EvpCipherInitSKeyCall extends EvpEX2Initializer { + EvpCipherInitSKeyCall() { this.getTarget().getName() = "EVP_CipherInit_SKEY" } + + override DataFlow::Node getInput(IOType type) { + result = super.getInput(type) + or + result.asExpr() = this.getArgument(5) and + type = KeyOperationSubtypeIO() + } +} + +//EVP_PKEY_encrypt_init +/** + * A Call to EVP_Cipher/Encrypt/DecryptUpdate. + * https://docs.openssl.org/3.2/man3/EVP_CipherUpdate + */ +class EvpCipherUpdateCall extends OperationStep { + EvpCipherUpdateCall() { + this.getTarget().getName() in ["EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(3) and type = PlaintextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(1) and type = CiphertextIO() + or + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override OperationStepType getStepType() { result = UpdateStep() } +} + +/** + * A base configuration for all EVP cipher operations. + */ +abstract class EvpCipherOperationFinalStep extends OperationStep { + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override OperationStepType getStepType() { result = FinalStep() } +} + +/** + * A Call to EVP_Cipher. + */ +class EvpCipherCall extends EvpCipherOperationFinalStep { + EvpCipherCall() { this.getTarget().getName() = "EVP_Cipher" } + + override DataFlow::Node getInput(IOType type) { + super.getInput(type) = result + or + result.asExpr() = this.getArgument(2) and type = PlaintextIO() + } + + override DataFlow::Node getOutput(IOType type) { + super.getOutput(type) = result + or + result.asExpr() = this.getArgument(1) and type = CiphertextIO() + } +} + +/** + * A Call to an EVP Cipher/Encrypt/Decrypt final operation. + */ +class EvpCipherFinalCall extends EvpCipherOperationFinalStep { + EvpCipherFinalCall() { + this.getTarget().getName() in [ + "EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal", + "EVP_DecryptFinal", "EVP_CipherFinal" + ] + } + + override DataFlow::Node getOutput(IOType type) { + super.getOutput(type) = result + or + result.asDefiningArgument() = this.getArgument(1) and + type = CiphertextIO() + // TODO: could indicate text lengths here, as well + } +} + +/** + * A call to a PKEY_encrypt or PKEY_decrypt operation. + * https://docs.openssl.org/3.2/man3/EVP_PKEY_decrypt/ + * https://docs.openssl.org/3.2/man3/EVP_PKEY_encrypt + */ +class EvpPKeyCipherOperation extends EvpCipherOperationFinalStep { + EvpPKeyCipherOperation() { + this.getTarget().getName() in ["EVP_PKEY_encrypt", "EVP_PKEY_decrypt"] + } + + override DataFlow::Node getInput(IOType type) { + super.getInput(type) = result + or + result.asExpr() = this.getArgument(3) and type = PlaintextIO() + } + + override DataFlow::Node getOutput(IOType type) { + super.getOutput(type) = result + or + result.asExpr() = this.getArgument(1) and type = CiphertextIO() + // TODO: could indicate text lengths here, as well + } +} + +/** + * An EVP cipher operation instance. + * Any operation step that is a final operation step for EVP cipher operation steps. + */ +class EvpCipherOperationInstance extends Crypto::KeyOperationInstance instanceof EvpCipherOperationFinalStep +{ + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + super.getPrimaryAlgorithmValueConsumer() = result + } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + result instanceof Crypto::TEncryptMode and + super.getTarget().getName().toLowerCase().matches("%encrypt%") + or + result instanceof Crypto::TDecryptMode and + super.getTarget().getName().toLowerCase().matches("%decrypt%") + or + super.getTarget().getName().toLowerCase().matches("%cipher%") and + resolveKeyOperationSubTypeOperationStep(super + .getDominatingInitializersToStep(KeyOperationSubtypeIO())) = result + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { + super.getDominatingInitializersToStep(IVorNonceIO()).getInput(IVorNonceIO()) = result + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + super.getDominatingInitializersToStep(KeyIO()).getInput(KeyIO()) = result + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + super.getOutputStepFlowingToStep(CiphertextIO()).getOutput(CiphertextIO()) = result + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + super.getDominatingInitializersToStep(PlaintextIO()).getInput(PlaintextIO()) = result + } +} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/ECKeyGenOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/ECKeyGenOperation.qll deleted file mode 100644 index 65eebae585b3..000000000000 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/ECKeyGenOperation.qll +++ /dev/null @@ -1,33 +0,0 @@ -private import experimental.quantum.Language -private import OpenSSLOperationBase -private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers -private import semmle.code.cpp.dataflow.new.DataFlow - -class ECKeyGenOperation extends OpenSslOperation, Crypto::KeyGenerationOperationInstance { - ECKeyGenOperation() { this.(Call).getTarget().getName() = "EC_KEY_generate_key" } - - override Expr getAlgorithmArg() { result = this.(Call).getArgument(0) } - - override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() } - - override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { - result.asExpr() = this.(Call).getArgument(0) - } - - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { - none() // no explicit key size, inferred from algorithm - } - - override int getKeySizeFixed() { - none() - // TODO: marked as none as the operation itself has no key size, it - // comes from the algorithm source, but note we could grab the - // algorithm source and get the key size (see below). - // We may need to reconsider what is the best approach here. - // result = - // this.getAnAlgorithmValueConsumer() - // .getAKnownAlgorithmSource() - // .(Crypto::EllipticCurveInstance) - // .getKeySize() - } -} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll deleted file mode 100644 index 2a2cf00b4622..000000000000 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll +++ /dev/null @@ -1,183 +0,0 @@ -private import experimental.quantum.Language -private import experimental.quantum.OpenSSL.CtxFlow -private import OpenSSLOperationBase -private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers - -// TODO: need to add key consumer -abstract class Evp_Cipher_Initializer extends EvpKeyOperationSubtypeInitializer, - EvpPrimaryAlgorithmInitializer, EvpKeyInitializer, EvpIVInitializer -{ - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } - - override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } -} - -abstract class Evp_EX_Initializer extends Evp_Cipher_Initializer { - override Expr getKeyArg() { - // Null key indicates the key is not actually set - // This pattern can occur during a multi-step initialization - // TODO/Note: not flowing 0 to the sink, assuming a direct use of NULL for now - result = this.(Call).getArgument(3) and - (exists(result.getValue()) implies result.getValue().toInt() != 0) - } - - override Expr getIVArg() { - // Null IV indicates the IV is not actually set - // This occurs given that setting the IV sometimes requires first setting the IV size. - // TODO/Note: not flowing 0 to the sink, assuming a direct use of NULL for now - result = this.(Call).getArgument(4) and - (exists(result.getValue()) implies result.getValue().toInt() != 0) - } -} - -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 EvpCipherEXInitCall extends Evp_EX_Initializer { - EvpCipherEXInitCall() { - this.(Call).getTarget().getName() in [ - "EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex" - ] - } - - override Expr getKeyOperationSubtypeArg() { - // NOTE: for EncryptInit and DecryptInit there is no subtype arg - // the subtype is determined automatically by the initializer based on the operation name - this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and - result = this.(Call).getArgument(5) - } -} - -// 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 -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 getKeyOperationSubtypeArg() { - 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() = "EVP_CipherInit_SKEY" } - - override Expr getKeyOperationSubtypeArg() { result = this.(Call).getArgument(5) } -} - -class Evp_Cipher_Update_Call extends EvpUpdate { - Evp_Cipher_Update_Call() { - this.(Call).getTarget().getName() in [ - "EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate" - ] - } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } - - override Expr getInputArg() { result = this.(Call).getArgument(3) } - - override Expr getOutputArg() { result = this.(Call).getArgument(1) } -} - -/** - * The EVP Cipher operations. - * See: https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis - * Base configuration for all EVP cipher operations. - */ -abstract class Evp_Cipher_Operation extends EvpOperation, Crypto::KeyOperationInstance { - 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().(EvpKeyOperationSubtypeInitializer).getKeyOperationSubtype() and - this.(Call).getTarget().getName().toLowerCase().matches("%cipher%") - } - - override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { - this.getInitCall().(EvpIVInitializer).getIVArg() = result.asExpr() - } - - override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { - this.getInitCall().(EvpKeyInitializer).getKeyArg() = result.asExpr() - // todo: or track to the EVP_PKEY_CTX_new - } - - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result = EvpOperation.super.getOutputArtifact() - } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = EvpOperation.super.getInputConsumer() - } -} - -class Evp_Cipher_Call extends EvpOperation, Evp_Cipher_Operation { - Evp_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } - - override Expr getInputArg() { result = this.(Call).getArgument(2) } - - override Expr getAlgorithmArg() { - result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() - } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -class Evp_Cipher_Final_Call extends EvpFinal, Evp_Cipher_Operation { - Evp_Cipher_Final_Call() { - this.(Call).getTarget().getName() in [ - "EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal", - "EVP_DecryptFinal", "EVP_CipherFinal" - ] - } - - /** - * Output is both from update calls and from the final call. - */ - override Expr getOutputArg() { - result = EvpFinal.super.getOutputArg() - or - result = Evp_Cipher_Operation.super.getOutputArg() - } - - override Expr getAlgorithmArg() { - result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() - } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -/** - * The EVP encryption/decryption operations. - * https://docs.openssl.org/3.2/man3/EVP_PKEY_decrypt/ - * https://docs.openssl.org/3.2/man3/EVP_PKEY_encrypt - */ -class Evp_PKey_Cipher_Operation extends Evp_Cipher_Operation { - Evp_PKey_Cipher_Operation() { - this.(Call).getTarget().getName() in ["EVP_PKEY_encrypt", "EVP_PKEY_decrypt"] - } - - override Expr getInputArg() { result = this.(Call).getArgument(3) } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } - - override Expr getAlgorithmArg() { - result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() - } -} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll deleted file mode 100644 index b99c5432a1a0..000000000000 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll +++ /dev/null @@ -1,106 +0,0 @@ -/** - * https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis - */ - -private import experimental.quantum.Language -private import experimental.quantum.OpenSSL.CtxFlow -private import OpenSSLOperationBase -private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers - -class Evp_DigestInit_Variant_Calls extends EvpPrimaryAlgorithmInitializer { - 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) } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -class Evp_Digest_Update_Call extends EvpUpdate { - Evp_Digest_Update_Call() { this.(Call).getTarget().getName() = "EVP_DigestUpdate" } - - override Expr getInputArg() { result = this.(Call).getArgument(1) } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -//https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis -class Evp_Q_Digest_Operation extends EvpOperation, Crypto::HashOperationInstance { - Evp_Q_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Q_digest" } - - override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } - - override EvpInitializer 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 getInputArg() { result = this.(Call).getArgument(3) } - - override Expr getOutputArg() { result = this.(Call).getArgument(5) } - - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result = EvpOperation.super.getOutputArtifact() - } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = EvpOperation.super.getInputConsumer() - } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -class Evp_Digest_Operation extends EvpOperation, Crypto::HashOperationInstance { - Evp_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Digest" } - - // There is no context argument for this function - override CtxPointerSource getContext() { none() } - - override Expr getAlgorithmArg() { result = this.(Call).getArgument(4) } - - override EvpPrimaryAlgorithmInitializer 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 getInputArg() { result = this.(Call).getArgument(0) } - - override Expr getOutputArg() { result = this.(Call).getArgument(2) } - - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result = EvpOperation.super.getOutputArtifact() - } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = EvpOperation.super.getInputConsumer() - } -} - -class Evp_Digest_Final_Call extends EvpFinal, Crypto::HashOperationInstance { - Evp_Digest_Final_Call() { - this.(Call).getTarget().getName() in [ - "EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF" - ] - } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } - - override Expr getOutputArg() { result = this.(Call).getArgument(1) } - - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result = EvpFinal.super.getOutputArtifact() - } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = EvpFinal.super.getInputConsumer() - } - - override Expr getAlgorithmArg() { - result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() - } -} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll deleted file mode 100644 index 47f341e17b1a..000000000000 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll +++ /dev/null @@ -1,96 +0,0 @@ -private import experimental.quantum.Language -private import experimental.quantum.OpenSSL.CtxFlow -private import OpenSSLOperationBase -private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers - -class EvpKeyGenInitialize extends EvpPrimaryAlgorithmInitializer { - EvpKeyGenInitialize() { - this.(Call).getTarget().getName() in [ - "EVP_PKEY_keygen_init", - "EVP_PKEY_paramgen_init" - ] - } - - /** - * Gets the algorithm argument. - * In this case the algorithm is encoded through the context argument. - * The context may be directly created from an algorithm consumer, - * or from a new operation off of a prior key. Either way, - * we will treat this argument as the algorithm argument. - */ - override Expr getAlgorithmArg() { result = this.getContext() } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -class EvpKeyGenOperation extends EvpOperation, Crypto::KeyGenerationOperationInstance { - DataFlow::Node keyResultNode; - - EvpKeyGenOperation() { - this.(Call).getTarget().getName() in ["EVP_RSA_gen", "EVP_PKEY_Q_keygen"] and - keyResultNode.asExpr() = this - or - this.(Call).getTarget().getName() in ["EVP_PKEY_generate", "EVP_PKEY_keygen"] and - keyResultNode.asDefiningArgument() = this.(Call).getArgument(1) - } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } - - override Expr getAlgorithmArg() { - this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" and - result = this.(Call).getArgument(0) - or - result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() - } - - override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() } - - override Expr getInputArg() { none() } - - override Expr getOutputArg() { result = keyResultNode.asExpr() } - - override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result = keyResultNode } - - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { - this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" and - result = DataFlow::exprNode(this.(Call).getArgument(3)) and - // Arg 3 (0 based) is only a key size if the 'type' parameter is RSA, however, - // as a crude approximation, assume that if the type of the argument is not a derived type - // the argument must specify a key size (this is to avoid tracing if "rsa" is in the type parameter) - not this.(Call).getArgument(3).getType().getUnderlyingType() instanceof DerivedType - or - this.(Call).getTarget().getName() = "EVP_RSA_gen" and - result = DataFlow::exprNode(this.(Call).getArgument(0)) - or - result = DataFlow::exprNode(this.getInitCall().(EvpKeySizeInitializer).getKeySizeArg()) - } -} - -/** - * A call to `EVP_PKEY_new_mac_key` that creatse a new generic MAC key. - * Signature: EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e, const unsigned char *key, int keylen); - */ -class EvpNewMacKey extends EvpOperation, Crypto::KeyGenerationOperationInstance { - DataFlow::Node keyResultNode; - - EvpNewMacKey() { - this.(Call).getTarget().getName() = "EVP_PKEY_new_mac_key" and keyResultNode.asExpr() = this - } - - override CtxPointerSource getContext() { none() } - - override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TSymmetricKeyType() } - - override Expr getOutputArg() { result = keyResultNode.asExpr() } - - override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result = keyResultNode } - - override Expr getInputArg() { none() } - - override Expr getAlgorithmArg() { result = this.(Call).getArgument(0) } - - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { - result = DataFlow::exprNode(this.(Call).getArgument(3)) - } -} -/// TODO: https://docs.openssl.org/3.0/man3/EVP_PKEY_new/#synopsis diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPPKeyCtxInitializer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPPKeyCtxInitializer.qll index d7060931317f..2208407e53ca 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPPKeyCtxInitializer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPPKeyCtxInitializer.qll @@ -6,7 +6,6 @@ */ import cpp -private import experimental.quantum.OpenSSL.CtxFlow private import OpenSSLOperations /** @@ -14,49 +13,66 @@ private import OpenSSLOperations * These calls initialize the context from a prior key. * The key may be generated previously, or merely had it's * parameters set (e.g., `EVP_PKEY_paramgen`). - * NOTE: for the case of `EVP_PKEY_paramgen`, these calls - * are encoded as context passthroughs, and any operation - * will get all associated initializers for the paramgen - * at the final keygen operation automatically. */ -class EvpNewKeyCtx extends EvpKeyInitializer { +class EvpNewKeyCtx extends OperationStep instanceof Call { Expr keyArg; EvpNewKeyCtx() { - this.(Call).getTarget().getName() = "EVP_PKEY_CTX_new" and - keyArg = this.(Call).getArgument(0) + this.getTarget().getName() = "EVP_PKEY_CTX_new" and + keyArg = this.getArgument(0) or - this.(Call).getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and - keyArg = this.(Call).getArgument(1) + this.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and + keyArg = this.getArgument(1) } - /** - * Context is returned - */ - override CtxPointerSource getContext() { result = this } + override DataFlow::Node getInput(IOType type) { + result.asExpr() = keyArg and type = KeyIO() + or + this.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and + result.asExpr() = this.getArgument(0) and + type = OsslLibContextIO() + } + + override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = ContextIO() } - override Expr getKeyArg() { result = keyArg } + override OperationStepType getStepType() { result = ContextCreationStep() } } /** * A call to "EVP_PKEY_CTX_set_ec_paramgen_curve_nid". - * Note that this is a primary algorithm as the pattenr is to specify an "EC" context, - * then set the specific curve later. Although the curve is set later, it is the primary - * algorithm intended for an operation. */ -class EvpCtxSetPrimaryAlgorithmInitializer extends EvpPrimaryAlgorithmInitializer { - EvpCtxSetPrimaryAlgorithmInitializer() { - this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_ec_paramgen_curve_nid" +class EvpCtxSetEcParamgenCurveNidInitializer extends OperationStep { + EvpCtxSetEcParamgenCurveNidInitializer() { + this.getTarget().getName() = "EVP_PKEY_CTX_set_ec_paramgen_curve_nid" } - override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = PrimaryAlgorithmIO() + } - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override OperationStepType getStepType() { result = InitializerStep() } } -class EvpCtxSetHashAlgorithmInitializer extends EvpHashAlgorithmInitializer { - EvpCtxSetHashAlgorithmInitializer() { - this.(Call).getTarget().getName() in [ +/** + * A call to the following: + * - `EVP_PKEY_CTX_set_signature_md` + * - `EVP_PKEY_CTX_set_rsa_mgf1_md_name` + * - `EVP_PKEY_CTX_set_rsa_mgf1_md` + * - `EVP_PKEY_CTX_set_rsa_oaep_md_name` + * - `EVP_PKEY_CTX_set_rsa_oaep_md` + * - `EVP_PKEY_CTX_set_dsa_paramgen_md` + * - `EVP_PKEY_CTX_set_dh_kdf_md` + * - `EVP_PKEY_CTX_set_ecdh_kdf_md` + */ +class EvpCtxSetHashInitializer extends OperationStep { + EvpCtxSetHashInitializer() { + this.getTarget().getName() in [ "EVP_PKEY_CTX_set_signature_md", "EVP_PKEY_CTX_set_rsa_mgf1_md_name", "EVP_PKEY_CTX_set_rsa_mgf1_md", "EVP_PKEY_CTX_set_rsa_oaep_md_name", "EVP_PKEY_CTX_set_rsa_oaep_md", "EVP_PKEY_CTX_set_dsa_paramgen_md", @@ -64,56 +80,95 @@ class EvpCtxSetHashAlgorithmInitializer extends EvpHashAlgorithmInitializer { ] } - override Expr getHashAlgorithmArg() { result = this.(Call).getArgument(1) } + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = HashAlgorithmIO() + } - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } -class EvpCtxSetKeySizeInitializer extends EvpKeySizeInitializer { - Expr arg; + override OperationStepType getStepType() { result = InitializerStep() } +} +/** + * A call to `EVP_PKEY_CTX_set_rsa_keygen_bits`, `EVP_PKEY_CTX_set_dsa_paramgen_bits`, + * or `EVP_CIPHER_CTX_set_key_length`. + */ +class EvpCtxSetKeySizeInitializer extends OperationStep { EvpCtxSetKeySizeInitializer() { - this.(Call).getTarget().getName() in [ + this.getTarget().getName() in [ "EVP_PKEY_CTX_set_rsa_keygen_bits", "EVP_PKEY_CTX_set_dsa_paramgen_bits", "EVP_CIPHER_CTX_set_key_length" - ] and - arg = this.(Call).getArgument(1) + ] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() or - this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" and - arg = this.(Call).getArgument(2) + result.asExpr() = this.getArgument(1) and type = KeySizeIO() } - override Expr getKeySizeArg() { result = arg } + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } + override OperationStepType getStepType() { result = InitializerStep() } } -class EvpCtxSetKeyInitializer extends EvpKeyInitializer { - EvpCtxSetKeyInitializer() { this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" } +class EvpCtxSetMacKeyInitializer extends OperationStep { + EvpCtxSetMacKeyInitializer() { this.getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" } - override Expr getKeyArg() { result = this.(Call).getArgument(1) } + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(2) and type = KeySizeIO() + or + // the raw key that is configured into the output key + result.asExpr() = this.getArgument(1) and type = KeyIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } + override OperationStepType getStepType() { result = InitializerStep() } } -class EvpCtxSetPaddingInitializer extends EvpPaddingInitializer { +class EvpCtxSetPaddingInitializer extends OperationStep { EvpCtxSetPaddingInitializer() { - this.(Call).getTarget().getName() in [ - "EVP_PKEY_CTX_set_rsa_padding", "EVP_CIPHER_CTX_set_padding" - ] + this.getTarget().getName() in ["EVP_PKEY_CTX_set_rsa_padding", "EVP_CIPHER_CTX_set_padding"] } - override Expr getPaddingArg() { result = this.(Call).getArgument(1) } + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = PaddingAlgorithmIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } + override OperationStepType getStepType() { result = InitializerStep() } } -class EvpCtxSetSaltLengthInitializer extends EvpSaltLengthInitializer { +class EvpCtxSetSaltLengthInitializer extends OperationStep { EvpCtxSetSaltLengthInitializer() { - this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_rsa_pss_saltlen" + this.getTarget().getName() = "EVP_PKEY_CTX_set_rsa_pss_saltlen" } - override Expr getSaltLengthArg() { result = this.(Call).getArgument(1) } + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = SaltLengthIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } + override OperationStepType getStepType() { result = InitializerStep() } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll deleted file mode 100644 index 41a828652917..000000000000 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Provides classes for modeling OpenSSL's EVP signature operations - */ - -private import experimental.quantum.Language -private import experimental.quantum.OpenSSL.AvcFlow -private import experimental.quantum.OpenSSL.CtxFlow -private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers -private import experimental.quantum.OpenSSL.Operations.OpenSSLOperations - -// TODO: verification functions -class EvpSignatureDigestInitializer extends EvpHashAlgorithmInitializer { - Expr arg; - - EvpSignatureDigestInitializer() { - this.(Call).getTarget().getName() in ["EVP_DigestSignInit_ex", "EVP_DigestSignInit"] and - arg = this.(Call).getArgument(2) - or - this.(Call).getTarget().getName() in ["EVP_SignInit", "EVP_SignInit_ex"] and - arg = this.(Call).getArgument(1) - } - - override Expr getHashAlgorithmArg() { result = arg } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -class EvpSignatureKeyInitializer extends EvpKeyInitializer { - Expr arg; - - EvpSignatureKeyInitializer() { - this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and - arg = this.(Call).getArgument(5) - or - this.(Call).getTarget().getName() = "EVP_DigestSignInit" and - arg = this.(Call).getArgument(4) - } - - override Expr getKeyArg() { result = arg } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -class EvpSignaturePrimaryAlgorithmInitializer extends EvpPrimaryAlgorithmInitializer { - Expr arg; - - EvpSignaturePrimaryAlgorithmInitializer() { - // signature algorithm - this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and - arg = this.(Call).getArgument(1) - or - // configuration through the context argument - this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex"] and - arg = this.getContext() - } - - override Expr getAlgorithmArg() { result = arg } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -class Evp_Signature_Update_Call extends EvpUpdate { - Evp_Signature_Update_Call() { - this.(Call).getTarget().getName() in [ - "EVP_DigestSignUpdate", "EVP_SignUpdate", "EVP_PKEY_sign_message_update" - ] - } - - /** - * Input is the message to sign. - */ - override Expr getInputArg() { result = this.(Call).getArgument(1) } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } -} - -/** - * We model output explicit output arguments as predicate to use it in constructors. - * The predicate must cover all EVP_Signature_Operation subclasses. - */ -pragma[inline] -private Expr signatureOperationOutputArg(Call call) { - if call.getTarget().getName() = "EVP_SignFinal_ex" - then result = call.getArgument(2) - else result = call.getArgument(1) -} - -/** - * The base configuration for all EVP signature operations. - */ -abstract class EvpSignatureOperation extends EvpOperation, Crypto::SignatureOperationInstance { - EvpSignatureOperation() { - this.(Call).getTarget().getName().matches("EVP_%") and - // NULL output argument means the call is to get the size of the signature and such call is not an operation - ( - not exists(signatureOperationOutputArg(this).getValue()) - or - signatureOperationOutputArg(this).getValue() != "0" - ) - } - - Expr getHashAlgorithmArg() { - this.getInitCall().(EvpHashAlgorithmInitializer).getHashAlgorithmArg() = result - } - - override Expr getAlgorithmArg() { - this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() = result - } - - override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() { - AvcToCallArgFlow::flow(result.(OpenSslAlgorithmValueConsumer).getResultNode(), - DataFlow::exprNode(this.getHashAlgorithmArg())) - } - - /** - * Signing, verification or unknown. - */ - override Crypto::KeyOperationSubtype getKeyOperationSubtype() { - // TODO: if this KeyOperationSubtype does not match initialization call's KeyOperationSubtype then we found a bug - if this.(Call).getTarget().getName().toLowerCase().matches("%sign%") - then result instanceof Crypto::TSignMode - else - if this.(Call).getTarget().getName().toLowerCase().matches("%verify%") - then result instanceof Crypto::TVerifyMode - else result instanceof Crypto::TUnknownKeyOperationMode - } - - override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { - // TODO: some signing operations may have explicit nonce generators - none() - } - - /** - * Keys provided in the initialization call or in a context are found by this method. - * Keys in explicit arguments are found by overridden methods in extending classes. - */ - override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { - result = DataFlow::exprNode(this.getInitCall().(EvpKeyInitializer).getKeyArg()) - } - - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result = EvpOperation.super.getOutputArtifact() - } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = EvpOperation.super.getInputConsumer() - } - - /** - * TODO: only signing operations for now, change when verificaiton is added - */ - override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { none() } -} - -class Evp_Signature_Call extends EvpSignatureOperation { - Evp_Signature_Call() { this.(Call).getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] } - - /** - * Output is the signature. - */ - override Expr getOutputArg() { result = signatureOperationOutputArg(this) } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } - - /** - * Input is the message to sign. - */ - override Expr getInputArg() { result = this.(Call).getArgument(3) } -} - -class Evp_Signature_Final_Call extends EvpFinal, EvpSignatureOperation { - Evp_Signature_Final_Call() { - this.(Call).getTarget().getName() in [ - "EVP_DigestSignFinal", - "EVP_SignFinal_ex", - "EVP_SignFinal", - "EVP_PKEY_sign_message_final" - ] - } - - override CtxPointerSource getContext() { result = this.(Call).getArgument(0) } - - override Expr getAlgorithmArg() { - this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() = result - } - - override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { - // key provided as an argument - this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"] and - result = DataFlow::exprNode(this.(Call).getArgument(3)) - or - // or find key in the initialization call - result = EvpSignatureOperation.super.getKeyConsumer() - } - - /** - * Output is the signature. - */ - override Expr getOutputArg() { result = signatureOperationOutputArg(this) } -} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/HashOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/HashOperation.qll new file mode 100644 index 000000000000..1878bfbe09f2 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/HashOperation.qll @@ -0,0 +1,134 @@ +/** + * https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis + */ + +private import experimental.quantum.Language +private import OpenSSLOperationBase +private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers + +/** + * A call to and EVP digest initializer, such as: + * - `EVP_DigestInit` + * - `EVP_DigestInit_ex` + * - `EVP_DigestInit_ex2` + */ +class EvpDigestInitVariantCalls extends OperationStep instanceof Call { + EvpDigestInitVariantCalls() { + this.getTarget().getName() in ["EVP_DigestInit", "EVP_DigestInit_ex", "EVP_DigestInit_ex2"] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = PrimaryAlgorithmIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and + type = ContextIO() + } + + override OperationStepType getStepType() { result = InitializerStep() } +} + +/** + * A call to `EVP_DigestUpdate`. + */ +class EvpDigestUpdateCall extends OperationStep instanceof Call { + EvpDigestUpdateCall() { this.getTarget().getName() = "EVP_DigestUpdate" } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = PlaintextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and + type = ContextIO() + } + + override OperationStepType getStepType() { result = UpdateStep() } +} + +/** + * A base class for final digest operations. + */ +abstract class EvpFinalDigestOperationStep extends OperationStep { + override OperationStepType getStepType() { result = FinalStep() } +} + +/** + * A call to `EVP_Q_digest` + * https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis + */ +class EvpQDigestOperation extends EvpFinalDigestOperationStep instanceof Call { + EvpQDigestOperation() { this.getTarget().getName() = "EVP_Q_digest" } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(1) and type = PrimaryAlgorithmIO() + or + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(3) and type = PlaintextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and + type = ContextIO() + or + result.asDefiningArgument() = this.getArgument(5) and type = DigestIO() + } +} + +class EvpDigestOperation extends EvpFinalDigestOperationStep instanceof Call { + EvpDigestOperation() { this.getTarget().getName() = "EVP_Digest" } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(4) and type = PrimaryAlgorithmIO() + or + result.asExpr() = this.getArgument(0) and type = PlaintextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asDefiningArgument() = this.getArgument(2) and type = DigestIO() + } +} + +/** + * A call to EVP_DigestFinal variants + */ +class EvpDigestFinalCall extends EvpFinalDigestOperationStep instanceof Call { + EvpDigestFinalCall() { + this.getTarget().getName() in ["EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF"] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and + type = ContextIO() + or + result.asDefiningArgument() = this.getArgument(1) and type = DigestIO() + } +} + +/** + * An openssl digest final hash operation instance + */ +class EvpDigestFinalOperationInstance extends Crypto::HashOperationInstance instanceof EvpFinalDigestOperationStep +{ + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + super.getPrimaryAlgorithmValueConsumer() = result + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + super.getOutputStepFlowingToStep(DigestIO()).getOutput(DigestIO()) = result + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + super.getDominatingInitializersToStep(PlaintextIO()).getInput(PlaintextIO()) = result + } +} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/KeyGenOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/KeyGenOperation.qll new file mode 100644 index 000000000000..2c146aec97f5 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/KeyGenOperation.qll @@ -0,0 +1,204 @@ +private import experimental.quantum.Language +private import OpenSSLOperationBase +private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers + +/** + * A call to EC_KEY_generate_key, which is used to generate an EC key pair. + * Note: this is an operation, though the input parameter is a "EC_KEY*". + * EC_KEY is really an empty context for a key that hasn't been generated, hence + * we consider this an operation generating a key and not accepting a key input. + */ +class ECKeyGen extends OperationStep instanceof Call { + //, Crypto::KeyGenerationOperationInstance { + ECKeyGen() { this.(Call).getTarget().getName() = "EC_KEY_generate_key" } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.(Call).getArgument(0) and type = ContextIO() + } + + override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = KeyIO() } + + override OperationStepType getStepType() { result = ContextCreationStep() } +} + +/** + * A call to EVP_PKEY_keygen_init or EVP_PKEY_paramgen_init. + */ +class EvpKeyGenInitialize extends OperationStep { + EvpKeyGenInitialize() { + this.getTarget().getName() in [ + "EVP_PKEY_keygen_init", + "EVP_PKEY_paramgen_init" + ] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override OperationStepType getStepType() { result = InitializerStep() } +} + +abstract class KeyGenFinalOperationStep extends OperationStep { + override OperationStepType getStepType() { result = FinalStep() } +} + +/** + * A call to `EVP_PKEY_Q_keygen` + */ +class EvpPKeyQKeyGen extends KeyGenFinalOperationStep instanceof Call { + EvpPKeyQKeyGen() { this.getTarget().getName() = "EVP_PKEY_Q_keygen" } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this and type = KeyIO() + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + // When arg 3 is a derived type, it is a curve name, otherwise it is a key size for RSA if provided + // and arg 2 is the algorithm type + this.getArgument(3).getType().getUnderlyingType() instanceof DerivedType and + result.asExpr() = this.getArgument(3) and + type = PrimaryAlgorithmIO() + or + not this.getArgument(3).getType().getUnderlyingType() instanceof DerivedType and + result.asExpr() = this.getArgument(2) and + type = PrimaryAlgorithmIO() + or + not this.getArgument(3).getType().getUnderlyingType() instanceof DerivedType and + result.asExpr() = this.getArgument(3) and + type = KeySizeIO() + } +} + +/** + * A call to `EVP_RSA_gen` + */ +class EvpRsaGen extends KeyGenFinalOperationStep instanceof Call { + EvpRsaGen() { this.getTarget().getName() = "EVP_RSA_gen" } + + override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = KeyIO() } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = KeySizeIO() + } +} + +/** + * A call to RSA_generate_key + */ +class RsaGenerateKey extends KeyGenFinalOperationStep instanceof Call { + RsaGenerateKey() { this.getTarget().getName() = "RSA_generate_key" } + + override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = KeyIO() } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = KeySizeIO() + } +} + +/** + * A call to RSA_generate_key_ex + */ +class RsaGenerateKeyEx extends KeyGenFinalOperationStep instanceof Call { + RsaGenerateKeyEx() { this.getTarget().getName() = "RSA_generate_key_ex" } + + override DataFlow::Node getOutput(IOType type) { + result.asDefiningArgument() = this.getArgument(0) and type = KeyIO() + } + + override DataFlow::Node getInput(IOType type) { + // arg 0 comes in as a blank RSA key, which we consider a context, + // on output it is considered a key + result.asExpr() = this.getArgument(0) and type = ContextIO() + } +} + +/** + * A call to `EVP_PKEY_generate` or `EVP_PKEY_keygen`. + */ +class EvpPkeyGen extends KeyGenFinalOperationStep instanceof Call { + EvpPkeyGen() { this.getTarget().getName() in ["EVP_PKEY_generate", "EVP_PKEY_keygen"] } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asDefiningArgument() = this.getArgument(1) and type = KeyIO() + or + result.asExpr() = this.getArgument(0) and type = ContextIO() + } +} + +/** + * A call to `EVP_PKEY_new_mac_key` that creates a new generic MAC key. + * - EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e, const unsigned char *key, int keylen); + */ +class EvpNewMacKey extends KeyGenFinalOperationStep { + EvpNewMacKey() { this.getTarget().getName() = "EVP_PKEY_new_mac_key" } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + // the raw key that is configured into the output key + result.asExpr() = this.getArgument(2) and type = KeyIO() + or + result.asExpr() = this.getArgument(3) and type = KeySizeIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this and type = KeyIO() + or + result.asExpr() = this.getArgument(0) and type = ContextIO() + } +} + +/// TODO: https://docs.openssl.org/3.0/man3/EVP_PKEY_new/#synopsis +/** + * An `KeyGenerationOperationInstance` for the for all key gen final operation steps. + */ +class KeyGenOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGenFinalOperationStep +{ + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + super.getPrimaryAlgorithmValueConsumer() = result + } + + override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() } + + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { + super.getOutputStepFlowingToStep(KeyIO()).getOutput(KeyIO()) = result + } + + override predicate hasKeyValueConsumer() { + exists(OperationStep s | s.flowsToOperationStep(this) and s.setsValue(KeyIO())) + } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + super.getDominatingInitializersToStep(KeySizeIO()).getInput(KeySizeIO()) = result + } + + override int getKeySizeFixed() { + none() + // TODO: marked as none as the operation itself has no key size, it + // comes from the algorithm source, but note we could grab the + // algorithm source and get the key size (see below). + // We may need to reconsider what is the best approach here. + // result = + // this.getAnAlgorithmValueConsumer() + // .getAKnownAlgorithmSource() + // .(Crypto::EllipticCurveInstance) + // .getKeySize() + } + + override Crypto::ConsumerInputDataFlowNode getKeyValueConsumer() { + super.getDominatingInitializersToStep(KeyIO()).getInput(KeyIO()) = result + } +} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index 34d7f6acec8c..f1ab394ad787 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -1,316 +1,523 @@ private import experimental.quantum.Language -private import experimental.quantum.OpenSSL.AvcFlow -private import experimental.quantum.OpenSSL.CtxFlow -private import experimental.quantum.OpenSSL.KeyFlow private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +import semmle.code.cpp.dataflow.new.DataFlow // Importing these intializers here to ensure the are part of any model that is // using OpenSslOperationBase. This further ensures that initializers are tied to opeartions // even if only importing the operation by itself. import EVPPKeyCtxInitializer -module EncValToInitEncArgConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] } - - predicate isSink(DataFlow::Node sink) { - exists(EvpKeyOperationSubtypeInitializer initCall | - sink.asExpr() = initCall.getKeyOperationSubtypeArg() +/** + * An openSSL CTX type, which is type for which the stripped underlying type + * matches the pattern 'evp_%ctx_%st'. + * This includes types like: + * - EVP_CIPHER_CTX + * - EVP_MD_CTX + * - EVP_PKEY_CTX + */ +class CtxType extends Type { + CtxType() { + // It is possible for users to use the underlying type of the CTX variables + // these have a name matching 'evp_%ctx_%st + this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st") + or + // In principal the above check should be sufficient, but in case of build mode none issues + // i.e., if a typedef cannot be resolved, + // or issues with properly stubbing test cases, we also explicitly check for the wrapping type defs + // i.e., patterns matching 'EVP_%_CTX' + exists(Type base | base = this or base = this.(DerivedType).getBaseType() | + base.getName().matches("EVP_%_CTX") ) } } -module EncValToInitEncArgFlow = DataFlow::Global; - -private predicate argToAvc(Expr arg, Crypto::AlgorithmValueConsumer avc) { - // NOTE: because we trace through keys to their sources we must consider that the arg is an avc - // Consider this example: - // EVP_PKEY *pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, key_len); - // The key may trace into a signing operation. Tracing through the key we will get the arg taking `EVP_PKEY_HMAC` - // as the algorithm value consumer (the input node of the AVC). The output node of this AVC - // is the call return of `EVP_PKEY_new_mac_key`. If we trace from the AVC result to - // the input argument this will not be possible (from the return to the call argument is a backwards flow). - // Therefore, we must consider the input node of the AVC as the argument. - // This should only occur due to tracing through keys to find configuration data. - avc.getInputNode().asExpr() = arg - or - AvcToCallArgFlow::flow(avc.(OpenSslAlgorithmValueConsumer).getResultNode(), - DataFlow::exprNode(arg)) +/** + * A pointer to a CtxType + */ +class CtxPointerExpr extends Expr { + CtxPointerExpr() { + this.getType() instanceof CtxType and + this.getType() instanceof PointerType + } } /** - * A class for all OpenSsl operations. + * A call argument of type CtxPointerExpr. */ -abstract class OpenSslOperation extends Crypto::OperationInstance instanceof Call { - /** - * Gets the argument that specifies the algorithm for the operation. - * This argument might not be immediately present at the specified operation. - * For example, it might be set in an initialization call. - * Modelers of the operation are resonsible for linking the operation to any - * initialization calls, and providing that argument as a returned value here. - */ - abstract Expr getAlgorithmArg(); +class CtxPointerArgument extends CtxPointerExpr { + CtxPointerArgument() { exists(Call c | c.getAnArgument() = this) } - /** - * Algorithm is specified in initialization call or is implicitly established by the key. - */ - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - argToAvc(this.getAlgorithmArg(), result) - } + Call getCall() { result.getAnArgument() = this } } /** - * A Call to an initialization function for an operation. - * These are not operations in the sense of Crypto::OperationInstance, - * but they are used to initialize the context for the operation. - * There may be multiple initialization calls for the same operation. - * Intended for use with EvPOperation. + * The type of inputs and ouputs for an `OperationStep`. */ -abstract class EvpInitializer extends Call { - /** - * Gets the context argument or return that ties together initialization, updates and/or final calls. - * The context is the context coming into the initializer and is the output as well. - * This is assumed to be the same argument. - */ - abstract CtxPointerSource getContext(); +newtype TIOType = + CiphertextIO() or + // Used for typical CTX types, but not for OSSL_PARAM or OSSL_LIB_CTX + // For OSSL_PARAM and OSSL_LIB_CTX use of OsslParamIO and OsslLibContextIO + ContextIO() or + DigestIO() or + HashAlgorithmIO() or + IVorNonceIO() or + KeyIO() or + KeyOperationSubtypeIO() or + KeySizeIO() or + // Used for OSSL_LIB_CTX + OsslLibContextIO() or + // Used for OSSL_PARAM + OsslParamIO() or + MacIO() or + PaddingAlgorithmIO() or + // Plaintext also includes a message for digest, signature, verification, and mac generation + PlaintextIO() or + PrimaryAlgorithmIO() or + RandomSourceIO() or + SaltLengthIO() or + SeedIO() or + SignatureIO() + +private string ioTypeToString(TIOType t) { + t = CiphertextIO() and result = "CiphertextIO" + or + t = ContextIO() and result = "ContextIO" + or + t = DigestIO() and result = "DigestIO" + or + t = HashAlgorithmIO() and result = "HashAlgorithmIO" + or + t = IVorNonceIO() and result = "IVorNonceIO" + or + t = KeyIO() and result = "KeyIO" + or + t = KeyOperationSubtypeIO() and result = "KeyOperationSubtypeIO" + or + t = KeySizeIO() and result = "KeySizeIO" + or + t = OsslLibContextIO() and result = "OsslLibContextIO" + or + t = OsslParamIO() and result = "OsslParamIO" + or + t = MacIO() and result = "MacIO" + or + t = PaddingAlgorithmIO() and result = "PaddingAlgorithmIO" + or + t = PlaintextIO() and result = "PlaintextIO" + or + t = PrimaryAlgorithmIO() and result = "PrimaryAlgorithmIO" + or + t = RandomSourceIO() and result = "RandomSourceIO" + or + t = SaltLengthIO() and result = "SaltLengthIO" + or + t = SeedIO() and result = "SeedIO" + or + t = SignatureIO() and result = "SignatureIO" +} + +class IOType extends TIOType { + string toString() { + result = ioTypeToString(this) + or + not exists(ioTypeToString(this)) and result = "UnknownIOType" + } } +//TODO: add more initializers as needed /** - * A call to initialize a key size. + * The type of step in an `OperationStep`. + * - `ContextCreationStep`: the creation of a context from an algorithm or key. + * for example `EVP_MD_CTX_create(EVP_sha256())` or `EVP_PKEY_CTX_new(pkey, NULL)` + * - `InitializerStep`: the initialization of an operation through some sort of shared/accumulated context + * for example `EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)` + * - `UpdateStep`: any operation that has and update/final paradigm, the update represents an intermediate step in an operation, + * such as `EVP_DigestUpdate(ctx, data, len)` + * - `FinalStep`: an ultimate operation step. This may be an explicit 'final' in an update/final paradigm, but not necessarily. + * Any operation that does nto operate through an update/final paradigm is considered a final step. */ -abstract class EvpKeySizeInitializer extends EvpInitializer { - abstract Expr getKeySizeArg(); -} +newtype OperationStepType = + // Context creation captures cases where a context is created from an algorithm or key + // + ContextCreationStep() or + InitializerStep() or + UpdateStep() or + FinalStep() /** - * A call to initialize a key operation subtype. + * A step in configuring an operation. + * Captures creation of contexts from algorithms or keys, + * initalization of configurations on contexts, + * update operations (intermediate steps in an operation) + * and the operation itself. + * + * NOTE: if an operation is configured through a means other than a call + * e.g., a pattern like ctx->alg = EVP_sha256() + * then this class will need to be modified to account for that paradigm. + * Currently, this is not a known pattern in OpenSSL. */ -abstract class EvpKeyOperationSubtypeInitializer extends EvpInitializer { - abstract Expr getKeyOperationSubtypeArg(); +abstract class OperationStep extends Call { + /** + * Gets the output nodes from the given operation step. + * These are the nodes that flow connecting this step + * to any other step in the operation should follow. + */ + abstract DataFlow::Node getOutput(IOType type); - private Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) { - i = 0 and - result instanceof Crypto::TEncryptMode - or - i = 1 and result instanceof Crypto::TDecryptMode + /** + * Gets any output node from the given operation step. + */ + final DataFlow::Node getAnOutput() { result = this.getOutput(_) } + + /** + * Gets the input nodes for the given operation step. + */ + abstract DataFlow::Node getInput(IOType type); + + /** + * Gets any input node for the given operation step. + */ + final DataFlow::Node getAnInput() { result = this.getInput(_) } + + /** + * Gets the type of the step, e.g., ContextCreationStep, InitializerStep, UpdateStep, FinalStep. + */ + abstract OperationStepType getStepType(); + + /** + * Holds if this operation step flows to the given `OperationStep` `sink`. + * If `sink` is `this`, then this holds true. + */ + predicate flowsToOperationStep(OperationStep sink) { + sink = this or + OperationStepFlow::flow(this.getAnOutput(), sink.getAnInput()) + } + + /** + * Holds if this operation step flows from the given `OperationStep` (`source`). + * If `source` is `this`, then this holds true. + */ + predicate flowsFromOperationStep(OperationStep source) { + source = this or + OperationStepFlow::flow(source.getAnOutput(), this.getAnInput()) } - Crypto::KeyOperationSubtype getKeyOperationSubtype() { - exists(DataFlow::Node a, DataFlow::Node b | - EncValToInitEncArgFlow::flow(a, b) and - b.asExpr() = this.getKeyOperationSubtypeArg() and - result = this.intToCipherOperationSubtype(a.asExpr().getValue().toInt()) + /** + * Holds if this operation step sets a value of the given `IOType`. + */ + predicate setsValue(IOType type) { exists(this.getInput(type)) } + + /** + * Gets operation steps that flow to `this` and set the given `IOType`. + * This checks for the last initializers that flow to the `this`, + * i.e., if a value is set then re-set, the last set operation step is returned, + * not both. + * Note: Any 'update' that sets a value is not considered to be 'resetting' an input. + * I.e., there is a difference between changing a configuration before use and + * the operation allows for multiple inputs (like plaintext for cipher update calls before final). + */ + OperationStep getDominatingInitializersToStep(IOType type) { + result.flowsToOperationStep(this) and + result.setsValue(type) and + ( + // Do not consider a 'reset' to occur on updates + result.getStepType() = UpdateStep() + or + not exists(OperationStep reset | + result != reset and + reset.setsValue(type) and + reset.flowsToOperationStep(this) and + result.flowsToOperationStep(reset) + ) ) - or - // Infer the subtype from the initialization call, and ignore the argument - this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") and - result instanceof Crypto::TEncryptMode - or - this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") and - result instanceof Crypto::TDecryptMode } -} -/** - * An primary algorithm initializer initializes the primary algorithm for a given operation. - * For example, for a signing operation, the algorithm initializer may initialize algorithms - * like RSA. Other algorithsm may be initialized on an operation, as part of a larger - * operation/protocol. For example, hashing operations on signing operations; however, - * these are not the primary algorithm. Any other algorithms initialized on an operation - * require a specialized initializer, such as EvpHashAlgorithmInitializer. - */ -abstract class EvpPrimaryAlgorithmInitializer extends EvpInitializer { - abstract Expr getAlgorithmArg(); + /** + * Gets all output of `type` that flow to `this` + * if `this` is a final step and the output is not from + * a separate final step. + */ + OperationStep getOutputStepFlowingToStep(IOType type) { + this.getStepType() = FinalStep() and + result.flowsToOperationStep(this) and + exists(result.getOutput(type)) and + (result = this or result.getStepType() != FinalStep()) + } - Crypto::AlgorithmValueConsumer getAlgorithmValueConsumer() { - argToAvc(this.getAlgorithmArg(), result) + /** + * Gets an AVC for the primary algorithm for this operation. + * A primary algorithm is an AVC that flows to a ctx input directly or + * an AVC that flows to a primary algorithm input directly. + * See `AvcContextCreationStep` for details about resetting scenarios. + * Gets the first OperationStep an AVC flows to. If a context input, + * the AVC is considered primary. + * If a primary algorithm input, then get the last set primary algorithm + * operation step (dominating operation step, see `getDominatingInitializersToStep`). + */ + Crypto::AlgorithmValueConsumer getPrimaryAlgorithmValueConsumer() { + exists(DataFlow::Node src, DataFlow::Node sink, IOType t, OperationStep avcSucc | + (t = PrimaryAlgorithmIO() or t = ContextIO()) and + avcSucc.flowsToOperationStep(this) and + src.asExpr() = result and + sink = avcSucc.getInput(t) and + AvcToOperationStepFlow::flow(src, sink) and + ( + // Case 1: the avcSucc step is a dominating initialization step + t = PrimaryAlgorithmIO() and + avcSucc = this.getDominatingInitializersToStep(PrimaryAlgorithmIO()) + or + // Case 2: the succ is a context input (any avcSucc is valid) + t = ContextIO() + ) + ) } -} -/** - * A call to initialize a key. - */ -abstract class EvpKeyInitializer extends EvpInitializer { - abstract Expr getKeyArg(); + /** + * Gets the algorithm value consumer for an input to `this` operation step + * of the given `type`. + * TODO: generalize to use this for `getPrimaryAlgorithmValueConsumer` + */ + Crypto::AlgorithmValueConsumer getAlgorithmValueConsumerForInput(IOType type) { + exists(DataFlow::Node src, DataFlow::Node sink | + AvcToOperationStepFlow::flow(src, sink) and + src.asExpr() = result and + sink = this.getInput(type) + ) + } } /** - * A key initializer may initialize the algorithm and the key size through - * the key. Extend any instance of key initializer provide initialization - * of the algorithm and key size from the key. + * An AVC is considered to output a 'context type', however, + * each AVC has it's own output types in practice. + * Some output algorithm containers (`EVP_get_cipherbyname`) + * some output explicit contexts (`EVP_PKEY_CTX_new_from_name`). + * The output of an AVC cannot be determined to be a primary algorithm (PrimaryAlgorithmIO), that depends + * on the use of the AVC output. + * The use is assumed to be of two forms: + * - The AVC output flows to a known input that accepts an algorithm + * e.g., `EVP_DigestInit(ctx, type)` the `type` parameter is known to be the primary algorithm. + * `EVP_SignInit(ctx, type)` the `type` parameter is known to be a digest algorithm for the signature. + * - The AVC output flows to a context initialization step + * e.g., `pkey_ctx = EVP_PKEY_CTX_new_from_name(libctx, name, propquery)` this is an AVC call, but the + * API says the output is a context. It is consumed typically by something like: + * `ctx = EVP_PKEY_keygen_init(pkey_ctx)`, but note I cannot consider the `pkey_ctx` parameter to always be a primary algorithm, + * a key gen can be inited by a prior key as well, e.g., `ctx = EVP_PKEY_CTX_new(pkey, NULL)`. + * Hence, these initialization steps take in a context that may have come from an AVC or something else, + * and therefore cannot be considered a primary algorithm. + * Assumption: The first operation step an AVC flows to will be of the above two forms. + * Resetting Algorithm Concerns and Assumptions: + * What if a user resets the algorithm through another AVC call? + * How would we detect that and only look at the 'dominating' (last set) AVC? + * From an AVC, always assess the first operation step it flows to. + * If the first step is to a context input, then we assume that reset is not possible in the same path. + * I.e., a user cannot reset the algorithm without starting an entirely new operation step chain. + * See the use patterns for `pkey_ctx = EVP_PKEY_CTX_new_from_name(...)` mentioned above. A user cannot + * reset the algorithm without calling a new `ctx = EVP_PKEY_keygen_init(pkey_ctx)`, + * i.e., subsequent flow follows the `ctx` output. + * If the first step is to any other input, then we use the `getDominatingInitializersToStep` + * to find the last AVC that set the algorithm for the operation step. + * Domination checks must occur at an operation step (e.g., at a final operation). + * This operation step does not find the dominating AVC. + * If a primary algorithm is explicitly set and and AVC is set through a context input, + * we will use both cases as primary inputs. */ -class EvpInitializerThroughKey extends EvpPrimaryAlgorithmInitializer, EvpKeySizeInitializer, - EvpKeyInitializer -{ - Expr arg; - CtxPointerSource context; - - EvpInitializerThroughKey() { - exists(EvpKeyInitializer keyInit | - arg = keyInit.getKeyArg() and this = keyInit and context = keyInit.getContext() - ) +class AvcContextCreationStep extends OperationStep instanceof OpenSslAlgorithmValueConsumer { + override DataFlow::Node getOutput(IOType type) { + type = ContextIO() and result = super.getResultNode() } - override CtxPointerSource getContext() { result = context } + override DataFlow::Node getInput(IOType type) { none() } - override Expr getAlgorithmArg() { - result = - getSourceKeyCreationInstanceFromArg(this.getKeyArg()).(OpenSslOperation).getAlgorithmArg() - } + override OperationStepType getStepType() { result = ContextCreationStep() } +} - override Expr getKeySizeArg() { - result = getSourceKeyCreationInstanceFromArg(this.getKeyArg()).getKeySizeConsumer().asExpr() - } +abstract private class CtxPassThroughCall extends Call { + abstract DataFlow::Node getNode1(); - override Expr getKeyArg() { result = arg } + abstract DataFlow::Node getNode2(); } /** - * A default initializer for any key operation that accepts a key as input. - * A key initializer allows for a mechanic to go backwards to the key creation operation - * and find the algorithm and key size. - * If a user were to stipualte a key consumer for an operation but fail to indicate it as an - * initializer, automatic tracing to the creation operation would not occur. - * USERS SHOULD NOT NEED TO USE OR EXTEND THIS CLASS DIRECTLY. - * - * TODO: re-evaluate this approach + * A call whose target contains 'free' or 'reset' and has an argument of type + * CtxPointerArgument. */ -class DefaultKeyInitializer extends EvpKeyInitializer instanceof Crypto::KeyOperationInstance { - Expr arg; - - DefaultKeyInitializer() { - exists(Call c | - c.getAChild*() = arg and - arg = this.(Crypto::KeyOperationInstance).getKeyConsumer().asExpr() and - c = this - ) +private class CtxClearCall extends Call { + CtxClearCall() { + this.getTarget().getName().toLowerCase().matches(["%free%", "%reset%"]) and + this.getAnArgument() instanceof CtxPointerArgument } +} - override Expr getKeyArg() { result = arg } +/** + * A call whose target contains 'copy' and has an argument of type + * CtxPointerArgument. + */ +private class CtxCopyOutArgCall extends CtxPassThroughCall { + DataFlow::Node n1; + DataFlow::Node n2; + + CtxCopyOutArgCall() { + this.getTarget().getName().toLowerCase().matches("%copy%") and + n1.asExpr() = this.getAnArgument() and + n1.getType() instanceof CtxType and + n2.asDefiningArgument() = this.getAnArgument() and + n2.getType() instanceof CtxType and + n1.asDefiningArgument() != n2.asExpr() + } - override CtxPointerSource getContext() { result = this.(EvpOperation).getContext() } -} + override DataFlow::Node getNode1() { result = n1 } -abstract class EvpIVInitializer extends EvpInitializer { - abstract Expr getIVArg(); + override DataFlow::Node getNode2() { result = n2 } } /** - * A call to initialize padding. + * A call whose target contains 'dup' and has an argument of type + * CtxPointerArgument. */ -abstract class EvpPaddingInitializer extends EvpInitializer { - /** - * Gets the padding mode argument. - * e.g., `EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING)` argument 1 (0-based) - */ - abstract Expr getPaddingArg(); +private class CtxCopyReturnCall extends CtxPassThroughCall, CtxPointerExpr { + DataFlow::Node n1; + + CtxCopyReturnCall() { + this.getTarget().getName().toLowerCase().matches("%dup%") and + n1.asExpr() = this.getAnArgument() and + n1.getType() instanceof CtxType + } + + override DataFlow::Node getNode1() { result = n1 } + + override DataFlow::Node getNode2() { result.asExpr() = this } } +// TODO: is this still needed? /** - * A call to initialize a salt length. + * A call to `EVP_PKEY_paramgen` acts as a kind of pass through. + * It's output pkey is eventually used in a new operation generating + * a fresh context pointer (e.g., `EVP_PKEY_CTX_new`). + * It is easier to model this as a pass through + * than to model the flow from the paramgen to the new key generation. */ -abstract class EvpSaltLengthInitializer extends EvpInitializer { - /** - * Gets the salt length argument. - * e.g., `EVP_PKEY_CTX_set_scrypt_salt_len(ctx, 16)` argument 1 (0-based) - */ - abstract Expr getSaltLengthArg(); +private class CtxParamGenCall extends CtxPassThroughCall { + DataFlow::Node n1; + DataFlow::Node n2; + + CtxParamGenCall() { + this.getTarget().getName() = "EVP_PKEY_paramgen" and + n1.asExpr() = this.getArgument(0) and + ( + n2.asExpr() = this.getArgument(1) + or + n2.asDefiningArgument() = this.getArgument(1) + ) + } + + override DataFlow::Node getNode1() { result = n1 } + + override DataFlow::Node getNode2() { result = n2 } } +//TODO: I am not sure CallArgToCtxRet is needed anymore /** - * A call to initialize a hash algorithm. + * If the current node is an argument to a function + * that returns a pointer type, immediately flow through. + * NOTE: this passthrough is required if we allow + * intermediate steps to go into variables that are not a CTX type. + * See for example `CtxParamGenCall`. */ -abstract class EvpHashAlgorithmInitializer extends EvpInitializer { - abstract Expr getHashAlgorithmArg(); +private class CallArgToCtxRet extends CtxPassThroughCall, CtxPointerExpr { + DataFlow::Node n1; + DataFlow::Node n2; - Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() { - argToAvc(this.getHashAlgorithmArg(), result) + CallArgToCtxRet() { + this.getAnArgument() = n1.asExpr() and + n2.asExpr() = this } + + override DataFlow::Node getNode1() { result = n1 } + + override DataFlow::Node getNode2() { result = n2 } } /** - * A Call to an "update" function. - * These are not operations in the sense of Crypto::OperationInstance, - * but produce intermediate results for the operation that are later finalized - * (see EvpFinal). - * Intended for use with EvPOperation. + * A flow configuration from any non-final `OperationStep` to any other `OperationStep`. */ -abstract class EvpUpdate extends Call { - /** - * Gets the context argument that ties together initialization, updates and/or final calls. - */ - abstract CtxPointerSource getContext(); +module OperationStepFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(OperationStep s | + s.getAnOutput() = source or + s.getAnInput() = source + ) + } - /** - * Update calls always have some input data like plaintext or message digest. - */ - abstract Expr getInputArg(); + predicate isSink(DataFlow::Node sink) { + exists(OperationStep s | + s.getAnInput() = sink or + s.getAnOutput() = sink + ) + } - /** - * Update calls sometimes have some output data like a plaintext. - */ - Expr getOutputArg() { none() } + predicate isBarrier(DataFlow::Node node) { + exists(CtxClearCall c | c.getAnArgument() = node.asExpr()) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2) + or + // Flow out through all outputs from an operation step if more than one output + // is defined. + exists(OperationStep s | s.getAnInput() = node1 and s.getAnOutput() = node2) + // TODO: consideration for additional alises defined as follows: + // if an output from an operation step itself flows from the output of another operation step + // then the source of that flow's outputs (all of them) are potential aliases + } } +module OperationStepFlow = DataFlow::Global; + /** - * The base class for all operations of the EVP API. - * This captures one-shot APIs (with and without an initilizer call) and final calls. - * Provides some default methods for Crypto::KeyOperationInstance class. + * A flow from AVC to the first `OperationStep` the AVC reaches as an input. */ -abstract class EvpOperation extends OpenSslOperation { - /** - * Gets the context argument that ties together initialization, updates and/or final calls. - */ - abstract CtxPointerSource getContext(); +module AvcToOperationStepFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(AvcContextCreationStep s | s.getAnOutput() = source) + } - /** - * Some input data like plaintext or message digest. - * Either argument provided direcly in the call or all arguments that were provided in update calls. - */ - abstract Expr getInputArg(); + predicate isSink(DataFlow::Node sink) { exists(OperationStep s | s.getAnInput() = sink) } - /** - * Some output data like ciphertext or signature. - */ - abstract Expr getOutputArg(); + predicate isBarrier(DataFlow::Node node) { + exists(CtxClearCall c | c.getAnArgument() = node.asExpr()) + } /** - * Finds the initialization call, may be none. + * Only get the first operation step encountered. */ - EvpInitializer getInitCall() { ctxSrcToSrcFlow(result.getContext(), this.getContext()) } + predicate isBarrierOut(DataFlow::Node node) { isSink(node) } - Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result = DataFlow::exprNode(this.getOutputArg()) + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2) } +} - /** - * Input consumer is the input argument of the call. - */ - Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = DataFlow::exprNode(this.getInputArg()) +module AvcToOperationStepFlow = DataFlow::Global; + +module EncValToInitEncArgConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] } + + predicate isSink(DataFlow::Node sink) { + exists(OperationStep s | sink = s.getInput(KeyOperationSubtypeIO())) } } -/** - * An EVP final call, - * which is typicall used in an update/final pattern. - * Final operations are typically identified by "final" in the name, - * e.g., "EVP_DigestFinal", "EVP_EncryptFinal", etc. - * however, this is not a strict rule. - */ -abstract class EvpFinal extends EvpOperation { - /** - * All update calls that were executed before this final call. - */ - EvpUpdate getUpdateCalls() { ctxSrcToSrcFlow(result.getContext(), this.getContext()) } +module EncValToInitEncArgFlow = DataFlow::Global; - /** - * Gets the input data provided to all update calls. - * If more input data was provided in the final call, override the method. - */ - override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() } +private Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) { + i = 0 and + result instanceof Crypto::TEncryptMode + or + i = 1 and result instanceof Crypto::TDecryptMode +} - /** - * Gets the output data provided to all update calls. - * If more output data was provided in the final call, override the method. - */ - override Expr getOutputArg() { result = this.getUpdateCalls().getOutputArg() } +Crypto::KeyOperationSubtype resolveKeyOperationSubTypeOperationStep(OperationStep s) { + exists(DataFlow::Node src | + EncValToInitEncArgFlow::flow(src, s.getInput(KeyOperationSubtypeIO())) and + result = intToCipherOperationSubtype(src.asExpr().getValue().toInt()) + ) } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll index 78b8f8ce080d..be65ef3e1c05 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll @@ -1,6 +1,5 @@ import OpenSSLOperationBase -import EVPCipherOperation -import EVPHashOperation -import ECKeyGenOperation -import EVPSignatureOperation -import EVPKeyGenOperation +import CipherOperation +import HashOperation +import SignatureOperation +import KeyGenOperation diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/SignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/SignatureOperation.qll new file mode 100644 index 000000000000..b9b498ee8df3 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/SignatureOperation.qll @@ -0,0 +1,260 @@ +/** + * Provides classes for modeling OpenSSL's EVP signature operations + */ + +private import experimental.quantum.Language +private import experimental.quantum.OpenSSL.AvcFlow +private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +private import experimental.quantum.OpenSSL.Operations.OpenSSLOperations + +// TODO: verification functions +/** + * A base class for final signature operations. + */ +abstract class EvpSignatureFinalOperation extends OperationStep { + override OperationStepType getStepType() { result = FinalStep() } +} + +/** + * A call to EVP_DigestSignInit or EVP_DigestSignInit_ex. + */ +class EvpSignatureDigestInitializer extends OperationStep { + EvpSignatureDigestInitializer() { + this.getTarget().getName() in ["EVP_DigestSignInit_ex", "EVP_DigestSignInit"] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + this.getTarget().getName() = "EVP_DigestSignInit_ex" and + result.asExpr() = this.getArgument(3) and + type = OsslLibContextIO() + or + result.asExpr() = this.getArgument(2) and type = HashAlgorithmIO() + or + this.getTarget().getName() = "EVP_DigestSignInit" and + result.asExpr() = this.getArgument(4) and + type = KeyIO() + or + this.getTarget().getName() = "EVP_DigestSignInit_ex" and + result.asExpr() = this.getArgument(5) and + type = KeyIO() + or + this.getTarget().getName() = "EVP_DigestSignInit_ex" and + result.asExpr() = this.getArgument(6) and + type = OsslParamIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + // EVP_PKEY_CTX + result.asExpr() = this.getArgument(1) and type = ContextIO() + or + this.getTarget().getName() = "EVP_DigestSignInit_ex" and + result.asExpr() = this.getArgument(6) and + type = ContextIO() + } + + override OperationStepType getStepType() { result = InitializerStep() } +} + +/** + * A call to EVP_SignInit or EVP_SignInit_ex. + */ +class EvpSignInit extends OperationStep { + EvpSignInit() { this.getTarget().getName() in ["EVP_SignInit", "EVP_SignInit_ex"] } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = HashAlgorithmIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override OperationStepType getStepType() { result = InitializerStep() } +} + +/** + * A call to: + * - EVP_PKEY_sign_init_ex + * - EVP_PKEY_sign_init_ex2 + * - EVP_PKEY_sign_init + * - EVP_PKEY_sign_message_init + */ +class EvpPkeySignInit extends OperationStep { + EvpPkeySignInit() { + this.getTarget().getName() in [ + "EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_init", + "EVP_PKEY_sign_message_init" + ] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + this.getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and + result.asExpr() = this.getArgument(1) and + type = PrimaryAlgorithmIO() + or + this.getTarget().getName() = "EVP_PKEY_sign_init_ex" and + result.asExpr() = this.getArgument(1) and + type = OsslParamIO() + or + // Argument 2 (0 based) only exists for EVP_PKEY_sign_init_ex2 and EVP_PKEY_sign_message_init + result.asExpr() = this.getArgument(2) and type = OsslParamIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override OperationStepType getStepType() { result = InitializerStep() } +} + +/** + * A call to EVP_DIgestSignUpdate, EVP_SignUpdate or EVP_PKEY_sign_message_update. + */ +class EvpSignatureUpdateCall extends OperationStep { + EvpSignatureUpdateCall() { + this.getTarget().getName() in [ + "EVP_DigestSignUpdate", "EVP_SignUpdate", "EVP_PKEY_sign_message_update" + ] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = PlaintextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override OperationStepType getStepType() { result = UpdateStep() } +} + +/** + * A call to EVP_SignFinal or EVP_SignFinal_ex. + */ +class EvpSignFinal extends EvpSignatureFinalOperation { + EvpSignFinal() { this.getTarget().getName() in ["EVP_SignFinal_ex", "EVP_SignFinal"] } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(3) and type = KeyIO() + or + // params above 3 (0-based) only exist for EVP_SignFinal_ex + result.asExpr() = this.getArgument(4) and + type = OsslLibContextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = SignatureIO() + } +} + +/** + * A call to EVP_DigestSign or EVP_PKEY_sign. + */ +class EvpDigestSign extends EvpSignatureFinalOperation { + EvpDigestSign() { this.getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(3) and type = PlaintextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = SignatureIO() + } +} + +/** + * A call to EVP_DigestSignFinal or EVP_PKEY_sign_message_final. + */ +class EvpDigestAndPkeySignFinal extends EvpSignatureFinalOperation { + EvpDigestAndPkeySignFinal() { + this.getTarget().getName() in [ + "EVP_DigestSignFinal", + "EVP_PKEY_sign_message_final" + ] + } + + override DataFlow::Node getInput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + } + + override DataFlow::Node getOutput(IOType type) { + result.asExpr() = this.getArgument(0) and type = ContextIO() + or + result.asExpr() = this.getArgument(1) and type = SignatureIO() + } + + override OperationStepType getStepType() { result = FinalStep() } +} + +/** + * An EVP signature operation instance. + */ +class EvpSignatureOperationInstance extends Crypto::SignatureOperationInstance instanceof EvpSignatureFinalOperation +{ + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + super.getPrimaryAlgorithmValueConsumer() = result + } + + /** + * Signing, verification or unknown. + */ + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + // TODO: if this KeyOperationSubtype does not match initialization call's KeyOperationSubtype then we found a bug + if super.getTarget().getName().toLowerCase().matches("%sign%") + then result instanceof Crypto::TSignMode + else + if super.getTarget().getName().toLowerCase().matches("%verify%") + then result instanceof Crypto::TVerifyMode + else result instanceof Crypto::TUnknownKeyOperationMode + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { + // TODO: some signing operations may have explicit nonce generators + none() + } + + /** + * Keys provided in the initialization call or in a context are found by this method. + * Keys in explicit arguments are found by overridden methods in extending classes. + */ + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + super.getDominatingInitializersToStep(KeyIO()).getInput(KeyIO()) = result + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + super.getOutputStepFlowingToStep(SignatureIO()).getOutput(SignatureIO()) = result + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + super.getDominatingInitializersToStep(PlaintextIO()).getInput(PlaintextIO()) = result + } + + /** + * TODO: only signing operations for now, change when verificaiton is added + */ + override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { none() } + + override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() { + super + .getDominatingInitializersToStep(HashAlgorithmIO()) + .getAlgorithmValueConsumerForInput(HashAlgorithmIO()) = result + } +} diff --git a/cpp/ql/test/experimental/library-tests/quantum/node_edges.expected b/cpp/ql/test/experimental/library-tests/quantum/node_edges.expected index e9e3bf868ae0..27be8e5cfba5 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/node_edges.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/node_edges.expected @@ -30,9 +30,12 @@ | openssl_basic.c:144:13:144:22 | HashOperation | Message | openssl_basic.c:144:24:144:30 | Message | | openssl_basic.c:144:24:144:30 | Message | Source | openssl_basic.c:181:49:181:87 | Constant | | openssl_basic.c:144:46:144:51 | Digest | Source | openssl_basic.c:144:46:144:51 | Digest | +| openssl_basic.c:155:22:155:41 | Key | Algorithm | openssl_basic.c:155:22:155:41 | Key | | openssl_basic.c:155:22:155:41 | KeyGeneration | Algorithm | openssl_basic.c:155:22:155:41 | KeyGeneration | +| openssl_basic.c:155:22:155:41 | KeyGeneration | KeyInput | openssl_basic.c:155:64:155:66 | Key | | openssl_basic.c:155:22:155:41 | KeyGeneration | Output | openssl_basic.c:155:22:155:41 | Key | | openssl_basic.c:155:43:155:55 | MACAlgorithm | H | openssl_basic.c:160:39:160:48 | HashAlgorithm | +| openssl_basic.c:155:64:155:66 | Key | Source | openssl_basic.c:179:43:179:76 | Constant | | openssl_basic.c:160:59:160:62 | Key | Source | openssl_basic.c:155:22:155:41 | Key | | openssl_basic.c:163:35:163:41 | Message | Source | openssl_basic.c:181:49:181:87 | Constant | | openssl_basic.c:167:9:167:27 | SignOperation | Algorithm | openssl_basic.c:167:9:167:27 | SignOperation | @@ -40,8 +43,11 @@ | openssl_basic.c:167:9:167:27 | SignOperation | Input | openssl_basic.c:163:35:163:41 | Message | | openssl_basic.c:167:9:167:27 | SignOperation | Key | openssl_basic.c:160:59:160:62 | Key | | openssl_basic.c:167:9:167:27 | SignOperation | Output | openssl_basic.c:167:34:167:36 | SignatureOutput | +| openssl_pkey.c:21:10:21:28 | KeyGeneration | Algorithm | openssl_pkey.c:21:10:21:28 | KeyGeneration | +| openssl_pkey.c:21:10:21:28 | KeyGeneration | Output | openssl_pkey.c:21:30:21:32 | Key | | openssl_pkey.c:21:10:21:28 | KeyOperationAlgorithm | Mode | openssl_pkey.c:21:10:21:28 | KeyOperationAlgorithm | | openssl_pkey.c:21:10:21:28 | KeyOperationAlgorithm | Padding | openssl_pkey.c:21:10:21:28 | KeyOperationAlgorithm | +| openssl_pkey.c:21:30:21:32 | Key | Algorithm | openssl_pkey.c:21:30:21:32 | Key | | openssl_pkey.c:50:31:50:42 | KeyOperationAlgorithm | Mode | openssl_pkey.c:50:31:50:42 | KeyOperationAlgorithm | | openssl_pkey.c:50:31:50:42 | KeyOperationAlgorithm | Padding | openssl_pkey.c:50:31:50:42 | KeyOperationAlgorithm | | openssl_pkey.c:55:9:55:23 | KeyGeneration | Algorithm | openssl_pkey.c:50:31:50:42 | KeyOperationAlgorithm | @@ -77,6 +83,13 @@ | openssl_signature.c:133:52:133:55 | Key | Source | openssl_signature.c:548:34:548:37 | Key | | openssl_signature.c:133:52:133:55 | Key | Source | openssl_signature.c:578:34:578:37 | Key | | openssl_signature.c:134:38:134:44 | Message | Source | openssl_signature.c:602:37:602:77 | Constant | +| openssl_signature.c:135:9:135:27 | SignOperation | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | +| openssl_signature.c:135:9:135:27 | SignOperation | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | +| openssl_signature.c:135:9:135:27 | SignOperation | HashAlgorithm | openssl_signature.c:684:24:684:33 | HashAlgorithm | +| openssl_signature.c:135:9:135:27 | SignOperation | HashAlgorithm | openssl_signature.c:740:24:740:33 | HashAlgorithm | +| openssl_signature.c:135:9:135:27 | SignOperation | Input | openssl_signature.c:134:38:134:44 | Message | +| openssl_signature.c:135:9:135:27 | SignOperation | Key | openssl_signature.c:133:52:133:55 | Key | +| openssl_signature.c:135:9:135:27 | SignOperation | Output | openssl_signature.c:135:37:135:40 | SignatureOutput | | openssl_signature.c:142:9:142:27 | SignOperation | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | | openssl_signature.c:142:9:142:27 | SignOperation | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | | openssl_signature.c:142:9:142:27 | SignOperation | HashAlgorithm | openssl_signature.c:684:24:684:33 | HashAlgorithm | @@ -87,6 +100,13 @@ | openssl_signature.c:190:57:190:60 | Key | Source | openssl_signature.c:548:34:548:37 | Key | | openssl_signature.c:190:57:190:60 | Key | Source | openssl_signature.c:578:34:578:37 | Key | | openssl_signature.c:196:38:196:44 | Message | Source | openssl_signature.c:602:37:602:77 | Constant | +| openssl_signature.c:197:9:197:27 | SignOperation | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | +| openssl_signature.c:197:9:197:27 | SignOperation | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | +| openssl_signature.c:197:9:197:27 | SignOperation | HashAlgorithm | openssl_signature.c:684:24:684:33 | HashAlgorithm | +| openssl_signature.c:197:9:197:27 | SignOperation | HashAlgorithm | openssl_signature.c:740:24:740:33 | HashAlgorithm | +| openssl_signature.c:197:9:197:27 | SignOperation | Input | openssl_signature.c:196:38:196:44 | Message | +| openssl_signature.c:197:9:197:27 | SignOperation | Key | openssl_signature.c:190:57:190:60 | Key | +| openssl_signature.c:197:9:197:27 | SignOperation | Output | openssl_signature.c:197:37:197:40 | SignatureOutput | | openssl_signature.c:204:9:204:27 | SignOperation | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | | openssl_signature.c:204:9:204:27 | SignOperation | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | | openssl_signature.c:204:9:204:27 | SignOperation | HashAlgorithm | openssl_signature.c:684:24:684:33 | HashAlgorithm | @@ -96,6 +116,14 @@ | openssl_signature.c:204:9:204:27 | SignOperation | Output | openssl_signature.c:204:37:204:46 | SignatureOutput | | openssl_signature.c:260:39:260:42 | Key | Source | openssl_signature.c:548:34:548:37 | Key | | openssl_signature.c:260:39:260:42 | Key | Source | openssl_signature.c:578:34:578:37 | Key | +| openssl_signature.c:263:9:263:21 | SignOperation | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | +| openssl_signature.c:263:9:263:21 | SignOperation | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | +| openssl_signature.c:263:9:263:21 | SignOperation | HashAlgorithm | openssl_signature.c:684:24:684:33 | HashAlgorithm | +| openssl_signature.c:263:9:263:21 | SignOperation | HashAlgorithm | openssl_signature.c:740:24:740:33 | HashAlgorithm | +| openssl_signature.c:263:9:263:21 | SignOperation | Input | openssl_signature.c:263:54:263:59 | Message | +| openssl_signature.c:263:9:263:21 | SignOperation | Key | openssl_signature.c:260:39:260:42 | Key | +| openssl_signature.c:263:9:263:21 | SignOperation | Output | openssl_signature.c:263:33:263:36 | SignatureOutput | +| openssl_signature.c:263:54:263:59 | Message | Source | openssl_signature.c:263:54:263:59 | Message | | openssl_signature.c:270:9:270:21 | SignOperation | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | | openssl_signature.c:270:9:270:21 | SignOperation | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | | openssl_signature.c:270:9:270:21 | SignOperation | HashAlgorithm | openssl_signature.c:684:24:684:33 | HashAlgorithm | @@ -107,6 +135,14 @@ | openssl_signature.c:321:39:321:42 | Key | Source | openssl_signature.c:548:34:548:37 | Key | | openssl_signature.c:321:39:321:42 | Key | Source | openssl_signature.c:578:34:578:37 | Key | | openssl_signature.c:326:48:326:54 | Message | Source | openssl_signature.c:602:37:602:77 | Constant | +| openssl_signature.c:327:9:327:35 | SignOperation | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | +| openssl_signature.c:327:9:327:35 | SignOperation | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | +| openssl_signature.c:327:9:327:35 | SignOperation | Algorithm | openssl_signature.c:702:60:702:71 | KeyOperationAlgorithm | +| openssl_signature.c:327:9:327:35 | SignOperation | Algorithm | openssl_signature.c:758:60:758:64 | KeyOperationAlgorithm | +| openssl_signature.c:327:9:327:35 | SignOperation | HashAlgorithm | openssl_signature.c:327:9:327:35 | SignOperation | +| openssl_signature.c:327:9:327:35 | SignOperation | Input | openssl_signature.c:326:48:326:54 | Message | +| openssl_signature.c:327:9:327:35 | SignOperation | Key | openssl_signature.c:321:39:321:42 | Key | +| openssl_signature.c:327:9:327:35 | SignOperation | Output | openssl_signature.c:327:47:327:50 | SignatureOutput | | openssl_signature.c:334:9:334:35 | SignOperation | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | | openssl_signature.c:334:9:334:35 | SignOperation | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | | openssl_signature.c:334:9:334:35 | SignOperation | Algorithm | openssl_signature.c:702:60:702:71 | KeyOperationAlgorithm | @@ -120,7 +156,9 @@ | openssl_signature.c:548:9:548:23 | KeyGeneration | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | | openssl_signature.c:548:9:548:23 | KeyGeneration | Output | openssl_signature.c:548:34:548:37 | Key | | openssl_signature.c:548:34:548:37 | Key | Algorithm | openssl_signature.c:543:35:543:46 | KeyOperationAlgorithm | +| openssl_signature.c:575:32:575:37 | Key | Source | openssl_signature.c:575:32:575:37 | Key | | openssl_signature.c:578:9:578:23 | KeyGeneration | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | +| openssl_signature.c:578:9:578:23 | KeyGeneration | KeyInput | openssl_signature.c:575:32:575:37 | Key | | openssl_signature.c:578:9:578:23 | KeyGeneration | Output | openssl_signature.c:578:34:578:37 | Key | | openssl_signature.c:578:34:578:37 | Key | Algorithm | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | | openssl_signature.c:702:60:702:71 | KeyOperationAlgorithm | Padding | openssl_signature.c:702:60:702:71 | KeyOperationAlgorithm | diff --git a/cpp/ql/test/experimental/library-tests/quantum/node_properties.expected b/cpp/ql/test/experimental/library-tests/quantum/node_properties.expected index 1ac047ad334e..52a7c61502bd 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/node_properties.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/node_properties.expected @@ -20,9 +20,10 @@ | openssl_basic.c:144:67:144:73 | HashAlgorithm | DigestSize | 128 | openssl_basic.c:144:67:144:73 | openssl_basic.c:144:67:144:73 | | openssl_basic.c:144:67:144:73 | HashAlgorithm | Name | MD5 | openssl_basic.c:144:67:144:73 | openssl_basic.c:144:67:144:73 | | openssl_basic.c:144:67:144:73 | HashAlgorithm | RawName | EVP_md5 | openssl_basic.c:144:67:144:73 | openssl_basic.c:144:67:144:73 | -| openssl_basic.c:155:22:155:41 | Key | KeyType | Symmetric | openssl_basic.c:155:22:155:41 | openssl_basic.c:155:22:155:41 | +| openssl_basic.c:155:22:155:41 | Key | KeyType | Asymmetric | openssl_basic.c:155:22:155:41 | openssl_basic.c:155:22:155:41 | | openssl_basic.c:155:43:155:55 | MACAlgorithm | Name | HMAC | openssl_basic.c:155:43:155:55 | openssl_basic.c:155:43:155:55 | | openssl_basic.c:155:43:155:55 | MACAlgorithm | RawName | 855 | openssl_basic.c:155:43:155:55 | openssl_basic.c:155:43:155:55 | +| openssl_basic.c:155:64:155:66 | Key | KeyType | Unknown | openssl_basic.c:155:64:155:66 | openssl_basic.c:155:64:155:66 | | openssl_basic.c:160:39:160:48 | HashAlgorithm | DigestSize | 256 | openssl_basic.c:160:39:160:48 | openssl_basic.c:160:39:160:48 | | openssl_basic.c:160:39:160:48 | HashAlgorithm | Name | SHA2 | openssl_basic.c:160:39:160:48 | openssl_basic.c:160:39:160:48 | | openssl_basic.c:160:39:160:48 | HashAlgorithm | RawName | EVP_sha256 | openssl_basic.c:160:39:160:48 | openssl_basic.c:160:39:160:48 | @@ -34,6 +35,7 @@ | openssl_basic.c:218:32:218:33 | Constant | Description | 32 | openssl_basic.c:218:32:218:33 | openssl_basic.c:218:32:218:33 | | openssl_pkey.c:21:10:21:28 | KeyOperationAlgorithm | Name | RSA | openssl_pkey.c:21:10:21:28 | openssl_pkey.c:21:10:21:28 | | openssl_pkey.c:21:10:21:28 | KeyOperationAlgorithm | RawName | RSA_generate_key_ex | openssl_pkey.c:21:10:21:28 | openssl_pkey.c:21:10:21:28 | +| openssl_pkey.c:21:30:21:32 | Key | KeyType | Asymmetric | openssl_pkey.c:21:30:21:32 | openssl_pkey.c:21:30:21:32 | | openssl_pkey.c:45:49:45:65 | Constant | Description | Hello, OpenSSL! | openssl_pkey.c:45:49:45:65 | openssl_pkey.c:45:49:45:65 | | openssl_pkey.c:50:31:50:42 | KeyOperationAlgorithm | Name | RSA | openssl_pkey.c:50:31:50:42 | openssl_pkey.c:50:31:50:42 | | openssl_pkey.c:50:31:50:42 | KeyOperationAlgorithm | RawName | 6 | openssl_pkey.c:50:31:50:42 | openssl_pkey.c:50:31:50:42 | @@ -44,12 +46,16 @@ | openssl_signature.c:80:9:80:21 | SignOperation | KeyOperationSubtype | Sign | openssl_signature.c:80:9:80:21 | openssl_signature.c:80:9:80:21 | | openssl_signature.c:80:53:80:56 | Key | KeyType | Unknown | openssl_signature.c:80:53:80:56 | openssl_signature.c:80:53:80:56 | | openssl_signature.c:133:52:133:55 | Key | KeyType | Unknown | openssl_signature.c:133:52:133:55 | openssl_signature.c:133:52:133:55 | +| openssl_signature.c:135:9:135:27 | SignOperation | KeyOperationSubtype | Sign | openssl_signature.c:135:9:135:27 | openssl_signature.c:135:9:135:27 | | openssl_signature.c:142:9:142:27 | SignOperation | KeyOperationSubtype | Sign | openssl_signature.c:142:9:142:27 | openssl_signature.c:142:9:142:27 | | openssl_signature.c:190:57:190:60 | Key | KeyType | Unknown | openssl_signature.c:190:57:190:60 | openssl_signature.c:190:57:190:60 | +| openssl_signature.c:197:9:197:27 | SignOperation | KeyOperationSubtype | Sign | openssl_signature.c:197:9:197:27 | openssl_signature.c:197:9:197:27 | | openssl_signature.c:204:9:204:27 | SignOperation | KeyOperationSubtype | Sign | openssl_signature.c:204:9:204:27 | openssl_signature.c:204:9:204:27 | | openssl_signature.c:260:39:260:42 | Key | KeyType | Unknown | openssl_signature.c:260:39:260:42 | openssl_signature.c:260:39:260:42 | +| openssl_signature.c:263:9:263:21 | SignOperation | KeyOperationSubtype | Sign | openssl_signature.c:263:9:263:21 | openssl_signature.c:263:9:263:21 | | openssl_signature.c:270:9:270:21 | SignOperation | KeyOperationSubtype | Sign | openssl_signature.c:270:9:270:21 | openssl_signature.c:270:9:270:21 | | openssl_signature.c:321:39:321:42 | Key | KeyType | Unknown | openssl_signature.c:321:39:321:42 | openssl_signature.c:321:39:321:42 | +| openssl_signature.c:327:9:327:35 | SignOperation | KeyOperationSubtype | Sign | openssl_signature.c:327:9:327:35 | openssl_signature.c:327:9:327:35 | | openssl_signature.c:334:9:334:35 | SignOperation | KeyOperationSubtype | Sign | openssl_signature.c:334:9:334:35 | openssl_signature.c:334:9:334:35 | | openssl_signature.c:521:46:521:66 | PaddingAlgorithm | Name | PSS | openssl_signature.c:521:46:521:66 | openssl_signature.c:521:46:521:66 | | openssl_signature.c:521:46:521:66 | PaddingAlgorithm | RawName | 6 | openssl_signature.c:521:46:521:66 | openssl_signature.c:521:46:521:66 | @@ -60,6 +66,7 @@ | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | Name | DSA | openssl_signature.c:565:50:565:54 | openssl_signature.c:565:50:565:54 | | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | RawName | dsa | openssl_signature.c:565:50:565:54 | openssl_signature.c:565:50:565:54 | | openssl_signature.c:569:55:569:58 | Constant | Description | 2048 | openssl_signature.c:569:55:569:58 | openssl_signature.c:569:55:569:58 | +| openssl_signature.c:575:32:575:37 | Key | KeyType | Unknown | openssl_signature.c:575:32:575:37 | openssl_signature.c:575:32:575:37 | | openssl_signature.c:578:34:578:37 | Key | KeyType | Asymmetric | openssl_signature.c:578:34:578:37 | openssl_signature.c:578:34:578:37 | | openssl_signature.c:602:37:602:77 | Constant | Description | Test message for OpenSSL signature APIs | openssl_signature.c:602:37:602:77 | openssl_signature.c:602:37:602:77 | | openssl_signature.c:684:24:684:33 | HashAlgorithm | DigestSize | 256 | openssl_signature.c:684:24:684:33 | openssl_signature.c:684:24:684:33 | diff --git a/cpp/ql/test/experimental/library-tests/quantum/nodes.expected b/cpp/ql/test/experimental/library-tests/quantum/nodes.expected index 5c3b212b0804..223f7bfca6c5 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/nodes.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/nodes.expected @@ -25,6 +25,7 @@ | openssl_basic.c:155:22:155:41 | Key | | openssl_basic.c:155:22:155:41 | KeyGeneration | | openssl_basic.c:155:43:155:55 | MACAlgorithm | +| openssl_basic.c:155:64:155:66 | Key | | openssl_basic.c:160:39:160:48 | HashAlgorithm | | openssl_basic.c:160:59:160:62 | Key | | openssl_basic.c:163:35:163:41 | Message | @@ -34,7 +35,9 @@ | openssl_basic.c:180:42:180:59 | Constant | | openssl_basic.c:181:49:181:87 | Constant | | openssl_basic.c:218:32:218:33 | Constant | +| openssl_pkey.c:21:10:21:28 | KeyGeneration | | openssl_pkey.c:21:10:21:28 | KeyOperationAlgorithm | +| openssl_pkey.c:21:30:21:32 | Key | | openssl_pkey.c:45:49:45:65 | Constant | | openssl_pkey.c:50:31:50:42 | KeyOperationAlgorithm | | openssl_pkey.c:54:47:54:50 | Constant | @@ -54,18 +57,27 @@ | openssl_signature.c:80:53:80:56 | Key | | openssl_signature.c:133:52:133:55 | Key | | openssl_signature.c:134:38:134:44 | Message | +| openssl_signature.c:135:9:135:27 | SignOperation | +| openssl_signature.c:135:37:135:40 | SignatureOutput | | openssl_signature.c:142:9:142:27 | SignOperation | | openssl_signature.c:142:37:142:46 | SignatureOutput | | openssl_signature.c:190:57:190:60 | Key | | openssl_signature.c:196:38:196:44 | Message | +| openssl_signature.c:197:9:197:27 | SignOperation | +| openssl_signature.c:197:37:197:40 | SignatureOutput | | openssl_signature.c:204:9:204:27 | SignOperation | | openssl_signature.c:204:37:204:46 | SignatureOutput | | openssl_signature.c:260:39:260:42 | Key | +| openssl_signature.c:263:9:263:21 | SignOperation | +| openssl_signature.c:263:33:263:36 | SignatureOutput | +| openssl_signature.c:263:54:263:59 | Message | | openssl_signature.c:270:9:270:21 | SignOperation | | openssl_signature.c:270:33:270:42 | SignatureOutput | | openssl_signature.c:270:60:270:65 | Message | | openssl_signature.c:321:39:321:42 | Key | | openssl_signature.c:326:48:326:54 | Message | +| openssl_signature.c:327:9:327:35 | SignOperation | +| openssl_signature.c:327:47:327:50 | SignatureOutput | | openssl_signature.c:334:9:334:35 | SignOperation | | openssl_signature.c:334:47:334:56 | SignatureOutput | | openssl_signature.c:521:46:521:66 | PaddingAlgorithm | @@ -75,6 +87,7 @@ | openssl_signature.c:548:34:548:37 | Key | | openssl_signature.c:565:50:565:54 | KeyOperationAlgorithm | | openssl_signature.c:569:55:569:58 | Constant | +| openssl_signature.c:575:32:575:37 | Key | | openssl_signature.c:578:9:578:23 | KeyGeneration | | openssl_signature.c:578:34:578:37 | Key | | openssl_signature.c:602:37:602:77 | Constant | diff --git a/java/ql/lib/experimental/quantum/JCA.qll b/java/ql/lib/experimental/quantum/JCA.qll index 8d13db1f68ab..dc86c4637505 100644 --- a/java/ql/lib/experimental/quantum/JCA.qll +++ b/java/ql/lib/experimental/quantum/JCA.qll @@ -1031,7 +1031,7 @@ module JCAModel { KeyGeneratorGetInstanceCall getInstantiationCall() { result = instantiationCall } } - // TODO: Link getAlgorithm from KeyPairGenerator to algorithm instances or AVCs? High priority. + //TODO: Link getAlgorithm from KeyPairGenerator to algorithm instances or AVCs? High priority. class KeyGeneratorGetInstanceCall extends MethodCall { KeyGeneratorGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance") @@ -1106,6 +1106,10 @@ module JCAModel { } override int getKeySizeFixed() { none() } + + override Crypto::ConsumerInputDataFlowNode getKeyValueConsumer() { none() } + + override predicate hasKeyValueConsumer() { none() } } class KeyGeneratorCipherAlgorithm extends CipherStringLiteralAlgorithmInstance { diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index 94b9b1e44dd9..d8b1402b5e88 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -297,6 +297,8 @@ module CryptographyBase Input> { ( exists(KeyCreationOperationInstance op | input = op.getKeySizeConsumer()) or + exists(KeyGenerationOperationInstance op | input = op.getKeyValueConsumer()) + or exists(KeyDerivationOperationInstance op | input = op.getIterationCountConsumer() or input = op.getOutputKeySizeConsumer() @@ -541,6 +543,8 @@ module CryptographyBase Input> { ( exists(KeyOperationInstance op | inputNode = op.getKeyConsumer()) or + exists(KeyGenerationOperationInstance op | inputNode = op.getKeyValueConsumer()) + or exists(MacOperationInstance op | inputNode = op.getKeyConsumer()) or exists(KeyAgreementSecretGenerationOperationInstance op | @@ -959,6 +963,20 @@ module CryptographyBase Input> { abstract class KeyGenerationOperationInstance extends KeyCreationOperationInstance { final override string getKeyCreationTypeDescription() { result = "KeyGeneration" } + + /** + * Gets the consumer of a key for this key generaiton operation. + * This occurs when a key generation operaiton is based on a raw key value + * or it generates another key or key context from a previously generated key. + */ + abstract ConsumerInputDataFlowNode getKeyValueConsumer(); + + /** + * Holds if the key generation operation has a key consumer + * i.e., an input that is explicitly used for the key value. + * This value should correspond to the value returned by `getKeyValueConsumer()`. + */ + abstract predicate hasKeyValueConsumer(); } abstract class KeyLoadOperationInstance extends KeyCreationOperationInstance { @@ -1704,12 +1722,21 @@ module CryptographyBase Input> { node instanceof KeyCreationCandidateAlgorithmNode } + KeyArtifactNode getKeyArtifact() { + result.asElement() = keyGenInstance.getKeyValueConsumer().getConsumer() + } + override NodeBase getChild(string key) { result = super.getChild(key) or // [ALWAYS_KNOWN] key = "Output" and result = this.getOutputKeyArtifact() + or + // [KnOWN_OR_UNKNOWN] only if a raw key is a known input + key = "KeyInput" and + keyGenInstance.hasKeyValueConsumer() and + result = this.getKeyArtifact() } }