Initial OpenSSL modeling work.

This commit is contained in:
REDMOND\brodes
2025-03-04 15:52:57 -05:00
parent 8865d89fe9
commit cce5f24b38
8 changed files with 3674 additions and 247 deletions

View File

@@ -1,12 +1,131 @@
private import codeql.cryptography.Model
import semmle.code.cpp.ir.IR
import semmle.code.cpp.security.FlowSources as FlowSources
private import cpp as Lang
module CryptoInput implements InputSig<Lang::Location> {
class LocatableElement = Lang::Locatable;
module CryptoInput implements InputSig<Lang::Location> {
class DataFlowNode = DataFlow::Node;
class LocatableElement = Lang::Locatable;
class UnknownLocation = Lang::UnknownDefaultLocation;
}
module Crypto = CryptographyBase<Lang::Location, CryptoInput>;
import OpenSSL
/**
* Artifact output to node input configuration
*/
abstract class AdditionalFlowInputStep extends DataFlow::Node {
abstract DataFlow::Node getOutput();
final DataFlow::Node getInput() { result = this }
}
/**
* Generic data source to node input configuration
*/
module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source = any(Crypto::GenericDataSourceInstance i).getOutputNode()
}
predicate isSink(DataFlow::Node sink) {
sink = any(Crypto::FlowAwareElement other).getInputNode()
}
predicate isBarrierOut(DataFlow::Node node) {
node = any(Crypto::FlowAwareElement element).getInputNode()
}
predicate isBarrierIn(DataFlow::Node node) {
node = any(Crypto::FlowAwareElement element).getOutputNode()
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
node1.(AdditionalFlowInputStep).getOutput() = node2
}
}
// TODO: I think this will be inefficient, no?
class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal {
override DataFlow::Node getOutputNode() {
result.asExpr() = this
}
override predicate flowsTo(Crypto::FlowAwareElement other) {
// TODO: separate config to avoid blowing up data-flow analysis
GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
}
override string getAdditionalDescription() { result = this.toString() }
}
/**
* Definitions of various generic data sources
*/
// final class DefaultFlowSource = SourceNode;
// final class DefaultRemoteFlowSource = RemoteFlowSource;
// class GenericLocalDataSource extends Crypto::GenericLocalDataSource {
// GenericLocalDataSource() {
// any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this
// }
// override DataFlow::Node getOutputNode() { result.asExpr() = this }
// override predicate flowsTo(Crypto::FlowAwareElement other) {
// GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
// }
// override string getAdditionalDescription() { result = this.toString() }
// }
// class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource {
// GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this }
// override DataFlow::Node getOutputNode() { result.asExpr() = this }
// override predicate flowsTo(Crypto::FlowAwareElement other) {
// GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
// }
// override string getAdditionalDescription() { result = this.toString() }
// }
module GenericDataSourceUniversalFlow = DataFlow::Global<GenericDataSourceUniversalFlowConfig>;
module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source = any(Crypto::ArtifactElement artifact).getOutputNode()
}
predicate isSink(DataFlow::Node sink) {
sink = any(Crypto::FlowAwareElement other).getInputNode()
}
predicate isBarrierOut(DataFlow::Node node) {
node = any(Crypto::FlowAwareElement element).getInputNode()
}
predicate isBarrierIn(DataFlow::Node node) {
node = any(Crypto::FlowAwareElement element).getOutputNode()
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
node1.(AdditionalFlowInputStep).getOutput() = node2
}
}
module ArtifactUniversalFlow = DataFlow::Global<ArtifactUniversalFlowConfig>;
abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance {
override predicate flowsTo(Crypto::FlowAwareElement other) {
ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
}
}
import OpenSSL.OpenSSL

View File

