Skip to content

Brodes/openssl refactor#11

Merged
nicolaswill merged 7 commits intocrypto-testfrom
brodes/openssl_refactor
May 7, 2025
Merged

Brodes/openssl refactor#11
nicolaswill merged 7 commits intocrypto-testfrom
brodes/openssl_refactor

Conversation

@bdrodes
Copy link
Collaborator

@bdrodes bdrodes commented May 2, 2025

No description provided.

bdrodes added 7 commits May 2, 2025 14:03
…eted connecting padding to algorithm instances if through a set padding interface.
… handle the final and update mechanics, matching the JCA. Similarly need to update cipher to follow the JCA for update/final as well.
@nicolaswill nicolaswill marked this pull request as ready for review May 5, 2025 16:55
// The operation is a direct algorithm consumer
// NOTE: the operation itself is already modeld as a value consumer, so we can
// simply return 'this', see modeled hash algorithm consuers for EVP_Q_Digest
this = result
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, why not simply define the class as follows?
class EVP_Q_Digest_Operation extends EVP_Hash_Operation, OpenSSLAlgorithmValueConsumer

I would try to reduce fragmentation across files for very closely intertwined concepts in general, but in this particular case, we can even define both concepts as one class.

// Flow out through any return or other argument of the same type
// Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed
// to be involved
// NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is supposed to be a heuristic, can you include examples?

Suggested change:

// Flow may propagate through return values or other arguments of the same type.
// Assume both incoming and outgoing flow use `asIndirectExpr` or `asDefiningArgument`,
// since pointers are likely involved.
// NOTE: This does not attempt to detect OpenSSL-specific copy/dup functions;
// it targets any function suspected to perform copying or duplication.

Also, what about type conversions, such as byte arrays to strings? Perhaps that's a different bit of logic and one handled anyway via taint-tracking, but I want to make clear what this case (vs the generic catch-all taint tracking flow steps) are for.

this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and
(
inNode.asIndirectExpr() = this.getArgument(0)
or
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only asExpr or asIndirectExpr are going to be correct in any one given instance. If it depends on the function, then we need to specialize that per-function to avoid issues, particularly in the indirect case.

alias = "ssl3-sha1" and target = "sha1"
}

predicate tbd(string normalized, string algType) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normalization to key size / alg family type should occur as it does in our JCA modelling and not via intermediate string types: map it to the 'fixed' internal family type as defined as Model.qll, with key size or algorithm variants defined separately (in this code, AES-128 and SHA-256 are two examples that stick out).

* there is no input, rather it directly gets an algorithm
* and returns it.
*/
class DirectAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to extend OpenSSLAlgorithmValueConsumer here?

abstract int getFixedDigestLength();
}

predicate fixedImplicitDigestLength(THashType type, int digestLength) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be handled not in the instance but rather implicitly in the node. Documentation should exist to clarify which algorithms need getDigestLength() specified or a global predicate in Model.qll should exist as a helper to define those implicit type/digestlen mappings. I suggest a predicate on the node that takes the type from this.

string getKeySizeFixed() {
result = instance.asAlg().getKeySizeFixed()
or
exists(int size |
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation points to inconsistent logic; either we put the responsibility of defining the key size on the modeller in all instances or we accept that we need to "fill in the blank with an implicit default" in other cases.

This needs to be defined, with one clear path and instructions added as QLDoc comments on the instance level for modellers. If that's a global predicate in Model.qll that provides defaults, so be it. Otherwise, we need to define which types need to have a size provided and which don't.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we should unify getDigestLength and getKeySizeFixed as well as the Length/Size/Fixed/ terminology.

override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashFamily(), result) }

int getDigestLength() { result = instance.asAlg().getDigestLength() }
int getDigestLength() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issues as for getKeySizeFixed above: "we should unify getDigestLength and getKeySizeFixed as well as the Length/Size/Fixed/ terminology" and concerns about inconsistent modelling paths.

override Expr getIVArg() { result = this.(Call).getArgument(4) }
}

abstract class EVP_EX2_Initializer extends EVP_Cipher_Inititalizer {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be abstract? Where are we using that functionality?

import experimental.Quantum.Language
import semmle.code.cpp.dataflow.new.DataFlow

abstract class OpenSSLAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof Call {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary as opposed to defining abstract classes for specific types of algorithm value consumers, as done in the JCA, where we define specific abstract library-specific AVC classes for specific algorithm types, e.g., HashAlgorithmValueConsumer?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be a giant "to-do" here at the very least: we should restrict source and sinks to analysis-relevant sources and sinks, with intermediate function calls that have context parameters modeled as additional steps (with state if necessary). As-is, this configuration is overly broad and under-specified.

abstract Expr getIVArg();

// abstract Crypto::CipherOperationSubtype getCipherOperationSubtype();
abstract Expr getOperataionSubtypeArg();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
abstract Expr getOperataionSubtypeArg();
abstract Expr getOperationSubtypeArg();

Fixes a typo.

@nicolaswill
Copy link
Owner

Merging the changes now regardless (to revisit feedback)

@nicolaswill nicolaswill merged commit f5a3656 into crypto-test May 7, 2025
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants