Crypto: Initial model of signatures. Still incomplete for verification and correct handling of MACs.

This commit is contained in:
REDMOND\brodes
2025-06-13 11:52:37 -04:00
parent eb20955a54
commit cf2f0f16b8
11 changed files with 152 additions and 173 deletions

View File

@@ -5,3 +5,4 @@ import BlockAlgorithmInstance
import HashAlgorithmInstance
import EllipticCurveAlgorithmInstance
import SignatureAlgorithmInstance
import MACAlgorithmInstance

View File

@@ -7,6 +7,7 @@
import cpp
private import experimental.quantum.OpenSSL.CtxFlow
private import OpenSSLOperations
private import OpenSSLOperationBase
/**

View File

@@ -4,113 +4,63 @@
private import experimental.quantum.Language
private import OpenSSLOperationBase
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
module OpenSSLKeyGenToArgConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(Crypto::KeyGenerationOperationInstance keygen | keygen.getOutputKeyArtifact() = source)
}
predicate isSink(DataFlow::Node sink) { exists(Call c | c.getAnArgument() = sink.asExpr()) }
}
module OpenSSLKeyGenToArgFlow = TaintTracking::Global<OpenSSLKeyGenToArgConfig>;
// TODO: verification functions
class EVP_Signature_Initializer extends EVPInitialize {
EVP_Signature_Initializer() {
this.(Call).getTarget().getName() in [
"EVP_DigestSignInit", "EVP_DigestSignInit_ex", "EVP_SignInit", "EVP_SignInit_ex",
"EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2",
"EVP_PKEY_sign_message_init"
]
}
class EvpSignatureDigestInitializer extends EvpHashAlgorithmInitializer {
Expr arg;
/**
* Gets the algorithm associated with this initialization by following
* where the algorithm is set through the context argument.
*/
Expr getAlgorithmArgFromCtx() {
// exists(EVPPKeyAlgorithmConsumer source, DataFlow::Node sink |
// result = source.getInputNode().asExpr() and
// sink.asExpr() = this.getContextArg() and
// OpenSSLCtxSourceToArgumentFlow::flow(source.getResultNode(), sink)
// )
// or
result = this.getAlgorithmArgFromKey(this.getKeyArgFromCtx())
}
Expr getAlgorithmArgFromKey(Expr keyArg) {
exists(Crypto::KeyGenerationOperationInstance keygen |
OpenSSLKeyGenToArgFlow::flow(keygen.getOutputKeyArtifact(), DataFlow::exprNode(keyArg)) and
result = keygen.(OpenSSLOperation).getAlgorithmArg()
)
}
/**
* Gets the argument ingesting a key
* by tracing the context arg back to a context creation
*/
Expr getKeyArgFromCtx() {
exists(Call contextCreationCall |
ctxArgOrRetFlowsToCtxArg(contextCreationCall, this.getContextArg()) and
(
contextCreationCall.getTarget().getName() = "EVP_PKEY_CTX_new" and
result = contextCreationCall.getArgument(0)
or
contextCreationCall.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and
result = contextCreationCall.getArgument(1)
)
)
}
override Expr getAlgorithmArg() {
// explicit algorithm as argument
this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and
result = this.(Call).getArgument(1)
// or
// // algorithm (and key) specified in the context
// this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex"] and
// result = getAlgorithmArgFromCtx()
// or
// // algorithm specified by the key
// this.(Call).getTarget().getName() in ["EVP_DigestSignInit", "EVP_DigestSignInit_ex"] and
// result = getAlgorithmArgFromKey()
// // NOTE: for EVP_SignInit and EVP_SignInit_ex the algorithm is not specified
// // rather the algorithm is specified by the key used for signing later in a final call.
}
/**
* Returns the key argument if there is one.
* If the key was provided via the context, we track it to the context.
*/
override Expr getKeyArg() {
this.(Call).getTarget().getName() = "EVP_DigestSignInit" and
result = this.(Call).getArgument(4)
EvpSignatureDigestInitializer() {
this.(Call).getTarget().getName() in ["EVP_DigestSignInit_ex", "EVP_DigestSignInit"] and
arg = this.(Call).getArgument(2)
or
this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and
result = this.(Call).getArgument(5)
or
this.(Call).getTarget().getName().matches("EVP_PKEY_%") and
result = this.getKeyArgFromCtx()
this.(Call).getTarget().getName() in ["EVP_SignInit", "EVP_SignInit_ex"] and
arg = this.(Call).getArgument(1)
}
/**
* Signing, verification or unknown.
*/
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
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 Expr getHashAlgorithmArg() { result = arg }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EVP_Signature_Update_Call extends EVPUpdate {
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"
@@ -121,6 +71,8 @@ class EVP_Signature_Update_Call extends EVPUpdate {
* Input is the message to sign.
*/
override Expr getInputArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
/**
@@ -131,22 +83,37 @@ private Expr signatureOperationOutputArg(Call call) {
if call.getTarget().getName() = "EVP_SignFinal_ex"
then result = call.getArgument(2)
else result = call.getArgument(1)
////*******todo get rid of this predicate */
}
/**
* Base configuration for all EVP signature operations.
*/
abstract class EVP_Signature_Operation extends EVPOperation, Crypto::SignatureOperationInstance {
EVP_Signature_Operation() {
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
(
// ******TODO review logic
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.
*/
@@ -170,15 +137,15 @@ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::SignatureOp
* Keys in explicit arguments are found by overriden methods in extending classes.
*/
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
result = DataFlow::exprNode(this.getInitCall().getKeyArg())
result = DataFlow::exprNode(this.getInitCall().(EvpKeyInitializer).getKeyArg())
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EVPOperation.super.getOutputArtifact()
result = EvpOperation.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EVPOperation.super.getInputConsumer()
result = EvpOperation.super.getInputConsumer()
}
/**
@@ -186,42 +153,58 @@ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::SignatureOp
*/
override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { none() }
}
// class EVP_Signature_Call extends EVPOperation, EVP_Signature_Operation {
// EVP_Signature_Call() { this.(Call).getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] }
// /**
// * Output is the signature.
// */
// override Expr getOutputArg() { result = signatureOperationOutputArg(this) }
// /**
// * Input is the message to sign.
// */
// override Expr getInputArg() { result = this.(Call).getArgument(3) }
// }
// class EVP_Signature_Final_Call extends EVPFinal, EVP_Signature_Operation {
// EVP_Signature_Final_Call() {
// this.(Call).getTarget().getName() in [
// "EVP_DigestSignFinal", "EVP_SignFinal_ex", "EVP_SignFinal", "EVP_PKEY_sign_message_final"
// ]
// }
// override Expr getAlgorithmArg() {
// none()
// // // algorithm specified by the key and the key is provided in this operation
// // if this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"]
// // then result = getAlgorithmFromKey(this.getKeyConsumer().asExpr())
// // else
// // // or find algorithm in the initialization call
// // result = EVP_Signature_Operation.super.getAlgorithmArg()
// }
// override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
// // key provided as an argument
// if this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"]
// then result = DataFlow::exprNode(this.(Call).getArgument(3))
// else
// // or find key in the initialization call
// result = EVP_Signature_Operation.super.getKeyConsumer()
// }
// /**
// * Output is the signature.
// */
// override Expr getOutputArg() { result = signatureOperationOutputArg(this) }
// }
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) }
//***********TODO: the algorithm arg might nto be the right type, can't use the initializer the same way if there
// are two initializers for two different algorithms */
override Expr getAlgorithmArg() {
this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() = result
// // algorithm specified by the key and the key is provided in this operation
// if this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"]
// then result = getAlgorithmFromKey(this.getKeyConsumer().asExpr())
// else
// // or find algorithm in the initialization call
// result = EVP_Signature_Operation.super.getAlgorithmArg()
}
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) }
}

