Crypto: Continued refactoring of operation steps and bug fixes.

This commit is contained in:
REDMOND\brodes
2025-08-26 11:49:26 -04:00
parent 48dc280e6c
commit 422352c632
21 changed files with 91 additions and 56 deletions

View File

@@ -14,9 +14,13 @@ private import PaddingAlgorithmInstance
*/ */
module KnownOpenSslAlgorithmToAlgorithmValueConsumerConfig implements DataFlow::ConfigSig { module KnownOpenSslAlgorithmToAlgorithmValueConsumerConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof KnownOpenSslAlgorithmExpr and (
source.asExpr() instanceof KnownOpenSslAlgorithmExpr or
source.asIndirectExpr() instanceof KnownOpenSslAlgorithmExpr
) and
// No need to flow direct operations to AVCs // No need to flow direct operations to AVCs
not source.asExpr() instanceof OpenSslDirectAlgorithmOperationCall not source.asExpr() instanceof OpenSslDirectAlgorithmOperationCall and
not source.asIndirectExpr() instanceof OpenSslDirectAlgorithmOperationCall
} }
predicate isSink(DataFlow::Node sink) { predicate isSink(DataFlow::Node sink) {

View File

@@ -53,7 +53,8 @@ class KnownOpenSslBlockModeConstantAlgorithmInstance extends OpenSslAlgorithmIns
// Sink is an argument to a CipherGetterCall // Sink is an argument to a CipherGetterCall
sink = getterCall.getInputNode() and sink = getterCall.getInputNode() and
// Source is `this` // Source is `this`
src.asExpr() = this and // NOTE: src literals can be ints or strings, so need to consider asExpr and asIndirectExpr
this = [src.asExpr(), src.asIndirectExpr()] and
// This traces to a getter // This traces to a getter
KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
) )

View File

@@ -77,7 +77,8 @@ class KnownOpenSslCipherConstantAlgorithmInstance extends OpenSslAlgorithmInstan
// Sink is an argument to a CipherGetterCall // Sink is an argument to a CipherGetterCall
sink = getterCall.getInputNode() and sink = getterCall.getInputNode() and
// Source is `this` // Source is `this`
src.asExpr() = this and // NOTE: src literals can be ints or strings, so need to consider asExpr and asIndirectExpr
this = [src.asExpr(), src.asIndirectExpr()] and
// This traces to a getter // This traces to a getter
KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
) )

View File

@@ -21,7 +21,8 @@ class KnownOpenSslEllipticCurveConstantAlgorithmInstance extends OpenSslAlgorith
// Sink is an argument to a CipherGetterCall // Sink is an argument to a CipherGetterCall
sink = getterCall.getInputNode() and sink = getterCall.getInputNode() and
// Source is `this` // Source is `this`
src.asExpr() = this and // NOTE: src literals can be ints or strings, so need to consider asExpr and asIndirectExpr
this = [src.asExpr(), src.asIndirectExpr()] and
// This traces to a getter // This traces to a getter
KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
) )

View File

@@ -59,7 +59,8 @@ class KnownOpenSslHashConstantAlgorithmInstance extends OpenSslAlgorithmInstance
// Sink is an argument to a CipherGetterCall // Sink is an argument to a CipherGetterCall
sink = getterCall.getInputNode() and sink = getterCall.getInputNode() and
// Source is `this` // Source is `this`
src.asExpr() = this and // NOTE: src literals can be ints or strings, so need to consider asExpr and asIndirectExpr
this = [src.asExpr(), src.asIndirectExpr()] and
// This traces to a getter // This traces to a getter
KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
) )

View File

@@ -37,7 +37,8 @@ class KnownOpenSslKeyAgreementConstantAlgorithmInstance extends OpenSslAlgorithm
// Sink is an argument to a CipherGetterCall // Sink is an argument to a CipherGetterCall
sink = getterCall.getInputNode() and sink = getterCall.getInputNode() and
// Source is `this` // Source is `this`
src.asExpr() = this and // NOTE: src literals can be ints or strings, so need to consider asExpr and asIndirectExpr
this = [src.asExpr(), src.asIndirectExpr()] and
// This traces to a getter // This traces to a getter
KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
) )

View File

