Add JCA key (generation) modelling

This commit is contained in:
Nicolas Will
2025-03-20 21:26:18 +01:00
parent 63aaebbea6
commit d18dac0c8e
5 changed files with 240 additions and 39 deletions

View File

@@ -84,7 +84,7 @@ module JCAModel {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral }
predicate isSink(DataFlow::Node sink) {
exists(CipherGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg())
exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode())
}
}
@@ -102,13 +102,13 @@ module JCAModel {
class CipherStringLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance,
Crypto::ModeOfOperationAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral
{
CipherGetInstanceAlgorithmArg consumer;
Crypto::AlgorithmValueConsumer consumer;
CipherStringLiteralAlgorithmInstance() {
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(consumer))
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode())
}
CipherGetInstanceAlgorithmArg getConsumer() { result = consumer }
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() {
result = this and exists(this.getRawModeAlgorithmName()) // TODO: provider defaults
@@ -411,6 +411,19 @@ module JCAModel {
}
}
// e.g., getPublic or getPrivate
class KeyPairGetKeyCall extends MethodCall {
KeyPairGetKeyCall() {
this.getCallee().hasQualifiedName("java.security", "KeyPair", "getPublic")
or
this.getCallee().hasQualifiedName("java.security", "KeyPair", "getPrivate")
}
DataFlow::Node getInputNode() { result.asExpr() = this.getQualifier() }
DataFlow::Node getOutputNode() { result.asExpr() = this }
}
predicate additionalFlowSteps(DataFlow::Node node1, DataFlow::Node node2) {
exists(IvParameterSpecGetIvCall m |
node1.asExpr() = m.getQualifier() and
@@ -421,12 +434,17 @@ module JCAModel {
node1 = n.getInputNode() and
node2 = n.getOutputNode()
)
or
exists(KeyPairGetKeyCall call |
node1 = call.getInputNode() and
node2 = call.getOutputNode()
)
}
class NonceAdditionalFlowInputStep extends AdditionalFlowInputStep {
class ArtifactAdditionalFlowStep extends AdditionalFlowInputStep {
DataFlow::Node output;
NonceAdditionalFlowInputStep() { additionalFlowSteps(this, output) }
ArtifactAdditionalFlowStep() { additionalFlowSteps(this, output) }
override DataFlow::Node getOutput() { result = output }
}
@@ -605,4 +623,72 @@ module JCAModel {
)
}
}
class KeyGeneratorCallAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer {
KeyGeneratorGetInstanceCall call;
KeyGeneratorCallAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
override DataFlow::Node getInputNode() { result.asExpr() = this }
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this
}
}
// flow from instance created by getInstance to generateKey
module KeyGeneratorGetInstanceToGenerateConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) {
exists(KeyGeneratorGetInstanceCall call | src.asExpr() = call)
}
predicate isSink(DataFlow::Node sink) {
exists(KeyGeneratorGenerateCall call | sink.asExpr() = call.(MethodCall).getQualifier())
}
}
module KeyGeneratorGetInstanceToGenerateFlow =
DataFlow::Global<KeyGeneratorGetInstanceToGenerateConfig>;
class KeyGeneratorGetInstanceCall extends MethodCall {
KeyGeneratorGetInstanceCall() {
this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance")
or
this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance")
}
Expr getAlgorithmArg() { result = super.getArgument(0) }
predicate flowsTo(KeyGeneratorGenerateCall sink) {
KeyGeneratorGetInstanceToGenerateFlow::flow(DataFlow::exprNode(this),
DataFlow::exprNode(sink.(MethodCall).getQualifier()))
}
}
class KeyGeneratorGenerateCall extends Crypto::KeyGenerationOperationInstance instanceof MethodCall
{
Crypto::KeyArtifactType type;
KeyGeneratorGenerateCall() {
this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "generateKey") and
type instanceof Crypto::TSymmetricKeyType
or
this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "generateKeyPair") and
type instanceof Crypto::TAsymmetricKeyType
}
override DataFlow::Node getOutputKeyArtifact() { result.asExpr() = this }
override Crypto::KeyArtifactType getOutputKeyType() { result = type }
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
exists(KeyGeneratorGetInstanceCall getInstance |
getInstance.flowsTo(this) and result = getInstance.getAlgorithmArg()
)
}
Crypto::AlgorithmInstance getAKnownAlgorithm() {
result = this.getAnAlgorithmValueConsumer().getAKnownAlgorithmSource()
}
}
}

View File

@@ -30,6 +30,12 @@ module CryptoInput implements InputSig<Language::Location> {
result = node.asExpr() or
result = node.asParameter()
}
predicate artifactOutputFlowsToGenericInput(
DataFlow::Node artifactOutput, DataFlow::Node otherInput
) {
ArtifactUniversalFlow::flow(artifactOutput, otherInput)
}
}
/**
@@ -70,16 +76,20 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource {
override string getAdditionalDescription() { result = this.toString() }
}
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() }
}
/*
* class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal {
* ConstantDataSource() { not this instanceof Crypto::KnownElement }
*
* 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() }
* }
*/
/**
* Random number generation, where each instance is modelled as the expression

View File

@@ -7,11 +7,12 @@ import experimental.Quantum.Language
from
Crypto::CipherOperationNode op, Crypto::CipherAlgorithmNode a,
Crypto::ModeOfOperationAlgorithmNode m, Crypto::PaddingAlgorithmNode p,
Crypto::NonceArtifactNode nonce
Crypto::NonceArtifactNode nonce, Crypto::KeyArtifactNode k
where
a = op.getAKnownCipherAlgorithm() and
m = a.getModeOfOperation() and
p = a.getPaddingAlgorithm() and
nonce = op.getANonce()
nonce = op.getANonce() and
k = op.getAKey()
select op, op.getCipherOperationSubtype(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(),
p, p.getRawAlgorithmName(), nonce
p, p.getRawAlgorithmName(), nonce, k, k.getSourceElement()

View File

@@ -0,0 +1,17 @@
/**
* @name "PQC Test"
*/
import experimental.Quantum.Language
from Crypto::CipherOperationNode op, Crypto::CipherAlgorithmNode a, Crypto::KeyArtifactNode k
where
a = op.getAKnownCipherAlgorithm() and
k = op.getAKey()
select op, op.getCipherOperationSubtype(), a, a.getRawAlgorithmName(), k, k.getSourceNode()
/*
* from Crypto::CipherOperationNode op
* where op.getLocation().getFile().getBaseName() = "AsymmetricEncryptionMacHybridCryptosystem.java"
* select op, op.getAKey().getSourceNode()
*/