Python: Port cryptography models to use API graphs (mostly)

This commit is contained in:
Rasmus Wriedt Larsen
2021-02-17 14:12:04 +01:00
parent bfbaa85272
commit 1eabfbd0e4

View File

@@ -6,457 +6,103 @@
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 {
// ---------------------------------------------------------------------------
// cryptography
// ---------------------------------------------------------------------------
/** Gets a reference to the `cryptography` module. */
private DataFlow::Node cryptography(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importNode("cryptography")
or
exists(DataFlow::TypeTracker t2 | result = cryptography(t2).track(t2, t))
}
/** Gets a reference to the `cryptography` module. */
DataFlow::Node cryptography() { result = cryptography(DataFlow::TypeTracker::end()) }
/** Provides models for the `cryptography` module. */
module cryptography {
/**
* Provides helper predicates for the eliptic curve cryptography parts in
* `cryptography.hazmat.primitives.asymmetric.ec`.
*/
module Ecc {
/**
* Gets a reference to the attribute `attr_name` of the `cryptography` module.
* WARNING: Only holds for a few predefined attributes.
* Gets a predefined curve class from
* `cryptography.hazmat.primitives.asymmetric.ec` with a specific key size (in bits).
*/
private DataFlow::Node cryptography_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["hazmat"] and
(
t.start() and
result = DataFlow::importNode("cryptography" + "." + attr_name)
private DataFlow::Node curveClassWithKeySize(int keySize) {
exists(string curveName |
result =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("asymmetric")
.getMember("ec")
.getMember(curveName)
.getAUse()
|
// 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
or
t.startInAttr(attr_name) and
result = cryptography()
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 instance with a specific key size (in bits), as well as the origin of the class. */
private DataFlow::Node curveClassInstanceWithKeySize(
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
) {
t.start() and
result.asCfgNode().(CallNode).getFunction() = curveClassWithKeySize(keySize).asCfgNode() and
origin = result
or
// Due to bad performance when using normal setup with `cryptography_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
// Due to bad performance when using normal setup with we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
cryptography_attr_first_join(t2, attr_name, result, summary) and
curveClassInstanceWithKeySize_first_join(t2, keySize, origin, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate cryptography_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
private predicate curveClassInstanceWithKeySize_first_join(
DataFlow::TypeTracker t2, int keySize, DataFlow::Node origin, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(cryptography_attr(t2, attr_name), res, summary)
DataFlow::StepSummary::step(curveClassInstanceWithKeySize(t2, keySize, origin), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `cryptography` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node cryptography_attr(string attr_name) {
result = cryptography_attr(DataFlow::TypeTracker::end(), attr_name)
}
// -------------------------------------------------------------------------
// cryptography.hazmat
// -------------------------------------------------------------------------
/** Gets a reference to the `cryptography.hazmat` module. */
DataFlow::Node hazmat() { result = cryptography_attr("hazmat") }
/** Provides models for the `cryptography.hazmat` module */
module hazmat {
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node hazmat_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["primitives"] and
(
t.start() and
result = DataFlow::importNode("cryptography.hazmat" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = hazmat()
)
or
// Due to bad performance when using normal setup with `hazmat_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
hazmat_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate hazmat_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(hazmat_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node hazmat_attr(string attr_name) {
result = hazmat_attr(DataFlow::TypeTracker::end(), attr_name)
}
// -------------------------------------------------------------------------
// cryptography.hazmat.primitives
// -------------------------------------------------------------------------
/** Gets a reference to the `cryptography.hazmat.primitives` module. */
DataFlow::Node primitives() { result = hazmat_attr("primitives") }
/** Provides models for the `cryptography.hazmat.primitives` module */
module primitives {
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node primitives_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["asymmetric"] and
(
t.start() and
result = DataFlow::importNode("cryptography.hazmat.primitives" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = primitives()
)
or
// Due to bad performance when using normal setup with `primitives_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
primitives_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate primitives_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(primitives_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node primitives_attr(string attr_name) {
result = primitives_attr(DataFlow::TypeTracker::end(), attr_name)
}
// -------------------------------------------------------------------------
// cryptography.hazmat.primitives.asymmetric
// -------------------------------------------------------------------------
/** Gets a reference to the `cryptography.hazmat.primitives.asymmetric` module. */
DataFlow::Node asymmetric() { result = primitives_attr("asymmetric") }
/** Provides models for the `cryptography.hazmat.primitives.asymmetric` module */
module asymmetric {
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives.asymmetric` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node asymmetric_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["rsa", "dsa", "ec"] and
(
t.start() and
result =
DataFlow::importNode("cryptography.hazmat.primitives.asymmetric" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = asymmetric()
)
or
// Due to bad performance when using normal setup with `asymmetric_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
asymmetric_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate asymmetric_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(asymmetric_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives.asymmetric` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node asymmetric_attr(string attr_name) {
result = asymmetric_attr(DataFlow::TypeTracker::end(), attr_name)
}
// -------------------------------------------------------------------------
// cryptography.hazmat.primitives.asymmetric.rsa
// -------------------------------------------------------------------------
/** Gets a reference to the `cryptography.hazmat.primitives.asymmetric.rsa` module. */
DataFlow::Node rsa() { result = asymmetric_attr("rsa") }
/** Provides models for the `cryptography.hazmat.primitives.asymmetric.rsa` module */
module rsa {
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives.asymmetric.rsa` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node rsa_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["generate_private_key"] and
(
t.start() and
result =
DataFlow::importNode("cryptography.hazmat.primitives.asymmetric.rsa" + "." +
attr_name)
or
t.startInAttr(attr_name) and
result = rsa()
)
or
// Due to bad performance when using normal setup with `rsa_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
rsa_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate rsa_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(rsa_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives.asymmetric.rsa` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node rsa_attr(string attr_name) {
result = rsa_attr(DataFlow::TypeTracker::end(), attr_name)
}
/** Gets a reference to the `cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key` function. */
DataFlow::Node generate_private_key() { result = rsa_attr("generate_private_key") }
}
// -------------------------------------------------------------------------
// cryptography.hazmat.primitives.asymmetric.dsa
// -------------------------------------------------------------------------
/** Gets a reference to the `cryptography.hazmat.primitives.asymmetric.dsa` module. */
DataFlow::Node dsa() { result = asymmetric_attr("dsa") }
/** Provides models for the `cryptography.hazmat.primitives.asymmetric.dsa` module */
module dsa {
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives.asymmetric.dsa` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node dsa_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["generate_private_key"] and
(
t.start() and
result =
DataFlow::importNode("cryptography.hazmat.primitives.asymmetric.dsa" + "." +
attr_name)
or
t.startInAttr(attr_name) and
result = dsa()
)
or
// Due to bad performance when using normal setup with `dsa_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
dsa_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate dsa_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(dsa_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives.asymmetric.dsa` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node dsa_attr(string attr_name) {
result = dsa_attr(DataFlow::TypeTracker::end(), attr_name)
}
/** Gets a reference to the `cryptography.hazmat.primitives.asymmetric.dsa.generate_private_key` function. */
DataFlow::Node generate_private_key() { result = dsa_attr("generate_private_key") }
}
// -------------------------------------------------------------------------
// cryptography.hazmat.primitives.asymmetric.ec
// -------------------------------------------------------------------------
/** Gets a reference to the `cryptography.hazmat.primitives.asymmetric.ec` module. */
DataFlow::Node ec() { result = asymmetric_attr("ec") }
/** Provides models for the `cryptography.hazmat.primitives.asymmetric.ec` module */
module ec {
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives.asymmetric.ec` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node ec_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in [
"generate_private_key",
// curves
"SECT571R1", "SECT409R1", "SECT283R1", "SECT233R1", "SECT163R2", "SECT571K1",
"SECT409K1", "SECT283K1", "SECT233K1", "SECT163K1", "SECP521R1", "SECP384R1",
"SECP256R1", "SECP256K1", "SECP224R1", "SECP192R1", "BrainpoolP256R1",
"BrainpoolP384R1", "BrainpoolP512R1"
] and
(
t.start() and
result =
DataFlow::importNode("cryptography.hazmat.primitives.asymmetric.ec" + "." +
attr_name)
or
t.startInAttr(attr_name) and
result = ec()
)
or
// Due to bad performance when using normal setup with `ec_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
ec_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate ec_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(ec_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `cryptography.hazmat.primitives.asymmetric.ec` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node ec_attr(string attr_name) {
result = ec_attr(DataFlow::TypeTracker::end(), attr_name)
}
/** Gets a reference to the `cryptography.hazmat.primitives.asymmetric.ec.generate_private_key` function. */
DataFlow::Node generate_private_key() { result = ec_attr("generate_private_key") }
/** Gets a predefined curve class with a specific key size (in bits). */
DataFlow::Node curveClassWithKeySize(int keySize) {
// obtained by manually looking at source code in
// https://github.com/pyca/cryptography/blob/cba69f1922803f4f29a3fde01741890d88b8e217/src/cryptography/hazmat/primitives/asymmetric/ec.py#L208-L300
result = ec_attr("SECT571R1") and keySize = 570
or
result = ec_attr("SECT409R1") and keySize = 409
or
result = ec_attr("SECT283R1") and keySize = 283
or
result = ec_attr("SECT233R1") and keySize = 233
or
result = ec_attr("SECT163R2") and keySize = 163
or
result = ec_attr("SECT571K1") and keySize = 571
or
result = ec_attr("SECT409K1") and keySize = 409
or
result = ec_attr("SECT283K1") and keySize = 283
or
result = ec_attr("SECT233K1") and keySize = 233
or
result = ec_attr("SECT163K1") and keySize = 163
or
result = ec_attr("SECP521R1") and keySize = 521
or
result = ec_attr("SECP384R1") and keySize = 384
or
result = ec_attr("SECP256R1") and keySize = 256
or
result = ec_attr("SECP256K1") and keySize = 256
or
result = ec_attr("SECP224R1") and keySize = 224
or
result = ec_attr("SECP192R1") and keySize = 192
or
result = ec_attr("BrainpoolP256R1") and keySize = 256
or
result = ec_attr("BrainpoolP384R1") and keySize = 384
or
result = ec_attr("BrainpoolP512R1") and keySize = 512
}
/** 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::Node curveClassInstanceWithKeySize(
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
) {
t.start() and
result.asCfgNode().(CallNode).getFunction() =
curveClassWithKeySize(keySize).asCfgNode() and
origin = result
or
// Due to bad performance when using normal setup with we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
curveClassInstanceWithKeySize_first_join(t2, keySize, origin, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate curveClassInstanceWithKeySize_first_join(
DataFlow::TypeTracker t2, int keySize, DataFlow::Node origin, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(curveClassInstanceWithKeySize(t2, keySize, origin), res,
summary)
}
/** 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) {
result = curveClassInstanceWithKeySize(DataFlow::TypeTracker::end(), keySize, origin)
}
}
}
}
/** 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) {
result = curveClassInstanceWithKeySize(DataFlow::TypeTracker::end(), keySize, origin)
}
}
@@ -467,16 +113,20 @@ private module CryptographyModel {
* 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::CfgNode {
override CallNode node;
DataFlow::CallCfgNode {
CryptographyRsaGeneratePrivateKeyCall() {
node.getFunction() =
cryptography::hazmat::primitives::asymmetric::rsa::generate_private_key().asCfgNode()
this =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("asymmetric")
.getMember("rsa")
.getMember("generate_private_key")
.getACall()
}
override DataFlow::Node getKeySizeArg() {
result.asCfgNode() in [node.getArg(1), node.getArgByName("key_size")]
result in [this.getArg(1), this.getArgByName("key_size")]
}
}
@@ -486,16 +136,20 @@ private module CryptographyModel {
* 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::CfgNode {
override CallNode node;
DataFlow::CallCfgNode {
CryptographyDsaGeneratePrivateKeyCall() {
node.getFunction() =
cryptography::hazmat::primitives::asymmetric::dsa::generate_private_key().asCfgNode()
this =
API::moduleImport("cryptography")
.getMember("hazmat")
.getMember("primitives")
.getMember("asymmetric")
.getMember("dsa")
.getMember("generate_private_key")
.getACall()
}
override DataFlow::Node getKeySizeArg() {
result.asCfgNode() in [node.getArg(0), node.getArgByName("key_size")]
result in [this.getArg(0), this.getArgByName("key_size")]
}
}
@@ -505,23 +159,23 @@ private module CryptographyModel {
* 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::CfgNode {
override CallNode node;
DataFlow::CallCfgNode {
CryptographyEcGeneratePrivateKeyCall() {
node.getFunction() =
cryptography::hazmat::primitives::asymmetric::ec::generate_private_key().asCfgNode()
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.asCfgNode() in [node.getArg(0), node.getArgByName("curve")]
}
DataFlow::Node getCurveArg() { result in [this.getArg(0), this.getArgByName("curve")] }
override int getKeySizeWithOrigin(DataFlow::Node origin) {
this.getCurveArg() =
cryptography::hazmat::primitives::asymmetric::ec::curveClassInstanceWithKeySize(result,
origin)
this.getCurveArg() = Ecc::curveClassInstanceWithKeySize(result, origin)
}
// Note: There is not really a key-size argument, since it's always specified by the curve.