@@ -177,6 +177,10 @@ predicate knownOpenSslAlgorithmOperationCall(Call c, string normalized, string a
] and ] and
normalized = "RSA" and normalized = "RSA" and
algType = "ASYMMETRIC_ENCRYPTION" algType = "ASYMMETRIC_ENCRYPTION"
or
c.getTarget().getName() in ["DSA_do_sign", "DSA_do_verify"] and
normalized = "DSA" and
algType = "SIGNATURE"
} }
/** /**

View File

@@ -22,7 +22,8 @@ class KnownOpenSslMacConstantAlgorithmInstance extends OpenSslAlgorithmInstance,
// Sink is an argument to a CipherGetterCall // Sink is an argument to a CipherGetterCall
sink = getterCall.getInputNode() and sink = getterCall.getInputNode() and
// Source is `this` // Source is `this`
src.asExpr() = this and // NOTE: src literals can be ints or strings, so need to consider asExpr and asIndirectExpr
this = [src.asExpr(), src.asIndirectExpr()] and
// This traces to a getter // This traces to a getter
KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
) )

View File

@@ -64,7 +64,8 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
// Sink is an argument to a CipherGetterCall // Sink is an argument to a CipherGetterCall
sink = getterCall.getInputNode() and sink = getterCall.getInputNode() and
// Source is `this` // Source is `this`
src.asExpr() = this and // NOTE: src literals can be ints or strings, so need to consider asExpr and asIndirectExpr
this = [src.asExpr(), src.asIndirectExpr()] and
// This traces to a getter // This traces to a getter
KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) and KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) and
isPaddingSpecificConsumer = false isPaddingSpecificConsumer = false
@@ -82,7 +83,8 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
// Sink is an argument to a CipherGetterCall // Sink is an argument to a CipherGetterCall
sink = getterCall.getInputNode() and sink = getterCall.getInputNode() and
// Source is `this` // Source is `this`
src.asExpr() = this and // NOTE: src literals can be ints or strings, so need to consider asExpr and asIndirectExpr
this = [src.asExpr(), src.asIndirectExpr()] and
// This traces to a padding-specific consumer // This traces to a padding-specific consumer
RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow::flow(src, sink) RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow::flow(src, sink)
) and ) and

View File

@@ -47,7 +47,8 @@ class KnownOpenSslSignatureConstantAlgorithmInstance extends OpenSslAlgorithmIns
// Sink is an argument to a signature getter call // Sink is an argument to a signature getter call
sink = getterCall.getInputNode() and sink = getterCall.getInputNode() and
// Source is `this` // Source is `this`
src.asExpr() = this and // NOTE: src literals can be ints or strings, so need to consider asExpr and asIndirectExpr
this = [src.asExpr(), src.asIndirectExpr()] and
// This traces to a getter // This traces to a getter
KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
) )

View File

@@ -14,13 +14,15 @@ class EvpCipherAlgorithmValueConsumer extends CipherAlgorithmValueConsumer {
EvpCipherAlgorithmValueConsumer() { EvpCipherAlgorithmValueConsumer() {
resultNode.asIndirectExpr() = this and resultNode.asIndirectExpr() = this and
( (
this.(Call).getTarget().getName() in [ this.(Call).getTarget().getName() in ["EVP_get_cipherbyname", "EVP_get_cipherbyobj"] and
"EVP_get_cipherbyname", "EVP_get_cipherbyobj", "EVP_get_cipherbynid" valueArgNode.asIndirectExpr() = this.(Call).getArgument(0)
] and or
this.(Call).getTarget().getName() = "EVP_get_cipherbynid" and
// algorithm is an NID (int), use asExpr()
valueArgNode.asExpr() = this.(Call).getArgument(0) valueArgNode.asExpr() = this.(Call).getArgument(0)
or or
this.(Call).getTarget().getName() in ["EVP_CIPHER_fetch", "EVP_ASYM_CIPHER_fetch"] and this.(Call).getTarget().getName() in ["EVP_CIPHER_fetch", "EVP_ASYM_CIPHER_fetch"] and
valueArgNode.asExpr() = this.(Call).getArgument(1) valueArgNode.asIndirectExpr() = this.(Call).getArgument(1)
) )
} }

View File

@@ -14,12 +14,17 @@ class EvpEllipticCurveAlgorithmConsumer extends EllipticCurveValueConsumer {
EvpEllipticCurveAlgorithmConsumer() { EvpEllipticCurveAlgorithmConsumer() {
resultNode.asIndirectExpr() = this.(Call) and // in all cases the result is the return resultNode.asIndirectExpr() = this.(Call) and // in all cases the result is the return
( (
this.(Call).getTarget().getName() in ["EVP_EC_gen", "EC_KEY_new_by_curve_name"] and this.(Call).getTarget().getName() = "EVP_EC_gen" and
valueArgNode.asIndirectExpr() = this.(Call).getArgument(0)
or
this.(Call).getTarget().getName() = "EC_KEY_new_by_curve_name" and
// algorithm is an NID (int), use asExpr()
valueArgNode.asExpr() = this.(Call).getArgument(0) valueArgNode.asExpr() = this.(Call).getArgument(0)
or or
this.(Call).getTarget().getName() in [ this.(Call).getTarget().getName() in [
"EC_KEY_new_by_curve_name_ex", "EVP_PKEY_CTX_set_ec_paramgen_curve_nid" "EC_KEY_new_by_curve_name_ex", "EVP_PKEY_CTX_set_ec_paramgen_curve_nid"
] and ] and
// algorithm is an NID (int), use asExpr
valueArgNode.asExpr() = this.(Call).getArgument(2) valueArgNode.asExpr() = this.(Call).getArgument(2)
) )
} }

View File

@@ -14,7 +14,7 @@ class EvpKemAlgorithmValueConsumer extends KemAlgorithmValueConsumer {
resultNode.asIndirectExpr() = this and resultNode.asIndirectExpr() = this and
( (
this.(Call).getTarget().getName() = "EVP_KEM_fetch" and this.(Call).getTarget().getName() = "EVP_KEM_fetch" and
valueArgNode.asExpr() = this.(Call).getArgument(1) valueArgNode.asIndirectExpr() = this.(Call).getArgument(1)
) )
} }

View File

@@ -14,7 +14,7 @@ class EvpKeyExchangeAlgorithmValueConsumer extends KeyExchangeAlgorithmValueCons
resultNode.asIndirectExpr() = this and resultNode.asIndirectExpr() = this and
( (
this.(Call).getTarget().getName() = "EVP_KEYEXCH_fetch" and this.(Call).getTarget().getName() = "EVP_KEYEXCH_fetch" and
valueArgNode.asExpr() = this.(Call).getArgument(1) valueArgNode.asIndirectExpr() = this.(Call).getArgument(1)
) )
} }

View File

@@ -19,6 +19,7 @@ class EvpPKeyAlgorithmConsumer extends PKeyValueConsumer {
"EVP_PKEY_CTX_new_id", "EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_CTX_new_id", "EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key",
"EVP_PKEY_new_mac_key" "EVP_PKEY_new_mac_key"
] and ] and
// Algorithm is an int, use asExpr
valueArgNode.asExpr() = this.(Call).getArgument(0) valueArgNode.asExpr() = this.(Call).getArgument(0)
or or
this.(Call).getTarget().getName() in [ this.(Call).getTarget().getName() in [
@@ -26,7 +27,8 @@ class EvpPKeyAlgorithmConsumer extends PKeyValueConsumer {
"EVP_PKEY_new_raw_public_key_ex", "EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_ctrl_uint64", "EVP_PKEY_new_raw_public_key_ex", "EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_ctrl_uint64",
"EVP_PKEY_CTX_ctrl_str", "EVP_PKEY_CTX_set_group_name" "EVP_PKEY_CTX_ctrl_str", "EVP_PKEY_CTX_set_group_name"
] and ] and
valueArgNode.asExpr() = this.(Call).getArgument(1) // AAlgorithm is a char*, use asIndirectExpr
valueArgNode.asIndirectExpr() = this.(Call).getArgument(1)
or or
// argInd 2 is 'type' which can be RSA, or EC // argInd 2 is 'type' which can be RSA, or EC
// if RSA argInd 3 is the key size, else if EC argInd 3 is the curve name // if RSA argInd 3 is the key size, else if EC argInd 3 is the curve name
@@ -38,10 +40,10 @@ class EvpPKeyAlgorithmConsumer extends PKeyValueConsumer {
// Elliptic curve case // Elliptic curve case
// If the argInd 3 is a derived type (pointer or array) then assume it is a curve name // If the argInd 3 is a derived type (pointer or array) then assume it is a curve name
if this.(Call).getArgument(3).getType().getUnderlyingType() instanceof DerivedType if this.(Call).getArgument(3).getType().getUnderlyingType() instanceof DerivedType
then valueArgNode.asExpr() = this.(Call).getArgument(3) then valueArgNode.asIndirectExpr() = this.(Call).getArgument(3)
else else
// All other cases // All other cases
valueArgNode.asExpr() = this.(Call).getArgument(2) valueArgNode.asIndirectExpr() = this.(Call).getArgument(2)
) )
) )
} }

View File

@@ -16,6 +16,7 @@ class Evp_PKey_Ctx_set_rsa_padding_AlgorithmValueConsumer extends PaddingAlgorit
Evp_PKey_Ctx_set_rsa_padding_AlgorithmValueConsumer() { Evp_PKey_Ctx_set_rsa_padding_AlgorithmValueConsumer() {
resultNode.asDefiningArgument() = this.(Call).getArgument(0) and resultNode.asDefiningArgument() = this.(Call).getArgument(0) and
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_rsa_padding" and this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_rsa_padding" and
// algorithm is an int, use asExpr
valueArgNode.asExpr() = this.(Call).getArgument(1) valueArgNode.asExpr() = this.(Call).getArgument(1)
} }

View File

@@ -16,7 +16,7 @@ class EvpSignatureAlgorithmValueConsumer extends SignatureAlgorithmValueConsumer
( (
// EVP_SIGNATURE // EVP_SIGNATURE
this.(Call).getTarget().getName() = "EVP_SIGNATURE_fetch" and this.(Call).getTarget().getName() = "EVP_SIGNATURE_fetch" and
valueArgNode.asExpr() = this.(Call).getArgument(1) valueArgNode.asIndirectExpr() = this.(Call).getArgument(1)
// EVP_PKEY_get1_DSA, EVP_PKEY_get1_RSA // EVP_PKEY_get1_DSA, EVP_PKEY_get1_RSA
// DSA_SIG_new, DSA_SIG_get0 ? // DSA_SIG_new, DSA_SIG_get0 ?
) )

View File

@@ -4,4 +4,5 @@ module OpenSslModel {
import Operations.OpenSSLOperations import Operations.OpenSSLOperations
import Random import Random
import GenericSourceCandidateLiteral import GenericSourceCandidateLiteral
import ArtifactPassthrough
} }

View File

@@ -31,10 +31,10 @@ class EvpNewKeyCtx extends OperationStep instanceof Call {
} }
override DataFlow::Node getInput(IOType type) { override DataFlow::Node getInput(IOType type) {
result.asExpr() = keyArg and type = KeyIO() result.asIndirectExpr() = keyArg and type = KeyIO()
or or
this.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and this.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and
result.asExpr() = this.getArgument(0) and result.asIndirectExpr() = this.getArgument(0) and
type = OsslLibContextIO() type = OsslLibContextIO()
} }
@@ -203,28 +203,3 @@ class EvpCtxSetSaltLengthInitializer extends OperationStep {
override OperationStepType getStepType() { result = InitializerStep() } override OperationStepType getStepType() { result = InitializerStep() }
} }
/**
* A call to `EVP_PKEY_get1_RSA` or `EVP_PKEY_get1_DSA`
* - RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey);
* - DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey);
*/
class EvpPkeyGet1RsaOrDsa extends OperationStep {
EvpPkeyGet1RsaOrDsa() { this.getTarget().getName() = ["EVP_PKEY_get1_RSA", "EVP_PKEY_get1_DSA"] }
override DataFlow::Node getOutput(IOType type) {
result.asIndirectExpr() = this and type = KeyIO()
}
override DataFlow::Node getInput(IOType type) {
// Key being loaded or created from another location
result.asIndirectExpr() = this.getArgument(0) and type = KeyIO()
}
/**
* Consider this to be an intialization step. A key is accepted and a different key is produced.
* It doesn't create a new context or new key. It isn't quite an initialiation for an operaiton
* either.
*/
override OperationStepType getStepType() { result = InitializerStep() }
}

