Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import cpp
private import experimental.quantum.Language
private import KnownAlgorithmConstants
private import Crypto::KeyOpAlg as KeyOpAlg
private import OpenSSLAlgorithmInstanceBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer
private import AlgToAVCFlow


/**
* Gets the signature algorithm type based on the normalized algorithm name.
*/
private predicate knownOpenSSLConstantToSignatureFamilyType(

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in knownOpenSSLConstantToSignatureFamilyType should be PascalCase/camelCase.
KnownOpenSSLSignatureAlgorithmConstant e, Crypto::KeyOpAlg::TAlgorithm type
) {
exists(string name |
name = e.getNormalizedName() and
Comment thread
GrosQuildu marked this conversation as resolved.
(
name.matches("rsa%") and type = KeyOpAlg::TAsymmetricCipher(KeyOpAlg::RSA())
or
name.matches("dsa%") and type = KeyOpAlg::TSignature(KeyOpAlg::DSA())
or
name.matches("ecdsa%") and type = KeyOpAlg::TSignature(KeyOpAlg::ECDSA())
or
name.matches("ed25519%") and type = KeyOpAlg::TSignature(KeyOpAlg::Ed25519())
or
name.matches("ed448%") and type = KeyOpAlg::TSignature(KeyOpAlg::Ed448())
// or
// name.matches("sm2%") and type = KeyOpAlg::TSignature(KeyOpAlg::SM2())
// or
// name.matches("ml-dsa%") and type = KeyOpAlg::TSignature(KeyOpAlg::MLDSA())
// or
// name.matches("slh-dsa%") and type = KeyOpAlg::TSignature(KeyOpAlg::SLHDSA())
)
)
}

/**
* A signature algorithm instance derived from an OpenSSL constant.
*/
class KnownOpenSSLSignatureConstantAlgorithmInstance extends OpenSSLAlgorithmInstance,

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in KnownOpenSSLSignatureConstantAlgorithmInstance should be PascalCase/camelCase.
Crypto::KeyOperationAlgorithmInstance instanceof KnownOpenSSLSignatureAlgorithmConstant
{
OpenSSLAlgorithmValueConsumer getterCall;

KnownOpenSSLSignatureConstantAlgorithmInstance() {
// Two possibilities:
// 1) The source is a literal and flows to a getter, then we know we have an instance
// 2) The source is a KnownOpenSSLAlgorithm call, and we know we have an instance immediately from that

Comment thread
GrosQuildu marked this conversation as resolved.
// Possibility 1:
this instanceof Literal and
exists(DataFlow::Node src, DataFlow::Node sink |
// Sink is an argument to a signature getter call
sink = getterCall.getInputNode() and
// Source is `this`
src.asExpr() = this and
// This traces to a getter
KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
)
or
// Possibility 2:
this instanceof DirectAlgorithmValueConsumer and getterCall = this
}

override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() {
none()
}

override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() {
none()
}

override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() }

override int getKeySizeFixed() {
// this.(KnownOpenSSLSignatureAlgorithmConstant).getExplicitKeySize() = result
none()
}

override KeyOpAlg::Algorithm getAlgorithmType() {
knownOpenSSLConstantToSignatureFamilyType(this, result)
or
not knownOpenSSLConstantToSignatureFamilyType(this, _) and
result = KeyOpAlg::TSignature(KeyOpAlg::OtherSignatureAlgorithmType())
}

override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall }

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in getAVC should be PascalCase/camelCase.

override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
// TODO: trace to any key size initializer, symmetric and asymmetric
none()
}

override predicate shouldHaveModeOfOperation() {
none()
}

override predicate shouldHavePaddingScheme() {
none()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import cpp
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants
private import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase
private import OpenSSLAlgorithmValueConsumerBase

abstract class SignatureAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { }

class EVPSignatureAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer {

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in EVPSignatureAlgorithmValueConsumer should be PascalCase/camelCase.
DataFlow::Node valueArgNode;
DataFlow::Node resultNode;
Function consumer;

Check notice

Code scanning / CodeQL

Field only used in CharPred Note

Field is only used in CharPred.

EVPSignatureAlgorithmValueConsumer() {
resultNode.asExpr() = this and
this.(Call).getTarget() = consumer and
(
// EVP_SIGNATURE
consumer.getName() = "EVP_SIGNATURE_fetch" and
valueArgNode.asExpr() = this.(Call).getArgument(1)
or
consumer.getName() = "EVP_SIGNATURE_is_a" and
Comment thread
GrosQuildu marked this conversation as resolved.
valueArgNode.asExpr() = this.(Call).getArgument(1)
// EVP_PKEY_get1_DSA, DSA_SIG_new, EVP_RSA_gen
)
}

override DataFlow::Node getResultNode() { result = resultNode }

override Crypto::ConsumerInputDataFlowNode getInputNode() { result = valueArgNode }

// override DataFlow::Node getInputNode() { result = valueArgNode }
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
exists(OpenSSLAlgorithmInstance i | i.getAVC() = this and result = i)
//TODO: As a potential alternative, for OpenSSL only, add a generic source node for literals and only create flow (flowsTo) to
// OpenSSL AVCs... the unknown literal sources would have to be any literals not in the known set.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,15 @@ private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig {

private module AlgGetterToAlgConsumerFlow = DataFlow::Global<AlgGetterToAlgConsumerConfig>;

class ECKeyGenOperation extends OpenSSLOperation, Crypto::KeyGenerationOperationInstance {
class ECKeyGenOperation extends Crypto::KeyGenerationOperationInstance {
ECKeyGenOperation() { this.(Call).getTarget().getName() = "EC_KEY_generate_key" }

override Expr getOutputArg() {
result = this.(Call) // return value of call
}

Expr getAlgorithmArg() { result = this.(Call).getArgument(0) }

override Expr getInputArg() {
// there is no 'input', in the sense that no data is being manipulated by the operation.
// There is an input of an algorithm, but that is not the intention of the operation input arg.
none()
}

override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() }

override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() {
result = this.getOutputNode()
result.asExpr() = this.(Call)
Comment thread
GrosQuildu marked this conversation as resolved.
}

override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow
private import OpenSSLOperationBase

module EncValToInitEncArgConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] }
Expand Down Expand Up @@ -34,19 +35,12 @@ Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) {
}