@@ -1,244 +0,0 @@
import cpp
import semmle.code.cpp.dataflow.new.DataFlow
module OpenSSLModel {
import Language
/**
* Hash function references in OpenSSL.
*/
predicate hash_ref_type_mapping_known(string name, Crypto::THashType algo) {
// `ma` name has an LN_ or SN_ prefix, which we want to ignore
// capture any name after the _ prefix using regex matching
name = ["sha1", "sha160"] and algo instanceof Crypto::SHA1
or
name = ["sha224", "sha256", "sha384", "sha512"] and algo instanceof Crypto::SHA2
or
name = ["sha3-224", "sha3-256", "sha3-384", "sha3-512"] and algo instanceof Crypto::SHA3
or
name = "md2" and algo instanceof Crypto::MD2
or
name = "md4" and algo instanceof Crypto::MD4
or
name = "md5" and algo instanceof Crypto::MD5
or
name = "ripemd160" and algo instanceof Crypto::RIPEMD160
or
name = "whirlpool" and algo instanceof Crypto::WHIRLPOOL
}
predicate hash_ref_type_mapping(FunctionCallOrMacroAccess ref, string name, Crypto::THashType algo) {
name = ref.getTargetName().regexpCapture("(?:SN_|LN_|EVP_)([a-z0-9]+)", 1) and
hash_ref_type_mapping_known(name, algo)
}
class FunctionCallOrMacroAccess extends Element {
FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess }
string getTargetName() {
result = this.(FunctionCall).getTarget().getName()
or
result = this.(MacroAccess).getMacroName()
}
}
class HashAlgorithmCallOrMacro extends Crypto::HashAlgorithmInstance instanceof FunctionCallOrMacroAccess
{
HashAlgorithmCallOrMacro() { hash_ref_type_mapping(this, _, _) }
string getTargetName() { result = this.(FunctionCallOrMacroAccess).getTargetName() }
}
class HashAlgorithm extends Crypto::HashAlgorithm {
HashAlgorithmCallOrMacro instance;
HashAlgorithm() { this = Crypto::THashAlgorithm(instance) }
override string getSHA2OrSHA3DigestSize(Location location) {
(
this.getHashType() instanceof Crypto::SHA2 or
this.getHashType() instanceof Crypto::SHA3
) and
exists(string name |
hash_ref_type_mapping(instance, name, this.getHashType()) and
result = name.regexpFind("\\d{3}", 0, _) and
location = instance.getLocation()
)
}
override string getRawAlgorithmName() { result = instance.getTargetName() }
override Crypto::THashType getHashType() { hash_ref_type_mapping(instance, _, result) }
Element getInstance() { result = instance }
override Location getLocation() { result = instance.getLocation() }
}
/**
* Data-flow configuration for key derivation algorithm flow to EVP_KDF_derive.
*/
module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source.asExpr() = any(KeyDerivationAlgorithm a).getInstance()
}
predicate isSink(DataFlow::Node sink) {
exists(EVP_KDF_derive kdo |
sink.asExpr() = kdo.getCall().getAlgorithmArg()
or
sink.asExpr() = kdo.getCall().getContextArg() // via `EVP_KDF_CTX_set_params`
)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
none() // TODO
}
}
module AlgorithmToEVPKeyDeriveFlow = DataFlow::Global<AlgorithmToEVPKeyDeriveConfig>;
predicate algorithm_to_EVP_KDF_derive(KeyDerivationAlgorithm algo, EVP_KDF_derive derive) {
none()
}
/**
* Key derivation operation (e.g., `EVP_KDF_derive`)
*/
class EVP_KDF_derive_FunctionCall extends Crypto::KeyDerivationOperationInstance instanceof FunctionCall
{
EVP_KDF_derive_FunctionCall() { this.getTarget().getName() = "EVP_KDF_derive" }
Expr getAlgorithmArg() { result = super.getArgument(3) }
Expr getContextArg() { result = super.getArgument(0) }
}
class EVP_KDF_derive extends Crypto::KeyDerivationOperation {
EVP_KDF_derive_FunctionCall instance;
EVP_KDF_derive() { this = Crypto::TKeyDerivationOperation(instance) }
override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) }
EVP_KDF_derive_FunctionCall getCall() { result = instance }
}
/**
* Key derivation algorithm nodes
*/
abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm {
abstract Expr getInstance();
}
/**
* `EVP_KDF_fetch` returns a key derivation algorithm.
*/
class EVP_KDF_fetch_Call extends FunctionCall {
EVP_KDF_fetch_Call() { this.getTarget().getName() = "EVP_KDF_fetch" }
Expr getAlgorithmArg() { result = this.getArgument(1) }
}
class EVP_KDF_fetch_AlgorithmArg extends Crypto::KeyDerivationAlgorithmInstance instanceof Expr {
EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) }
}
predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF", "PBKDF2"] }
class KDFAlgorithmStringLiteral extends StringLiteral {
KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) }
}
private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KDFAlgorithmStringLiteral }
predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof EVP_KDF_fetch_AlgorithmArg }
}
module AlgorithmStringToFetchFlow = DataFlow::Global<AlgorithmStringToFetchConfig>;
predicate algorithmStringToKDFFetchArgFlow(
string name, KDFAlgorithmStringLiteral origin, EVP_KDF_fetch_AlgorithmArg arg
) {
origin.getValue().toUpperCase() = name and
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg))
}
/**
* HKDF key derivation algorithm.
*/
class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF {
KDFAlgorithmStringLiteral origin;
EVP_KDF_fetch_AlgorithmArg instance;
HKDF() {
this = Crypto::TKeyDerivationAlgorithm(instance) and
algorithmStringToKDFFetchArgFlow("HKDF", origin, instance)
}
override string getRawAlgorithmName() { result = origin.getValue() }
override Crypto::HashAlgorithm getHashAlgorithm() { none() }
override Crypto::LocatableElement getOrigin(string name) {
result = origin and name = origin.toString()
}
override Expr getInstance() { result = origin }
}
/**
* PBKDF2 key derivation algorithm.
*/
class PBKDF2 extends KeyDerivationAlgorithm, Crypto::PBKDF2 {
KDFAlgorithmStringLiteral origin;
EVP_KDF_fetch_AlgorithmArg instance;
PBKDF2() {
this = Crypto::TKeyDerivationAlgorithm(instance) and
algorithmStringToKDFFetchArgFlow("PBKDF2", origin, instance)
}
override string getRawAlgorithmName() { result = origin.getValue() }
override string getIterationCount(Location location) { none() } // TODO
override string getKeyLength(Location location) { none() } // TODO
override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO
override Crypto::LocatableElement getOrigin(string name) {
result = origin and name = origin.toString()
}
override Expr getInstance() { result = instance }
}
/**
* PKCS12KDF key derivation algorithm.
*/
class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF {
KDFAlgorithmStringLiteral origin;
EVP_KDF_fetch_AlgorithmArg instance;
PKCS12KDF() {
this = Crypto::TKeyDerivationAlgorithm(instance) and
algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, instance)
}
override string getRawAlgorithmName() { result = origin.getValue() }
override string getIterationCount(Location location) { none() } // TODO
override string getIDByte(Location location) { none() } // TODO
override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO
override Crypto::LocatableElement getOrigin(string name) {
result = origin and name = origin.toString()
}
override Expr getInstance() { result = instance }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
import semmle.code.cpp.dataflow.new.DataFlow
class CTXType extends Type {
CTXType() {
// TODO: should we limit this to an openssl path?
this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st")
}
}
class CTXPointerExpr extends Expr {
CTXPointerExpr() {
this.getType() instanceof CTXType and
this.getType() instanceof PointerType
}
}
class CTXPointerArgument extends CTXPointerExpr {
CTXPointerArgument() {
exists(Call c | c.getAnArgument() = this)
}
Call getCall() { result.getAnArgument() = this }
}
class CTXClearCall extends Call {
CTXClearCall() {
this.getTarget().getName().toLowerCase().matches(["%free%", "%reset%"]) and
this.getAnArgument() instanceof CTXPointerArgument
}
}
class CTXCopyOutArgCall extends Call {
CTXCopyOutArgCall() {
this.getTarget().getName().toLowerCase().matches(["%copy%"]) and
this.getAnArgument() instanceof CTXPointerArgument
}
}
class CTXCopyReturnCall extends Call {
CTXCopyReturnCall() {
this.getTarget().getName().toLowerCase().matches(["%dup%"]) and
this.getAnArgument() instanceof CTXPointerArgument and
this instanceof CTXPointerExpr
}
}
module OpenSSLCTXArgumentFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CTXPointerArgument }
predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof CTXPointerArgument }
predicate isBarrier(DataFlow::Node node) {
exists(CTXClearCall c | c.getAnArgument() = node.asExpr())
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(CTXCopyOutArgCall c |
c.getAnArgument() = node1.asExpr() and
c.getAnArgument() = node2.asExpr() and
node1.asExpr() != node2.asExpr() and
node2.asExpr().getType() instanceof CTXType
)
or
exists(CTXCopyReturnCall c |
c.getAnArgument() = node1.asExpr() and
c = node2.asExpr() and
node1.asExpr() != node2.asExpr() and
node2.asExpr().getType() instanceof CTXType
)
}
}
module OpenSSLCTXArgumentFlow = DataFlow::Global<OpenSSLCTXArgumentFlowConfig>;
predicate ctxFlowsTo(CTXPointerArgument source, CTXPointerArgument sink) {
exists(DataFlow::Node a, DataFlow::Node b |
OpenSSLCTXArgumentFlow::flow(a, b) and
a.asExpr() = source and
b.asExpr() = sink
)
}

