mirror of
https://github.com/github/codeql.git
synced 2025-12-20 02:44:30 +01:00
317 lines
11 KiB
Plaintext
317 lines
11 KiB
Plaintext
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
|
|
// 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
|
|
|
|
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()
|
|
)
|
|
}
|
|
}
|
|
|
|
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
|
|
or
|
|
AvcToCallArgFlow::flow(avc.(OpenSslAlgorithmValueConsumer).getResultNode(),
|
|
DataFlow::exprNode(arg))
|
|
}
|
|
|
|
/**
|
|
* 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() }
|
|
}
|