// TODO: need to add key consumer
abstract class EVP_Cipher_Initializer extends Call {
Expr getContextArg() { result = this.(Call).getArgument(0) }
abstract class EVP_Cipher_Initializer extends EVPInitialize {
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }

Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }

abstract Expr getKeyArg();

abstract Expr getIVArg();

// abstract Crypto::CipherOperationSubtype getCipherOperationSubtype();
abstract Expr getOperationSubtypeArg();

Crypto::KeyOperationSubtype getCipherOperationSubtype() {
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%")
then result instanceof Crypto::TEncryptMode
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,24 @@ private import EVPCipherInitializer
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers

private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source)
}

predicate isSink(DataFlow::Node sink) {
exists(EVP_Cipher_Operation c | c.getInitCall().getAlgorithmArg() = sink.asExpr())
class EVP_Cipher_Update_Call extends EVPUpdate {
EVP_Cipher_Update_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"
]
}
}

private module AlgGetterToAlgConsumerFlow = DataFlow::Global<AlgGetterToAlgConsumerConfig>;
override Expr getInputArg() { result = this.(Call).getArgument(3) }
}

// import experimental.quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers
// import OpenSSLOperation
// class EVPCipherOutput extends CipherOutputArtifact {
// EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) }
// override DataFlow::Node getOutputNode() { result.asDefiningArgument() = this }
// }
//
/**
* see: https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis
* Base configuration for all EVP cipher operations.
* NOTE: cannot extend instance of OpenSSLOperation, as we need to override
* elements of OpenSSLOperation (i.e., we are creating an instance)
*/
abstract class EVP_Cipher_Operation extends OpenSSLOperation, Crypto::KeyOperationInstance {
Expr getContextArg() { result = this.(Call).getArgument(0) }

abstract class EVP_Cipher_Operation extends EVPOperation, Crypto::KeyOperationInstance {
override Expr getOutputArg() { result = this.(Call).getArgument(1) }

override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
Expand All @@ -41,81 +31,40 @@ abstract class EVP_Cipher_Operation extends OpenSSLOperation, Crypto::KeyOperati
result instanceof Crypto::TDecryptMode and
this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%")
or
result = this.getInitCall().getCipherOperationSubtype() and
result = this.getInitCall().getKeyOperationSubtype() and
this.(Call).getTarget().getName().toLowerCase().matches("%cipher%")
}

EVP_Cipher_Initializer getInitCall() {
CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg())
}

override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
this.getInitCall().getIVArg() = result.asExpr()
}

override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() }

override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
this.getInitCall().getKeyArg() = result.asExpr()
// todo: or track to the EVP_PKEY_CTX_new
}

override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() }
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = this.(EVPOperation).getOutputArtifact()
}

override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(),
DataFlow::exprNode(this.getInitCall().getAlgorithmArg()))
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = this.(EVPOperation).getInputConsumer()
}
}

class EVP_Cipher_Call extends EVP_Cipher_Operation {
class EVP_Cipher_Call extends EVPOneShot, EVP_Cipher_Operation {
EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" }

override Expr getInputArg() { result = this.(Call).getArgument(2) }
}

// NOTE: not modeled as cipher operations, these are intermediate calls
class EVP_Cipher_Update_Call extends Call {
EVP_Cipher_Update_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"
]
}

Expr getInputArg() { result = this.(Call).getArgument(3) }

DataFlow::Node getInputNode() { result.asExpr() = this.getInputArg() }

Expr getContextArg() { result = this.(Call).getArgument(0) }
}

class EVP_Cipher_Final_Call extends EVP_Cipher_Operation {
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"
]
}

EVP_Cipher_Update_Call getUpdateCalls() {
CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg())
}

override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() }

override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() }
}

class EVP_PKEY_Operation extends EVP_Cipher_Operation {
EVP_PKEY_Operation() {
this.(Call).getTarget().getName() in ["EVP_PKEY_decrypt", "EVP_PKEY_encrypt"]
}

override Expr getInputArg() { result = this.(Call).getArgument(3) }
// TODO: how PKEY is initialized is different that symmetric cipher
// Consider making an entirely new class for this and specializing
// the get init call
}

class EVPCipherInputArgument extends Expr {
EVPCipherInputArgument() { exists(EVP_Cipher_Operation op | op.getInputArg() = this) }
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import cpp
private import OpenSSLOperationBase

abstract class EVP_Hash_Initializer extends Call {
Expr getContextArg() { result = this.(Call).getArgument(0) }

abstract Expr getAlgorithmArg();
}
abstract class EVP_Hash_Initializer extends EVPInitialize {}

class EVP_DigestInit_Variant_Calls extends EVP_Hash_Initializer {
EVP_DigestInit_Variant_Calls() {
Expand Down
Loading
Loading