mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Merge pull request #19880 from bdrodes/operation_step_refactor
Crypto: Refactor OpenSSL operation step data-flow logic
This commit is contained in:
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<OpenSslCtxSourceToSourceFlowConfig>;
|
||||
|
||||
/**
|
||||
* 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()
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 Expr getKeyArg() { result = keyArg }
|
||||
override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = ContextIO() }
|
||||
|
||||
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 CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
|
||||
}
|
||||
|
||||
class EvpCtxSetKeySizeInitializer extends EvpKeySizeInitializer {
|
||||
Expr arg;
|
||||
|
||||
EvpCtxSetKeySizeInitializer() {
|
||||
this.(Call).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 = HashAlgorithmIO()
|
||||
}
|
||||
|
||||
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" }
|
||||
|
||||
override Expr getKeyArg() { result = this.(Call).getArgument(1) }
|
||||
|
||||
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
|
||||
}
|
||||
|
||||
class EvpCtxSetPaddingInitializer extends EvpPaddingInitializer {
|
||||
EvpCtxSetPaddingInitializer() {
|
||||
this.(Call).getTarget().getName() in [
|
||||
"EVP_PKEY_CTX_set_rsa_padding", "EVP_CIPHER_CTX_set_padding"
|
||||
/**
|
||||
* 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.getTarget().getName() in [
|
||||
"EVP_PKEY_CTX_set_rsa_keygen_bits", "EVP_PKEY_CTX_set_dsa_paramgen_bits",
|
||||
"EVP_CIPHER_CTX_set_key_length"
|
||||
]
|
||||
}
|
||||
|
||||
override Expr getPaddingArg() { result = this.(Call).getArgument(1) }
|
||||
|
||||
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
|
||||
}
|
||||
|
||||
class EvpCtxSetSaltLengthInitializer extends EvpSaltLengthInitializer {
|
||||
EvpCtxSetSaltLengthInitializer() {
|
||||
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_rsa_pss_saltlen"
|
||||
override DataFlow::Node getInput(IOType type) {
|
||||
result.asExpr() = this.getArgument(0) and type = ContextIO()
|
||||
or
|
||||
result.asExpr() = this.getArgument(1) and type = KeySizeIO()
|
||||
}
|
||||
|
||||
override Expr getSaltLengthArg() { result = this.(Call).getArgument(1) }
|
||||
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 EvpCtxSetMacKeyInitializer extends OperationStep {
|
||||
EvpCtxSetMacKeyInitializer() { this.getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" }
|
||||
|
||||
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 OperationStepType getStepType() { result = InitializerStep() }
|
||||
}
|
||||
|
||||
class EvpCtxSetPaddingInitializer extends OperationStep {
|
||||
EvpCtxSetPaddingInitializer() {
|
||||
this.getTarget().getName() in ["EVP_PKEY_CTX_set_rsa_padding", "EVP_CIPHER_CTX_set_padding"]
|
||||
}
|
||||
|
||||
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 OperationStepType getStepType() { result = InitializerStep() }
|
||||
}
|
||||
|
||||
class EvpCtxSetSaltLengthInitializer extends OperationStep {
|
||||
EvpCtxSetSaltLengthInitializer() {
|
||||
this.getTarget().getName() = "EVP_PKEY_CTX_set_rsa_pss_saltlen"
|
||||
}
|
||||
|
||||
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 OperationStepType getStepType() { result = InitializerStep() }
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of inputs and ouputs for an `OperationStep`.
|
||||
*/
|
||||
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
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
newtype OperationStepType =
|
||||
// Context creation captures cases where a context is created from an algorithm or key
|
||||
//
|
||||
ContextCreationStep() or
|
||||
InitializerStep() or
|
||||
UpdateStep() or
|
||||
FinalStep()
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
|
||||
/**
|
||||
* 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())
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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())
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 AvcContextCreationStep extends OperationStep instanceof OpenSslAlgorithmValueConsumer {
|
||||
override DataFlow::Node getOutput(IOType type) {
|
||||
type = ContextIO() and result = super.getResultNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput(IOType type) { none() }
|
||||
|
||||
override OperationStepType getStepType() { result = ContextCreationStep() }
|
||||
}
|
||||
|
||||
abstract private class CtxPassThroughCall extends Call {
|
||||
abstract DataFlow::Node getNode1();
|
||||
|
||||
abstract DataFlow::Node getNode2();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
|
||||
// TODO: is this still needed?
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
|
||||
//TODO: I am not sure CallArgToCtxRet is needed anymore
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
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 flow configuration from any non-final `OperationStep` to any other `OperationStep`.
|
||||
*/
|
||||
module OperationStepFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(OperationStep s |
|
||||
s.getAnOutput() = source or
|
||||
s.getAnInput() = source
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(OperationStep s |
|
||||
s.getAnInput() = sink or
|
||||
s.getAnOutput() = 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)
|
||||
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<OperationStepFlowConfig>;
|
||||
|
||||
/**
|
||||
* A flow from AVC to the first `OperationStep` the AVC reaches as an input.
|
||||
*/
|
||||
module AvcToOperationStepFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(AvcContextCreationStep s | s.getAnOutput() = source)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { exists(OperationStep s | s.getAnInput() = sink) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
exists(CtxClearCall c | c.getAnArgument() = node.asExpr())
|
||||
}
|
||||
|
||||
/**
|
||||
* Only get the first operation step encountered.
|
||||
*/
|
||||
predicate isBarrierOut(DataFlow::Node node) { isSink(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2)
|
||||
}
|
||||
}
|
||||
|
||||
module AvcToOperationStepFlow = DataFlow::Global<AvcToOperationStepFlowConfig>;
|
||||
|
||||
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()
|
||||
)
|
||||
exists(OperationStep s | sink = s.getInput(KeyOperationSubtypeIO()))
|
||||
}
|
||||
}
|
||||
|
||||
module EncValToInitEncArgFlow = DataFlow::Global<EncValToInitEncArgConfig>;
|
||||
|
||||
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
|
||||
private Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) {
|
||||
i = 0 and
|
||||
result instanceof Crypto::TEncryptMode
|
||||
or
|
||||
AvcToCallArgFlow::flow(avc.(OpenSslAlgorithmValueConsumer).getResultNode(),
|
||||
DataFlow::exprNode(arg))
|
||||
i = 1 and result instanceof Crypto::TDecryptMode
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for all OpenSsl operations.
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* Algorithm is specified in initialization call or is implicitly established by the key.
|
||||
*/
|
||||
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
|
||||
argToAvc(this.getAlgorithmArg(), result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to initialize a key size.
|
||||
*/
|
||||
abstract class EvpKeySizeInitializer extends EvpInitializer {
|
||||
abstract Expr getKeySizeArg();
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to initialize a key operation subtype.
|
||||
*/
|
||||
abstract class EvpKeyOperationSubtypeInitializer extends EvpInitializer {
|
||||
abstract Expr getKeyOperationSubtypeArg();
|
||||
|
||||
private Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) {
|
||||
i = 0 and
|
||||
result instanceof Crypto::TEncryptMode
|
||||
or
|
||||
i = 1 and result instanceof Crypto::TDecryptMode
|
||||
}
|
||||
|
||||
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())
|
||||
)
|
||||
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();
|
||||
|
||||
Crypto::AlgorithmValueConsumer getAlgorithmValueConsumer() {
|
||||
argToAvc(this.getAlgorithmArg(), result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to initialize a key.
|
||||
*/
|
||||
abstract class EvpKeyInitializer extends EvpInitializer {
|
||||
abstract Expr getKeyArg();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
class EvpInitializerThroughKey extends EvpPrimaryAlgorithmInitializer, EvpKeySizeInitializer,
|
||||
EvpKeyInitializer
|
||||
{
|
||||
Expr arg;
|
||||
CtxPointerSource context;
|
||||
|
||||
EvpInitializerThroughKey() {
|
||||
exists(EvpKeyInitializer keyInit |
|
||||
arg = keyInit.getKeyArg() and this = keyInit and context = keyInit.getContext()
|
||||
)
|
||||
}
|
||||
|
||||
override CtxPointerSource getContext() { result = context }
|
||||
|
||||
override Expr getAlgorithmArg() {
|
||||
result =
|
||||
getSourceKeyCreationInstanceFromArg(this.getKeyArg()).(OpenSslOperation).getAlgorithmArg()
|
||||
}
|
||||
|
||||
override Expr getKeySizeArg() {
|
||||
result = getSourceKeyCreationInstanceFromArg(this.getKeyArg()).getKeySizeConsumer().asExpr()
|
||||
}
|
||||
|
||||
override Expr getKeyArg() { result = arg }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getKeyArg() { result = arg }
|
||||
|
||||
override CtxPointerSource getContext() { result = this.(EvpOperation).getContext() }
|
||||
}
|
||||
|
||||
abstract class EvpIVInitializer extends EvpInitializer {
|
||||
abstract Expr getIVArg();
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to initialize padding.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to initialize a salt length.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to initialize a hash algorithm.
|
||||
*/
|
||||
abstract class EvpHashAlgorithmInitializer extends EvpInitializer {
|
||||
abstract Expr getHashAlgorithmArg();
|
||||
|
||||
Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() {
|
||||
argToAvc(this.getHashAlgorithmArg(), result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
abstract class EvpUpdate extends Call {
|
||||
/**
|
||||
* Gets the context argument that ties together initialization, updates and/or final calls.
|
||||
*/
|
||||
abstract CtxPointerSource getContext();
|
||||
|
||||
/**
|
||||
* Update calls always have some input data like plaintext or message digest.
|
||||
*/
|
||||
abstract Expr getInputArg();
|
||||
|
||||
/**
|
||||
* Update calls sometimes have some output data like a plaintext.
|
||||
*/
|
||||
Expr getOutputArg() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
abstract class EvpOperation extends OpenSslOperation {
|
||||
/**
|
||||
* Gets the context argument that ties together initialization, updates and/or final calls.
|
||||
*/
|
||||
abstract CtxPointerSource getContext();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Some output data like ciphertext or signature.
|
||||
*/
|
||||
abstract Expr getOutputArg();
|
||||
|
||||
/**
|
||||
* Finds the initialization call, may be none.
|
||||
*/
|
||||
EvpInitializer getInitCall() { ctxSrcToSrcFlow(result.getContext(), this.getContext()) }
|
||||
|
||||
Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
|
||||
result = DataFlow::exprNode(this.getOutputArg())
|
||||
}
|
||||
|
||||
/**
|
||||
* Input consumer is the input argument of the call.
|
||||
*/
|
||||
Crypto::ConsumerInputDataFlowNode getInputConsumer() {
|
||||
result = DataFlow::exprNode(this.getInputArg())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()) }
|
||||
|
||||
/**
|
||||
* 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() }
|
||||
|
||||
/**
|
||||
* 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())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import OpenSSLOperationBase
|
||||
import EVPCipherOperation
|
||||
import EVPHashOperation
|
||||
import ECKeyGenOperation
|
||||
import EVPSignatureOperation
|
||||
import EVPKeyGenOperation
|
||||
import CipherOperation
|
||||
import HashOperation
|
||||
import SignatureOperation
|
||||
import KeyGenOperation
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -297,6 +297,8 @@ module CryptographyBase<LocationSig Location, InputSig<Location> 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<LocationSig Location, InputSig<Location> 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<LocationSig Location, InputSig<Location> 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<LocationSig Location, InputSig<Location> 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user