View File

@@ -0,0 +1,25 @@
import EVPCipherInitializer
import EVPCipherOperation
import AlgorithmSource
class EVP_Cipher_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVPCipherInitializerAlgorithmArgument
{
override DataFlow::Node getInputNode() { result.asExpr() = this }
override Crypto::AlgorithmElement getAKnownAlgorithmSource() {
result.(CipherLiteralAlgorithmInstance).getConsumer() = this
}
}
// //TODO: need a key consumer
// class EVP_Initializer_Key_Consumer extends Crypto::KeyConsumer instanceof EVP_Cipher_Inititalizer isntanceof InitializerKeyArgument{
// }
class EVP_Cipher_Initializer_IV_Consumer extends Crypto::NonceArtifactConsumer instanceof EVPCipherInitializerIVArgument{
override DataFlow::Node getInputNode() { result.asExpr() = this }
}
class EVP_Cipher_Input_Consumer extends Crypto::CipherInputConsumer instanceof EVPCipherInputArgument{
override DataFlow::Node getInputNode() { result.asExpr() = this }
}

View File

@@ -0,0 +1,153 @@
import experimental.Quantum.Language
import CtxFlow as CTXFlow
module EncValToInitEncArgConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] }
predicate isSink(DataFlow::Node sink) {
exists(EVP_Cipher_Inititalizer initCall | sink.asExpr() = initCall.getOperataionSubtypeArg())
}
}
module EncValToInitEncArgFlow = DataFlow::Global<EncValToInitEncArgConfig>;
int getEncConfigValue(Expr e) {
exists(EVP_Cipher_Inititalizer initCall | e = initCall.getOperataionSubtypeArg()) and
exists(DataFlow::Node a, DataFlow::Node b |
EncValToInitEncArgFlow::flow(a, b) and b.asExpr() = e and result = a.asExpr().getValue().toInt()
)
}
bindingset[i]
Crypto::CipherOperationSubtype intToCipherOperationSubtype(int i) {
if i = 0
then result instanceof Crypto::EncryptionSubtype
else
if i = 1
then result instanceof Crypto::DecryptionSubtype
else result instanceof Crypto::UnknownCipherOperationSubtype
}
// TODO: need to add key consumer
abstract class EVP_Cipher_Inititalizer extends Call {
Expr getContextArg() { result = this.(Call).getArgument(0) }
Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
abstract Expr getKeyArg();
abstract Expr getIVArg();
// abstract Crypto::CipherOperationSubtype getCipherOperationSubtype();
abstract Expr getOperataionSubtypeArg();
Crypto::CipherOperationSubtype getCipherOperationSubtype() {
if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%")
then result instanceof Crypto::EncryptionSubtype
else
if this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%")
then result instanceof Crypto::DecryptionSubtype
else
if exists(getEncConfigValue(this.getOperataionSubtypeArg()))
then result = intToCipherOperationSubtype(getEncConfigValue(this.getOperataionSubtypeArg()))
else result instanceof Crypto::UnknownCipherOperationSubtype
}
}
abstract class EVP_EX_Initializer extends EVP_Cipher_Inititalizer {
override Expr getKeyArg() { result = this.(Call).getArgument(3) }
override Expr getIVArg() { result = this.(Call).getArgument(4) }
}
abstract class EVP_EX2_Initializer extends EVP_Cipher_Inititalizer {
override Expr getKeyArg() { result = this.(Call).getArgument(2) }
override Expr getIVArg() { result = this.(Call).getArgument(3) }
}
class EVP_Cipher_EX_Init_Call extends EVP_EX_Initializer {
EVP_Cipher_EX_Init_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex"
]
}
override Expr getOperataionSubtypeArg() {
this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and
result = this.(Call).getArgument(5)
}
}
// abstract class EVP_CipherInit extends EVP_Cipher_Inititalizer{
// abstract Expr getOperataionSubtypeArg();
// }
// class EVP_CipherInit_ex_Call extends EVP_EX_Initializer, EVP_CipherInit {
// EVP_CipherInit_ex_Call() { this.(Call).getTarget().getName() = "EVP_CipherInit_ex" }
// override Crypto::CipherOperationSubtype getCipherOperationSubtype() {
// result instanceof Crypto::EncryptionSubtype
// }
// override Expr getOperataionSubtypeArg(){
// result = this.(Call).getArgument(5)
// }
// }
// class EVP_CipherInit_ex2_Call extends EVP_EX_Initializer, EVP_CipherInit {
// EVP_CipherInit_ex2_Call() { this.(Call).getTarget().getName() = "EVP_CipherInit_ex2" }
// override Crypto::CipherOperationSubtype getCipherOperationSubtype() {
// result instanceof Crypto::EncryptionSubtype
// }
// override Expr getOperataionSubtypeArg(){
// result = this.(Call).getArgument(4)
// }
// }
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 getOperataionSubtypeArg() {
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() in [
"EVP_CipherInit_SKEY"
]
}
override Expr getOperataionSubtypeArg() {
result = this.(Call).getArgument(5)
}
}
// class EVP_CipherInit extends EVP_Cipher_Inititalizer {
// EVP_CipherInit() { this.(Call).getTarget().getName() = "EVP_CipherInit" }
// override Expr getKeyArg() { result = this.(Call).getArgument(2) }
// override Expr getIVArg() { result = this.(Call).getArgument(3) }
// override Crypto::CipherOperationSubtype getCipherOperationSubtype() {
// result instanceof Crypto::EncryptionSubtype
// }
// }
class EVPCipherInitializerAlgorithmArgument extends Expr {
EVPCipherInitializerAlgorithmArgument() {
exists(EVP_Cipher_Inititalizer initCall | this = initCall.getAlgorithmArg())
}
}
class EVPCipherInitializerKeyArgument extends Expr {
EVPCipherInitializerKeyArgument() {
exists(EVP_Cipher_Inititalizer initCall | this = initCall.getKeyArg())
}
}
class EVPCipherInitializerIVArgument extends Expr {
EVPCipherInitializerIVArgument() { exists(EVP_Cipher_Inititalizer initCall | this = initCall.getIVArg()) }
}

