autoformat

This commit is contained in:
Josh Brown
2023-10-03 13:40:17 -07:00
parent b683a3caf8
commit ad86e576a4
38 changed files with 16249 additions and 15659 deletions

View File

@@ -1,5 +1,3 @@
import experimental.cryptography.CryptoArtifact
import experimental.cryptography.CryptoAlgorithmNames
import experimental.cryptography.modules.OpenSSL as OpenSSL

View File

@@ -11,35 +11,47 @@
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" }
string getAsymmetricType(){
result in [getAsymmetricEncryptionType(), getSignatureType(), getKeyExchangeType(), getEllipticCurveType()]
string getAsymmetricType() {
result in [
getAsymmetricEncryptionType(), getSignatureType(), getKeyExchangeType(),
getEllipticCurveType()
]
}
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
@@ -60,11 +72,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"
]
}
@@ -86,10 +98,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"
]
}
@@ -201,20 +213,25 @@ predicate isEllipticCurveAlgorithm(string curveName, int keySize) {
curveName = "NUMSP512T1" and keySize = 512
or
curveName = "SM2" and keySize in [256, 512]
}
/**
* 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"
]
}
/**
* Holds if `name` is a key exchange algorithm.
*/
predicate isKeyExchangeAlgorithm(string name) { name = ["ECDH", "DH", "DIFFIEHELLMAN", "X25519", "X448"] }
predicate isKeyExchangeAlgorithm(string name) {
name = ["ECDH", "DH", "DIFFIEHELLMAN", "X25519", "X448"]
}
/**
* Holds if `name` corresponds to a known asymmetric encryption.

View File

@@ -2,20 +2,19 @@ import cpp
private import experimental.cryptography.CryptoAlgorithmNames
import semmle.code.cpp.ir.dataflow.TaintTracking
/*
* A cryptographic artifact is a DataFlow::Node associated with some
* operation, algorithm, or any other aspect of cryptography.
*/
abstract class CryptographicArtifact extends Expr {}
* A cryptographic artifact is a DataFlow::Node associated with some
* operation, algorithm, or any other aspect of cryptography.
*/
abstract class CryptographicArtifact extends Expr { }
// /**
// * 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 SymmetricEncryptionAlgorithm getEncryptionAlgorithm();
@@ -24,112 +23,91 @@ abstract class CryptographicArtifact extends Expr {}
// 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, Call{
// // bindingset[paramName, ind]
// // final DataFlow::Node getParameterSource(int ind, string paramName){
// // result = Utils::getUltimateSrcFromApiNode(this.(API::CallNode).getParameter(ind, paramName))
// // }
// final string getAlgorithmName(){
// if exists(this.getAlgorithm().getName())
// then result = this.getAlgorithm().getName()
// else result = unknownAlgorithm()
// }
// final predicate hasAlgorithm(){
// exists(this.getAlgorithm())
// }
// final predicate isUnknownAlgorithm(){
// this.getAlgorithmName() = unknownAlgorithm()
// or
// not this.hasAlgorithm()
// }
// // TODO: this might have to be parameterized by a configuration source for
// // situations where an operation is passed an algorithm
// abstract CryptographicAlgorithm getAlgorithm();
// }
// /** A key generation operation for asymmetric keys */
// abstract class KeyGen extends CryptographicOperation{
// int getAKeySizeInBits(){
// result = getKeySizeInBits(_)
// }
// final predicate hasKeySize(Expr configSrc){
// exists(this.getKeySizeInBits(configSrc))
// }
// final predicate hasKeySize(){
// exists(this.getAKeySizeInBits())
// }
// abstract Expr getKeyConfigSrc();
// abstract int getKeySizeInBits(Expr configSrc);
// }
abstract class CryptographicOperation extends CryptographicArtifact, Call{}
abstract class CryptographicOperation extends CryptographicArtifact, Call { }
abstract class KeyGeneration extends CryptographicOperation {
// TODO: what if the algorithm is UNKNOWN?
abstract Expr getKeyConfigurationSource(CryptographicAlgorithm alg);
abstract CryptographicAlgorithm getAlgorithm();
int getKeySizeInBits(CryptographicAlgorithm alg){
int getKeySizeInBits(CryptographicAlgorithm alg) {
result = getKeyConfigurationSource(alg).(Literal).getValue().toInt()
}
predicate hasConstantKeySize(CryptographicAlgorithm alg){
exists(this.getKeySizeInBits(alg))
}
predicate hasConstantKeySize(CryptographicAlgorithm alg) { exists(this.getKeySizeInBits(alg)) }
predicate hasKeyConfigurationSource(CryptographicAlgorithm alg){
predicate hasKeyConfigurationSource(CryptographicAlgorithm alg) {
exists(this.getKeyConfigurationSource(alg))
}
Expr getAKeyConfigurationSource(){
result = getKeyConfigurationSource(_)
}
Expr getAKeyConfigurationSource() { result = getKeyConfigurationSource(_) }
}
abstract class AsymmetricKeyGeneration extends KeyGeneration{}
abstract class SymmetricKeyGeneration extends KeyGeneration{}
abstract class AsymmetricKeyGeneration extends KeyGeneration { }
abstract class SymmetricKeyGeneration extends KeyGeneration { }
/**
* 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();
abstract string getAlgType();
// string getAlgType(){
@@ -145,97 +123,74 @@ abstract class CryptographicAlgorithm extends CryptographicArtifact
// else if this instanceof SigningAlgorithm then result = getSignatureType()
// else result = unknownAlgorithm()
// }
// 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)
)
}
abstract Expr configurationSink();
predicate hasConfigurationSink(){
exists(this.configurationSink())
}
predicate hasConfigurationSink() { exists(this.configurationSink()) }
}
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()
}
override string getAlgType(){
result = getHashType()
}
override string getAlgType() { result = getHashType() }
}
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()
}
override string getAlgType(){
result = getKeyDerivationType()
}
override string getAlgType() { result = getKeyDerivationType() }
}
// abstract class KeyDerivationOperation extends CryptographicOperation{
// DataFlow::Node getIterationSizeSrc(){
// none()
// }
// DataFlow::Node getSaltConfigSrc(){
// none()
// }
// DataFlow::Node getHashConfigSrc(){
// none()
// }
// // TODO: get encryption algorithm for CBC-based KDF?
// DataFlow::Node getDerivedKeySizeSrc(){
// none()
// }
// DataFlow::Node getModeSrc(){
// none()
// }
// // TODO: add more to cover all the parameters of most KDF operations? Perhaps subclass for each type?
// abstract predicate requiresIteration();
// abstract predicate requiresSalt();
// abstract predicate requiresHash();
// //abstract predicate requiresKeySize(); // Going to assume all requires a size
// abstract predicate requiresMode();
// }
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.
}
@@ -244,141 +199,118 @@ abstract class EncryptionAlgorithm extends CryptographicAlgorithm
* 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 { }
/**
* 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()
}
override string getAlgType(){
result = getAsymmetricEncryptionType()
}
override string getAlgType() { result = getAsymmetricEncryptionType() }
}
/**
* 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?
override string getAlgType(){
result = getSymmetricEncryptionType()
}
// TODO: add a stream cipher predicate?
override string getAlgType() { result = getSymmetricEncryptionType() }
}
// 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()
}
override string getAlgType(){
result = getSymmetricPaddingType()
}
override string getAlgType() { result = getSymmetricPaddingType() }
}
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()
}
override string getAlgType(){
result = getAsymmetricPaddingType()
}
override string getAlgType() { result = getAsymmetricPaddingType() }
}
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)
}
override string getAlgType(){
result = getEllipticCurveType()
}
final int getCurveBitSize() { isEllipticCurveAlgorithm(this.getCurveName(), result) }
override string getAlgType() { result = getEllipticCurveType() }
}
abstract class BlockModeAlgorithm extends CryptographicAlgorithm{
final string getBlockModeName(){
abstract class BlockModeAlgorithm 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 Expr getIVorNonce();
final predicate hasIVorNonce() { exists(this.getIVorNonce()) }
override string getAlgType(){
result = getCipherBlockModeType()
}
override string getAlgType() { result = getCipherBlockModeType() }
}
// 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()
}
override string getAlgType(){
result = getKeyExchangeType()
}
override string getAlgType() { result = getKeyExchangeType() }
}
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()
}
override string getAlgType(){
result = getSignatureType()
}
override string getAlgType() { result = getSignatureType() }
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/**
* Predicates/classes for identifying algorithm sinks.
* Predicates/classes for identifying algorithm sinks.
* An Algorithm Sink is a function that takes an algorithm as an argument.
* In particular, any function that takes in an algorithm that until the call
* In particular, any function that takes in an algorithm that until the call
* the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm)
*/
@@ -10,274 +10,287 @@ import cpp
import experimental.cryptography.utils.OpenSSL.LibraryFunction
import experimental.cryptography.CryptoAlgorithmNames
predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType){
arg.algType() = algType
}
predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType) { arg.algType() = algType }
abstract class AlgorithmSinkArgument extends Expr {
AlgorithmSinkArgument() {
exists(Call c| c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget()))
}
AlgorithmSinkArgument() {
exists(Call c | c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget()))
}
/**
* Gets the function call in which the argument exists
*/
Call getSinkCall(){
result.getAnArgument() = this
}
/**
* Gets the function call in which the argument exists
*/
Call getSinkCall() { result.getAnArgument() = this }
abstract string algType();
abstract string algType();
}
// https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html
predicate cipherAlgorithmSink(string funcName, int argInd){
(funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0)
or
(funcName = "EVP_CIPHER_fetch" and argInd = 1)
predicate cipherAlgorithmSink(string funcName, int argInd) {
funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0
or
funcName = "EVP_CIPHER_fetch" and argInd = 1
}
class CipherAlgorithmSink extends AlgorithmSinkArgument {
CipherAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
cipherAlgorithmSink(funcName, argInd)
)
}
CipherAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
cipherAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getSymmetricEncryptionType() }
override string algType() { result = getSymmetricEncryptionType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch
predicate macAlgorithmSink(string funcName, int argInd){
(funcName = "EVP_MAC_fetch" and argInd = 1)
predicate macAlgorithmSink(string funcName, int argInd) {
(funcName = "EVP_MAC_fetch" and argInd = 1)
}
class MACAlgorithmSink extends AlgorithmSinkArgument {
MACAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
macAlgorithmSink(funcName, argInd)
)
}
MACAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
macAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = "TBD" }
override string algType() { result = "TBD" }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch
predicate messageDigestAlgorithmSink(string funcName, int argInd){
(funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0)
or
funcName = "EVP_MD_fetch" and argInd = 1
predicate messageDigestAlgorithmSink(string funcName, int argInd) {
funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0
or
funcName = "EVP_MD_fetch" and argInd = 1
}
class MessageDigestAlgorithmSink extends AlgorithmSinkArgument {
MessageDigestAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
messageDigestAlgorithmSink(funcName, argInd)
)
}
MessageDigestAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
messageDigestAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getHashType() }
override string algType() { result = getHashType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch
// https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch
predicate keyExchangeAlgorithmSink(string funcName, int argInd){
funcName = "EVP_KEYEXCH_fetch" and argInd = 1
or
funcName = "EVP_KEM_fetch" and argInd = 1
predicate keyExchangeAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_KEYEXCH_fetch" and argInd = 1
or
funcName = "EVP_KEM_fetch" and argInd = 1
}
class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument {
KeyExchangeAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
keyExchangeAlgorithmSink(funcName, argInd)
)
}
KeyExchangeAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
keyExchangeAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getKeyExchangeType() }
override string algType() { result = getKeyExchangeType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch
predicate keyManagementAlgorithmSink(string funcName, int argInd){
funcName = "EVP_KEYMGMT_fetch" and argInd = 1
predicate keyManagementAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_KEYMGMT_fetch" and argInd = 1
}
class KeyManagementAlgorithmSink extends AlgorithmSinkArgument {
KeyManagementAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
keyManagementAlgorithmSink(funcName, argInd)
)
}
KeyManagementAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
keyManagementAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = "TBD" }
override string algType() { result = "TBD" }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_KDF
predicate keyDerivationAlgorithmSink(string funcName, int argInd){
funcName = "EVP_KDF_fetch" and argInd = 1
predicate keyDerivationAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_KDF_fetch" and argInd = 1
}
class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument {
KeyDerivationAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
keyDerivationAlgorithmSink(funcName, argInd)
)
}
KeyDerivationAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
keyDerivationAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getKeyDerivationType() }
override string algType() { result = getKeyDerivationType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html
predicate asymmetricCipherAlgorithmSink(string funcName, int argInd){
(funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1)
or
(funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3)
// NOTE: other cases are handled by AsymmetricAlgorithmSink
predicate asymmetricCipherAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1
or
funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3
// NOTE: other cases are handled by AsymmetricAlgorithmSink
}
class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument {
AsymmetricCipherAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
asymmetricCipherAlgorithmSink(funcName, argInd)
)
}
AsymmetricCipherAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
asymmetricCipherAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = "ASYMMETRIC_ENCRYPTION"}
override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
}
class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() {
exists(Call c, string funcName |
funcName = c.getTarget().getName() and
this = c.getArgument(3) |
funcName = "EVP_PKEY_Q_keygen" and
c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType
)
}
AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() {
exists(Call c, string funcName |
funcName = c.getTarget().getName() and
this = c.getArgument(3)
|
funcName = "EVP_PKEY_Q_keygen" and
c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType
)
}
override string algType() { result = "ASYMMETRIC_ENCRYPTION"}
override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch
predicate randomAlgorithmSink(string funcName, int argInd){
funcName = "EVP_RAND_fetch" and argInd = 1
// https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch
predicate randomAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_RAND_fetch" and argInd = 1
}
class RandomAlgorithmSink extends AlgorithmSinkArgument {
RandomAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
randomAlgorithmSink(funcName, argInd)
)
}
RandomAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
randomAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = "TBD" }
override string algType() { result = "TBD" }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch
predicate signatureAlgorithmSink(string funcName, int argInd){
funcName = "EVP_SIGNATURE_fetch" and argInd = 1
// https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch
predicate signatureAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_SIGNATURE_fetch" and argInd = 1
}
class SignatureAlgorithmSink extends AlgorithmSinkArgument {
SignatureAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
signatureAlgorithmSink(funcName, argInd)
)
}
SignatureAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
signatureAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getSignatureType() }
override string algType() { result = getSignatureType() }
}
// https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html
predicate ellipticCurveAlgorithmSink(string funcName, int argInd){
funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0
or
funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2
or
funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1
predicate ellipticCurveAlgorithmSink(string funcName, int argInd) {
funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0
or
funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2
or
funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1
}
class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument {
EllipticCurveAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
ellipticCurveAlgorithmSink(funcName, argInd)
)
}
EllipticCurveAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
ellipticCurveAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getEllipticCurveType() }
override string algType() { result = getEllipticCurveType() }
}
/**
* Special cased to address the fact that arg index 3 (zero offset based) is the curve name.
* Special cased to address the fact that arg index 3 (zero offset based) is the curve name.
* ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve
*/
class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() {
exists(Call c, string funcName |
funcName = c.getTarget().getName() and
this = c.getArgument(3) |
funcName = "EVP_PKEY_Q_keygen" and
c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and
c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType
)
}
EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() {
exists(Call c, string funcName |
funcName = c.getTarget().getName() and
this = c.getArgument(3)
|
funcName = "EVP_PKEY_Q_keygen" and
c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and
c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType
)
}
override string algType() { result = getEllipticCurveType() }
override string algType() { result = getEllipticCurveType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html
// https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
predicate asymmetricAlgorithmSink(string funcName, int argInd){
funcName = "EVP_PKEY_CTX_new_id" and argInd = 0
or
funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1
or
funcName in ["EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key"] and argInd = 0
or
funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1
// special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs
// funcName = "EVP_PKEY_Q_keygen" and argInd = 2
or
funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1
// TODO consider void cases EVP_PKEY_new
predicate asymmetricAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_PKEY_CTX_new_id" and argInd = 0
or
funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1
or
funcName in [
"EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key"
] and
argInd = 0
or
funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1
or
// special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs
// funcName = "EVP_PKEY_Q_keygen" and argInd = 2
funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1
// TODO consider void cases EVP_PKEY_new
}
class AsymmetricAlgorithmSink extends AlgorithmSinkArgument {
AsymmetricAlgorithmSink() {
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
asymmetricAlgorithmSink(funcName, argInd)
)
}
AsymmetricAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
asymmetricAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getAsymmetricType() }
override string algType() { result = getAsymmetricType() }
}
class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() {
exists(Call c, string funcName |
funcName = c.getTarget().getName() and
this = c.getArgument(2)
|
funcName = "EVP_PKEY_Q_keygen" and
not exists(c.getArgument(3))
)
}
class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() {
exists(Call c, string funcName |
funcName = c.getTarget().getName() and
this = c.getArgument(2) |
funcName = "EVP_PKEY_Q_keygen" and
not exists(c.getArgument(3))
)
}
override string algType() { result = getAsymmetricType() }
override string algType() { result = getAsymmetricType() }
}

