Files
codeql/python/ql/lib/semmle/python/frameworks/Cryptography.qll
Andrew Eisenberg 3660c64328 Packaging: Rafactor Python core libraries
Extract the external facing `qll` files into the codeql/python-all
query pack.
2021-08-24 13:23:45 -07:00

341 lines
13 KiB
Plaintext

/**
* Provides classes modeling security-relevant aspects of the `cryptography` PyPI package.
* See https://cryptography.io/en/latest/.
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
/**
* Provides models for the `cryptography` PyPI package.
* See https://cryptography.io/en/latest/.
*/
private module CryptographyModel {
/**
* Provides helper predicates for the eliptic curve cryptography parts in
* `cryptography.hazmat.primitives.asymmetric.ec`.
*/
module Ecc {
/**
* Gets a predefined curve class from
* `cryptography.hazmat.primitives.asymmetric.ec` with a specific key size (in bits).
*/
private API::Node predefinedCurveClass(int keySize) {
exists(string curveName |
result =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("asymmetric")
.getMember("ec")
.getMember(curveName)
|
// obtained by manually looking at source code in
// https://github.com/pyca/cryptography/blob/cba69f1922803f4f29a3fde01741890d88b8e217/src/cryptography/hazmat/primitives/asymmetric/ec.py#L208-L300
curveName = "SECT571R1" and keySize = 570 // Indeed the numbers do not match.
or
curveName = "SECT409R1" and keySize = 409
or
curveName = "SECT283R1" and keySize = 283
or
curveName = "SECT233R1" and keySize = 233
or
curveName = "SECT163R2" and keySize = 163
or
curveName = "SECT571K1" and keySize = 571
or
curveName = "SECT409K1" and keySize = 409
or
curveName = "SECT283K1" and keySize = 283
or
curveName = "SECT233K1" and keySize = 233
or
curveName = "SECT163K1" and keySize = 163
or
curveName = "SECP521R1" and keySize = 521
or
curveName = "SECP384R1" and keySize = 384
or
curveName = "SECP256R1" and keySize = 256
or
curveName = "SECP256K1" and keySize = 256
or
curveName = "SECP224R1" and keySize = 224
or
curveName = "SECP192R1" and keySize = 192
or
curveName = "BrainpoolP256R1" and keySize = 256
or
curveName = "BrainpoolP384R1" and keySize = 384
or
curveName = "BrainpoolP512R1" and keySize = 512
)
}
/** Gets a reference to a predefined curve class with a specific key size (in bits), as well as the origin of the class. */
private DataFlow::TypeTrackingNode curveClassWithKeySize(
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
) {
t.start() and
result = predefinedCurveClass(keySize).getAnImmediateUse() and
origin = result
or
exists(DataFlow::TypeTracker t2 |
result = curveClassWithKeySize(t2, keySize, origin).track(t2, t)
)
}
/** Gets a reference to a predefined curve class with a specific key size (in bits), as well as the origin of the class. */
DataFlow::Node curveClassWithKeySize(int keySize, DataFlow::Node origin) {
curveClassWithKeySize(DataFlow::TypeTracker::end(), keySize, origin).flowsTo(result)
}
/** Gets a reference to a predefined curve class instance with a specific key size (in bits), as well as the origin of the class. */
private DataFlow::TypeTrackingNode curveClassInstanceWithKeySize(
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
) {
t.start() and
result.(DataFlow::CallCfgNode).getFunction() = curveClassWithKeySize(keySize, origin)
or
exists(DataFlow::TypeTracker t2 |
result = curveClassInstanceWithKeySize(t2, keySize, origin).track(t2, t)
)
}
/** Gets a reference to a predefined curve class instance with a specific key size (in bits), as well as the origin of the class. */
DataFlow::Node curveClassInstanceWithKeySize(int keySize, DataFlow::Node origin) {
curveClassInstanceWithKeySize(DataFlow::TypeTracker::end(), keySize, origin).flowsTo(result)
}
}
// ---------------------------------------------------------------------------
/**
* A call to `cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key`
*
* See https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa.html#cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key
*/
class CryptographyRsaGeneratePrivateKeyCall extends Cryptography::PublicKey::KeyGeneration::RsaRange,
DataFlow::CallCfgNode {
CryptographyRsaGeneratePrivateKeyCall() {
this =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("asymmetric")
.getMember("rsa")
.getMember("generate_private_key")
.getACall()
}
override DataFlow::Node getKeySizeArg() {
result in [this.getArg(1), this.getArgByName("key_size")]
}
}
/**
* A call to `cryptography.hazmat.primitives.asymmetric.dsa.generate_private_key`
*
* See https://cryptography.io/en/latest/hazmat/primitives/asymmetric/dsa.html#cryptography.hazmat.primitives.asymmetric.dsa.generate_private_key
*/
class CryptographyDsaGeneratePrivateKeyCall extends Cryptography::PublicKey::KeyGeneration::DsaRange,
DataFlow::CallCfgNode {
CryptographyDsaGeneratePrivateKeyCall() {
this =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("asymmetric")
.getMember("dsa")
.getMember("generate_private_key")
.getACall()
}
override DataFlow::Node getKeySizeArg() {
result in [this.getArg(0), this.getArgByName("key_size")]
}
}
/**
* A call to `cryptography.hazmat.primitives.asymmetric.ec.generate_private_key`
*
* See https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec.html#cryptography.hazmat.primitives.asymmetric.ec.generate_private_key
*/
class CryptographyEcGeneratePrivateKeyCall extends Cryptography::PublicKey::KeyGeneration::EccRange,
DataFlow::CallCfgNode {
CryptographyEcGeneratePrivateKeyCall() {
this =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("asymmetric")
.getMember("ec")
.getMember("generate_private_key")
.getACall()
}
/** Gets the argument that specifies the curve to use. */
DataFlow::Node getCurveArg() { result in [this.getArg(0), this.getArgByName("curve")] }
override int getKeySizeWithOrigin(DataFlow::Node origin) {
this.getCurveArg() = Ecc::curveClassInstanceWithKeySize(result, origin)
or
this.getCurveArg() = Ecc::curveClassWithKeySize(result, origin)
}
// Note: There is not really a key-size argument, since it's always specified by the curve.
override DataFlow::Node getKeySizeArg() { none() }
}
/** Provides models for the `cryptography.hazmat.primitives.ciphers` package */
private module Ciphers {
/** Gets a reference to a `cryptography.hazmat.primitives.ciphers.algorithms` Class */
API::Node algorithmClassRef(string algorithmName) {
result =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("ciphers")
.getMember("algorithms")
.getMember(algorithmName)
}
/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
DataFlow::TypeTrackingNode cipherInstance(DataFlow::TypeTracker t, string algorithmName) {
t.start() and
exists(DataFlow::CallCfgNode call | result = call |
call =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("ciphers")
.getMember("Cipher")
.getACall() and
algorithmClassRef(algorithmName).getReturn().getAUse() in [
call.getArg(0), call.getArgByName("algorithm")
]
)
or
exists(DataFlow::TypeTracker t2 | result = cipherInstance(t2, algorithmName).track(t2, t))
}
/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
DataFlow::Node cipherInstance(string algorithmName) {
cipherInstance(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
}
/** Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`. */
DataFlow::TypeTrackingNode cipherEncryptor(DataFlow::TypeTracker t, string algorithmName) {
t.start() and
result.(DataFlow::MethodCallNode).calls(cipherInstance(algorithmName), "encryptor")
or
exists(DataFlow::TypeTracker t2 | result = cipherEncryptor(t2, algorithmName).track(t2, t))
}
/**
* Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`.
*
* You obtain an encryptor by using the `encryptor()` method on a Cipher instance.
*/
DataFlow::Node cipherEncryptor(string algorithmName) {
cipherEncryptor(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
}
/** Gets a reference to the dncryptor of a Cipher instance using algorithm with `algorithmName`. */
DataFlow::TypeTrackingNode cipherDecryptor(DataFlow::TypeTracker t, string algorithmName) {
t.start() and
result.(DataFlow::MethodCallNode).calls(cipherInstance(algorithmName), "decryptor")
or
exists(DataFlow::TypeTracker t2 | result = cipherDecryptor(t2, algorithmName).track(t2, t))
}
/**
* Gets a reference to the decryptor of a Cipher instance using algorithm with `algorithmName`.
*
* You obtain an decryptor by using the `decryptor()` method on a Cipher instance.
*/
DataFlow::Node cipherDecryptor(string algorithmName) {
cipherDecryptor(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
}
/**
* An encrypt or decrypt operation from `cryptography.hazmat.primitives.ciphers`.
*/
class CryptographyGenericCipherOperation extends Cryptography::CryptographicOperation::Range,
DataFlow::MethodCallNode {
string algorithmName;
CryptographyGenericCipherOperation() {
exists(DataFlow::Node object, string method |
object in [cipherEncryptor(algorithmName), cipherDecryptor(algorithmName)] and
method in ["update", "update_into"] and
this.calls(object, method)
)
}
override Cryptography::CryptographicAlgorithm getAlgorithm() {
result.matchesName(algorithmName)
}
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
}
}
/** Provides models for the `cryptography.hazmat.primitives.hashes` package */
private module Hashes {
/**
* Gets a reference to a `cryptography.hazmat.primitives.hashes` class, representing
* a hashing algorithm.
*/
API::Node algorithmClassRef(string algorithmName) {
result =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("hashes")
.getMember(algorithmName)
}
/** Gets a reference to a Hash instance using algorithm with `algorithmName`. */
private DataFlow::TypeTrackingNode hashInstance(DataFlow::TypeTracker t, string algorithmName) {
t.start() and
exists(DataFlow::CallCfgNode call | result = call |
call =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("hashes")
.getMember("Hash")
.getACall() and
algorithmClassRef(algorithmName).getReturn().getAUse() in [
call.getArg(0), call.getArgByName("algorithm")
]
)
or
exists(DataFlow::TypeTracker t2 | result = hashInstance(t2, algorithmName).track(t2, t))
}
/** Gets a reference to a Hash instance using algorithm with `algorithmName`. */
DataFlow::Node hashInstance(string algorithmName) {
hashInstance(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
}
/**
* An hashing operation from `cryptography.hazmat.primitives.hashes`.
*/
class CryptographyGenericHashOperation extends Cryptography::CryptographicOperation::Range,
DataFlow::MethodCallNode {
string algorithmName;
CryptographyGenericHashOperation() { this.calls(hashInstance(algorithmName), "update") }
override Cryptography::CryptographicAlgorithm getAlgorithm() {
result.matchesName(algorithmName)
}
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
}
}
}