View File

@@ -0,0 +1,87 @@
import experimental.Quantum.Language
import CtxFlow as CTXFlow
import EVPCipherInitializer
import EVPCipherConsumers
//https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis
abstract class EVP_Cipher_Operation extends Crypto::CipherOperationInstance instanceof Call {
Expr getContextArg() { result = this.(Call).getArgument(0) }
abstract Expr getInputArg();
Expr getOutputArg() { result = this.(Call).getArgument(1) }
override Crypto::CipherOperationSubtype getCipherOperationSubtype() {
result instanceof Crypto::EncryptionSubtype and
this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%")
or
result instanceof Crypto::DecryptionSubtype and
this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%")
or
result = this.getInitCall().getCipherOperationSubtype() and
this.(Call).getTarget().getName().toLowerCase().matches("%cipher%")
}
EVP_Cipher_Inititalizer getInitCall() {
CTXFlow::ctxFlowsTo(result.getContextArg(), this.getContextArg())
}
override Crypto::NonceArtifactConsumer getNonceConsumer() {
this.getInitCall().getIVArg() = result
}
override Crypto::CipherInputConsumer getInputConsumer() { this.getInputArg() = result }
override Crypto::CipherOutputArtifactInstance getOutputArtifact() { this.getOutputArg() = result }
override Crypto::AlgorithmConsumer getAlgorithmConsumer() {
this.getInitCall().getAlgorithmArg() = result
}
}
abstract class EVP_Update_Call extends EVP_Cipher_Operation { }
abstract class EVP_Final_Call extends EVP_Cipher_Operation {
override Expr getInputArg() { none() }
}
class EVP_Cipher_Call extends EVP_Cipher_Operation {
// TODO/QUESTION: what is the better way to do this?
EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" }
override Expr getInputArg() { result = this.(Call).getArgument(2) }
}
class EVP_Encrypt_Decrypt_or_Cipher_Update_Call extends EVP_Update_Call {
// TODO/QUESTION: what is the better way to do this?
EVP_Encrypt_Decrypt_or_Cipher_Update_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"
]
}
override Expr getInputArg() { result = this.(Call).getArgument(3) }
}
class EVP_Encrypt_Decrypt_or_Cipher_Final_Call extends EVP_Final_Call {
// TODO/QUESTION: what is the better way to do this?
EVP_Encrypt_Decrypt_or_Cipher_Final_Call() {
this.(Call).getTarget().getName() in
[
"EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal",
"EVP_DecryptFinal", "EVP_CipherFinal"
]
}
}
class EVPCipherOutput extends CipherOutputArtifact {
EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) }
override DataFlow::Node getOutputNode(){
result.asExpr() = this
}
}
class EVPCipherInputArgument extends Expr {
EVPCipherInputArgument() { exists(EVP_Cipher_Operation op | op.getInputArg() = this) }
}

