8000 Quantum: OpenSSL signatures by GrosQuildu · Pull Request #19628 · github/codeql · GitHub
[go: up one dir, main page]

Skip to content

Quantum: OpenSSL signatures #19628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0f874e4
Crypto: Adding initial openssl tests, fixing a bug in hash modeling f…
bdrodes May 22, 2025
10d8504
refactor EVP common classes
GrosQuildu May 23, 2025
8b96ec9
fix openssl outputs
GrosQuildu May 28, 2025
adb9c3b
Update cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOper…
GrosQuildu May 29, 2025
408224a
Apply docs suggestions
GrosQuildu May 29, 2025
0c17cbf
rm one-shot class
GrosQuildu May 29, 2025
c3b384d
init work for openssl signatures
GrosQuildu May 28, 2025
c14fba5
openssl signatures - inputs, outputs and algorithms base
GrosQuildu May 29, 2025
b068833
start on openssl signature tests
GrosQuildu May 29, 2025
310469a
fix super/parent bug, use new SignatureOperationNode
GrosQuildu May 29, 2025
804bd89
flows for key from contexts
GrosQuildu May 29, 2025
02fb52c
make signature tests work for algorithms
GrosQuildu May 29, 2025
16c136e
openssl keygen & tracking of keys-contexts-algorithms
GrosQuildu May 29, 2025
3672358
signature algorithms are tracked from keys and contexts
GrosQuildu May 30, 2025
54a3e5c
fix cipher tests after adding new stubs
GrosQuildu May 30, 2025
0178bf3
more tests
GrosQuildu May 30, 2025
e618097
signature and keygen tests - expected
GrosQuildu May 30, 2025
c6b2165
change model.qll - KeyArtifactNode getAKnownAlgorithm fix
GrosQuildu May 30, 2025
a70cd60
key sizes basic support
GrosQuildu May 30, 2025
9b87f1f
rm redundant predicate
GrosQuildu May 30, 2025
5dbaf1b
merge main
GrosQuildu Jun 4, 2025
ca67e45
Merge branch 'main' into openssl-signatures
GrosQuildu Jun 4, 2025
30bc605
fix formatting
GrosQuildu Jun 4, 2025
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
Expand Up @@ -86,7 +86,12 @@ class KnownOpenSSLKeyAgreementAlgorithmConstant extends KnownOpenSSLAlgorithmCon
predicate resolveAlgorithmFromCall(Call c, string normalized, string algType) {
exists(string name, string parsedTargetName |
parsedTargetName =
c.getTarget().getName().replaceAll("EVP_", "").toLowerCase().replaceAll("_", "-") and
c.getTarget()
.getName()
.replaceAll("EVP_", "")
.replaceAll("_gen", "")
.toLowerCase()
.replaceAll("_", "-") and
name = resolveAlgorithmAlias(parsedTargetName) and
knownOpenSSLAlgorithmLiteral(name, _, normalized, algType)
)
Expand Down Expand Up @@ -2866,6 +2871,8 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized,
or
name = "rsa-sha256" and nid = 668 and normalized = "SHA-256" and algType = "HASH"
or
name = "rsa-sha256" and nid = 668 and normalized = "SHA-256" and algType = "SIGNATURE"
or
name = "rsa-sha3-224" and nid = 1116 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION"
or
name = "rsa-sha3-224" and nid = 1116 and normalized = "SHA3-224" and algType = "HASH"
Expand Down Expand Up @@ -2910,6 +2917,8 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized,
or
name = "rsaencryption" and nid = 6 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION"
or
name = "rsaencryption" and nid = 6 and normalized = "RSA" and algType = "SIGNATURE"
or
name = "rsaes-oaep" and nid = 919 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION"
or
name = "rsaes-oaep" and nid = 919 and normalized = "OAEP" and algType = "ASYMMETRIC_PADDING"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ import PaddingAlgorithmInstance
import BlockAlgorithmInstance
import HashAlgorithmInstance
import EllipticCurveAlgorithmInstance
import SignatureAlgorithmInstance
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(
KnownOpenSSLSignatureAlgorithmConstant e, Crypto::KeyOpAlg::TAlgorithm type
) {
exists(string name |
name = e.getNormalizedName() and
(
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,
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
// 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() {
// TODO: use ellipticCurveNameToKeySizeAndFamilyMapping or KnownOpenSSLEllipticCurveConstantAlgorithmInstance
// TODO: maybe add getExplicitKeySize to KnownOpenSSLSignatureAlgorithmConstant and use it here
none()
}

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

override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall }

override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
// TODO: trace to any key size initializer
// probably PKeyAlgorithmValueConsumer and SignatureAlgorithmValueConsumer
none()
}

/**
* No mode for signatures.
*/
override predicate shouldHaveModeOfOperation() { none() }

/**
* Padding only for RSA.
*/
override predicate shouldHavePaddingScheme() {
this.getAlgorithmType() instanceof KeyOpAlg::TAsymmetricCipher
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ import PaddingAlgorithmValueConsumer
import HashAlgorithmValueConsumer
import EllipticCurveAlgorithmValueConsumer
import PKeyAlgorithmValueConsumer
import SignatureAlgorithmValueConsumer
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase
private import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstances
private import experimental.quantum.OpenSSL.Operations.EVPKeyGenOperation
private import experimental.quantum.OpenSSL.Operations.OpenSSLOperationBase
private import experimental.quantum.OpenSSL.CtxFlow

abstract class PKeyValueConsumer extends OpenSSLAlgorithmValueConsumer { }

Expand All @@ -23,7 +26,7 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer {
or
this.(Call).getTarget().getName() in [
"EVP_PKEY_CTX_new_from_name", "EVP_PKEY_new_raw_private_key_ex",
"EVP_PKEY_new_raw_public_key_ex", "EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"
"EVP_PKEY_new_raw_public_key_ex", "EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name",
] and
valueArgNode.asExpr() = this.(Call).getArgument(1)
or
Expand All @@ -42,6 +45,7 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer {
// All other cases
valueArgNode.asExpr() = this.(Call).getArgument(2)
)
// TODO: data flow for EVP_PKEY_CTX_dup
)
}

Expand All @@ -52,4 +56,62 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer {
override DataFlow::Node getResultNode() { result = resultNode }

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

// expose the valueArg as an Expr to avoid circular dependency that may arise
// when trying to get the valueArg with getInputNode.
Expr getValueArgExpr() { result = valueArgNode.asExpr() }
}

// TODO: not sure where to put all these predicates below
/**
* Given context expression (EVP_PKEY_CTX), finds the algorithm.
*/
Expr getAlgorithmFromCtx(CtxPointerExpr ctx) {
exists(EVPPKeyAlgorithmConsumer source |
result = source.getValueArgExpr() and
ctxFlowsToCtxArg(source.getResultNode().asExpr(), ctx)
)
or
result = getAlgorithmFromKey(getKeyFromCtx(ctx))
}

/**
* Given context expression (EVP_PKEY_CTX), finds the key used to initialize the context.
*/
Expr getKeyFromCtx(CtxPointerExpr ctx) {
exists(Call contextCreationCall |
ctxFlowsToCtxArg(contextCreationCall, ctx) 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)
)
)
}