View File

@@ -224,10 +224,7 @@ abstract class OperationStep extends Call {
* the operation allows for multiple inputs (like plaintext for cipher update calls before final). * the operation allows for multiple inputs (like plaintext for cipher update calls before final).
*/ */
OperationStep getDominatingInitializersToStep(IOType type) { OperationStep getDominatingInitializersToStep(IOType type) {
//exists(IOType sinkInType | (result.flowsToOperationStep(this) or result = this) and
//sinkInType = ContextIO() or sinkInType = type |
result.flowsToOperationStep(this) and //, sinkInType)
//)
result.setsValue(type) and result.setsValue(type) and
( (
// Do not consider a 'reset' to occur on updates // Do not consider a 'reset' to occur on updates
@@ -432,7 +429,9 @@ private class CtxParamGenCall extends CtxPassThroughCall {
CtxParamGenCall() { CtxParamGenCall() {
this.getTarget().getName() = "EVP_PKEY_paramgen" and this.getTarget().getName() = "EVP_PKEY_paramgen" and
n1.asExpr() = this.getArgument(0) and //Arg 0 is *ctx
n1.asIndirectExpr() = this.getArgument(0) and
//Arg 1 is **pkey
n2.asDefiningArgument() = this.getArgument(1) n2.asDefiningArgument() = this.getArgument(1)
} }
@@ -464,6 +463,8 @@ module OperationStepFlowConfig implements DataFlow::ConfigSig {
} }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
node1.(AdditionalFlowInputStep).getOutput() = node2
or
exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2) exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2)
or or
// Flow only through context and key inputs and outputs // Flow only through context and key inputs and outputs

View File

@@ -465,8 +465,8 @@ class EvpVerify extends SignatureFinalOperation {
* A call to `RSA_sign` or `RSA_verify`. * A call to `RSA_sign` or `RSA_verify`.
* https://docs.openssl.org/3.0/man3/RSA_sign/ * https://docs.openssl.org/3.0/man3/RSA_sign/
*/ */
class RsaSign extends SignatureFinalOperation { class RsaSignorVerify extends SignatureFinalOperation {
RsaSign() { this.getTarget().getName() in ["RSA_sign", "RSA_verify"] } RsaSignorVerify() { this.getTarget().getName() in ["RSA_sign", "RSA_verify"] }
override DataFlow::Node getInput(IOType type) { override DataFlow::Node getInput(IOType type) {
// Arg 0 is an NID (so asExpr not asIndirectExpr) // Arg 0 is an NID (so asExpr not asIndirectExpr)
@@ -500,6 +500,37 @@ class RsaSign extends SignatureFinalOperation {
} }
} }
/**
* A call to `DSA_do_sign` or `DSA_do_verify`
*/
class DSADoSignOrVerify extends SignatureFinalOperation {
DSADoSignOrVerify() { this.getTarget().getName() in ["DSA_do_sign", "DSA_do_verify"] }
override DataFlow::Node getInput(IOType type) {
result.asIndirectExpr() = this.getArgument(0) and type = PlaintextIO()
or
result.asExpr() = this.getArgument(1) and type = PlaintextSizeIO()
or
this.getTarget().getName() = "DSA_do_sign" and
result.asIndirectExpr() = this.getArgument(2) and
type = KeyIO()
or
this.getTarget().getName() = "DSA_do_verify" and
result.asIndirectExpr() = this.getArgument(2) and
type = SignatureIO()
or
this.getTarget().getName() = "DSA_do_verify" and
result.asIndirectExpr() = this.getArgument(3) and
type = KeyIO()
}
override DataFlow::Node getOutput(IOType type) {
this.getTarget().getName() = "DSA_do_sign" and
result.asIndirectExpr() = this and
type = SignatureIO()
}
}
/** /**
* An instance of a signature operation. * An instance of a signature operation.
* This is an OpenSSL specific class that extends the base SignatureOperationInstance. * This is an OpenSSL specific class that extends the base SignatureOperationInstance.