Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ private import internal.CryptoAlgorithmNames
/**
* A cryptographic algorithm.
*/
private newtype TCryptographicAlgorithm =
private newtype TCryptographicAlgorithm =
MkHashingAlgorithm(string name, boolean isWeak) {
isStrongHashingAlgorithm(name) and isWeak = false
or
Expand All@@ -25,6 +25,9 @@ private newtype TCryptographicAlgorithm =
or
isWeakPasswordHashingAlgorithm(name) and isWeak = true
}
or
MkUnknown()


/**
* A cryptographic algorithm.
Expand DownExpand Up@@ -53,6 +56,26 @@ abstract class CryptographicAlgorithm extends TCryptographicAlgorithm {
* Holds if this algorithm is weak.
*/
abstract predicate isWeak();

/**
* Holds if this algorithm is not known.
*/
predicate isUnknown() { this instanceof UnknownAlgorithm }
}

/**
* An 'Unknown' cryptographic algorithm, typically encountered when extracting as yet unmodelled API algorithms.
*/
class UnknownAlgorithm extends MkUnknown, CryptographicAlgorithm {
override predicate isUnknown() { any() }

override string getName() { result = unknownAlgorithm() }

override predicate isWeak() { any() }

bindingset[name]
override predicate matchesName(string name) { none()}

}

/**
Expand DownExpand Up@@ -82,6 +105,8 @@ class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm

override predicate isWeak() { isWeak = true }

predicate isAsymmetricEncryption() { isAsymmetricEncryption(name) }

/** Holds if this algorithm is a stream cipher. */
predicate isStreamCipher() { isStreamCipher(name) }
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,14 +8,34 @@
* The classification into strong and weak are based on Wikipedia, OWASP and Google (2021).
*/

/**
* Returns a string to represent generally unknown algorithms.
* Predicate is to be used to get a consistent string representation
* for unknown algorithms.
*/
string unknownAlgorithm()
{
result = "UNKNOWN"
}


/**
* Holds if `name` is a known hashing algorithm in the model/library.
*/
predicate isKnownHashingAlgorithm(string name)
{
isStrongHashingAlgorithm(name) or isWeakHashingAlgorithm(name)
}

/**
* Holds if `name` corresponds to a strong hashing algorithm.
*/
predicate isStrongHashingAlgorithm(string name) {
name =
[
"DSA", "ED25519", "ES256", "ECDSA256", "ES384", "ECDSA384", "ES512", "ECDSA512", "SHA2",
"SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512"
"BLAKE2", "BLAKE2b", "BLAKE2s", "DSA", "ED25519", "ES256", "ECDSA256", "ES384", "ECDSA384", "ES512", "ECDSA512", "SHA2",
"SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512",
"SHAKE128", "SHAKE256"
]
}

Expand All@@ -30,16 +50,25 @@ predicate isWeakHashingAlgorithm(string name) {
]
}

/**
* Holds if `name` is a known encryption algorithm in the model/library.
*/
predicate isKnownEncryptionAlgorithm(string name)
{
isStrongEncryptionAlgorithm(name) or isWeakEncryptionAlgorithm(name)
}


/**
* Holds if `name` corresponds to a strong encryption algorithm.
*/
predicate isStrongEncryptionAlgorithm(string name) {
name =
[
"AES", "AES128", "AES192", "AES256", "AES512", "AES-128", "AES-192", "AES-256", "AES-512",
"AES", "AES128", "AES192", "AES256", "AES512",
"ARIA", "BLOWFISH", "BF", "ECIES", "CAST", "CAST5", "CAMELLIA", "CAMELLIA128", "CAMELLIA192",
"CAMELLIA256", "CAMELLIA-128", "CAMELLIA-192", "CAMELLIA-256", "CHACHA", "GOST", "GOST89",
"IDEA", "RABBIT", "RSA", "SEED", "SM4"
"CAMELLIA256", "CHACHA", "GOST", "GOST89",
"IDEA", "RABBIT", "RSA", "SEED", "SM3", "SM4"
]
}

Expand All@@ -54,6 +83,15 @@ predicate isWeakEncryptionAlgorithm(string name) {
]
}

/**
* Holds if `name` is a known password hashing algorithm in the model/library.
*/
predicate isKnownPasswordHashingAlgorithm(string name)
{
isStrongPasswordHashingAlgorithm(name) or isWeakPasswordHashingAlgorithm(name)
}


