Files
codeql/python/ql/lib/experimental/cryptography/CryptoArtifact.qll
2025-09-01 15:16:25 +02:00

265 lines
9.6 KiB
Plaintext

import python
private import semmle.python.ApiGraphs
private import experimental.cryptography.CryptoAlgorithmNames
private import experimental.cryptography.utils.Utils as Utils
/*
* A cryptographic artifact is a DataFlow::Node associated with some
* operation, algorithm, or any other aspect of cryptography.
*/
abstract class CryptographicArtifact extends DataFlow::Node { }
/**
* Associates a symmetric encryption algorithm with a block mode.
* The DataFlow::Node representing this association should be the
* point where the algorithm and block mode are combined.
* This may be at the call to encryption or in the construction
* of an object prior to encryption.
*/
abstract class SymmetricCipher extends CryptographicArtifact {
abstract SymmetricEncryptionAlgorithm getEncryptionAlgorithm();
abstract BlockMode getBlockMode();
final predicate hasBlockMode() { exists(this.getBlockMode()) }
}
/**
* A cryptographic operation is a method call that invokes a cryptographic
* algorithm (encrypt/decrypt) or a function in support of a cryptographic algorithm
* (key generation).
*
* Since operations are related to or in support of algorithms, operations must
* provide a reference to their associated algorithm. Often operataions themselves
* encapsulate algorithms, so operations can also extend CryptographicAlgorithm
* and refer to themselves as the target algorithm.
*/
abstract class CryptographicOperation extends CryptographicArtifact, API::CallNode {
bindingset[paramName, ind]
final DataFlow::Node getParameterSource(int ind, string paramName) {
result = Utils::getUltimateSrcFromApiNode(this.(API::CallNode).getParameter(ind, paramName))
}
final string getAlgorithmName() {
if exists(this.getAlgorithm().getName())
then result = this.getAlgorithm().getName()
else result = unknownAlgorithm()
}
final predicate hasAlgorithm() { exists(this.getAlgorithm()) }
final predicate isUnknownAlgorithm() {
this.getAlgorithmName() = unknownAlgorithm()
or
not this.hasAlgorithm()
}
// TODO: this might have to be parameterized by a configuration source for
// situations where an operation is passed an algorithm
abstract CryptographicAlgorithm getAlgorithm();
}
/** A key generation operation for asymmetric keys */
abstract class KeyGen extends CryptographicOperation {
int getAKeySizeInBits() { result = this.getKeySizeInBits(_) }
final predicate hasKeySize(DataFlow::Node configSrc) { exists(this.getKeySizeInBits(configSrc)) }
final predicate hasKeySize() { exists(this.getAKeySizeInBits()) }
abstract DataFlow::Node getKeyConfigSrc();
abstract int getKeySizeInBits(DataFlow::Node configSrc);
}
abstract class AsymmetricKeyGen extends KeyGen { }
abstract class SymmetricKeyGen extends KeyGen { }
/**
* A cryptographic algorithm is a `CryptographicArtifact`
* representing a cryptographic algorithm (see `CryptoAlgorithmNames.qll`).
* Cryptographic algorithms can be functions referencing common crypto algorithms (e.g., hashlib.md5)
* or strings that are used in cryptographic operation configurations (e.g., hashlib.new("md5")).
* Cryptogrpahic algorithms may also be operations that wrap or abstract one or
* more algorithms (e.g., cyrptography.fernet.Fernet and AES, CBC and PKCS7).
*
* In principle, this class should model the location where an algorithm enters the program, not
* necessarily where it is used.
*/
abstract class CryptographicAlgorithm extends CryptographicArtifact {
abstract string getName();
// TODO: handle case where name isn't known, not just unknown?
/**
* Normalizes a raw name into a normalized name as found in `CryptoAlgorithmNames.qll`.
* Subclassess should override for more api-specific normalization.
* By default, converts a raw name to upper-case with no hyphen, underscore, hash, or space.
*/
bindingset[s]
string normalizeName(string s) {
exists(string normStr | normStr = s.toUpperCase().regexpReplaceAll("[-_ ]", "") |
result = normStr and isKnownAlgorithm(result)
or
result = unknownAlgorithm() and not isKnownAlgorithm(normStr)
)
}
}
// class UnknownAlgorithm extends DataFlow::Node instanceof CryptographicAlgorithm{
// UnknownAlgorithm(){
// super.getName() = unknownAlgorithm()
// }
// }
abstract class HashAlgorithm extends CryptographicAlgorithm {
final string getHashName() {
if exists(string n | n = this.getName() and isHashingAlgorithm(n))
then isHashingAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}
abstract class KeyDerivationAlgorithm extends CryptographicAlgorithm {
final string getKDFName() {
if exists(string n | n = this.getName() and isKeyDerivationAlgorithm(n))
then isKeyDerivationAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}
abstract class KeyDerivationOperation extends CryptographicOperation {
DataFlow::Node getIterationSizeSrc() { none() }
DataFlow::Node getSaltConfigSrc() { none() }
DataFlow::Node getHashConfigSrc() { none() }
// TODO: get encryption algorithm for CBC-based KDF?
DataFlow::Node getDerivedKeySizeSrc() { none() }
DataFlow::Node getModeSrc() { none() }
// TODO: add more to cover all the parameters of most KDF operations? Perhaps subclass for each type?
abstract predicate requiresIteration();
abstract predicate requiresSalt();
abstract predicate requiresHash();
//abstract predicate requiresKeySize(); // Going to assume all requires a size
abstract predicate requiresMode();
}
/**
* A parent class to represent any algorithm for which
* asymmetric cryptography is involved.
* Intended to be distinct from AsymmetricEncryptionAlgorithm
* which is intended only for asymmetric algorithms that specifically encrypt.
*/
abstract class AsymmetricAlgorithm extends CryptographicAlgorithm { }
abstract class EncryptionAlgorithm extends CryptographicAlgorithm {
final predicate isAsymmetric() { this instanceof AsymmetricEncryptionAlgorithm }
final predicate isSymmetric() { not this.isAsymmetric() }
// NOTE: DO_NOT add getEncryptionName here, we rely on the fact the parent
// class does not have this common predicate.
}
/**
* Algorithms directly or indirectly related to asymmetric encryption,
* e.g., RSA, DSA, but also RSA padding algorithms
*/
abstract class AsymmetricEncryptionAlgorithm extends AsymmetricAlgorithm, EncryptionAlgorithm {
final string getEncryptionName() {
if exists(string n | n = this.getName() and isAsymmetricEncryptionAlgorithm(n))
then isAsymmetricEncryptionAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}
/**
* Algorithms directly or indirectly related to symmetric encryption,
* e.g., AES, DES, but also block modes and padding
*/
abstract class SymmetricEncryptionAlgorithm extends EncryptionAlgorithm {
final string getEncryptionName() {
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
// TODO: add a stream cipher predicate?
}
// Used only to categorize all padding into a single object,
// DO_NOT add predicates here. Only for categorization purposes.
abstract class PaddingAlgorithm extends CryptographicAlgorithm { }
abstract class SymmetricPadding extends PaddingAlgorithm {
final string getPaddingName() {
if exists(string n | n = this.getName() and isSymmetricPaddingAlgorithm(n))
then isSymmetricPaddingAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}
abstract class AsymmetricPadding extends PaddingAlgorithm {
final string getPaddingName() {
if exists(string n | n = this.getName() and isAsymmetricPaddingAlgorithm(n))
then isAsymmetricPaddingAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}
abstract class EllipticCurveAlgorithm extends AsymmetricAlgorithm {
final string getCurveName() {
if exists(string n | n = this.getName() and isEllipticCurveAlgorithm(n))
then isEllipticCurveAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
final int getCurveBitSize() { isEllipticCurveAlgorithm(this.getCurveName(), result) }
}
abstract class BlockMode extends CryptographicAlgorithm {
final string getBlockModeName() {
if exists(string n | n = this.getName() and isCipherBlockModeAlgorithm(n))
then isCipherBlockModeAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
/**
* Gets the source of the IV configuration.
*/
abstract DataFlow::Node getIVorNonce();
final predicate hasIVorNonce() { exists(this.getIVorNonce()) }
}
abstract class KeyWrapOperation extends CryptographicOperation { }
abstract class AuthenticatedEncryptionAlgorithm extends SymmetricEncryptionAlgorithm {
final string getAuthticatedEncryptionName() {
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}
abstract class KeyExchangeAlgorithm extends AsymmetricAlgorithm {
final string getKeyExchangeName() {
if exists(string n | n = this.getName() and isKeyExchangeAlgorithm(n))
then isKeyExchangeAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}
abstract class SigningAlgorithm extends AsymmetricAlgorithm {
final string getSigningName() {
if exists(string n | n = this.getName() and isSignatureAlgorithm(n))
then isSignatureAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}