View File

@@ -0,0 +1,357 @@
import cpp
import semmle.code.cpp.dataflow.new.DataFlow
module OpenSSLModel {
import experimental.Quantum.Language
import experimental.Quantum.OpenSSL.EVPCipherOperation
// // TODO: trace CTX from init variants to the context arg of EVP update calls
// //https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis
// abstract class EVP_Cipher_Init_Call extends Call {
// Expr getContextArg() { result = this.getArgument(0) }
// abstract Expr getKeyArg();
// abstract Expr getIVArg();
// abstract Crypto::CipherOperationSubtype getCipherOperationSubtype();
// }
// abstract class EVP_Cipher_EX_Init_Call extends EVP_Cipher_Init_Call {
// override Expr getKeyArg() { result = this.getArgument(3) }
// override Expr getIVArg() { result = this.getArgument(4) }
// }
// abstract class EVP_Cipher_EX2_Init_Call extends EVP_Cipher_Init_Call {
// override Expr getKeyArg() { result = this.getArgument(2) }
// override Expr getIVArg() { result = this.getArgument(3) }
// }
// abstract class EVP_Cipher_Operation_Call extends Crypto::CipherOperationInstance instanceof Call {
// Expr getContextArg() { result = this.(Call).getArgument(0) }
// abstract Expr getInputArg();
// Expr getOutputArg() { result = this.(Call).getArgument(1) }
// abstract Expr getInitCall();
// }
// abstract class EVP_Update_Call extends EVP_Cipher_Operation_Call {
// override Expr getInputArg() { result = this.(Call).getArgument(3) }
// }
// abstract class EVP_Final_Call extends EVP_Cipher_Operation_Call{
// override Expr getInputArg() { none() }
// }
// class EVP_Cipher_Call extends EVP_Cipher_Operation_Call{
// // TODO/QUESTION: what is the better way to do this?
// EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" }
// override Expr getInputArg() { result = this.(Call).getArgument(2) }
// override Expr getOutputArg() { result = this.(Call).getArgument(1) }
// override Crypto::CipherOperationSubtype getCipherOperationSubtype(){
// result instanceof Crypto::EncryptionSubtype
// }
// override Expr getInitCall(){
// //TODO:
// none()
// }
// override Crypto::NonceArtifactConsumer getNonceConsumer(){
// none()
// }
// override Crypto::CipherInputConsumer getInputConsumer(){
// none()
// }
// override Crypto::CipherOutputArtifactInstance getOutputArtifact(){
// none()
// }
// override Crypto::AlgorithmConsumer getAlgorithmConsumer(){
// none()
// }
// }
//TODO: what about EVP_CIpher
// class EVP_EncryptUpdateCall extends Crypto::CipherOperationInstance instanceof Call {
// // NICK QUESTION: is there a better way to tie this to openssl?
// EVP_EncryptUpdateCall() { this.getTarget().getName() = "EVP_EncryptUpdate" }
// Expr getContextArg() { result = super.getArgument(0) }
// Expr getInputArg() { result = super.getArgument(3) }
// Expr getOutputArg() { result = super.getArgument(1) }
// override Crypto::CipherOperationSubtype getCipherOperationSubtype(){
// result instanceof Crypto::EncryptionSubtype
// }
// override Crypto::NonceArtifactConsumer getNonceConsumer(){
// none()
// }
// override Crypto::CipherInputConsumer getInputConsumer(){
// none()
// }
// override Crypto::CipherOutputArtifactInstance getOutputArtifact(){
// none()
// }
// override Crypto::AlgorithmConsumer getAlgorithmConsumer(){
// none()
// }
// }
//EVP_EncryptUpdate
// /**
// * Hash function references in OpenSSL.
// */
// predicate hash_ref_type_mapping_known(string name, Crypto::THashType algo) {
// // `ma` name has an LN_ or SN_ prefix, which we want to ignore
// // capture any name after the _ prefix using regex matching
// name = ["sha1", "sha160"] and algo instanceof Crypto::SHA1
// or
// name = ["sha224", "sha256", "sha384", "sha512"] and algo instanceof Crypto::SHA2
// or
// name = ["sha3-224", "sha3-256", "sha3-384", "sha3-512"] and algo instanceof Crypto::SHA3
// or
// name = "md2" and algo instanceof Crypto::MD2
// or
// name = "md4" and algo instanceof Crypto::MD4
// or
// name = "md5" and algo instanceof Crypto::MD5
// or
// name = "ripemd160" and algo instanceof Crypto::RIPEMD160
// or
// name = "whirlpool" and algo instanceof Crypto::WHIRLPOOL
// }
// predicate hash_ref_type_mapping(FunctionCallOrMacroAccess ref, string name, Crypto::THashType algo) {
// name = ref.getTargetName().regexpCapture("(?:SN_|LN_|EVP_)([a-z0-9]+)", 1) and
// hash_ref_type_mapping_known(name, algo)
// }
// class FunctionCallOrMacroAccess extends Element {
// FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess }
// string getTargetName() {
// result = this.(FunctionCall).getTarget().getName()
// or
// result = this.(MacroAccess).getMacroName()
// }
// }
// class HashAlgorithmCallOrMacro extends Crypto::HashAlgorithmInstance instanceof FunctionCallOrMacroAccess
// {
// HashAlgorithmCallOrMacro() { hash_ref_type_mapping(this, _, _) }
// string getTargetName() { result = this.(FunctionCallOrMacroAccess).getTargetName() }
// }
// class HashAlgorithm extends Crypto::HashAlgorithm {
// HashAlgorithmCallOrMacro instance;
// HashAlgorithm() { this = Crypto::THashAlgorithm(instance) }
// override string getSHA2OrSHA3DigestSize(Location location) {
// (
// this.getHashType() instanceof Crypto::SHA2 or
// this.getHashType() instanceof Crypto::SHA3
// ) and
// exists(string name |
// hash_ref_type_mapping(instance, name, this.getHashType()) and
// result = name.regexpFind("\\d{3}", 0, _) and
// location = instance.getLocation()
// )
// }
// override string getRawAlgorithmName() { result = instance.getTargetName() }
// override Crypto::THashType getHashType() { hash_ref_type_mapping(instance, _, result) }
// Element getInstance() { result = instance }
// override Location getLocation() { result = instance.getLocation() }
// }
// /**
// * Data-flow configuration for key derivation algorithm flow to EVP_KDF_derive.
// */
// module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig {
// predicate isSource(DataFlow::Node source) {
// source.asExpr() = any(KeyDerivationAlgorithm a).getInstance()
// }
// predicate isSink(DataFlow::Node sink) {
// exists(EVP_KDF_derive kdo |
// sink.asExpr() = kdo.getCall().getAlgorithmArg()
// or
// sink.asExpr() = kdo.getCall().getContextArg() // via `EVP_KDF_CTX_set_params`
// )
// }
// predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
// none() // TODO
// }
// }
// module AlgorithmToEVPKeyDeriveFlow = DataFlow::Global<AlgorithmToEVPKeyDeriveConfig>;
// predicate algorithm_to_EVP_KDF_derive(KeyDerivationAlgorithm algo, EVP_KDF_derive derive) {
// none()
// }
// /**
// * Key derivation operation (e.g., `EVP_KDF_derive`)
// */
// class EVP_KDF_derive_FunctionCall extends Crypto::KeyDerivationOperationInstance instanceof FunctionCall
// {
// EVP_KDF_derive_FunctionCall() { this.getTarget().getName() = "EVP_KDF_derive" }
// Expr getAlgorithmArg() { result = super.getArgument(3) }
// Expr getContextArg() { result = super.getArgument(0) }
// }
// class EVP_KDF_derive extends Crypto::KeyDerivationOperation {
// EVP_KDF_derive_FunctionCall instance;
// EVP_KDF_derive() { this = Crypto::TKeyDerivationOperation(instance) }
// override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) }
// EVP_KDF_derive_FunctionCall getCall() { result = instance }
// }
// /**
// * Key derivation algorithm nodes
// */
// abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm {
// abstract Expr getInstance();
// }
// /**
// * `EVP_KDF_fetch` returns a key derivation algorithm.
// */
// class EVP_KDF_fetch_Call extends FunctionCall {
// EVP_KDF_fetch_Call() { this.getTarget().getName() = "EVP_KDF_fetch" }
// Expr getAlgorithmArg() { result = this.getArgument(1) }
// }
// class EVP_KDF_fetch_AlgorithmArg extends Crypto::KeyDerivationAlgorithmInstance instanceof Expr {
// EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) }
// }
// predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF", "PBKDF2"] }
// class KDFAlgorithmStringLiteral extends StringLiteral {
// KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) }
// }
// private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
// predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KDFAlgorithmStringLiteral }
// predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof EVP_KDF_fetch_AlgorithmArg }
// }
// module AlgorithmStringToFetchFlow = DataFlow::Global<AlgorithmStringToFetchConfig>;
// predicate algorithmStringToKDFFetchArgFlow(
// string name, KDFAlgorithmStringLiteral origin, EVP_KDF_fetch_AlgorithmArg arg
// ) {
// origin.getValue().toUpperCase() = name and
// AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg))
// }
// /**
// * HKDF key derivation algorithm.
// */
// class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF {
// KDFAlgorithmStringLiteral origin;
// EVP_KDF_fetch_AlgorithmArg instance;
// HKDF() {
// this = Crypto::TKeyDerivationAlgorithm(instance) and
// algorithmStringToKDFFetchArgFlow("HKDF", origin, instance)
// }
// override string getRawAlgorithmName() { result = origin.getValue() }
// override Crypto::HashAlgorithm getHashAlgorithm() { none() }
// override Crypto::LocatableElement getOrigin(string name) {
// result = origin and name = origin.toString()
// }
// override Expr getInstance() { result = origin }
// }
// /**
// * PBKDF2 key derivation algorithm.
// */
// class PBKDF2 extends KeyDerivationAlgorithm, Crypto::PBKDF2 {
// KDFAlgorithmStringLiteral origin;
// EVP_KDF_fetch_AlgorithmArg instance;
// PBKDF2() {
// this = Crypto::TKeyDerivationAlgorithm(instance) and
// algorithmStringToKDFFetchArgFlow("PBKDF2", origin, instance)
// }
// override string getRawAlgorithmName() { result = origin.getValue() }
// override string getIterationCount(Location location) { none() } // TODO
// override string getKeyLength(Location location) { none() } // TODO
// override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO
// override Crypto::LocatableElement getOrigin(string name) {
// result = origin and name = origin.toString()
// }
// override Expr getInstance() { result = instance }
// }
// /**
// * PKCS12KDF key derivation algorithm.
// */
// class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF {
// KDFAlgorithmStringLiteral origin;
// EVP_KDF_fetch_AlgorithmArg instance;
// PKCS12KDF() {
// this = Crypto::TKeyDerivationAlgorithm(instance) and
// algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, instance)
// }
// override string getRawAlgorithmName() { result = origin.getValue() }
// override string getIterationCount(Location location) { none() } // TODO
// override string getIDByte(Location location) { none() } // TODO
// override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO
// override Crypto::LocatableElement getOrigin(string name) {
// result = origin and name = origin.toString()
// }
// override Expr getInstance() { result = instance }
// }
}