Python: Port py/weak-crypto-key to use type-tracking

instead of points-to.

Looking at query results also made me realize I didn't supply a very good
"origin" for ECC in cryptography package, so I improved that 👍 -- maybe that
sohuld have been split into multiple commits... too late :(
This commit is contained in:
Rasmus Wriedt Larsen
2021-02-02 17:10:36 +01:00
parent 2429c6c450
commit 46ad611d57
5 changed files with 102 additions and 78 deletions

View File

@@ -10,72 +10,13 @@
*/
import python
import semmle.python.Concepts
import semmle.python.dataflow.new.DataFlow
int minimumSecureKeySize(string algo) {
algo = "DSA" and result = 2048
or
algo = "RSA" and result = 2048
or
algo = "ECC" and result = 224
}
predicate dsaRsaKeySizeArg(FunctionValue func, string algorithm, string arg) {
exists(ModuleValue mod | func = mod.attr(_) |
algorithm = "DSA" and
(
mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size"
or
mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits"
or
mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits"
)
or
algorithm = "RSA" and
(
mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size"
or
mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits"
or
mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits"
)
)
}
predicate ecKeySizeArg(FunctionValue func, string arg) {
exists(ModuleValue mod | func = mod.attr(_) |
mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve"
)
}
int keySizeFromCurve(ClassValue curveClass) {
result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue()
}
predicate algorithmAndKeysizeForCall(
CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin
) {
exists(FunctionValue func, string argname, ControlFlowNode arg |
arg = func.getNamedArgumentForCall(call, argname)
|
exists(NumericValue key |
arg.pointsTo(key, keyOrigin) and
dsaRsaKeySizeArg(func, algorithm, argname) and
keySize = key.getIntValue()
)
or
exists(Value curveClassInstance |
algorithm = "ECC" and
ecKeySizeArg(func, argname) and
arg.pointsTo(_, curveClassInstance, keyOrigin) and
keySize = keySizeFromCurve(curveClassInstance.getClass())
)
)
}
from CallNode call, string algo, int keySize, ControlFlowNode origin
from Cryptography::PublicKey::KeyGeneration keyGen, int keySize, DataFlow::Node origin
where
algorithmAndKeysizeForCall(call, algo, keySize, origin) and
keySize < minimumSecureKeySize(algo)
select call,
"Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) +
" and considered breakable.", origin, keySize.toString()
keySize = keyGen.getKeySizeWithOrigin(origin) and
keySize < keyGen.minimumSecureKeySize()
select keyGen,
"Creation of an " + keyGen.getName() + " key uses $@ bits, which is below " +
keyGen.minimumSecureKeySize() + " and considered breakable.", origin, keySize.toString()

View File

@@ -0,0 +1,81 @@
/**
* @name Use of weak cryptographic key
* @description Use of a cryptographic key that is too small may allow the encryption to be broken.
* @kind problem
* @problem.severity error
* @precision high
* @id py/weak-crypto-key
* @tags security
* external/cwe/cwe-326
*/
import python
int minimumSecureKeySize(string algo) {
algo = "DSA" and result = 2048
or
algo = "RSA" and result = 2048
or
algo = "ECC" and result = 224
}
predicate dsaRsaKeySizeArg(FunctionValue func, string algorithm, string arg) {
exists(ModuleValue mod | func = mod.attr(_) |
algorithm = "DSA" and
(
mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size"
or
mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits"
or
mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits"
)
or
algorithm = "RSA" and
(
mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size"
or
mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits"
or
mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits"
)
)
}
predicate ecKeySizeArg(FunctionValue func, string arg) {
exists(ModuleValue mod | func = mod.attr(_) |
mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve"
)
}
int keySizeFromCurve(ClassValue curveClass) {
result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue()
}
predicate algorithmAndKeysizeForCall(
CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin
) {
exists(FunctionValue func, string argname, ControlFlowNode arg |
arg = func.getNamedArgumentForCall(call, argname)
|
exists(NumericValue key |
arg.pointsTo(key, keyOrigin) and
dsaRsaKeySizeArg(func, algorithm, argname) and
keySize = key.getIntValue()
)
or
exists(Value curveClassInstance |
algorithm = "ECC" and
ecKeySizeArg(func, argname) and
arg.pointsTo(_, curveClassInstance, keyOrigin) and
keySize = keySizeFromCurve(curveClassInstance.getClass())
)
)
}
from CallNode call, string algo, int keySize, ControlFlowNode origin
where
algorithmAndKeysizeForCall(call, algo, keySize, origin) and
keySize < minimumSecureKeySize(algo)
select call,
"Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) +
" and considered breakable.", origin, keySize.toString()

View File

@@ -423,22 +423,23 @@ private module CryptographyModel {
result = ec_attr("BrainpoolP512R1") and keySize = 512
}
/** Gets a predefined curve class instance with a specific key size (in bits). */
/** 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::TypeTracker t, int keySize, DataFlow::Node origin
) {
t.start() and
result.asCfgNode().(CallNode).getFunction() =
curveClassWithKeySize(keySize).asCfgNode()
curveClassWithKeySize(keySize).asCfgNode() and
origin = result
or
exists(DataFlow::TypeTracker t2 |
result = curveClassInstanceWithKeySize(t2, keySize).track(t2, t)
result = curveClassInstanceWithKeySize(t2, keySize, origin).track(t2, t)
)
}
/** Gets a predefined curve class instance with a specific key size (in bits). */
DataFlow::Node curveClassInstanceWithKeySize(int keySize) {
result = curveClassInstanceWithKeySize(DataFlow::TypeTracker::end(), keySize)
/** 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)
}
}
}
@@ -505,9 +506,9 @@ private module CryptographyModel {
}
override int getKeySizeWithOrigin(DataFlow::Node origin) {
origin = this.getCurveArg() and
origin =
cryptography::hazmat::primitives::asymmetric::ec::curveClassInstanceWithKeySize(result)
this.getCurveArg() =
cryptography::hazmat::primitives::asymmetric::ec::curveClassInstanceWithKeySize(result,
origin)
}
// Note: There is not really a key-size argument, since it's always specified by the curve.