/**
* Flow from key creation to key used in a call
*/
module OpenSSLKeyFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(EVPKeyGenOperation keygen | keygen.getOutputKeyArtifact() = source)
}

predicate isSink(DataFlow::Node sink) {
exists(OpenSSLCall call | call.(Call).getAnArgument() = sink.asExpr())
}
}

module OpenSSLKeyFlow = TaintTracking::Global<OpenSSLKeyFlowConfig>;

/**
* Given key expression (EVP_PKEY), finds the algorithm.
*/
Expr getAlgorithmFromKey(Expr key) {
exists(EVPKeyGenOperation keygen |
OpenSSLKeyFlow::flow(keygen.getOutputKeyArtifact(), DataFlow::exprNode(key)) and
result = keygen.getAlgorithmArg()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import cpp
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants
private import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase
private import OpenSSLAlgorithmValueConsumerBase
private import experimental.quantum.OpenSSL.LibraryDetector

abstract class SignatureAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { }

class EVPSignatureAlgorithmValueConsumer extends SignatureAlgorithmValueConsumer {
DataFlow::Node valueArgNode;
DataFlow::Node resultNode;

EVPSignatureAlgorithmValueConsumer() {
resultNode.asExpr() = this and
(
// EVP_SIGNATURE
this.(Call).getTarget().getName() = "EVP_SIGNATURE_fetch" and
valueArgNode.asExpr() = this.(Call).getArgument(1)
// EVP_PKEY_get1_DSA, EVP_PKEY_get1_RSA
// DSA_SIG_new, DSA_SIG_get0, RSA_sign ?
)
}

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

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

override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
exists(OpenSSLAlgorithmInstance i | i.getAVC() = this and result = i)
}
}
19 changes: 16 additions & 3 deletions cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ private class CtxType extends Type {
/**
* A pointer to a CtxType
*/
private class CtxPointerExpr extends Expr {
class CtxPointerExpr extends Expr {
CtxPointerExpr() {
this.getType() instanceof CtxType and
this.getType() instanceof PointerType
Expand Down Expand Up @@ -100,7 +100,7 @@ private class CtxCopyReturnCall extends Call, CtxPointerExpr {
* Flow from any CtxPointerArgument to any other CtxPointerArgument
*/
module OpenSSLCtxArgumentFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CtxPointerArgument }
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CtxPointerExpr }

predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof CtxPointerArgument }

Expand Down Expand Up @@ -128,7 +128,8 @@ module OpenSSLCtxArgumentFlowConfig implements DataFlow::ConfigSig {
module OpenSSLCtxArgumentFlow = DataFlow::Global<OpenSSLCtxArgumentFlowConfig>;

/**
* Holds if there is a context flow from the source to the sink.
* Holds if there is a context flow from the source to the sink,
* where the source and sink are arguments to function calls.
*/
predicate ctxArgFlowsToCtxArg(CtxPointerArgument source, CtxPointerArgument sink) {
exists(DataFlow::Node a, DataFlow::Node b |
Expand All @@ -137,3 +138,15 @@ predicate ctxArgFlowsToCtxArg(CtxPointerArgument source, CtxPointerArgument sink
b.asExpr() = sink
)
}

/**
* Holds if there is a context flow from the source to the sink,
* where the source is a variable and the sink is an argument to a function call.
*/
predicate ctxFlowsToCtxArg(CtxPointerExpr source, CtxPointerArgument sink) {
exists(DataFlow::Node a, DataFlow::Node b |
OpenSSLCtxArgumentFlow::flow(a, b) and
a.asExpr() = source and
b.asExpr() = sink
)
}
Loading
0