/**
* Holds if `name` corresponds to a strong password hashing algorithm.
*/
Expand All@@ -64,9 +102,46 @@ predicate isStrongPasswordHashingAlgorithm(string name) {
/**
* Holds if `name` corresponds to a weak password hashing algorithm.
*/
predicate isWeakPasswordHashingAlgorithm(string name) { name = "EVPKDF" }
predicate isWeakPasswordHashingAlgorithm(string name) {
name = "EVPKDF"
}

/**
* Holds if `name` is a known cipher block mode algorithm in the model/library.
*/
predicate isKnownCipherBlockModeAlgorithm(string name)
{
isStrongCipherBlockModeAlgorithm(name) or isWeakCipherBlockModeAlgorithm(name)
}


/**
* Holds if `name` corresponds to a strong cipher block mode
*/
predicate isStrongCipherBlockModeAlgorithm(string name)
{
name = ["CBC", "GCM", "CCM", "CFB", "OFB", "CFB8", "CTR", "OPENPGP", "XTS", "EAX"]
}

/**
* Holds if `name` corresponds to a weak cipher block mode
*/
predicate isWeakCipherBlockModeAlgorithm(string name)
{
name = ["ECB"]
}


/**
* Holds if `name` corresponds to a stream cipher.
*/
predicate isStreamCipher(string name) { name = ["CHACHA", "RC4", "ARC4", "ARCFOUR", "RABBIT"] }


/**
* Holds if `name` corresponds to an asymmetric encryption.
*/
bindingset[name]
predicate isAsymmetricEncryption(string name){
name.regexpMatch("(?i)^rsa.*")
}
46 changes: 28 additions & 18 deletions python/ql/lib/semmle/python/frameworks/Cryptodome.qll
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@ private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
import semmle.python.concepts.internal.CryptoAlgorithmNames

/**
* Provides models for
Expand DownExpand Up@@ -124,7 +125,9 @@ private module CryptodomeModel {
this = newCall.getReturn().getMember(methodName).getACall()
}

override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(cipherName) }
override string getAlgorithmRaw() {
result = cipherName
}

override DataFlow::Node getAnInput() {
methodName = "encrypt" and
Expand DownExpand Up@@ -156,18 +159,23 @@ private module CryptodomeModel {
]
}

override Cryptography::BlockMode getBlockMode() {
// `modeName` is of the form "MODE_<BlockMode>"
exists(string modeName |
newCall.getArg(1) =
API::moduleImport(["Crypto", "Cryptodome"])
.getMember("Cipher")
.getMember(cipherName)
.getMember(modeName)
.getAValueReachableFromSource()
|
result = modeName.splitAt("_", 1)
)
private predicate resolveModeName(string modeName)
{
newCall.getArg(1) =
API::moduleImport(["Crypto", "Cryptodome"])
.getMember("Cipher")
.getMember(cipherName)
.getMember(modeName)
.getAValueReachableFromSource()
}

override Cryptography::BlockMode getBlockModeRaw() {
// `modeName` is of the form "MODE_<BlockMode>"
exists(string modeName |
if resolveModeName(modeName)
then result = modeName.splitAt("_", 1)
else modeName = unknownAlgorithm() and
result = unknownAlgorithm())
}
}

Expand All@@ -191,8 +199,8 @@ private module CryptodomeModel {
.getACall()
}

override Cryptography::CryptographicAlgorithm getAlgorithm() {
result.matchesName(signatureName)
override string getAlgorithmRaw() {
result = signatureName
}

override DataFlow::Node getAnInput() {
Expand All@@ -207,7 +215,7 @@ private module CryptodomeModel {
)
}

override Cryptography::BlockMode getBlockMode() { none() }
override string getBlockModeRaw() { none() }
}

/**
Expand All@@ -228,10 +236,12 @@ private module CryptodomeModel {
)
}

override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
override string getAlgorithmRaw() {
result = hashName
}

override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }

override Cryptography::BlockMode getBlockMode() { none() }
override string getBlockModeRaw() { none() }
}
}
21 changes: 12 additions & 9 deletions python/ql/lib/semmle/python/frameworks/Cryptography.qll
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
import semmle.python.concepts.internal.CryptoAlgorithmNames

/**
* Provides models for the `cryptography` PyPI package.
Expand DownExpand Up@@ -180,7 +181,7 @@ private module CryptographyModel {
}

/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
API::Node cipherInstance(string algorithmName, string modeName) {
API::Node symmetricCipherInstance(string algorithmName, string modeName) {
exists(API::CallNode call | result = call.getReturn() |
call =
API::moduleImport("cryptography")
Expand All@@ -193,10 +194,12 @@ private module CryptographyModel {
call.getArg(0), call.getArgByName("algorithm")
] and
exists(DataFlow::Node modeArg | modeArg in [call.getArg(1), call.getArgByName("mode")] |
// Find the used mode name
if modeArg = modeClassRef(_).getReturn().getAValueReachableFromSource()
then modeArg = modeClassRef(modeName).getReturn().getAValueReachableFromSource()
else modeName = "<None or unknown>"
else modeName = unknownAlgorithm()
)

)
}

Expand All@@ -210,20 +213,20 @@ private module CryptographyModel {

CryptographyGenericCipherOperation() {
this =
cipherInstance(algorithmName, modeName)
symmetricCipherInstance(algorithmName, modeName)
.getMember(["decryptor", "encryptor"])
.getReturn()
.getMember(["update", "update_into"])
.getACall()
}

override Cryptography::CryptographicAlgorithm getAlgorithm() {
result.matchesName(algorithmName)
override string getAlgorithmRaw() {
result = algorithmName
}

override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }

override Cryptography::BlockMode getBlockMode() { result = modeName }
override string getBlockModeRaw() { result = modeName }
}
}

Expand DownExpand Up@@ -269,13 +272,13 @@ private module CryptographyModel {
this = hashInstance(algorithmName).getMember("update").getACall()
}

override Cryptography::CryptographicAlgorithm getAlgorithm() {
result.matchesName(algorithmName)
override string getAlgorithmRaw() {
result = algorithmName
}

override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }

override Cryptography::BlockMode getBlockMode() { none() }
override string getBlockModeRaw() { none() }
}
}
}
Loading