mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
autoformat
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
import experimental.cryptography.modules.stdlib.HashlibModule as HashLibModule
|
||||
import experimental.cryptography.modules.stdlib.HmacModule as HMacModule
|
||||
import experimental.cryptography.modules.CryptographyModule as CryptographyModule
|
||||
|
||||
@@ -11,31 +11,40 @@
|
||||
string unknownAlgorithm() { result = "UNKNOWN" }
|
||||
|
||||
string getHashType() { result = "HASH" }
|
||||
|
||||
string getSymmetricEncryptionType() { result = "SYMMETRIC_ENCRYPTION" }
|
||||
|
||||
string getAsymmetricEncryptionType() { result = "ASYMMETRIC_ENCRYPTION" }
|
||||
|
||||
string getKeyDerivationType() { result = "KEY_DERIVATION" }
|
||||
|
||||
string getCipherBlockModeType() { result = "BLOCK_MODE" }
|
||||
|
||||
string getSymmetricPaddingType() { result = "SYMMETRIC_PADDING" }
|
||||
|
||||
string getAsymmetricPaddingType() { result = "ASYMMETRIC_PADDING" }
|
||||
|
||||
string getEllipticCurveType() { result = "ELLIPTIC_CURVE" }
|
||||
|
||||
string getSignatureType() { result = "SIGNATURE" }
|
||||
|
||||
string getKeyExchangeType() { result = "KEY_EXCHANGE" }
|
||||
|
||||
predicate isKnownType(string algType){
|
||||
predicate isKnownType(string algType) {
|
||||
algType in [
|
||||
getHashType(), getSymmetricEncryptionType(), getAsymmetricEncryptionType(), getKeyDerivationType(),
|
||||
getCipherBlockModeType(), getSymmetricPaddingType(), getAsymmetricPaddingType(), getEllipticCurveType(),
|
||||
getSignatureType(), getKeyExchangeType()
|
||||
]
|
||||
getHashType(), getSymmetricEncryptionType(), getAsymmetricEncryptionType(),
|
||||
getKeyDerivationType(), getCipherBlockModeType(), getSymmetricPaddingType(),
|
||||
getAsymmetricPaddingType(), getEllipticCurveType(), getSignatureType(), getKeyExchangeType()
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
predicate isKnownAlgorithm(string name) { isKnownAlgorithm(name, _) }
|
||||
|
||||
predicate isKnownAlgorithm(string name, string algType) {
|
||||
isHashingAlgorithm(name) and algType = "HASH"
|
||||
or
|
||||
isEncryptionAlgorithm(name, algType) and algType in ["SYMMETRIC_ENCRYPTION", "ASYMMETRIC_ENCRYPTION"]
|
||||
isEncryptionAlgorithm(name, algType) and
|
||||
algType in ["SYMMETRIC_ENCRYPTION", "ASYMMETRIC_ENCRYPTION"]
|
||||
or
|
||||
isKeyDerivationAlgorithm(name) and algType = "KEY_DERIVATION"
|
||||
or
|
||||
@@ -56,11 +65,11 @@ predicate isKnownAlgorithm(string name, string algType) {
|
||||
predicate isHashingAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"BLAKE2", "BLAKE2B", "BLAKE2S",
|
||||
"SHA2", "SHA224", "SHA256", "SHA384", "SHA512", "SHA512224", "SHA512256",
|
||||
"SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512", "SHAKE128", "SHAKE256", "SM3",
|
||||
"WHIRLPOOL", "POLY1305", "HAVEL128", "MD2", "MD4", "MD5", "PANAMA", "RIPEMD", "RIPEMD128",
|
||||
"RIPEMD256", "RIPEMD160", "RIPEMD320", "SHA0", "SHA1", "SHA", "MGF1","MGF1SHA1", "MDC2", "SIPHASH"
|
||||
"BLAKE2", "BLAKE2B", "BLAKE2S", "SHA2", "SHA224", "SHA256", "SHA384", "SHA512", "SHA512224",
|
||||
"SHA512256", "SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512", "SHAKE128", "SHAKE256",
|
||||
"SM3", "WHIRLPOOL", "POLY1305", "HAVEL128", "MD2", "MD4", "MD5", "PANAMA", "RIPEMD",
|
||||
"RIPEMD128", "RIPEMD256", "RIPEMD160", "RIPEMD320", "SHA0", "SHA1", "SHA", "MGF1", "MGF1SHA1",
|
||||
"MDC2", "SIPHASH"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -82,10 +91,10 @@ predicate isSymmetricEncryptionAlgorithm(string name) {
|
||||
"AES", "AES128", "AES192", "AES256", "ARIA", "BLOWFISH", "BF", "ECIES", "CAST", "CAST5",
|
||||
"CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "CHACHA", "CHACHA20",
|
||||
"CHACHA20POLY1305", "GOST", "GOSTR34102001", "GOSTR341094", "GOSTR341194", "GOST2814789",
|
||||
"GOSTR341194", "GOST2814789", "GOST28147", "GOSTR341094", "GOST89", "GOST94", "GOST34102012",
|
||||
"GOST34112012", "IDEA", "RABBIT",
|
||||
"SEED", "SM4", "DES", "DESX", "3DES", "TDES", "2DES", "DES3", "TRIPLEDES", "TDEA", "TRIPLEDEA",
|
||||
"ARC2", "RC2", "ARC4", "RC4", "ARCFOUR", "ARC5", "RC5", "MAGMA", "KUZNYECHIK"
|
||||
"GOSTR341194", "GOST2814789", "GOST28147", "GOSTR341094", "GOST89", "GOST94", "GOST34102012",
|
||||
"GOST34112012", "IDEA", "RABBIT", "SEED", "SM4", "DES", "DESX", "3DES", "TDES", "2DES",
|
||||
"DES3", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4", "RC4", "ARCFOUR", "ARC5",
|
||||
"RC5", "MAGMA", "KUZNYECHIK"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -200,8 +209,12 @@ predicate isEllipticCurveAlgorithm(string curveName, int keySize) {
|
||||
/**
|
||||
* Holds if `name` corresponds to a known signature algorithm.
|
||||
*/
|
||||
predicate isSignatureAlgorithm(string name) {
|
||||
name = ["DSA", "ECDSA", "EDDSA", "ES256", "ES256K", "ES384", "ES512", "ED25519", "ED448", "ECDSA256", "ECDSA384", "ECDSA512"]
|
||||
predicate isSignatureAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"DSA", "ECDSA", "EDDSA", "ES256", "ES256K", "ES384", "ES512", "ED25519", "ED448", "ECDSA256",
|
||||
"ECDSA384", "ECDSA512"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,56 +3,53 @@ 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 {}
|
||||
* 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.
|
||||
* 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 class SymmetricCipher extends CryptographicArtifact {
|
||||
abstract SymmetricEncryptionAlgorithm getEncryptionAlgorithm();
|
||||
|
||||
abstract BlockMode getBlockMode();
|
||||
final predicate hasBlockMode(){
|
||||
exists(this.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.
|
||||
* and refer to themselves as the target algorithm.
|
||||
*/
|
||||
abstract class CryptographicOperation extends CryptographicArtifact, API::CallNode{
|
||||
|
||||
abstract class CryptographicOperation extends CryptographicArtifact, API::CallNode {
|
||||
bindingset[paramName, ind]
|
||||
final DataFlow::Node getParameterSource(int ind, string paramName){
|
||||
final DataFlow::Node getParameterSource(int ind, string paramName) {
|
||||
result = Utils::getUltimateSrcFromApiNode(this.(API::CallNode).getParameter(ind, paramName))
|
||||
}
|
||||
|
||||
final string getAlgorithmName(){
|
||||
final string getAlgorithmName() {
|
||||
if exists(this.getAlgorithm().getName())
|
||||
then result = this.getAlgorithm().getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
final predicate hasAlgorithm(){
|
||||
exists(this.getAlgorithm())
|
||||
}
|
||||
final predicate hasAlgorithm() { exists(this.getAlgorithm()) }
|
||||
|
||||
final predicate isUnknownAlgorithm(){
|
||||
final predicate isUnknownAlgorithm() {
|
||||
this.getAlgorithmName() = unknownAlgorithm()
|
||||
or
|
||||
not this.hasAlgorithm()
|
||||
@@ -61,65 +58,51 @@ abstract class CryptographicOperation extends CryptographicArtifact, API::CallNo
|
||||
// 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{
|
||||
abstract class KeyGen extends CryptographicOperation {
|
||||
int getAKeySizeInBits() { result = getKeySizeInBits(_) }
|
||||
|
||||
|
||||
int getAKeySizeInBits(){
|
||||
result = getKeySizeInBits(_)
|
||||
}
|
||||
final predicate hasKeySize(DataFlow::Node configSrc) { exists(this.getKeySizeInBits(configSrc)) }
|
||||
|
||||
final predicate hasKeySize(DataFlow::Node configSrc){
|
||||
exists(this.getKeySizeInBits(configSrc))
|
||||
}
|
||||
|
||||
final predicate hasKeySize(){
|
||||
exists(this.getAKeySizeInBits())
|
||||
}
|
||||
final predicate hasKeySize() { exists(this.getAKeySizeInBits()) }
|
||||
|
||||
abstract DataFlow::Node getKeyConfigSrc();
|
||||
abstract int getKeySizeInBits(DataFlow::Node configSrc);
|
||||
|
||||
abstract int getKeySizeInBits(DataFlow::Node configSrc);
|
||||
}
|
||||
|
||||
abstract class AsymmetricKeyGen extends KeyGen{}
|
||||
abstract class SymmetricKeyGen extends KeyGen{}
|
||||
abstract class AsymmetricKeyGen extends KeyGen { }
|
||||
|
||||
abstract class SymmetricKeyGen extends KeyGen { }
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm is a `CryptographicArtifact`
|
||||
* 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.
|
||||
* necessarily where it is used.
|
||||
*/
|
||||
abstract class CryptographicAlgorithm extends CryptographicArtifact
|
||||
{
|
||||
abstract class CryptographicAlgorithm extends CryptographicArtifact {
|
||||
abstract string getName();
|
||||
|
||||
// TODO: handle case where name isn't known, not just unknown?
|
||||
|
||||
// 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 deafult, converts a raw name to upper-case with no hyphen, underscore, hash, or space.
|
||||
* By deafult, 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))
|
||||
string normalizeName(string s) {
|
||||
exists(string normStr | normStr = s.toUpperCase().regexpReplaceAll("[-_ ]", "") |
|
||||
result = normStr and isKnownAlgorithm(result)
|
||||
or
|
||||
(result = unknownAlgorithm() and not isKnownAlgorithm(normStr))
|
||||
result = unknownAlgorithm() and not isKnownAlgorithm(normStr)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -129,84 +112,69 @@ abstract class CryptographicAlgorithm extends CryptographicArtifact
|
||||
// super.getName() = unknownAlgorithm()
|
||||
// }
|
||||
// }
|
||||
|
||||
abstract class HashAlgorithm extends CryptographicAlgorithm{
|
||||
final string getHashName(){
|
||||
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())
|
||||
then isHashingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class KeyDerivationAlgorithm extends CryptographicAlgorithm{
|
||||
final string getKDFName(){
|
||||
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())
|
||||
then isKeyDerivationAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KeyDerivationOperation extends CryptographicOperation{
|
||||
DataFlow::Node getIterationSizeSrc(){
|
||||
none()
|
||||
}
|
||||
abstract class KeyDerivationOperation extends CryptographicOperation {
|
||||
DataFlow::Node getIterationSizeSrc() { none() }
|
||||
|
||||
DataFlow::Node getSaltConfigSrc(){
|
||||
none()
|
||||
}
|
||||
DataFlow::Node getSaltConfigSrc() { none() }
|
||||
|
||||
DataFlow::Node getHashConfigSrc(){
|
||||
none()
|
||||
}
|
||||
DataFlow::Node getHashConfigSrc() { none() }
|
||||
|
||||
// TODO: get encryption algorithm for CBC-based KDF?
|
||||
DataFlow::Node getDerivedKeySizeSrc() { none() }
|
||||
|
||||
DataFlow::Node getDerivedKeySizeSrc(){
|
||||
none()
|
||||
}
|
||||
|
||||
DataFlow::Node getModeSrc(){
|
||||
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.
|
||||
* which is intended only for asymmetric algorithms that specifically encrypt.
|
||||
*/
|
||||
abstract class AsymmetricAlgorithm extends CryptographicAlgorithm{}
|
||||
abstract class AsymmetricAlgorithm extends CryptographicAlgorithm { }
|
||||
|
||||
|
||||
abstract class EncryptionAlgorithm extends CryptographicAlgorithm
|
||||
{
|
||||
abstract class EncryptionAlgorithm extends CryptographicAlgorithm {
|
||||
final predicate isAsymmetric() { this instanceof AsymmetricEncryptionAlgorithm }
|
||||
final predicate isSymmetric() { not this.isAsymmetric() }
|
||||
|
||||
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(){
|
||||
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())
|
||||
then isAsymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
@@ -215,87 +183,82 @@ abstract class AsymmetricEncryptionAlgorithm extends AsymmetricAlgorithm, Encryp
|
||||
* 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(){
|
||||
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())
|
||||
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{}
|
||||
// DO_NOT add predicates here. Only for categorization purposes.
|
||||
abstract class PaddingAlgorithm extends CryptographicAlgorithm { }
|
||||
|
||||
abstract class SymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName(){
|
||||
final string getPaddingName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricPaddingAlgorithm(n))
|
||||
then (isSymmetricPaddingAlgorithm(result) and result = this.getName())
|
||||
then isSymmetricPaddingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AsymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName(){
|
||||
final string getPaddingName() {
|
||||
if exists(string n | n = this.getName() and isAsymmetricPaddingAlgorithm(n))
|
||||
then (isAsymmetricPaddingAlgorithm(result) and result = this.getName())
|
||||
then isAsymmetricPaddingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class EllipticCurveAlgorithm extends AsymmetricAlgorithm{
|
||||
final string getCurveName(){
|
||||
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())
|
||||
then isEllipticCurveAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
final int getCurveBitSize(){
|
||||
isEllipticCurveAlgorithm(this.getCurveName(), result)
|
||||
}
|
||||
final int getCurveBitSize() { isEllipticCurveAlgorithm(this.getCurveName(), result) }
|
||||
}
|
||||
|
||||
abstract class BlockMode extends CryptographicAlgorithm{
|
||||
final string getBlockModeName(){
|
||||
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())
|
||||
then isCipherBlockModeAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source of the IV configuration.
|
||||
* Gets the source of the IV configuration.
|
||||
*/
|
||||
abstract DataFlow::Node getIVorNonce();
|
||||
|
||||
final predicate hasIVorNonce() { exists(this.getIVorNonce()) }
|
||||
}
|
||||
|
||||
abstract class KeyWrapOperation extends CryptographicOperation{
|
||||
}
|
||||
abstract class KeyWrapOperation extends CryptographicOperation { }
|
||||
|
||||
abstract class AuthenticatedEncryptionAlgorithm extends SymmetricEncryptionAlgorithm{
|
||||
final string getAuthticatedEncryptionName(){
|
||||
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())
|
||||
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KeyExchangeAlgorithm extends AsymmetricAlgorithm{
|
||||
final string getKeyExchangeName(){
|
||||
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())
|
||||
then isKeyExchangeAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SigningAlgorithm extends AsymmetricAlgorithm{
|
||||
final string getSigningName(){
|
||||
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())
|
||||
then isSignatureAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ import python
|
||||
import semmle.python.ApiGraphs
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
private import experimental.cryptography.utils.Utils as Utils
|
||||
private import experimental.cryptography.CryptoAlgorithmNames
|
||||
private import experimental.cryptography.CryptoAlgorithmNames
|
||||
|
||||
/**
|
||||
* `hashlib` is a ptyhon standard library module for hashing algorithms.
|
||||
@@ -12,142 +12,140 @@ private import experimental.cryptography.CryptoAlgorithmNames
|
||||
// -----------------------------------------------
|
||||
// Hash Artifacts
|
||||
// -----------------------------------------------
|
||||
module Hashes{
|
||||
|
||||
|
||||
module Hashes {
|
||||
/**
|
||||
* Represents a hash algorithm used by `hashlib.new`, where the hash algorithm is a string in the first argument.
|
||||
*/
|
||||
class HashlibNewHashAlgorithm extends HashAlgorithm
|
||||
{
|
||||
HashlibNewHashAlgorithm(){
|
||||
this = Utils::getUltimateSrcFromApiNode(API::moduleImport("hashlib").getMember("new").getACall().getParameter(0, "name"))
|
||||
class HashlibNewHashAlgorithm extends HashAlgorithm {
|
||||
HashlibNewHashAlgorithm() {
|
||||
this =
|
||||
Utils::getUltimateSrcFromApiNode(API::moduleImport("hashlib")
|
||||
.getMember("new")
|
||||
.getACall()
|
||||
.getParameter(0, "name"))
|
||||
}
|
||||
|
||||
override string getName(){
|
||||
|
||||
override string getName() {
|
||||
result = super.normalizeName(this.asExpr().(StrConst).getText())
|
||||
or
|
||||
// if not a known/static string, assume from an outside source and the algorithm is UNKNOWN
|
||||
(not this.asExpr() instanceof StrConst and result = unknownAlgorithm())
|
||||
not this.asExpr() instanceof StrConst and result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies hashlib.pbdkf2_hmac calls, identifying the hash algorithm used
|
||||
* in the hmac (matching kdf is handled separately by `HashlibPbkdf2HMACArtifact`).
|
||||
*
|
||||
*
|
||||
* https://docs.python.org/3/library/hashlib.html#hashlib.pbkdf2_hmac
|
||||
*/
|
||||
class HashlibPbkdf2HMACHashAlgorithm extends HashAlgorithm
|
||||
{
|
||||
HashlibPbkdf2HMACHashAlgorithm(){
|
||||
this = Utils::getUltimateSrcFromApiNode(API::moduleImport("hashlib").getMember("pbkdf2_hmac").getACall().getParameter(0, "hash_name"))
|
||||
class HashlibPbkdf2HMACHashAlgorithm extends HashAlgorithm {
|
||||
HashlibPbkdf2HMACHashAlgorithm() {
|
||||
this =
|
||||
Utils::getUltimateSrcFromApiNode(API::moduleImport("hashlib")
|
||||
.getMember("pbkdf2_hmac")
|
||||
.getACall()
|
||||
.getParameter(0, "hash_name"))
|
||||
}
|
||||
|
||||
override string getName(){
|
||||
override string getName() {
|
||||
result = super.normalizeName(this.asExpr().(StrConst).getText())
|
||||
or
|
||||
// if not a known/static string, assume from an outside source and the algorithm is UNKNOWN
|
||||
(not this.asExpr() instanceof StrConst and result = unknownAlgorithm())
|
||||
not this.asExpr() instanceof StrConst and result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call to `hashlib.file_digest` where the hash algorithm is the first argument in `digest`
|
||||
* `nameSrc` is the source of the first argument.
|
||||
*
|
||||
*
|
||||
* https://docs.python.org/3/library/hashlib.html#hashlib.file_digest
|
||||
*
|
||||
*
|
||||
* NOTE: the digest argument can be, in addition to a string,
|
||||
* a callable that returns a hash object or a hash constructor.
|
||||
* These cases are not considered here since they would be detected separately.
|
||||
* Specifically, other non-string cases are themselves considered sources for alerts, e.g.,
|
||||
* These cases are not considered here since they would be detected separately.
|
||||
* Specifically, other non-string cases are themselves considered sources for alerts, e.g.,
|
||||
* references to hashlib.sha512 is found by `HashlibMemberAlgorithm`.
|
||||
* The only exception is if the source is not a string constant or HashlibMemberAlgorithm.
|
||||
* In these cases, the algorithm is considered 'UNKNOWN'.
|
||||
*
|
||||
|
||||
*/
|
||||
class HashlibFileDigestAlgorithm extends HashAlgorithm
|
||||
{
|
||||
HashlibFileDigestAlgorithm(){
|
||||
this = Utils::getUltimateSrcFromApiNode(API::moduleImport("hashlib").getMember("file_digest").getACall().getParameter(1, "digest"))
|
||||
and
|
||||
class HashlibFileDigestAlgorithm extends HashAlgorithm {
|
||||
HashlibFileDigestAlgorithm() {
|
||||
this =
|
||||
Utils::getUltimateSrcFromApiNode(API::moduleImport("hashlib")
|
||||
.getMember("file_digest")
|
||||
.getACall()
|
||||
.getParameter(1, "digest")) and
|
||||
// Ignore sources that are hash constructors, allow `HashlibMemberAlgorithm` to detect these
|
||||
this != hashlibMemberHashAlgorithm(_)
|
||||
this != hashlibMemberHashAlgorithm(_) and
|
||||
// Ignore sources that are HMAC objects, to be handled by HmacModule
|
||||
and
|
||||
this != API::moduleImport("hmac").getMember("new").getACall()
|
||||
and
|
||||
this != API::moduleImport("hmac").getMember("new").getACall() and
|
||||
this != API::moduleImport("hmac").getMember("HMAC").getACall()
|
||||
|
||||
}
|
||||
|
||||
override string getName(){
|
||||
override string getName() {
|
||||
// Name is a string constant or consider the name unknown
|
||||
// NOTE: we are excluding hmac.new and hmac.HMAC constructor calls so we are expecting
|
||||
// a string or an outside configuration only
|
||||
// a string or an outside configuration only
|
||||
result = super.normalizeName(this.asExpr().(StrConst).getText())
|
||||
or
|
||||
(
|
||||
not this.asExpr() instanceof StrConst
|
||||
and
|
||||
result = unknownAlgorithm()
|
||||
)
|
||||
not this.asExpr() instanceof StrConst and
|
||||
result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a member access of hashlib that is an algorithm invocation.
|
||||
* `hashName` is the name of the hash algorithm.
|
||||
*
|
||||
* Note: oringally a variant of this predicate was in codeql/github/main
|
||||
* to a predicate to avoid a bad join order.
|
||||
*/
|
||||
pragma[nomagic] // Copying use of nomagic from similar predicate in codeql/main
|
||||
DataFlow::Node hashlibMemberHashAlgorithm(string hashName) {
|
||||
* Gets a member access of hashlib that is an algorithm invocation.
|
||||
* `hashName` is the name of the hash algorithm.
|
||||
*
|
||||
* Note: oringally a variant of this predicate was in codeql/github/main
|
||||
* to a predicate to avoid a bad join order.
|
||||
*/
|
||||
// Copying use of nomagic from similar predicate in codeql/main
|
||||
pragma[nomagic]
|
||||
DataFlow::Node hashlibMemberHashAlgorithm(string hashName) {
|
||||
result = API::moduleImport("hashlib").getMember(hashName).asSource() and
|
||||
// Don't matches known non-hash members
|
||||
not hashName in ["new", "pbkdf2_hmac", "algorithms_available",
|
||||
"algorithms_guaranteed", "file_digest"]
|
||||
not hashName in [
|
||||
"new", "pbkdf2_hmac", "algorithms_available", "algorithms_guaranteed", "file_digest"
|
||||
] and
|
||||
// Don't match things like __file__
|
||||
and not hashName.regexpMatch("_.*")
|
||||
not hashName.regexpMatch("_.*")
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies hashing algorithm members (i.e., functions) of the `hashlib` module,
|
||||
* e.g., `hashlib.sha512`.
|
||||
*/
|
||||
class HashlibMemberAlgorithm extends HashAlgorithm
|
||||
{
|
||||
HashlibMemberAlgorithm(){
|
||||
this = hashlibMemberHashAlgorithm(_)
|
||||
}
|
||||
*/
|
||||
class HashlibMemberAlgorithm extends HashAlgorithm {
|
||||
HashlibMemberAlgorithm() { this = hashlibMemberHashAlgorithm(_) }
|
||||
|
||||
override string getName(){
|
||||
exists(string rawName |
|
||||
override string getName() {
|
||||
exists(string rawName |
|
||||
result = super.normalizeName(rawName) and this = hashlibMemberHashAlgorithm(rawName)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// Key Derivation Functions
|
||||
// -----------------------------------------------
|
||||
module KDF{
|
||||
|
||||
module KDF {
|
||||
// NOTE: Only finds the params of `pbkdf2_hmac` that are non-optional
|
||||
// dk_len is optional, i.e., can be None, and if addressed in this predicate
|
||||
// would result in an unsatisfiable predicate.
|
||||
predicate hashlibPBDKF2HMACKDFRequiredParams(HashlibPbkdf2HMACOperation kdf,
|
||||
API::Node hashParam, API::Node saltParam, API::Node iterationParam){
|
||||
// would result in an unsatisfiable predicate.
|
||||
predicate hashlibPBDKF2HMACKDFRequiredParams(
|
||||
HashlibPbkdf2HMACOperation kdf, API::Node hashParam, API::Node saltParam,
|
||||
API::Node iterationParam
|
||||
) {
|
||||
kdf.getParameter(0, "hash_name") = hashParam and
|
||||
kdf.getParameter(2, "salt") = saltParam and
|
||||
kdf.getParameter(3, "iterations") = iterationParam
|
||||
kdf.getParameter(3, "iterations") = iterationParam
|
||||
}
|
||||
|
||||
predicate hashlibPBDKF2HMACKDFOptionalParams(HashlibPbkdf2HMACOperation kdf, API::Node keylenParam){
|
||||
predicate hashlibPBDKF2HMACKDFOptionalParams(HashlibPbkdf2HMACOperation kdf, API::Node keylenParam) {
|
||||
kdf.getParameter(4, "dklen") = keylenParam
|
||||
}
|
||||
|
||||
@@ -155,36 +153,33 @@ private import experimental.cryptography.CryptoAlgorithmNames
|
||||
* Identifies kery derivation function hashlib.pbdkf2_hmac accesses.
|
||||
* https://docs.python.org/3/library/hashlib.html#hashlib.pbkdf2_hmac
|
||||
*/
|
||||
class HashlibPbkdf2HMACOperation extends KeyDerivationAlgorithm, KeyDerivationOperation
|
||||
{
|
||||
HashlibPbkdf2HMACOperation(){
|
||||
class HashlibPbkdf2HMACOperation extends KeyDerivationAlgorithm, KeyDerivationOperation {
|
||||
HashlibPbkdf2HMACOperation() {
|
||||
this = API::moduleImport("hashlib").getMember("pbkdf2_hmac").getACall()
|
||||
}
|
||||
|
||||
override string getName(){
|
||||
result = super.normalizeName("pbkdf2_hmac")
|
||||
}
|
||||
override string getName() { result = super.normalizeName("pbkdf2_hmac") }
|
||||
|
||||
override DataFlow::Node getIterationSizeSrc(){
|
||||
exists(API::Node it | hashlibPBDKF2HMACKDFRequiredParams(this, _, _, it) |
|
||||
override DataFlow::Node getIterationSizeSrc() {
|
||||
exists(API::Node it | hashlibPBDKF2HMACKDFRequiredParams(this, _, _, it) |
|
||||
result = Utils::getUltimateSrcFromApiNode(it)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getSaltConfigSrc(){
|
||||
exists(API::Node s | hashlibPBDKF2HMACKDFRequiredParams(this, _, s, _) |
|
||||
override DataFlow::Node getSaltConfigSrc() {
|
||||
exists(API::Node s | hashlibPBDKF2HMACKDFRequiredParams(this, _, s, _) |
|
||||
result = Utils::getUltimateSrcFromApiNode(s)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getHashConfigSrc(){
|
||||
exists(API::Node h | hashlibPBDKF2HMACKDFRequiredParams(this,h,_,_) |
|
||||
|
||||
override DataFlow::Node getHashConfigSrc() {
|
||||
exists(API::Node h | hashlibPBDKF2HMACKDFRequiredParams(this, h, _, _) |
|
||||
result = Utils::getUltimateSrcFromApiNode(h)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getDerivedKeySizeSrc(){
|
||||
exists(API::Node dk | hashlibPBDKF2HMACKDFOptionalParams(this,dk) |
|
||||
|
||||
override DataFlow::Node getDerivedKeySizeSrc() {
|
||||
exists(API::Node dk | hashlibPBDKF2HMACKDFOptionalParams(this, dk) |
|
||||
result = Utils::getUltimateSrcFromApiNode(dk)
|
||||
)
|
||||
}
|
||||
@@ -193,66 +188,50 @@ private import experimental.cryptography.CryptoAlgorithmNames
|
||||
// The issue is the src is what we model not the size
|
||||
// For now, we are not modeling this and are relying on the fact that the accepted hashes are of accepted length.
|
||||
// I.e., any query looking at length will ignore cases where it is unknown
|
||||
override KeyDerivationAlgorithm getAlgorithm() { result = this }
|
||||
|
||||
override KeyDerivationAlgorithm getAlgorithm(){
|
||||
result = this
|
||||
}
|
||||
override predicate requiresHash() { any() }
|
||||
|
||||
override predicate requiresHash(){ any() }
|
||||
override predicate requiresMode() { none() }
|
||||
|
||||
override predicate requiresMode(){none()}
|
||||
override predicate requiresSalt() { any() }
|
||||
|
||||
override predicate requiresSalt(){any()}
|
||||
|
||||
override predicate requiresIteration(){any()}
|
||||
override predicate requiresIteration() { any() }
|
||||
}
|
||||
|
||||
|
||||
// TODO: better modeling of scrypt
|
||||
|
||||
/**
|
||||
* Identifies key derivation fucntion hashlib.scrypt accesses.
|
||||
*/
|
||||
class HashlibScryptAlgorithm extends KeyDerivationAlgorithm, KeyDerivationOperation
|
||||
{
|
||||
HashlibScryptAlgorithm(){
|
||||
this = API::moduleImport("hashlib").getMember("scrypt").getACall()
|
||||
}
|
||||
class HashlibScryptAlgorithm extends KeyDerivationAlgorithm, KeyDerivationOperation {
|
||||
HashlibScryptAlgorithm() { this = API::moduleImport("hashlib").getMember("scrypt").getACall() }
|
||||
|
||||
override string getName(){
|
||||
result = super.normalizeName("scrypt")
|
||||
}
|
||||
override string getName() { result = super.normalizeName("scrypt") }
|
||||
|
||||
override DataFlow::Node getIterationSizeSrc(){
|
||||
none()
|
||||
}
|
||||
override DataFlow::Node getIterationSizeSrc() { none() }
|
||||
|
||||
override DataFlow::Node getSaltConfigSrc(){
|
||||
override DataFlow::Node getSaltConfigSrc() {
|
||||
// TODO: need to address getting salt from params, unsure how this works in CodeQL
|
||||
// since the signature is defined as hashlib.scrypt(password, *, salt, n, r, p, maxmem=0, dklen=64)
|
||||
// What position is 'salt' then such that we can reliably extract it?
|
||||
none()
|
||||
}
|
||||
|
||||
override DataFlow::Node getHashConfigSrc(){
|
||||
none()
|
||||
}
|
||||
|
||||
override DataFlow::Node getDerivedKeySizeSrc(){
|
||||
|
||||
override DataFlow::Node getHashConfigSrc() { none() }
|
||||
|
||||
override DataFlow::Node getDerivedKeySizeSrc() {
|
||||
//TODO: see comment for getSaltConfigSrc above
|
||||
none()
|
||||
}
|
||||
|
||||
override KeyDerivationAlgorithm getAlgorithm(){
|
||||
result = this
|
||||
}
|
||||
override KeyDerivationAlgorithm getAlgorithm() { result = this }
|
||||
|
||||
override predicate requiresHash(){ none() }
|
||||
override predicate requiresHash() { none() }
|
||||
|
||||
override predicate requiresMode(){none()}
|
||||
override predicate requiresMode() { none() }
|
||||
|
||||
override predicate requiresSalt(){any()}
|
||||
override predicate requiresSalt() { any() }
|
||||
|
||||
override predicate requiresIteration(){none()}
|
||||
override predicate requiresIteration() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ private import experimental.cryptography.modules.stdlib.HashlibModule as Hashlib
|
||||
* `hmac` is a ptyhon standard library module for key-based hashing algorithms.
|
||||
* https://docs.python.org/3/library/hmac.html
|
||||
*/
|
||||
|
||||
// -----------------------------------------------
|
||||
// Hash Artifacts
|
||||
// -----------------------------------------------
|
||||
module Hashes{
|
||||
|
||||
class GenericHmacHashCall extends API::CallNode
|
||||
{
|
||||
GenericHmacHashCall(){
|
||||
this = API::moduleImport("hmac").getMember("new").getACall()
|
||||
or this = API::moduleImport("hmac").getMember("HMAC").getACall()
|
||||
or this = API::moduleImport("hmac").getMember("digest").getACall()
|
||||
module Hashes {
|
||||
class GenericHmacHashCall extends API::CallNode {
|
||||
GenericHmacHashCall() {
|
||||
this = API::moduleImport("hmac").getMember("new").getACall() or
|
||||
this = API::moduleImport("hmac").getMember("HMAC").getACall() or
|
||||
this = API::moduleImport("hmac").getMember("digest").getACall()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DataFlow::Node getDigestModParamSrc(GenericHmacHashCall call){
|
||||
DataFlow::Node getDigestModParamSrc(GenericHmacHashCall call) {
|
||||
result = Utils::getUltimateSrcFromApiNode(call.(API::CallNode).getParameter(2, "digestmod"))
|
||||
}
|
||||
|
||||
@@ -34,46 +30,41 @@ module Hashes{
|
||||
* hmac.HMAC https://docs.python.org/3/library/hmac.html#hmac.HMAC
|
||||
* hmac.new https://docs.python.org/3/library/hmac.html#hmac.new
|
||||
* hmac.digest https://docs.python.org/3/library/hmac.html#hmac.digest
|
||||
* These operations commonly set the algorithm as a string in the third argument (`digestmod`)
|
||||
* These operations commonly set the algorithm as a string in the third argument (`digestmod`)
|
||||
* of the operation itself.
|
||||
*
|
||||
*
|
||||
* NOTE: `digestmod` is the digest name, digest constructor or module for the HMAC object to use, however
|
||||
* this class only identifies string names. The other forms are found by CryptopgraphicArtifacts,
|
||||
* modeled in `HmacHMACConsArtifact` and `Hashlib.qll`, specifically through hashlib.new and
|
||||
* modeled in `HmacHMACConsArtifact` and `Hashlib.qll`, specifically through hashlib.new and
|
||||
* direct member accesses (e.g., hashlib.md5).
|
||||
*
|
||||
*
|
||||
* Where no `digestmod` exists, the algorithm is assumed to be `md5` per the docs found here:
|
||||
* https://docs.python.org/3/library/hmac.html#hmac.new
|
||||
*
|
||||
*
|
||||
* Where `digestmod` exists but is not a string and not a hashlib algorithm, it is assumed
|
||||
* to be `UNKNOWN`. Note this includes cases wheere the digest is provided as a `A module supporting PEP 247.`
|
||||
* Such modules are currently not modeled.
|
||||
* Such modules are currently not modeled.
|
||||
*/
|
||||
class HmacGenericAlgorithm extends HashAlgorithm {
|
||||
|
||||
HmacGenericAlgorithm(){
|
||||
exists(GenericHmacHashCall c |
|
||||
if not exists(getDigestModParamSrc(c))
|
||||
then this = c
|
||||
else this = getDigestModParamSrc(c)
|
||||
)
|
||||
and
|
||||
HmacGenericAlgorithm() {
|
||||
exists(GenericHmacHashCall c |
|
||||
if not exists(getDigestModParamSrc(c)) then this = c else this = getDigestModParamSrc(c)
|
||||
) and
|
||||
// Ignore case where the algorithm is a hashlib algorithm, rely on `HashlibMemberAlgorithm` to catch these cases
|
||||
not this instanceof HashlibModule::Hashes::HashlibMemberAlgorithm
|
||||
// NOTE: the docs say the digest can be `A module supporting PEP 247.`, however, this is not modeled, and will be considered UNKNOWN
|
||||
}
|
||||
|
||||
override string getName(){
|
||||
override string getName() {
|
||||
// when the this is a generic hmac call
|
||||
// it means the algorithm parameter was not identified, assume the default case of 'md5' (per the docs)
|
||||
if this instanceof GenericHmacHashCall
|
||||
if this instanceof GenericHmacHashCall
|
||||
then result = super.normalizeName("MD5")
|
||||
else
|
||||
(
|
||||
else (
|
||||
// Else get the string name, if its a string constant, or UNKNOWN if otherwise
|
||||
result = super.normalizeName(this.asExpr().(StrConst).getText())
|
||||
or
|
||||
(not this.asExpr() instanceof StrConst and result = unknownAlgorithm())
|
||||
not this.asExpr() instanceof StrConst and result = unknownAlgorithm()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Patches all DataFlow::CallCfgNode adding a getTarget predicate to a new
|
||||
* Patches all DataFlow::CallCfgNode adding a getTarget predicate to a new
|
||||
* subclass of CallCfgNode
|
||||
*/
|
||||
|
||||
@@ -7,9 +7,6 @@ import python
|
||||
private import semmle.python.dataflow.new.internal.TypeTrackerSpecific
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
class CallCfgNodeWithTarget extends DataFlow::Node instanceof DataFlow::CallCfgNode{
|
||||
|
||||
DataFlow::Node getTarget(){
|
||||
returnStep(result, this)
|
||||
}
|
||||
}
|
||||
class CallCfgNodeWithTarget extends DataFlow::Node instanceof DataFlow::CallCfgNode {
|
||||
DataFlow::Node getTarget() { returnStep(result, this) }
|
||||
}
|
||||
|
||||
@@ -1,28 +1,21 @@
|
||||
import python
|
||||
private import semmle.python.ApiGraphs
|
||||
private import experimental.cryptography.utils.CallCfgNodeWithTarget
|
||||
|
||||
/**
|
||||
* Gets an ultimate local source (not a source in a library)
|
||||
*/
|
||||
DataFlow::Node getUltimateSrcFromApiNode(API::Node n){
|
||||
result = n.getAValueReachingSink()
|
||||
and
|
||||
(
|
||||
DataFlow::Node getUltimateSrcFromApiNode(API::Node n) {
|
||||
result = n.getAValueReachingSink() and
|
||||
(
|
||||
// the result is a call to a library only
|
||||
(
|
||||
result instanceof CallCfgNodeWithTarget and
|
||||
not result.(CallCfgNodeWithTarget).getTarget().asExpr().getEnclosingModule().inSource()
|
||||
)
|
||||
// the result is not a call, and not a function signataure or parameter
|
||||
result instanceof CallCfgNodeWithTarget and
|
||||
not result.(CallCfgNodeWithTarget).getTarget().asExpr().getEnclosingModule().inSource()
|
||||
or
|
||||
(
|
||||
not result instanceof CallCfgNodeWithTarget
|
||||
and
|
||||
not result instanceof DataFlow::ParameterNode
|
||||
and
|
||||
not result.asExpr() instanceof FunctionExpr
|
||||
and
|
||||
result.asExpr().getEnclosingModule().inSource()
|
||||
)
|
||||
)
|
||||
}
|
||||
// the result is not a call, and not a function signataure or parameter
|
||||
not result instanceof CallCfgNodeWithTarget and
|
||||
not result instanceof DataFlow::ParameterNode and
|
||||
not result.asExpr() instanceof FunctionExpr and
|
||||
result.asExpr().getEnclosingModule().inSource()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
/**
|
||||
* @name Unknown key generation key size
|
||||
* @description
|
||||
* @description
|
||||
* @id py/unknown-asymmetric-key-gen-size
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags external/cwe/cwe-326
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
from AsymmetricKeyGen op, DataFlow::Node configSrc, string algName
|
||||
where
|
||||
not op.hasKeySize(configSrc) and
|
||||
configSrc = op.getKeyConfigSrc() and
|
||||
algName = op.getAlgorithm().getName()
|
||||
select op, "Non-statically verifiable key size used for key generation for algorithm " + algName.toString() + " at config source $@",
|
||||
configSrc, configSrc.toString()
|
||||
where
|
||||
not op.hasKeySize(configSrc) and
|
||||
configSrc = op.getKeyConfigSrc() and
|
||||
algName = op.getAlgorithm().getName()
|
||||
select op,
|
||||
"Non-statically verifiable key size used for key generation for algorithm " + algName.toString() +
|
||||
" at config source $@", configSrc, configSrc.toString()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Weak key generation key size (< 2048 bits)
|
||||
* @description
|
||||
* @description
|
||||
* @id py/weak-asymmetric-key-gen-size
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
@@ -12,11 +12,12 @@ import python
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
from AsymmetricKeyGen op, DataFlow::Node configSrc, int keySize, string algName
|
||||
where
|
||||
keySize = op.getKeySizeInBits(configSrc)
|
||||
and keySize < 2048
|
||||
and algName = op.getAlgorithm().getName()
|
||||
// Can't be an elliptic curve
|
||||
and not isEllipticCurveAlgorithm(algName, _)
|
||||
select op, "Use of weak asymmetric key size (int bits)" + keySize.toString() + " for algorithm " + algName.toString() +" at config source $@",
|
||||
configSrc, configSrc.toString()
|
||||
where
|
||||
keySize = op.getKeySizeInBits(configSrc) and
|
||||
keySize < 2048 and
|
||||
algName = op.getAlgorithm().getName() and
|
||||
// Can't be an elliptic curve
|
||||
not isEllipticCurveAlgorithm(algName, _)
|
||||
select op,
|
||||
"Use of weak asymmetric key size (int bits)" + keySize.toString() + " for algorithm " +
|
||||
algName.toString() + " at config source $@", configSrc, configSrc.toString()
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
/**
|
||||
* @name Weak or unknown asymmetric padding
|
||||
* @description
|
||||
* @description
|
||||
* @id py/weak-asymmetric-padding
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
from AsymmetricPadding pad, string name
|
||||
where
|
||||
name = pad.getPaddingName() and
|
||||
not name = ["OAEP", "KEM", "PSS"]
|
||||
where
|
||||
name = pad.getPaddingName() and
|
||||
not name = ["OAEP", "KEM", "PSS"]
|
||||
select pad, "Use of unapproved, weak, or unknown asymmetric padding algorithm or API: " + name
|
||||
|
||||
@@ -8,41 +8,41 @@
|
||||
* @tags external/cwe/cwe-327
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
|
||||
from CryptographicArtifact op, string msg
|
||||
where
|
||||
// False positive hack, some projects are directly including all of cryptography,
|
||||
// filter any match that is in cryptography/hazmat
|
||||
// Specifically happening for ECB being used in keywrap operations internally to the cryptography keywrap/unwrap API
|
||||
not op.asExpr().getLocation().getFile().getAbsolutePath().toString().matches("%cryptography/hazmat/%") and
|
||||
(
|
||||
from CryptographicArtifact op, string msg
|
||||
where
|
||||
// False positive hack, some projects are directly including all of cryptography,
|
||||
// filter any match that is in cryptography/hazmat
|
||||
// Specifically happening for ECB being used in keywrap operations internally to the cryptography keywrap/unwrap API
|
||||
not op.asExpr()
|
||||
.getLocation()
|
||||
.getFile()
|
||||
.getAbsolutePath()
|
||||
.toString()
|
||||
.matches("%cryptography/hazmat/%") and
|
||||
(
|
||||
op instanceof BlockMode and
|
||||
// ECB is only allowed for KeyWrapOperations, i.e., only alert on ECB is not a KeyWrapOperation
|
||||
(op.(BlockMode).getBlockModeName() = "ECB" implies not op instanceof KeyWrapOperation)
|
||||
and
|
||||
exists(string name | name = op.(BlockMode).getBlockModeName() |
|
||||
// Only CBC, CTS, XTS modes are allowed.
|
||||
// https://liquid.microsoft.com/Web/Object/Read/MS.Security/Requirements/Microsoft.Security.Cryptography.10002
|
||||
not name = ["CBC","CTS","XTS"] and
|
||||
if name = unknownAlgorithm()
|
||||
then msg = "Use of unrecognized block mode algorithm."
|
||||
else if name in ["GCM", "CCM"]
|
||||
then msg = "Use of block mode algorithm " + name + " requires special crypto board approval/review."
|
||||
(op.(BlockMode).getBlockModeName() = "ECB" implies not op instanceof KeyWrapOperation) and
|
||||
exists(string name | name = op.(BlockMode).getBlockModeName() |
|
||||
// Only CBC, CTS, XTS modes are allowed.
|
||||
// https://liquid.microsoft.com/Web/Object/Read/MS.Security/Requirements/Microsoft.Security.Cryptography.10002
|
||||
not name = ["CBC", "CTS", "XTS"] and
|
||||
if name = unknownAlgorithm()
|
||||
then msg = "Use of unrecognized block mode algorithm."
|
||||
else
|
||||
if name in ["GCM", "CCM"]
|
||||
then
|
||||
msg =
|
||||
"Use of block mode algorithm " + name +
|
||||
" requires special crypto board approval/review."
|
||||
else msg = "Use of unapproved block mode algorithm or API " + name + "."
|
||||
)
|
||||
)
|
||||
or
|
||||
(
|
||||
op instanceof SymmetricCipher and
|
||||
)
|
||||
or
|
||||
op instanceof SymmetricCipher and
|
||||
not op.(SymmetricCipher).hasBlockMode() and
|
||||
msg = "Cipher has unspecified block mode algorithm."
|
||||
)
|
||||
)
|
||||
select op, msg
|
||||
|
||||
|
||||
|
||||
@@ -2,34 +2,36 @@
|
||||
* @name Weak block mode IV or nonce
|
||||
* @description Finds initialization vectors or nonces used by block modes that are weak, obsolete, or otherwise unaccepted.
|
||||
* Looks for IVs or nonces that are not generated by a cryptographically secure random number generator
|
||||
*
|
||||
* NOTE: for simplicity, if an IV or nonce is not known or not form os.urandom it is flagged.
|
||||
* More specific considerations, such as correct use of nonces are currently not handled.
|
||||
* In particular, GCM requires the use of a nonce. Using urandom is possible but may still be configured
|
||||
*
|
||||
* NOTE: for simplicity, if an IV or nonce is not known or not form os.urandom it is flagged.
|
||||
* More specific considerations, such as correct use of nonces are currently not handled.
|
||||
* In particular, GCM requires the use of a nonce. Using urandom is possible but may still be configured
|
||||
* incorrectly. We currently assume that GCM is flagged as a block mode regardless through a separate
|
||||
* query, and such uses will need to be reivewed by the crypto board.
|
||||
*
|
||||
* query, and such uses will need to be reivewed by the crypto board.
|
||||
*
|
||||
* Additionally, some functions, which infer a mode and IV may be flagged by this query.
|
||||
* For now, we will rely on users suppressing these cases rather than filtering them out.
|
||||
* The exception is Fernet, which is explicitly ignored since it's implementation uses os.urandom.
|
||||
*
|
||||
* @id py/weak-block-mode-iv-or-nonce
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
from BlockMode op, string msg, DataFlow::Node conf
|
||||
where
|
||||
not op instanceof CryptographyModule::Encryption::SymmetricEncryption::Fernet::CryptographyFernet and
|
||||
(not op.hasIVorNonce() or not API::moduleImport("os").getMember("urandom").getACall() = op.getIVorNonce())
|
||||
and
|
||||
if not op.hasIVorNonce()
|
||||
then conf = op and msg = "Block mode is missing IV/Nonce initialization."
|
||||
else conf = op.getIVorNonce() and msg = "Block mode is not using an accepted IV/Nonce initialization: $@"
|
||||
where
|
||||
not op instanceof CryptographyModule::Encryption::SymmetricEncryption::Fernet::CryptographyFernet and
|
||||
(
|
||||
not op.hasIVorNonce() or
|
||||
not API::moduleImport("os").getMember("urandom").getACall() = op.getIVorNonce()
|
||||
) and
|
||||
(
|
||||
if not op.hasIVorNonce()
|
||||
then conf = op and msg = "Block mode is missing IV/Nonce initialization."
|
||||
else conf = op.getIVorNonce()
|
||||
) and
|
||||
msg = "Block mode is not using an accepted IV/Nonce initialization: $@"
|
||||
select op, msg, conf, conf.toString()
|
||||
|
||||
|
||||
@@ -7,22 +7,26 @@
|
||||
* @precision high
|
||||
* @tags external/cwe/cwe-327
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
from EllipticCurveAlgorithm op, string msg, string name
|
||||
where
|
||||
(
|
||||
(name = op.getCurveName() and name = unknownAlgorithm() and msg = "Use of unrecognized curve algorithm.")
|
||||
or
|
||||
(
|
||||
name != unknownAlgorithm() and
|
||||
name = op.getCurveName() and
|
||||
not name = ["SECP256R1", "PRIME256V1",//P-256
|
||||
"SECP384R1", //P-384
|
||||
"SECP521R1", //P-521
|
||||
"ED25519", "X25519"] and
|
||||
msg = "Use of weak curve algorithm " + name + "."
|
||||
)
|
||||
)
|
||||
select op, msg
|
||||
where
|
||||
(
|
||||
name = op.getCurveName() and
|
||||
name = unknownAlgorithm() and
|
||||
msg = "Use of unrecognized curve algorithm."
|
||||
or
|
||||
name != unknownAlgorithm() and
|
||||
name = op.getCurveName() and
|
||||
not name =
|
||||
[
|
||||
"SECP256R1", "PRIME256V1", //P-256
|
||||
"SECP384R1", //P-384
|
||||
"SECP521R1", //P-521
|
||||
"ED25519", "X25519"
|
||||
] and
|
||||
msg = "Use of weak curve algorithm " + name + "."
|
||||
)
|
||||
select op, msg
|
||||
|
||||
@@ -12,10 +12,10 @@ import python
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
from HashAlgorithm op, string name, string msg
|
||||
where
|
||||
name = op.getHashName() and
|
||||
not name = ["SHA256", "SHA384", "SHA512"] and
|
||||
if name = unknownAlgorithm()
|
||||
then msg = "Use of unrecognized hash algorithm."
|
||||
else msg = "Use of unapproved hash algorithm or API " + name + "."
|
||||
where
|
||||
name = op.getHashName() and
|
||||
not name = ["SHA256", "SHA384", "SHA512"] and
|
||||
if name = unknownAlgorithm()
|
||||
then msg = "Use of unrecognized hash algorithm."
|
||||
else msg = "Use of unapproved hash algorithm or API " + name + "."
|
||||
select op, msg
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
/**
|
||||
* @name Weak KDF algorithm.
|
||||
* @name Weak KDF algorithm.
|
||||
* @description Approved KDF algorithms must one of the following
|
||||
* ["PBKDF2" , "PBKDF2HMAC", "KBKDF", "KBKDFHMAC", "CONCATKDF", "CONCATKDFHASH"]
|
||||
* @assumption The value being used to derive a key (either a key or a password) is correct for the algorithm (i.e., a key is used for KBKDF and a password for PBKDF).
|
||||
@@ -9,10 +8,15 @@
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
from KeyDerivationAlgorithm op
|
||||
where
|
||||
not op.getKDFName() = ["PBKDF2" , "PBKDF2HMAC", "KBKDF", "KBKDFHMAC", "KBKDFCMAC", "CONCATKDF", "CONCATKDFHASH", "CONCATKDFHMAC"]
|
||||
select op, "Use of unapproved, weak, or unknown key derivation algorithm or API."
|
||||
from KeyDerivationAlgorithm op
|
||||
where
|
||||
not op.getKDFName() =
|
||||
[
|
||||
"PBKDF2", "PBKDF2HMAC", "KBKDF", "KBKDFHMAC", "KBKDFCMAC", "CONCATKDF", "CONCATKDFHASH",
|
||||
"CONCATKDFHMAC"
|
||||
]
|
||||
select op, "Use of unapproved, weak, or unknown key derivation algorithm or API."
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
/**
|
||||
* @name Use iteration count at least 100k to prevent brute force attacks
|
||||
* @description When deriving cryptographic keys from user-provided inputs such as password,
|
||||
* @description When deriving cryptographic keys from user-provided inputs such as password,
|
||||
* use sufficient iteration count (at least 100k).
|
||||
*
|
||||
*
|
||||
* This query will alert if the iteration count is less than 10000 (i.e., a constant <100000 is observed)
|
||||
* or if the source for the iteration count is not known statically.
|
||||
|
||||
* or if the source for the iteration count is not known statically.
|
||||
* @kind problem
|
||||
* @id py/kdf-low-iteration-count
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
private import experimental.cryptography.utils.Utils as Utils
|
||||
|
||||
from KeyDerivationOperation op, string msg, DataFlow::Node iterConfSrc
|
||||
where
|
||||
op.requiresIteration() and
|
||||
iterConfSrc = op.getIterationSizeSrc() and
|
||||
(
|
||||
(
|
||||
exists(iterConfSrc.asExpr().(IntegerLiteral).getValue())
|
||||
and iterConfSrc.asExpr().(IntegerLiteral).getValue()< 10000
|
||||
and msg = "Iteration count is too low. "
|
||||
)
|
||||
or
|
||||
(
|
||||
not exists(iterConfSrc.asExpr().(IntegerLiteral).getValue())
|
||||
and msg = "Iteration count is not a statically verifiable size. "
|
||||
)
|
||||
)
|
||||
select op, msg + "Iteration count must be a minimum of 10000. Iteration Config: $@", iterConfSrc.asExpr(), iterConfSrc.asExpr().toString()
|
||||
where
|
||||
op.requiresIteration() and
|
||||
iterConfSrc = op.getIterationSizeSrc() and
|
||||
(
|
||||
exists(iterConfSrc.asExpr().(IntegerLiteral).getValue()) and
|
||||
iterConfSrc.asExpr().(IntegerLiteral).getValue() < 10000 and
|
||||
msg = "Iteration count is too low. "
|
||||
or
|
||||
not exists(iterConfSrc.asExpr().(IntegerLiteral).getValue()) and
|
||||
msg = "Iteration count is not a statically verifiable size. "
|
||||
)
|
||||
select op, msg + "Iteration count must be a minimum of 10000. Iteration Config: $@",
|
||||
iterConfSrc.asExpr(), iterConfSrc.asExpr().toString()
|
||||
|
||||
@@ -1,37 +1,32 @@
|
||||
/**
|
||||
* @name Small KDF derived key length.
|
||||
* @description KDF derived keys should be a minimum of 128 bits (16 bytes).
|
||||
* @assumption If the key length is not explicitly provided (e.g., it is None or otherwise not specified) assumes the length is derived from the hash length.
|
||||
* @assumption The query assumes only hashes with adequately sized lengths are chosen ("SHA256", "SHA384", "SHA512"), not verified here.
|
||||
* This alerts if a constant traces to to a key length sink less than 128-bits or
|
||||
* if the value that traces to a key length sink is not known statically. If the value is None or does not exist, we will assume the length
|
||||
* is from the hash length, and again assume other queries will assess that those are of sufficient quality (length).
|
||||
* @assumption If the key length is not explicitly provided (e.g., it is None or otherwise not specified) assumes the length is derived from the hash length.
|
||||
* @kind problem
|
||||
* @id py/kdf-small-key-size
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
private import experimental.cryptography.utils.Utils as Utils
|
||||
|
||||
from KeyDerivationOperation op, string msg, DataFlow::Node derivedKeySizeSrc
|
||||
where
|
||||
// NOTE/ASSUMPTION: if the key size is not specified or explicitly None, it is common that the size is derived from the hash used.
|
||||
// The only acceptable hashes are currently "SHA256", "SHA384", "SHA512", which are all large enough.
|
||||
// We will currently rely on other acceptable hash queries therefore to determine if the size is
|
||||
// is sufficient where the key is not specified.
|
||||
derivedKeySizeSrc = op.getDerivedKeySizeSrc() and not derivedKeySizeSrc.asExpr() instanceof None and
|
||||
(
|
||||
(
|
||||
exists(derivedKeySizeSrc.asExpr().(IntegerLiteral).getValue())
|
||||
and derivedKeySizeSrc.asExpr().(IntegerLiteral).getValue() < 16
|
||||
and msg = "Derived key size is too small. "
|
||||
)
|
||||
or
|
||||
(
|
||||
not exists(derivedKeySizeSrc.asExpr().(IntegerLiteral).getValue())
|
||||
and msg = "Derived key size is not a statically verifiable size. "
|
||||
)
|
||||
)
|
||||
select op, msg + "Derived key size must be a minimum of 16 (bytes). Derived Key Size Config: $@", derivedKeySizeSrc.asExpr(), derivedKeySizeSrc.asExpr().toString()
|
||||
where
|
||||
// NOTE/ASSUMPTION: if the key size is not specified or explicitly None, it is common that the size is derived from the hash used.
|
||||
// The only acceptable hashes are currently "SHA256", "SHA384", "SHA512", which are all large enough.
|
||||
// We will currently rely on other acceptable hash queries therefore to determine if the size is
|
||||
// is sufficient where the key is not specified.
|
||||
derivedKeySizeSrc = op.getDerivedKeySizeSrc() and
|
||||
not derivedKeySizeSrc.asExpr() instanceof None and
|
||||
(
|
||||
exists(derivedKeySizeSrc.asExpr().(IntegerLiteral).getValue()) and
|
||||
derivedKeySizeSrc.asExpr().(IntegerLiteral).getValue() < 16 and
|
||||
msg = "Derived key size is too small. "
|
||||
or
|
||||
not exists(derivedKeySizeSrc.asExpr().(IntegerLiteral).getValue()) and
|
||||
msg = "Derived key size is not a statically verifiable size. "
|
||||
)
|
||||
select op, msg + "Derived key size must be a minimum of 16 (bytes). Derived Key Size Config: $@",
|
||||
derivedKeySizeSrc.asExpr(), derivedKeySizeSrc.asExpr().toString()
|
||||
|
||||
@@ -6,20 +6,23 @@
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
private import experimental.cryptography.utils.Utils as Utils
|
||||
|
||||
|
||||
from KeyDerivationOperation op, DataFlow::Node modeConfSrc
|
||||
where
|
||||
op.requiresMode()
|
||||
and modeConfSrc = op.getModeSrc()
|
||||
and not modeConfSrc = API::moduleImport("cryptography")
|
||||
.getMember("hazmat")
|
||||
.getMember("primitives")
|
||||
.getMember("kdf")
|
||||
.getMember("kbkdf")
|
||||
.getMember("Mode")
|
||||
.getMember("CounterMode").asSource()
|
||||
select op, "Key derivation mode is not set to CounterMode. Mode Config: $@", modeConfSrc, modeConfSrc.toString()
|
||||
where
|
||||
op.requiresMode() and
|
||||
modeConfSrc = op.getModeSrc() and
|
||||
not modeConfSrc =
|
||||
API::moduleImport("cryptography")
|
||||
.getMember("hazmat")
|
||||
.getMember("primitives")
|
||||
.getMember("kdf")
|
||||
.getMember("kbkdf")
|
||||
.getMember("Mode")
|
||||
.getMember("CounterMode")
|
||||
.asSource()
|
||||
select op, "Key derivation mode is not set to CounterMode. Mode Config: $@", modeConfSrc,
|
||||
modeConfSrc.toString()
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
private import experimental.cryptography.utils.Utils as Utils
|
||||
|
||||
from KeyDerivationOperation op, DataFlow::Node saltSrc
|
||||
where
|
||||
op.requiresSalt()
|
||||
and not API::moduleImport("os").getMember("urandom").getACall() = saltSrc
|
||||
and saltSrc = op.getSaltConfigSrc()
|
||||
select op, "Salt configuration is not from an accepted random source: $@. Must be os.urandom", saltSrc, saltSrc.toString()
|
||||
where
|
||||
op.requiresSalt() and
|
||||
not API::moduleImport("os").getMember("urandom").getACall() = saltSrc and
|
||||
saltSrc = op.getSaltConfigSrc()
|
||||
select op, "Salt configuration is not from an accepted random source: $@. Must be os.urandom",
|
||||
saltSrc, saltSrc.toString()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* @name Small KDF salt length.
|
||||
* @description KDF salts should be a minimum of 128 bits (16 bytes).
|
||||
*
|
||||
*
|
||||
* This alerts if a constant traces to to a salt length sink less than 128-bits or
|
||||
* if the value that traces to a salt length sink is not known statically.
|
||||
* if the value that traces to a salt length sink is not known statically.
|
||||
* @kind problem
|
||||
* @id py/kdf-small-salt-size
|
||||
* @problem.severity error
|
||||
@@ -15,15 +15,18 @@ import experimental.cryptography.Concepts
|
||||
private import experimental.cryptography.utils.Utils as Utils
|
||||
|
||||
from KeyDerivationOperation op, DataFlow::Node randConfSrc, API::CallNode call, string msg
|
||||
where
|
||||
op.requiresSalt() and
|
||||
API::moduleImport("os").getMember("urandom").getACall() = call
|
||||
and call = op.getSaltConfigSrc()
|
||||
and randConfSrc = Utils::getUltimateSrcFromApiNode(call.getParameter(0, "size"))
|
||||
and
|
||||
(
|
||||
(not exists(randConfSrc.asExpr().(IntegerLiteral).getValue()) and msg = "Salt config is not a statically verifiable size. ")
|
||||
or
|
||||
(randConfSrc.asExpr().(IntegerLiteral).getValue() < 16 and msg = "Salt config is insufficiently large. ")
|
||||
)
|
||||
select op, msg + "Salt size must be a minimum of 16 (bytes). os.urandom Config: $@, Size Config: $@", call, call.toString(), randConfSrc, randConfSrc.toString()
|
||||
where
|
||||
op.requiresSalt() and
|
||||
API::moduleImport("os").getMember("urandom").getACall() = call and
|
||||
call = op.getSaltConfigSrc() and
|
||||
randConfSrc = Utils::getUltimateSrcFromApiNode(call.getParameter(0, "size")) and
|
||||
(
|
||||
not exists(randConfSrc.asExpr().(IntegerLiteral).getValue()) and
|
||||
msg = "Salt config is not a statically verifiable size. "
|
||||
or
|
||||
randConfSrc.asExpr().(IntegerLiteral).getValue() < 16 and
|
||||
msg = "Salt config is insufficiently large. "
|
||||
)
|
||||
select op,
|
||||
msg + "Salt size must be a minimum of 16 (bytes). os.urandom Config: $@, Size Config: $@", call,
|
||||
call.toString(), randConfSrc, randConfSrc.toString()
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/**
|
||||
* @name Weak symmetric encryption algorithm
|
||||
* @description Finds uses of symmetric cryptography algorithms that are weak, obsolete, or otherwise unaccepted.
|
||||
|
||||
* The key lengths allowed are 128, 192, and 256 bits. These are all the key lengths supported by AES, so any
|
||||
*
|
||||
* The key lengths allowed are 128, 192, and 256 bits. These are all the key lengths supported by AES, so any
|
||||
* application of AES is considered acceptable.
|
||||
*
|
||||
* @id py/weak-symmetric-encryption
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
@@ -12,17 +11,14 @@
|
||||
* @tags external/cwe/cwe-327
|
||||
*/
|
||||
|
||||
|
||||
import python
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
from
|
||||
SymmetricEncryptionAlgorithm op, string name, string msg
|
||||
from SymmetricEncryptionAlgorithm op, string name, string msg
|
||||
where
|
||||
name = op.getEncryptionName() and
|
||||
not name = ["AES","AES128", "AES192", "AES256"] and
|
||||
if name = unknownAlgorithm()
|
||||
then msg = "Use of unrecognized symmetric encryption algorithm."
|
||||
else msg = "Use of unapproved symmetric encryption algorithm or API " + name + "."
|
||||
name = op.getEncryptionName() and
|
||||
not name = ["AES", "AES128", "AES192", "AES256"] and
|
||||
if name = unknownAlgorithm()
|
||||
then msg = "Use of unrecognized symmetric encryption algorithm."
|
||||
else msg = "Use of unapproved symmetric encryption algorithm or API " + name + "."
|
||||
select op, msg
|
||||
|
||||
|
||||
Reference in New Issue
Block a user