View File

@@ -145,6 +145,32 @@ class EvpInitializerThroughKey extends EvpPrimaryAlgorithmInitializer, EvpKeySiz
Expr getKeyArg() { result = EvpKeyInitializer.super.getKeyArg() }
}
/**
* 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();
}

View File

@@ -2,6 +2,6 @@ import OpenSSLOperationBase
import EVPCipherOperation
import EVPHashOperation
import ECKeyGenOperation
//import EVPSignatureOperation
import EVPSignatureOperation
import EVPKeyGenOperation
import EVPPKeyCtxInitializer

View File

@@ -1,5 +0,0 @@
| openssl_signature.c:565:50:565:54 | dsa | DSA | openssl_signature.c:565:17:565:42 | call to EVP_PKEY_CTX_new_from_name |
| openssl_signature.c:702:60:702:71 | RSA-SHA256 | RSA | openssl_signature.c:323:11:323:29 | call to EVP_SIGNATURE_fetch |
| openssl_signature.c:702:60:702:71 | RSA-SHA256 | RSA | openssl_signature.c:359:11:359:29 | call to EVP_SIGNATURE_fetch |
| openssl_signature.c:758:60:758:64 | dsa | DSA | openssl_signature.c:323:11:323:29 | call to EVP_SIGNATURE_fetch |
| openssl_signature.c:758:60:758:64 | dsa | DSA | openssl_signature.c:359:11:359:29 | call to EVP_SIGNATURE_fetch |

View File

@@ -1,6 +0,0 @@
import cpp
import experimental.quantum.Language
import experimental.quantum.OpenSSL.AlgorithmInstances.SignatureAlgorithmInstance
from KnownOpenSSLSignatureConstantAlgorithmInstance algoInstance
select algoInstance, algoInstance.getAlgorithmType(), algoInstance.getAVC()

View File

@@ -1,5 +0,0 @@
| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:89:53:89:56 | Key |
| openssl_signature.c:151:9:151:27 | SignOperation | openssl_signature.c:142:52:142:55 | Key |
| openssl_signature.c:213:9:213:27 | SignOperation | openssl_signature.c:199:57:199:60 | Key |
| openssl_signature.c:279:9:279:21 | SignOperation | openssl_signature.c:269:39:269:42 | Key |
| openssl_signature.c:343:9:343:35 | SignOperation | openssl_signature.c:330:39:330:42 | Key |

View File

@@ -1,6 +0,0 @@
import cpp
import experimental.quantum.Language
from Crypto::SignatureOperationNode op, Crypto::KeyArtifactNode key
where op.getAKey() = key
select op, key

View File

@@ -1,4 +0,0 @@
| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:79:32:79:38 | Message | openssl_signature.c:611:37:611:77 | Constant |
| openssl_signature.c:151:9:151:27 | SignOperation | openssl_signature.c:143:38:143:44 | Message | openssl_signature.c:611:37:611:77 | Constant |
| openssl_signature.c:213:9:213:27 | SignOperation | openssl_signature.c:205:38:205:44 | Message | openssl_signature.c:611:37:611:77 | Constant |
| openssl_signature.c:343:9:343:35 | SignOperation | openssl_signature.c:335:48:335:54 | Message | openssl_signature.c:611:37:611:77 | Constant |

View File

@@ -1,6 +0,0 @@
import cpp
import experimental.quantum.Language
from Crypto::SignatureOperationNode n, Crypto::MessageArtifactNode m
where n.getAnInputArtifact() = m
select n, m, m.getSourceNode()