View File

@@ -2,37 +2,33 @@ import cpp
import experimental.cryptography.utils.OpenSSL.LibraryFunction
import experimental.cryptography.CryptoAlgorithmNames
predicate inferredOpenSSLCryptoFunctionCall(Call c , string normalized, string algType){
inferredOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
predicate inferredOpenSSLCryptoFunctionCall(Call c, string normalized, string algType) {
inferredOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
}
predicate inferredOpenSSLCryptoFunction(Function f, string normalized, string algType){
isPossibleOpenSSLFunction(f) and
normalizeFunctionName(f, algType) = normalized
predicate inferredOpenSSLCryptoFunction(Function f, string normalized, string algType) {
isPossibleOpenSSLFunction(f) and
normalizeFunctionName(f, algType) = normalized
}
predicate isOpenSSLCryptoFunction(Function f, string normalized, string algType){
// NOTE: relying on inference as there are thousands of functions for crypto
// enumerating them all and maintaining the list seems problematic.
// For now, we will rely on dynamically inferring algorithms for function names.
// This has been seen to be reasonably efficient and accurate.
inferredOpenSSLCryptoFunction(f, normalized, algType)
predicate isOpenSSLCryptoFunction(Function f, string normalized, string algType) {
// NOTE: relying on inference as there are thousands of functions for crypto
// enumerating them all and maintaining the list seems problematic.
// For now, we will rely on dynamically inferring algorithms for function names.
// This has been seen to be reasonably efficient and accurate.
inferredOpenSSLCryptoFunction(f, normalized, algType)
}
predicate isOpenSSLCryptoFunctionCall(Call c, string normalized, string algType){
isOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
predicate isOpenSSLCryptoFunctionCall(Call c, string normalized, string algType) {
isOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
}
private string basicNormalizeFunctionName(Function f, string algType) {
isPossibleOpenSSLFunction(f) and
isKnownAlgorithm(result, algType) and
exists(string normStr |
normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "")
|
normStr.matches("%" + result + "%")
)
isPossibleOpenSSLFunction(f) and
isKnownAlgorithm(result, algType) and
exists(string normStr | normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
normStr.matches("%" + result + "%")
)
}
/**
@@ -42,71 +38,84 @@ private string basicNormalizeFunctionName(Function f, string algType) {
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
*
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
*
* `f` is the function name to normalize.
*
* `f` is the function name to normalize.
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
*/
private string privateNormalizeFunctionName(Function f, string algType) {
isPossibleOpenSSLFunction(f) and
(
result = basicNormalizeFunctionName(f, algType)
) and
not exists(string res2 |
isPossibleOpenSSLFunction(f) and
result = basicNormalizeFunctionName(f, algType) and
not exists(string res2 |
result != res2 and
res2 = basicNormalizeFunctionName(f, algType) and
res2.length() > result.length()
)
)
}
/**
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
* A function is not, however, allowed to be UNKNOWN. The function either
* normalizes to a known algorithm name, or the predicate does not hold (no result).
*
*
* The predicate attempts to restrict normalization to what looks like an openssl
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
* This may give false postive functions if a directory erronously appears to be openssl;
* This may give false postive functions if a directory erronously appears to be openssl;
* however, we take the stance that if a function
* exists strongly mapping to a known function name in a directory such as these,
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
*/
private string normalizeFunctionName(Function f, string algType) {
algType != "UNKNOWN" and
isPossibleOpenSSLFunction(f) and
result = privateNormalizeFunctionName(f, algType) and
// Addressing false positives
// For algorithm names less than or equal to 4, we must see the algorithm name
// in the original function as upper case (it can't be split between tokens)
// One exception found is DES_xcbc_encrypt, this is DESX
((result.length() <= 4 and result != "DESX") implies (f.getName().toUpperCase().matches("%" + result + "%")))
and
((result.length() <= 4 and result = "DESX") implies (f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%")))
and
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
// and
// DES specific false positives
(result.matches("DES") implies (not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")))
and
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
// f.getName().toUpperCase().matches("%" + s + "%"))) and
// SEED specific false positives
((result.matches("SEED")) implies not exists(string s | s in ["SEED_SRC_GENERATE", "RAND","NEW_SEED", "GEN_SEED", "SEED_GEN", "SET_SEED", "GET_SEED", "GET0_SEED", "RESEED", "SEEDING"] |
f.getName().toUpperCase().matches("%" + s + "%"))) and
// ARIA specific false positives
((result.matches("ARIA")) implies not f.getName().toUpperCase().matches("%VARIANT%")) and
// CTR false positives
((result.matches("CTR") implies not f.getName().toUpperCase().matches("%CTRL%"))) and
// ES false positives (e.g., ES256 from AES256)
((result.matches("ES%")) implies not f.getName().toUpperCase().matches("%AES%")) and
// RSA false positives
(((result.matches("RSA")) implies not f.getName().toUpperCase().matches("%UNIVERSAL%")))
and
//rsaz functions deemed to be too low level, and can be ignored
not f.getLocation().getFile().getBaseName().matches("rsaz_exp.c") and
// General False positives
// Functions that 'get' do not set an algorithm, and therefore are considered ignorable
not f.getName().toLowerCase().matches("%get%")
algType != "UNKNOWN" and
isPossibleOpenSSLFunction(f) and
result = privateNormalizeFunctionName(f, algType) and
// Addressing false positives
// For algorithm names less than or equal to 4, we must see the algorithm name
// in the original function as upper case (it can't be split between tokens)
// One exception found is DES_xcbc_encrypt, this is DESX
(
(result.length() <= 4 and result != "DESX")
implies
f.getName().toUpperCase().matches("%" + result + "%")
) and
(
(result.length() <= 4 and result = "DESX")
implies
(f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%"))
) and
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
// and
// DES specific false positives
(
result.matches("DES")
implies
not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")
) and
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
// f.getName().toUpperCase().matches("%" + s + "%"))) and
// SEED specific false positives
(
result.matches("SEED")
implies
not exists(string s |
s in [
"SEED_SRC_GENERATE", "RAND", "NEW_SEED", "GEN_SEED", "SEED_GEN", "SET_SEED", "GET_SEED",
"GET0_SEED", "RESEED", "SEEDING"
]
|
f.getName().toUpperCase().matches("%" + s + "%")
)
) and
// ARIA specific false positives
(result.matches("ARIA") implies not f.getName().toUpperCase().matches("%VARIANT%")) and
// CTR false positives
(result.matches("CTR") implies not f.getName().toUpperCase().matches("%CTRL%")) and
// ES false positives (e.g., ES256 from AES256)
(result.matches("ES%") implies not f.getName().toUpperCase().matches("%AES%")) and
// RSA false positives
(result.matches("RSA") implies not f.getName().toUpperCase().matches("%UNIVERSAL%")) and
//rsaz functions deemed to be too low level, and can be ignored
not f.getLocation().getFile().getBaseName().matches("rsaz_exp.c") and
// General False positives
// Functions that 'get' do not set an algorithm, and therefore are considered ignorable
not f.getName().toLowerCase().matches("%get%")
}

View File

@@ -1,21 +1,18 @@
/**
* This file contains predicates create to build up initial data sets for OpenSSL
* predicates. E.g., These predicates were used to assist in associating all
* openSSL functions with their known crypto algorithms.
* openSSL functions with their known crypto algorithms.
*/
import cpp
import experimental.cryptography.CryptoAlgorithmNames
import experimental.cryptography.utils.OpenSSL.CryptoFunction
private string basicNormalizeFunctionName(Function f, string algType) {
isKnownAlgorithm(result, algType) and
exists(string normStr |
normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "")
|
normStr.matches("%" + result + "%")
)
isKnownAlgorithm(result, algType) and
exists(string normStr | normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
normStr.matches("%" + result + "%")
)
}
/**
@@ -25,14 +22,12 @@ private string basicNormalizeFunctionName(Function f, string algType) {
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
*
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
*
* `f` is the function name to normalize.
*
* `f` is the function name to normalize.
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
*/
private string privateNormalizeFunctionName(Function f, string algType) {
(
result = basicNormalizeFunctionName(f, algType)
) and
result = basicNormalizeFunctionName(f, algType) and
not exists(string res2 |
result != res2 and
res2 = basicNormalizeFunctionName(f, algType) and
@@ -40,56 +35,74 @@ private string privateNormalizeFunctionName(Function f, string algType) {
) and
// Addressing bad normalization case-by-case
// CASE: ES256 being identified when the algorithm is AES256
(result.matches("ES256") implies not exists(string res2 | res2 = basicNormalizeFunctionName(f, _) and res2.matches("AES%")))
(
result.matches("ES256")
implies
not exists(string res2 | res2 = basicNormalizeFunctionName(f, _) and res2.matches("AES%"))
)
}
/**
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
* A function is not, however, allowed to be UNKNOWN. The function either
* normalizes to a known algorithm name, or the predicate does not hold (no result).
*
*
* The predicate attempts to restrict normalization to what looks like an openssl
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
* This may give false postive functions if a directory erronously appears to be openssl;
* This may give false postive functions if a directory erronously appears to be openssl;
* however, we take the stance that if a function
* exists strongly mapping to a known function name in a directory such as these,
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
*/
string normalizeFunctionName(Function f, string algType) {
algType != "UNKNOWN" and
result = privateNormalizeFunctionName(f, algType) and
openSSLLibraryFunc(f) and
// Addressing false positives
// For algorithm names less than or equal to 4, we must see the algorithm name
// in the original function as upper case (it can't be split between tokens)
// One exception found is DES_xcbc_encrypt, this is DESX
((result.length() <= 4 and result != "DESX") implies (f.getName().toUpperCase().matches("%" + result + "%")))
and
((result.length() <= 4 and result = "DESX") implies (f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%")))
and
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
// and
// DES specific false positives
(result.matches("DES") implies (not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")))
and
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
// f.getName().toUpperCase().matches("%" + s + "%"))) and
// SEED specific false positives
((result.matches("%SEED%")) implies not not exists(string s | s in ["NEW_SEED", "GEN_SEED", "SET_SEED", "GET_SEED", "GET0_SEED", "RESEED", "SEEDING"] |
f.getName().toUpperCase().matches("%" + s + "%"))) and
// ARIA specific false positives
((result.matches("%ARIA%")) implies not f.getName().toUpperCase().matches("%VARIANT%"))
algType != "UNKNOWN" and
result = privateNormalizeFunctionName(f, algType) and
openSSLLibraryFunc(f) and
// Addressing false positives
// For algorithm names less than or equal to 4, we must see the algorithm name
// in the original function as upper case (it can't be split between tokens)
// One exception found is DES_xcbc_encrypt, this is DESX
(
(result.length() <= 4 and result != "DESX")
implies
f.getName().toUpperCase().matches("%" + result + "%")
) and
(
(result.length() <= 4 and result = "DESX")
implies
(f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%"))
) and
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
// and
// DES specific false positives
(
result.matches("DES")
implies
not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")
) and
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
// f.getName().toUpperCase().matches("%" + s + "%"))) and
// SEED specific false positives
(
result.matches("%SEED%")
implies
not not exists(string s |
s in ["NEW_SEED", "GEN_SEED", "SET_SEED", "GET_SEED", "GET0_SEED", "RESEED", "SEEDING"]
|
f.getName().toUpperCase().matches("%" + s + "%")
)
) and
// ARIA specific false positives
(result.matches("%ARIA%") implies not f.getName().toUpperCase().matches("%VARIANT%"))
}
/**
* Predicate to support name normalization.
* Converts the raw name upper-case with no hyphen, slash, underscore, hash, or space.
* Looks for substrings that are known algorithms, and normalizes the name.
* If the algorithm cannot be determined or is in the ignorable list (`isIgnorableOpenSSLAlgorithm`)
* this predicate will not resolve a name.
*
* this predicate will not resolve a name.
*
* Rationale for private: For normalization, we want to get the longest string for a normalized name match
* for a given algorithm type. I found this easier to express if the public normalizeName
* checks that the name is the longest, and that UNKNOWN is reserved if there exists no
@@ -100,14 +113,11 @@ string privateNormalizeName(string name, string algType) {
//not isIgnorableOpenSSLAlgorithm(name, _, _) and
// targetOpenSSLAlgorithm(name, _) and
isKnownAlgorithm(result, algType) and
exists(string normStr |
normStr = name.toUpperCase().regexpReplaceAll("[-_ ]|/", "")
|
exists(string normStr | normStr = name.toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
normStr.matches("%" + result + "%")
)
}
/**
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
*
@@ -115,8 +125,8 @@ string privateNormalizeName(string name, string algType) {
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
*
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
*
* `name` is the name to normalize.
*
* `name` is the name to normalize.
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
*/
bindingset[name]
@@ -135,5 +145,9 @@ string normalizeName(string name, string algType) {
) and
// Addressing bad normalization case-by-case
// CASE: ES256 being identified when the algorithm is AES256
(result.matches("ES256") implies not exists(string res2 | res2 = privateNormalizeName(name, _) and res2.matches("AES%")))
(
result.matches("ES256")
implies
not exists(string res2 | res2 = privateNormalizeName(name, _) and res2.matches("AES%"))
)
}

View File

@@ -1,60 +1,59 @@
import cpp
import cpp
import experimental.cryptography.utils.OpenSSL.LibraryFunction
import semmle.code.cpp.ir.dataflow.DataFlow
// TODO: possible use of extensible predicates here
// NOTE: -1 for outInd represents the return value
predicate knownPassthroughFunction(Function f, int inInd, int outInd){
// Trace through functions
// See https://www.openssl.org/docs/man1.1.1/man3/OBJ_obj2txt
// https://www.openssl.org/docs/man3.0/man3/EVP_CIPHER_get0_name
openSSLLibraryFunc(f)
and
(
(
f.getName() in [
"OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn",
"OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid",
"OBJ_txt2nid","OBJ_txt2obj", "OBJ_dup",
"EVP_CIPHER_get0_name"]
and inInd = 0 and outInd = -1)
or
(
f.getName() in ["OBJ_obj2txt","i2t_ASN1_OBJECT"] and
inInd = 2 and outInd = 0
)
or
// Dup/copy pattern occurs in more places,
//see: https://www.openssl.org/docs/manmaster/man3/EC_KEY_copy.html and https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_dup.html
f.getName().matches("%_dup") and inInd = 0 and outInd = -1
or
f.getName().matches("%_copy") and inInd = 0 and outInd = -1
)
predicate knownPassthroughFunction(Function f, int inInd, int outInd) {
// Trace through functions
// See https://www.openssl.org/docs/man1.1.1/man3/OBJ_obj2txt
// https://www.openssl.org/docs/man3.0/man3/EVP_CIPHER_get0_name
openSSLLibraryFunc(f) and
(
f.getName() in [
"OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn", "OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid",
"OBJ_txt2nid", "OBJ_txt2obj", "OBJ_dup", "EVP_CIPHER_get0_name"
] and
inInd = 0 and
outInd = -1
or
f.getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and
inInd = 2 and
outInd = 0
or
// Dup/copy pattern occurs in more places,
//see: https://www.openssl.org/docs/manmaster/man3/EC_KEY_copy.html and https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_dup.html
f.getName().matches("%_dup") and inInd = 0 and outInd = -1
or
f.getName().matches("%_copy") and inInd = 0 and outInd = -1
)
}
/**
* `c` is a call to a function that preserves the algorithm but changes its form.
* `c` is a call to a function that preserves the algorithm but changes its form.
* `onExpr` is the input argument passing through to, `outExpr` is the next expression in a dataflow step associated with `c`
*/
predicate knownPassthoughCall(Call c, Expr inExpr, Expr outExpr){
exists(int inInd, int outInd |
knownPassthroughFunction(c.getTarget(), inInd, outInd) and
inExpr = c.getArgument(inInd) and
if(outInd = -1)
then outExpr = c
else outExpr = c.getArgument(outInd)
)
predicate knownPassthoughCall(Call c, Expr inExpr, Expr outExpr) {
exists(int inInd, int outInd |
knownPassthroughFunction(c.getTarget(), inInd, outInd) and
inExpr = c.getArgument(inInd) and
if outInd = -1 then outExpr = c else outExpr = c.getArgument(outInd)
)
}
/*
* Explicitly add flow through openssl functions that preserve the algorithm but alter the form (e.g., from NID to string)
*/
predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2){
exists(Expr cur, Expr next |
(cur = node1.asExpr() or cur = node1.asIndirectArgument()) and
(next = node2.asExpr() or next = node2.asIndirectArgument() or next = node2.asDefiningArgument())
|
exists(Call c | knownPassthoughCall(c, cur, next)))
}
* Explicitly add flow through openssl functions that preserve the algorithm but alter the form (e.g., from NID to string)
*/
predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(Expr cur, Expr next |
(cur = node1.asExpr() or cur = node1.asIndirectArgument()) and
(
next = node2.asExpr() or
next = node2.asIndirectArgument() or
next = node2.asDefiningArgument()
)
|
exists(Call c | knownPassthoughCall(c, cur, next))
)
}

View File

@@ -1,20 +1,19 @@
/**
* @name Unknown key generation key size
* @description
* @description
* @id cpp/unknown-asymmetric-key-gen-size
* @kind problem
* @problem.severity error
* @precision high
* @tags external/cwe/cwe-326
*/
import cpp
import cpp
import experimental.cryptography.Concepts
from AsymmetricKeyGeneration op, AsymmetricAlgorithm alg
where
alg = op.getAlgorithm() and
not alg instanceof EllipticCurveAlgorithm and
not exists(op.getKeySizeInBits(alg))
where
alg = op.getAlgorithm() and
not alg instanceof EllipticCurveAlgorithm and
not exists(op.getKeySizeInBits(alg))
select op, "Use of unknown asymmetric key size for algorithm $@", alg, alg.getName().toString()

View File

@@ -1,21 +1,23 @@
/**
* @name Weak asymmetric key generation key size (< 2048 bits)
* @description
* @description
* @id cpp/weak-asymmetric-key-gen-size
* @kind problem
* @problem.severity error
* @precision high
* @tags external/cwe/cwe-326
*/
import cpp
import cpp
import experimental.cryptography.Concepts
from AsymmetricKeyGeneration op, AsymmetricAlgorithm alg, Expr configSrc, int size
where
alg = op.getAlgorithm() and
not alg instanceof EllipticCurveAlgorithm and
configSrc = op.getKeyConfigurationSource(alg) and
size = configSrc.getValue().toInt() and
size < 2048
select op, "Use of weak asymmetric key size (in bits) " + size + " configured at $@ for algorithm $@", configSrc, configSrc.toString(), alg, alg.getName().toString()
where
alg = op.getAlgorithm() and
not alg instanceof EllipticCurveAlgorithm and
configSrc = op.getKeyConfigurationSource(alg) and
size = configSrc.getValue().toInt() and
size < 2048
select op,
"Use of weak asymmetric key size (in bits) " + size + " configured at $@ for algorithm $@",
configSrc, configSrc.toString(), alg, alg.getName().toString()

View File

@@ -1,31 +1,34 @@
/**
* @name Weak block mode
* @description Finds uses of symmetric encryption block modes that are weak, obsolete, or otherwise unaccepted.
* @id cpp/weak-block-mode
* @kind problem
* @problem.severity error
* @precision high
* @tags external/cwe/cwe-327
*/
/**
* @name Weak block mode
* @description Finds uses of symmetric encryption block modes that are weak, obsolete, or otherwise unaccepted.
* @id cpp/weak-block-mode
* @kind problem
* @problem.severity error
* @precision high
* @tags external/cwe/cwe-327
*/
import cpp
import experimental.cryptography.Concepts
from BlockModeAlgorithm alg, string name, string msg, Expr confSink
where
exists(string tmpMsg |
exists(string tmpMsg |
(
(name = alg.getBlockModeName() and name = unknownAlgorithm() and tmpMsg = "Use of unrecognized block mode algorithm.")
or
(
name != unknownAlgorithm() and
name = alg.getBlockModeName() and
not name = ["CBC","CTS","XTS"] and
tmpMsg = "Use of weak block mode algorithm " + name + "."
)
)
and
name = alg.getBlockModeName() and
name = unknownAlgorithm() and
tmpMsg = "Use of unrecognized block mode algorithm."
or
name != unknownAlgorithm() and
name = alg.getBlockModeName() and
not name = ["CBC", "CTS", "XTS"] and
tmpMsg = "Use of weak block mode algorithm " + name + "."
) and
if alg.hasConfigurationSink() and alg.configurationSink() != alg
then (confSink = alg.configurationSink() and msg = tmpMsg + " Algorithm used at sink: $@.")
else (confSink = alg and msg = tmpMsg)
)
select alg, msg, confSink, confSink.toString()
then (
confSink = alg.configurationSink() and msg = tmpMsg + " Algorithm used at sink: $@."
) else (
confSink = alg and msg = tmpMsg
)
)
select alg, msg, confSink, confSink.toString()

View File

@@ -7,28 +7,34 @@
* @precision high
* @tags external/cwe/cwe-327
*/
import cpp
import experimental.cryptography.Concepts
from EllipticCurveAlgorithm alg, string name, string msg, Expr confSink
where
exists(string tmpMsg |
exists(string tmpMsg |
(
(name = alg.getCurveName() and name = unknownAlgorithm() and tmpMsg = "Use of unrecognized curve algorithm.")
or
(
name != unknownAlgorithm() and
name = alg.getCurveName() and
not name = ["SECP256R1", "PRIME256V1",//P-256
"SECP384R1", //P-384
"SECP521R1", //P-521
"ED25519", "X25519"] and
tmpMsg = "Use of weak curve algorithm " + name + "."
)
)
and
name = alg.getCurveName() and
name = unknownAlgorithm() and
tmpMsg = "Use of unrecognized curve algorithm."
or
name != unknownAlgorithm() and
name = alg.getCurveName() and
not name =
[
"SECP256R1", "PRIME256V1", //P-256
"SECP384R1", //P-384
"SECP521R1", //P-521
"ED25519", "X25519"
] and
tmpMsg = "Use of weak curve algorithm " + name + "."
) and
if alg.hasConfigurationSink() and alg.configurationSink() != alg
then (confSink = alg.configurationSink() and msg = tmpMsg + " Algorithm used at sink: $@.")
else (confSink = alg and msg = tmpMsg)
)
select alg, msg, confSink, confSink.toString()
then (
confSink = alg.configurationSink() and msg = tmpMsg + " Algorithm used at sink: $@."
) else (
confSink = alg and msg = tmpMsg
)
)
select alg, msg, confSink, confSink.toString()

View File

@@ -11,24 +11,31 @@
import cpp
import experimental.cryptography.Concepts
from SymmetricEncryptionAlgorithm alg, Expr confSink, string msg
where
exists (string resMsg |
(
if alg.getEncryptionName() = unknownAlgorithm()
then (
alg instanceof Literal and resMsg = "Use of unrecognized symmetric encryption algorithm: " + alg.(Literal).getValueText().toString() + "."
or
not alg instanceof Literal and resMsg = "Use of unrecognized symmetric encryption algorithm."
where
exists(string resMsg |
(
if alg.getEncryptionName() = unknownAlgorithm()
then (
alg instanceof Literal and
resMsg =
"Use of unrecognized symmetric encryption algorithm: " +
alg.(Literal).getValueText().toString() + "."
or
not alg instanceof Literal and
resMsg = "Use of unrecognized symmetric encryption algorithm."
) else (
not alg.getEncryptionName().matches("AES%") and
resMsg = "Use of banned symmetric encryption algorithm: " + alg.getEncryptionName() + "."
)
) and
(
if alg.hasConfigurationSink() and alg.configurationSink() != alg
then (
confSink = alg.configurationSink() and msg = resMsg + " Algorithm used at sink: $@."
) else (
confSink = alg and msg = resMsg
)
)
else (not alg.getEncryptionName().matches("AES%") and resMsg = "Use of banned symmetric encryption algorithm: " + alg.getEncryptionName() + ".")
)
and
(
if alg.hasConfigurationSink() and alg.configurationSink() != alg
then (confSink = alg.configurationSink() and msg = resMsg + " Algorithm used at sink: $@.")
else (confSink = alg and msg = resMsg)
)
)
select alg, msg, confSink, confSink.toString()

View File

@@ -1,6 +1,6 @@
/**
* @name Weak cryptography
* @description Finds explicit uses of cryptographic hash algorithms that are weak and obsolete.
* @description Finds explicit uses of cryptographic hash algorithms that are weak and obsolete.
* @kind problem
* @id cpp/weak-crypto/banned-hash-algorithms
* @problem.severity error
@@ -11,25 +11,26 @@
import cpp
import semmle.code.cpp.dataflow.DataFlow as ASTDataFlow
import experimental.cryptography.Concepts
from HashAlgorithm alg, Expr confSink, string msg
where
exists(string name, string msgTmp | name = alg.getHashName() |
not name = ["SHA256", "SHA384", "SHA512"] and
(
if name = unknownAlgorithm()
then
(
not alg instanceof Literal and msgTmp = "Use of unrecognized hash algorithm."
or
alg instanceof Literal and msgTmp = "Use of unrecognized hash algorithm: " + alg.(Literal).getValueText().toString() + "."
)
else msgTmp = "Use of banned hash algorithm " + name + "."
)
and
if alg.hasConfigurationSink() and alg.configurationSink() != alg
then (confSink = alg.configurationSink() and msg = msgTmp + " Algorithm used at sink: $@.")
else (confSink = alg and msg = msgTmp)
)
from HashAlgorithm alg, Expr confSink, string msg
where
exists(string name, string msgTmp | name = alg.getHashName() |
not name = ["SHA256", "SHA384", "SHA512"] and
(
if name = unknownAlgorithm()
then
not alg instanceof Literal and msgTmp = "Use of unrecognized hash algorithm."
or
alg instanceof Literal and
msgTmp =
"Use of unrecognized hash algorithm: " + alg.(Literal).getValueText().toString() + "."
else msgTmp = "Use of banned hash algorithm " + name + "."
) and
if alg.hasConfigurationSink() and alg.configurationSink() != alg
then (
confSink = alg.configurationSink() and msg = msgTmp + " Algorithm used at sink: $@."
) else (
confSink = alg and msg = msgTmp
)
)
select alg, msg, confSink, confSink.toString()

View File

@@ -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

View File

@@ -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"
]
}
/**

View File

@@ -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()
}
}

View File

@@ -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() }
}
}

View File

@@ -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()
)
}
}

View File

@@ -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) }
}

View File

@@ -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()
)
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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."

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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