mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Merge pull request #2 from nicolaswill/knewbury01/JCA-sample
Implement first stage cryptography modelling and queries
This commit is contained in:
1618
java/ql/lib/experimental/Quantum/JCA.qll
Normal file
1618
java/ql/lib/experimental/Quantum/JCA.qll
Normal file
File diff suppressed because it is too large
Load Diff
218
java/ql/lib/experimental/Quantum/Language.qll
Normal file
218
java/ql/lib/experimental/Quantum/Language.qll
Normal file
@@ -0,0 +1,218 @@
|
||||
private import codeql.cryptography.Model
|
||||
private import java as Language
|
||||
private import semmle.code.java.security.InsecureRandomnessQuery
|
||||
private import semmle.code.java.security.RandomQuery
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
private class UnknownLocation extends Language::Location {
|
||||
UnknownLocation() { this.getFile().getAbsolutePath() = "" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy location which is used when something doesn't have a location in
|
||||
* the source code but needs to have a `Location` associated with it. There
|
||||
* may be several distinct kinds of unknown locations. For example: one for
|
||||
* expressions, one for statements and one for other program elements.
|
||||
*/
|
||||
private class UnknownDefaultLocation extends UnknownLocation {
|
||||
UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) }
|
||||
}
|
||||
|
||||
module CryptoInput implements InputSig<Language::Location> {
|
||||
class DataFlowNode = DataFlow::Node;
|
||||
|
||||
class LocatableElement = Language::Element;
|
||||
|
||||
class UnknownLocation = UnknownDefaultLocation;
|
||||
|
||||
string locationToFileBaseNameAndLineNumberString(Location location) {
|
||||
result = location.getFile().getBaseName() + ":" + location.getStartLine()
|
||||
}
|
||||
|
||||
LocatableElement dfn_to_element(DataFlow::Node node) {
|
||||
result = node.asExpr() or
|
||||
result = node.asParameter()
|
||||
}
|
||||
|
||||
predicate artifactOutputFlowsToGenericInput(
|
||||
DataFlow::Node artifactOutput, DataFlow::Node otherInput
|
||||
) {
|
||||
ArtifactFlow::flow(artifactOutput, otherInput)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate the model
|
||||
*/
|
||||
module Crypto = CryptographyBase<Language::Location, CryptoInput>;
|
||||
|
||||
/**
|
||||
* Definitions of various generic data sources
|
||||
*/
|
||||
final class DefaultFlowSource = SourceNode;
|
||||
|
||||
final class DefaultRemoteFlowSource = RemoteFlowSource;
|
||||
|
||||
class GenericUnreferencedParameterSource extends Crypto::GenericUnreferencedParameterSource {
|
||||
GenericUnreferencedParameterSource() {
|
||||
exists(Parameter p | this = p and not exists(p.getAnArgument()))
|
||||
}
|
||||
|
||||
override predicate flowsTo(Crypto::FlowAwareElement other) {
|
||||
GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode())
|
||||
}
|
||||
|
||||
override DataFlow::Node getOutputNode() { result.asParameter() = this }
|
||||
|
||||
override string getAdditionalDescription() { result = this.toString() }
|
||||
}
|
||||
|
||||
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) {
|
||||
GenericDataSourceFlow::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) {
|
||||
GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode())
|
||||
}
|
||||
|
||||
override string getAdditionalDescription() { result = this.toString() }
|
||||
}
|
||||
|
||||
class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof Literal {
|
||||
ConstantDataSource() {
|
||||
// TODO: this is an API specific workaround for JCA, as 'EC' is a constant that may be used
|
||||
// where typical algorithms are specified, but EC specifically means set up a
|
||||
// default curve container, that will later be specified explicitly (or if not a default)
|
||||
// curve is used.
|
||||
this.getValue() != "EC"
|
||||
}
|
||||
|
||||
override DataFlow::Node getOutputNode() { result.asExpr() = this }
|
||||
|
||||
override predicate flowsTo(Crypto::FlowAwareElement other) {
|
||||
// TODO: separate config to avoid blowing up data-flow analysis
|
||||
GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode())
|
||||
}
|
||||
|
||||
override string getAdditionalDescription() { result = this.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Random number generation, where each instance is modelled as the expression
|
||||
* tied to an output node (i.e., the result of the source of randomness)
|
||||
*/
|
||||
abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance {
|
||||
override DataFlow::Node getOutputNode() { result.asExpr() = this }
|
||||
}
|
||||
|
||||
class SecureRandomnessInstance extends RandomnessInstance {
|
||||
RandomDataSource source;
|
||||
|
||||
SecureRandomnessInstance() {
|
||||
this = source.getOutput() and
|
||||
source.getSourceOfRandomness() instanceof SecureRandomNumberGenerator
|
||||
}
|
||||
|
||||
override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() }
|
||||
}
|
||||
|
||||
class InsecureRandomnessInstance extends RandomnessInstance {
|
||||
RandomDataSource source;
|
||||
|
||||
InsecureRandomnessInstance() {
|
||||
any(InsecureRandomnessSource src).asExpr() = this and source.getOutput() = this
|
||||
}
|
||||
|
||||
override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Artifact output to node input configuration
|
||||
*/
|
||||
abstract class AdditionalFlowInputStep extends DataFlow::Node {
|
||||
abstract DataFlow::Node getOutput();
|
||||
|
||||
final DataFlow::Node getInput() { result = this }
|
||||
}
|
||||
|
||||
module ArtifactFlow = DataFlow::Global<ArtifactFlowConfig>;
|
||||
|
||||
/**
|
||||
* Generic data source to node input configuration
|
||||
*/
|
||||
module GenericDataSourceFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source = any(Crypto::GenericSourceInstance 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
|
||||
or
|
||||
exists(MethodCall m |
|
||||
m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and
|
||||
node1.asExpr() = m.getQualifier() and
|
||||
node2.asExpr() = m
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module ArtifactFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source = any(Crypto::ArtifactInstance 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
|
||||
or
|
||||
exists(MethodCall m |
|
||||
m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and
|
||||
node1.asExpr() = m.getQualifier() and
|
||||
node2.asExpr() = m
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module GenericDataSourceFlow = TaintTracking::Global<GenericDataSourceFlowConfig>;
|
||||
|
||||
// Import library-specific modeling
|
||||
import JCA
|
||||
@@ -6,6 +6,7 @@ extractor: java
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/cryptography: ${workspace}
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/rangeanalysis: ${workspace}
|
||||
|
||||
77
java/ql/src/experimental/Quantum/BrokenCrypto.ql
Normal file
77
java/ql/src/experimental/Quantum/BrokenCrypto.ql
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @name Use of a broken or risky cryptographic algorithm
|
||||
* @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id java/weak-cryptographic-algorithm-new-model
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
* external/cwe/cwe-328
|
||||
*/
|
||||
|
||||
|
||||
|
||||
//THIS QUERY IS A REPLICA OF: https://github.com/github/codeql/blob/main/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
//but uses the **NEW MODELLING**
|
||||
import experimental.Quantum.Language
|
||||
|
||||
|
||||
/**
|
||||
* Gets the name of an algorithm that is known to be insecure.
|
||||
*/
|
||||
string getAnInsecureAlgorithmName() {
|
||||
result =
|
||||
[
|
||||
"DES", "RC2", "RC4", "RC5",
|
||||
// ARCFOUR is a variant of RC4
|
||||
"ARCFOUR",
|
||||
// Encryption mode ECB like AES/ECB/NoPadding is vulnerable to replay and other attacks
|
||||
"ECB",
|
||||
// CBC mode of operation with PKCS#5 or PKCS#7 padding is vulnerable to padding oracle attacks
|
||||
"AES/CBC/PKCS[57]Padding"
|
||||
]
|
||||
}
|
||||
|
||||
private string rankedInsecureAlgorithm(int i) {
|
||||
result = rank[i](string s | s = getAnInsecureAlgorithmName())
|
||||
}
|
||||
|
||||
private string insecureAlgorithmString(int i) {
|
||||
i = 1 and result = rankedInsecureAlgorithm(i)
|
||||
or
|
||||
result = rankedInsecureAlgorithm(i) + "|" + insecureAlgorithmString(i - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the regular expression used for matching strings that look like they
|
||||
* contain an algorithm that is known to be insecure.
|
||||
*/
|
||||
string getInsecureAlgorithmRegex() {
|
||||
result = algorithmRegex(insecureAlgorithmString(max(int i | exists(rankedInsecureAlgorithm(i)))))
|
||||
}
|
||||
|
||||
bindingset[algorithmString]
|
||||
private string algorithmRegex(string algorithmString) {
|
||||
// Algorithms usually appear in names surrounded by characters that are not
|
||||
// alphabetical characters in the same case. This handles the upper and lower
|
||||
// case cases.
|
||||
result =
|
||||
"((^|.*[^A-Z])(" + algorithmString + ")([^A-Z].*|$))" +
|
||||
// or...
|
||||
"|" +
|
||||
// For lowercase, we want to be careful to avoid being confused by camelCase
|
||||
// hence we require two preceding uppercase letters to be sure of a case switch,
|
||||
// or a preceding non-alphabetic character
|
||||
"((^|.*[A-Z]{2}|.*[^a-zA-Z])(" + algorithmString.toLowerCase() + ")([^a-z].*|$))"
|
||||
}
|
||||
|
||||
from Crypto::Algorithm alg
|
||||
where alg.getAlgorithmName().regexpMatch(getInsecureAlgorithmRegex()) and
|
||||
// Exclude RSA/ECB/.* ciphers.
|
||||
not alg.getAlgorithmName().regexpMatch("RSA/ECB.*") and
|
||||
// Exclude German and French sentences.
|
||||
not alg.getAlgorithmName().regexpMatch(".*\\p{IsLowercase} des \\p{IsLetter}.*")
|
||||
select alg, "Cryptographic algorithm $@ is weak and should not be used.", alg,
|
||||
alg.getAlgorithmName()
|
||||
20
java/ql/src/experimental/Quantum/InsecureNonceSource.ql
Normal file
20
java/ql/src/experimental/Quantum/InsecureNonceSource.ql
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Insecure nonce at a cipher operation
|
||||
* @id java/insecure-nonce
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @description A nonce is generated from a source that is not secure. This can lead to
|
||||
* vulnerabilities such as replay attacks or key recovery.
|
||||
*/
|
||||
|
||||
import experimental.Quantum.Language
|
||||
|
||||
predicate isInsecureNonceSource(Crypto::NonceArtifactNode n, Crypto::NodeBase src) {
|
||||
src = n.getSourceNode() and
|
||||
not src.asElement() instanceof SecureRandomnessInstance
|
||||
}
|
||||
|
||||
from Crypto::KeyOperationNode op, Crypto::NodeBase src
|
||||
where isInsecureNonceSource(op.getANonce(), src)
|
||||
select op, "Operation uses insecure nonce source $@", src, src.toString()
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @name Insecure or unknown nonce source at a cipher operation
|
||||
* @id java/insecure-or-unknown-nonce-at-operation
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from
|
||||
Crypto::NonceArtifactNode n, Crypto::KeyOperationNode op, Crypto::FlowAwareElement src, string msg
|
||||
where
|
||||
op.getANonce() = n and
|
||||
// Only encryption mode is relevant for insecure nonces, consder any 'unknown' subtype
|
||||
// as possibly encryption.
|
||||
(
|
||||
op.getKeyOperationSubtype() instanceof Crypto::EncryptionSubtype
|
||||
or
|
||||
op.getKeyOperationSubtype() instanceof Crypto::WrapSubtype
|
||||
or
|
||||
op.getKeyOperationSubtype() instanceof Crypto::UnwrapSubtype
|
||||
) and
|
||||
(
|
||||
// Known sources cases that are not secure
|
||||
src = n.getSourceElement() and
|
||||
not src instanceof SecureRandomnessInstance and
|
||||
msg = "Operation uses insecure nonce source $@"
|
||||
or
|
||||
// Totally unknown sources (unmodeled input sources)
|
||||
not exists(n.getSourceElement()) and
|
||||
msg = "Operation uses unknown nonce source" and
|
||||
src = n.asElement()
|
||||
)
|
||||
select n, msg, src, src.toString()
|
||||
// variant using instances, does not yield the same results
|
||||
// from Crypto::NonceArtifactConsumer n, Crypto::CipherOperationInstance op, Crypto::FlowAwareElement src, string msg
|
||||
// where
|
||||
// op.getNonceConsumer() = n and
|
||||
// TODO: only perform the query on encryption
|
||||
// (
|
||||
// // Known sources cases that are not secure
|
||||
// src = n.getAKnownArtifactSource()and
|
||||
// not src instanceof SecureRandomnessInstance and
|
||||
// msg = "Operation uses insecure nonce source $@"
|
||||
// or
|
||||
// // Totally unknown sources (unmodeled input sources)
|
||||
// // When this occurs set src to n, just to bind it, but the output message will not report any source
|
||||
// not exists(n.getAKnownArtifactSource()) and msg = "Operation uses unknown nonce source" and src = n
|
||||
// )
|
||||
// select n, msg, src, src.toString()
|
||||
// NOTE: this will find all unknowns too, constants, and allocations, without needing to model them
|
||||
// which is kinda nice, but accidental, since getSourceElement is not modeled for everything
|
||||
// If users want to find constants or unallocated, they need to model those sources, and output the
|
||||
// getSourceElement
|
||||
// QUESTION: why isn't the source element a node?
|
||||
// NOTE: when not all sources are modeled, if one source is secure, even if others do exist, you
|
||||
// will see the nonce and operation are secure, regardless of potentially insecure IV sources
|
||||
// resulting in False Negatives
|
||||
// NOTE: need to have a query where the op has no Nonce
|
||||
// // Ideal query
|
||||
// from Crypto::NonceNode n, Crypto::CipherOperationNode op
|
||||
// where
|
||||
// n = op.getANonce() and
|
||||
// // n = op.getAnUnknownNonce()
|
||||
// not n.asElement() instanceof SecureRandomSource
|
||||
// select op, "Operation uses insecure nonce source @", n, n.toString()
|
||||
// from Crypto::Nonce n, Crypto::ArtifactLocatableElement nonceSrc
|
||||
// where
|
||||
// n.() = nonceSrc and
|
||||
// not nonceSrc instanceof SecureRandomnessInstance
|
||||
// select n, nonceSrc
|
||||
@@ -0,0 +1,70 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import experimental.Quantum.Language
|
||||
|
||||
/**
|
||||
* Flow from any function that appears to return a value
|
||||
* to an artifact node.
|
||||
* NOTE: TODO: need to handle call by refernece for now. Need to re-evaluate (see notes below)
|
||||
* Such functions may be 'wrappers' for some derived value.
|
||||
*/
|
||||
private module WrapperConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(Call c |
|
||||
c = source.asExpr()
|
||||
// not handling references yet, I think we want to flat say references are only ok
|
||||
// if I know the source, otherwise, it has to be through an additional flow step, which
|
||||
// we filter as a source, i.e., references are only allowed as sources only,
|
||||
// no inferrece? Not sure if that would work
|
||||
//or
|
||||
// source.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = c.getAnArgument()
|
||||
) and
|
||||
// Filter out sources that are known additional flow steps, as these are likely not the
|
||||
// kind of wrapper source we are looking for.
|
||||
not exists(AdditionalFlowInputStep s | s.getOutput() = source)
|
||||
}
|
||||
|
||||
// Flow through additional flow steps
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
node1.(AdditionalFlowInputStep).getOutput() = node2
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(Crypto::ArtifactNode i).asElement() }
|
||||
}
|
||||
|
||||
module WrapperFlow = DataFlow::Global<WrapperConfig>;
|
||||
|
||||
/**
|
||||
* Using a set approach to determine if reuse of an artifact exists.
|
||||
* This predicate produces a set of 'wrappers' that flow to the artifact node.
|
||||
* This set can be compared with the set to another artifact node to determine if they are the same.
|
||||
*/
|
||||
private DataFlow::Node getWrapperSet(Crypto::NonceArtifactNode a) {
|
||||
WrapperFlow::flow(result, DataFlow::exprNode(a.asElement()))
|
||||
or
|
||||
result.asExpr() = a.getSourceElement()
|
||||
}
|
||||
|
||||
/**
|
||||
* Two different artifact nodes are considered reuse if any of the following conditions are met:
|
||||
* 1. The source for artifact `a` and artifact `b` are the same and the source is a literal.
|
||||
* 2. The source for artifact `a` and artifact `b` are not the same and the source is a literal of the same value.
|
||||
* 3. For all 'wrappers' that return the source of artifact `a`, and that wrapper also exists for artifact `b`.
|
||||
* 4. For all 'wrappers' that return the source of artifact `b`, and that wrapper also exists for artifact `a`.
|
||||
*/
|
||||
predicate isArtifactReuse(Crypto::ArtifactNode a, Crypto::ArtifactNode b) {
|
||||
a != b and
|
||||
(
|
||||
a.getSourceElement() = b.getSourceElement() and a.getSourceElement() instanceof Literal
|
||||
or
|
||||
a.getSourceElement().(Literal).getValue() = b.getSourceElement().(Literal).getValue()
|
||||
or
|
||||
forex(DataFlow::Node e | e = getWrapperSet(a) |
|
||||
exists(DataFlow::Node e2 | e2 = getWrapperSet(b) | e = e2)
|
||||
)
|
||||
or
|
||||
forex(DataFlow::Node e | e = getWrapperSet(b) |
|
||||
exists(DataFlow::Node e2 | e2 = getWrapperSet(a) | e = e2)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @name Detects known weak KDf iteration counts (less than 100k and the count is statically known)
|
||||
* @id java/crypto_inventory_filters/known_weak_kdf_iteration_count
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::KeyDerivationOperationNode op, Literal l
|
||||
where
|
||||
op.getIterationCount().asElement() = l and
|
||||
l.getValue().toInt() < 100000
|
||||
select op, "Key derivation operation configures iteration count below 100k: $@", l,
|
||||
l.getValue().toString()
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @name Detects reuse of the same nonce in multiple operations
|
||||
* @id java/crypto_inventory_filter/nonce_reuse
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import ArtifactReuse
|
||||
|
||||
from Crypto::NonceArtifactNode nonce1, Crypto::NonceArtifactNode nonce2
|
||||
where isArtifactReuse(nonce1, nonce2)
|
||||
select nonce1, "Reuse with nonce $@", nonce2, nonce2.toString()
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @name Detects unknown KDf iteration counts
|
||||
* @id java/crypto_inventory_filters/unknown_kdf_iteration_count
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::KeyDerivationOperationNode op, Element e, string msg
|
||||
where
|
||||
e = op.getIterationCount().asElement() and
|
||||
not e instanceof Literal and
|
||||
msg = "Key derivation operation with unknown iteration: $@"
|
||||
or
|
||||
not exists(op.getIterationCount()) and
|
||||
e = op.asElement() and
|
||||
msg = "Key derivation operation with no iteration configuration."
|
||||
select op, msg, e, e.toString()
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @name Detects known asymmetric algorithms
|
||||
* @id java/crypto_inventory_slices/known_asymmetric_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::AlgorithmNode a
|
||||
where Crypto::isKnownAsymmetricAlgorithm(a)
|
||||
select a, "Instance of asymmetric algorithm " + a.getAlgorithmName()
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @name Detects known asymmetric cipher algorithms
|
||||
* @id java/crypto_inventory_slices/known_symmetric_cipher_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::KeyOperationAlgorithmNode a
|
||||
where a.getAlgorithmType() instanceof Crypto::KeyOpAlg::AsymmetricCipherAlgorithm
|
||||
select a, "Instance of asymmetric cipher algorithm " + a.getAlgorithmName()
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @name Detects operations where the algorithm applied is a known asymmetric algorithms
|
||||
* @id java/crypto_inventory_slices/known_asymmetric_operation_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::OperationNode op, Crypto::AlgorithmNode a
|
||||
where a = op.getAKnownAlgorithm() and Crypto::isKnownAsymmetricAlgorithm(a)
|
||||
select op, "Operation using asymmetric algorithm $@", a, a.getAlgorithmName()
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @name Detects known cipher algorithms
|
||||
* @id java/crypto_inventory_slices/known_cipher_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
// TODO: should there be a cipher algorithm node?
|
||||
from Crypto::KeyOperationAlgorithmNode a
|
||||
where
|
||||
a.getAlgorithmType() instanceof Crypto::KeyOpAlg::AsymmetricCipherAlgorithm or
|
||||
a.getAlgorithmType() instanceof Crypto::KeyOpAlg::SymmetricCipherAlgorithm
|
||||
select a, "Instance of cipher algorithm " + a.getAlgorithmName()
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name Detects known elliptic curve algorithms
|
||||
* @id java/crypto_inventory_slices/known_elliptic_curve_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::EllipticCurveNode a
|
||||
select a, "Instance of elliptic curve algorithm " + a.getAlgorithmName()
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name Detects algorithms that are known hashing algorithms
|
||||
* @id java/crypto_inventory_slices/known_hashing_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::HashAlgorithmNode a
|
||||
select a, "Instance of hashing algorithm " + a.getAlgorithmName()
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name Detects uses of hashing operations (operations exlicitly for hashing only, irrespective of the algorithm used)
|
||||
* @id java/crypto_inventory_slices/known_hashing_operation
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::HashOperationNode op
|
||||
select op, "Known hashing operation"
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @name Detects operations where the algorithm applied is a known hashing algorithm
|
||||
* @id java/crypto_inventory_slices/operation_with_known_hashing_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::OperationNode op, Crypto::HashAlgorithmNode a
|
||||
where a = op.getAKnownAlgorithm()
|
||||
select op, "Operation using hashing algorithm $@", a, a.getAlgorithmName()
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name Detects known key derivation algorithms
|
||||
* @id java/crypto_inventory_slices/known_key_derivation_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::KeyDerivationAlgorithmNode alg
|
||||
select alg, "Known key derivation algorithm " + alg.getAlgorithmName()
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name Detects uses of key derivation operations (operations exlicitly for key derivation only, irrespective of the algorithm used)
|
||||
* @id java/crypto_inventory_slices/known_key_derivation_operation
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::KeyDerivationOperationNode op
|
||||
select op, "Known key derivation operation"
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @name Detects operations where the algorithm applied is a known key derivation algorithm
|
||||
* @id java/crypto_inventory_slices/operation_with_known_key_derivation_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::OperationNode op, Crypto::KeyDerivationAlgorithmNode a
|
||||
where a = op.getAKnownAlgorithm()
|
||||
select op, "Operation using key derivation algorithm $@", a, a.getAlgorithmName()
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Detects functions that take in crypto configuration parameters but calls are not detected in source.
|
||||
* @id java/crypto_inventory_slices/likely_crypto_api_function
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Callable f, Parameter p, Crypto::OperationNode op
|
||||
where
|
||||
op.asElement().(Expr).getEnclosingCallable() = f and
|
||||
op.getAnAlgorithmOrGenericSource().asElement() = p
|
||||
select f,
|
||||
"Likely crypto API function: Operation $@ configured by parameter $@ with no known configuring call",
|
||||
op, op.toString(), p, p.toString()
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @name Detects operations where the algorithm applied is unknown
|
||||
* @id java/crypto_inventory_slices/unknown_operation_algorithm
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.Quantum.Language
|
||||
|
||||
//TODO: can we have an unknown node concept?
|
||||
from Crypto::OperationNode op, Element e, string msg
|
||||
where
|
||||
not exists(op.getAnAlgorithmOrGenericSource()) and
|
||||
e = op.asElement() and
|
||||
msg = "Operation with unconfigured algorithm (no known sources)."
|
||||
or
|
||||
exists(Crypto::GenericSourceNode n |
|
||||
n = op.getAnAlgorithmOrGenericSource() and
|
||||
e = n.asElement()
|
||||
) and
|
||||
msg = "Operation with unknown algorithm source: $@"
|
||||
select op, msg, e, e.toString()
|
||||
21
java/ql/src/experimental/Quantum/PrintCBOMGraph.ql
Normal file
21
java/ql/src/experimental/Quantum/PrintCBOMGraph.ql
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Print CBOM Graph
|
||||
* @description Outputs a graph representation of the cryptographic bill of materials.
|
||||
* This query only supports DGML output, as CodeQL DOT output omits properties.
|
||||
* @kind graph
|
||||
* @id java/print-cbom-graph
|
||||
*/
|
||||
|
||||
import experimental.Quantum.Language
|
||||
|
||||
query predicate nodes(Crypto::NodeBase node, string key, string value) {
|
||||
Crypto::nodes_graph_impl(node, key, value)
|
||||
}
|
||||
|
||||
query predicate edges(Crypto::NodeBase source, Crypto::NodeBase target, string key, string value) {
|
||||
Crypto::edges_graph_impl(source, target, key, value)
|
||||
}
|
||||
|
||||
query predicate graphProperties(string key, string value) {
|
||||
key = "semmle.graphKind" and value = "graph"
|
||||
}
|
||||
16
java/ql/src/experimental/Quantum/TestAESGCMNonce.ql
Normal file
16
java/ql/src/experimental/Quantum/TestAESGCMNonce.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name "PQC Test"
|
||||
*/
|
||||
|
||||
import experimental.Quantum.Language
|
||||
|
||||
class AESGCMAlgorithmNode extends Crypto::KeyOperationAlgorithmNode {
|
||||
AESGCMAlgorithmNode() {
|
||||
this.getAlgorithmType() = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES()) and
|
||||
this.getModeOfOperation().getModeType() = Crypto::GCM()
|
||||
}
|
||||
}
|
||||
|
||||
from Crypto::KeyOperationNode op, Crypto::NonceArtifactNode nonce
|
||||
where op.getAKnownAlgorithm() instanceof AESGCMAlgorithmNode and nonce = op.getANonce()
|
||||
select op, nonce.getSourceNode()
|
||||
18
java/ql/src/experimental/Quantum/TestCipher.ql
Normal file
18
java/ql/src/experimental/Quantum/TestCipher.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name "Key operation slice table demo query"
|
||||
*/
|
||||
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from
|
||||
Crypto::KeyOperationNode op, Crypto::KeyOperationAlgorithmNode a,
|
||||
Crypto::ModeOfOperationAlgorithmNode m, Crypto::PaddingAlgorithmNode p,
|
||||
Crypto::NonceArtifactNode nonce, Crypto::KeyArtifactNode k
|
||||
where
|
||||
a = op.getAKnownAlgorithm() and
|
||||
m = a.getModeOfOperation() and
|
||||
p = a.getPaddingAlgorithm() and
|
||||
nonce = op.getANonce() and
|
||||
k = op.getAKey()
|
||||
select op, op.getKeyOperationSubtype(), a, a.getRawAlgorithmName(), m, m.getRawAlgorithmName(), p,
|
||||
p.getRawAlgorithmName(), nonce, k
|
||||
17
java/ql/src/experimental/Quantum/TestCipherKey.ql
Normal file
17
java/ql/src/experimental/Quantum/TestCipherKey.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name "PQC Test"
|
||||
*/
|
||||
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::KeyOperationNode op, Crypto::CipherAlgorithmNode a, Crypto::KeyArtifactNode k
|
||||
where
|
||||
a = op.getAKnownCipherAlgorithm() and
|
||||
k = op.getAKey()
|
||||
select op, op.getKeyOperationSubtype(), a, a.getRawAlgorithmName(), k, k.getSourceNode()
|
||||
/*
|
||||
* from Crypto::CipherOperationNode op
|
||||
* where op.getLocation().getFile().getBaseName() = "AsymmetricEncryptionMacHybridCryptosystem.java"
|
||||
* select op, op.getAKey().getSourceNode()
|
||||
*/
|
||||
|
||||
9
java/ql/src/experimental/Quantum/TestHash.ql
Normal file
9
java/ql/src/experimental/Quantum/TestHash.ql
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @name "Hash operation slice table demo query"
|
||||
*/
|
||||
|
||||
import experimental.Quantum.Language
|
||||
|
||||
from Crypto::HashOperationNode op, Crypto::HashAlgorithmNode alg
|
||||
where alg = op.getAKnownAlgorithm()
|
||||
select op, op.getDigest(), alg, alg.getRawAlgorithmName()
|
||||
Reference in New Issue
Block a user