mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge pull request #5635 from RasmusWL/port-weak-crypto-algorithm
Approved by yoff
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.python.dataflow.new.SensitiveDataSources
|
||||
|
||||
class SensitiveDataSourcesTest extends InlineExpectationsTest {
|
||||
SensitiveDataSourcesTest() { this = "SensitiveDataSourcesTest" }
|
||||
|
||||
override string getARelevantTag() { result = "SensitiveDataSource" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(SensitiveDataSource source |
|
||||
location = source.getLocation() and
|
||||
element = source.toString() and
|
||||
value = source.getClassification() and
|
||||
tag = "SensitiveDataSource"
|
||||
)
|
||||
}
|
||||
}
|
||||
33
python/ql/test/experimental/dataflow/sensitive-data/test.py
Normal file
33
python/ql/test/experimental/dataflow/sensitive-data/test.py
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
from not_found import get_passwd, account_id
|
||||
|
||||
def get_password():
|
||||
pass
|
||||
|
||||
def get_secret():
|
||||
pass
|
||||
|
||||
def fetch_certificate():
|
||||
pass
|
||||
|
||||
def encrypt_password(pwd):
|
||||
pass
|
||||
|
||||
get_password() # $ SensitiveDataSource=password
|
||||
get_passwd() # $ SensitiveDataSource=password
|
||||
get_secret() # $ SensitiveDataSource=secret
|
||||
fetch_certificate() # $ SensitiveDataSource=certificate
|
||||
account_id() # $ SensitiveDataSource=id
|
||||
safe_to_store = encrypt_password(pwd)
|
||||
|
||||
# attributes
|
||||
foo = ObjectFromDatabase()
|
||||
foo.secret # $ SensitiveDataSource=secret
|
||||
foo.username # $ SensitiveDataSource=id
|
||||
|
||||
# Special handling of lookups of sensitive properties
|
||||
request.args["password"], # $ MISSING: SensitiveDataSource=password
|
||||
request.args.get("password") # $ SensitiveDataSource=password
|
||||
|
||||
# I don't think handling `getlist` is super important, just included it to show what we don't handle
|
||||
request.args.getlist("password")[0] # $ MISSING: SensitiveDataSource=password
|
||||
@@ -341,3 +341,33 @@ class PublicKeyGenerationTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CryptographicOperationTest extends InlineExpectationsTest {
|
||||
CryptographicOperationTest() { this = "CryptographicOperationTest" }
|
||||
|
||||
override string getARelevantTag() {
|
||||
result in [
|
||||
"CryptographicOperation", "CryptographicOperationInput", "CryptographicOperationAlgorithm"
|
||||
]
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(Cryptography::CryptographicOperation cryptoOperation |
|
||||
location = cryptoOperation.getLocation() and
|
||||
(
|
||||
element = cryptoOperation.toString() and
|
||||
value = "" and
|
||||
tag = "CryptographicOperation"
|
||||
or
|
||||
element = cryptoOperation.toString() and
|
||||
value = value_from_expr(cryptoOperation.getAnInput().asExpr()) and
|
||||
tag = "CryptographicOperationInput"
|
||||
or
|
||||
element = cryptoOperation.toString() and
|
||||
value = cryptoOperation.getAlgorithm().getName() and
|
||||
tag = "CryptographicOperationAlgorithm"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
13
python/ql/test/library-tests/frameworks/crypto/README.md
Normal file
13
python/ql/test/library-tests/frameworks/crypto/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
These tests are a copy of the tests in [../cryptodome](../cryptodome) with `Cryptodome` replaced by `Crypto`.
|
||||
|
||||
You can run the following command to update the tests:
|
||||
|
||||
```sh
|
||||
rm *.py && cp ../cryptodome/*.py . && sed -i -e 's/Cryptodome/Crypto/' *.py
|
||||
```
|
||||
|
||||
The original [`pycrypto` PyPI package](https://pypi.org/project/pycrypto/) that provided the `Crypto` Python package has not been updated since 2013, so it is reasonable to assume that people will use the replacement [`pycryptodome` PyPI package](https://pypi.org/project/pycryptodome/) that also provides a `Crypto` Python package and has a (mostly) compatible API.
|
||||
|
||||
The pycryptodome functionality is also available in the [`pycryptodomex` PyPI package](https://pypi.org/project/pycryptodomex/) which provides the `Cryptodome` Python package.
|
||||
|
||||
To ensure our modeling actually covers _both_ ways of importing the same functionality, we have this convoluted test setup.
|
||||
36
python/ql/test/library-tests/frameworks/crypto/test_aes.py
Normal file
36
python/ql/test/library-tests/frameworks/crypto/test_aes.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# https://pycryptodome.readthedocs.io/en/latest/src/cipher/aes.html
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
import os
|
||||
|
||||
key = os.urandom(256//8)
|
||||
iv = os.urandom(16)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# encrypt/decrypt
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
print("encrypt/decrypt")
|
||||
|
||||
secret_message = b"secret message"
|
||||
|
||||
padding_len = 16 - (len(secret_message) % 16)
|
||||
padding = b"\0"*padding_len
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
|
||||
# using separate .encrypt calls on individual lines does not work
|
||||
whole_plantext = secret_message + padding
|
||||
encrypted = cipher.encrypt(whole_plantext) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=whole_plantext
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
print()
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
|
||||
decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted
|
||||
|
||||
decrypted = decrypted[:-padding_len]
|
||||
|
||||
print("decrypted={}".format(decrypted))
|
||||
assert decrypted == secret_message
|
||||
@@ -20,8 +20,8 @@ message = b"message"
|
||||
|
||||
signer = DSS.new(private_key, mode='fips-186-3')
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
signature = signer.sign(hasher)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=DSA
|
||||
|
||||
print("signature={}".format(signature))
|
||||
|
||||
@@ -29,13 +29,13 @@ print()
|
||||
|
||||
verifier = DSS.new(public_key, mode='fips-186-3')
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=DSA
|
||||
print("Signature verified (as expected)")
|
||||
|
||||
try:
|
||||
hasher = SHA256.new(b"other message")
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message"
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=DSA
|
||||
raise Exception("Signature verified (unexpected)")
|
||||
except ValueError:
|
||||
print("Signature mismatch (as expected)")
|
||||
|
||||
@@ -17,8 +17,8 @@ message = b"message"
|
||||
|
||||
signer = DSS.new(private_key, mode='fips-186-3')
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
signature = signer.sign(hasher)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=ECDSA
|
||||
|
||||
print("signature={}".format(signature))
|
||||
|
||||
@@ -26,13 +26,13 @@ print()
|
||||
|
||||
verifier = DSS.new(public_key, mode='fips-186-3')
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature
|
||||
print("Signature verified (as expected)")
|
||||
|
||||
try:
|
||||
hasher = SHA256.new(b"other message")
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message"
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=ECDSA
|
||||
raise Exception("Signature verified (unexpected)")
|
||||
except ValueError:
|
||||
print("Signature mismatch (as expected)")
|
||||
|
||||
10
python/ql/test/library-tests/frameworks/crypto/test_md5.py
Normal file
10
python/ql/test/library-tests/frameworks/crypto/test_md5.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from Crypto.Hash import MD5
|
||||
|
||||
hasher = MD5.new(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
|
||||
|
||||
hasher = MD5.new() # $ CryptographicOperation CryptographicOperationAlgorithm=MD5
|
||||
hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5
|
||||
hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
30
python/ql/test/library-tests/frameworks/crypto/test_rc4.py
Normal file
30
python/ql/test/library-tests/frameworks/crypto/test_rc4.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# https://pycryptodome.readthedocs.io/en/latest/src/cipher/arc4.html
|
||||
from Crypto.Cipher import ARC4
|
||||
|
||||
import os
|
||||
|
||||
key = os.urandom(256//8)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# encrypt/decrypt
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
print("encrypt/decrypt")
|
||||
|
||||
secret_message = b"secret message"
|
||||
|
||||
cipher = ARC4.new(key)
|
||||
encrypted = cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=secret_message
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
print()
|
||||
|
||||
cipher = ARC4.new(key)
|
||||
decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=encrypted
|
||||
|
||||
print("decrypted={}".format(decrypted))
|
||||
assert decrypted == secret_message
|
||||
@@ -23,7 +23,7 @@ secret_message = b"secret message"
|
||||
|
||||
encrypt_cipher = PKCS1_OAEP.new(public_key)
|
||||
|
||||
encrypted = encrypt_cipher.encrypt(secret_message)
|
||||
encrypted = encrypt_cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationInput=secret_message # MISSING: CryptographicOperationAlgorithm=RSA-OAEP?
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
@@ -31,9 +31,7 @@ print()
|
||||
|
||||
decrypt_cipher = PKCS1_OAEP.new(private_key)
|
||||
|
||||
decrypted = decrypt_cipher.decrypt(
|
||||
encrypted,
|
||||
)
|
||||
decrypted = decrypt_cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationInput=encrypted # MISSING: CryptographicOperationAlgorithm=RSA-OAEP?
|
||||
|
||||
print("decrypted={}".format(decrypted))
|
||||
assert decrypted == secret_message
|
||||
@@ -51,23 +49,23 @@ message = b"message"
|
||||
|
||||
signer = pss.new(private_key)
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
signature = signer.sign(hasher)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=RSA-PSS?
|
||||
|
||||
print("signature={}".format(signature))
|
||||
|
||||
print()
|
||||
|
||||
|
||||
verifier = pss.new(public_key)
|
||||
hasher = SHA256.new(message)
|
||||
verifier.verify(hasher, signature)
|
||||
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=RSA-PSS?
|
||||
print("Signature verified (as expected)")
|
||||
|
||||
try:
|
||||
verifier = pss.new(public_key)
|
||||
hasher = SHA256.new(b"other message")
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message"
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=RSA-PSS?
|
||||
raise Exception("Signature verified (unexpected)")
|
||||
except ValueError:
|
||||
print("Signature mismatch (as expected)")
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# https://pycryptodome.readthedocs.io/en/latest/src/cipher/aes.html
|
||||
from Cryptodome.Cipher import AES
|
||||
|
||||
import os
|
||||
|
||||
key = os.urandom(256//8)
|
||||
iv = os.urandom(16)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# encrypt/decrypt
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
print("encrypt/decrypt")
|
||||
|
||||
secret_message = b"secret message"
|
||||
|
||||
padding_len = 16 - (len(secret_message) % 16)
|
||||
padding = b"\0"*padding_len
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
|
||||
# using separate .encrypt calls on individual lines does not work
|
||||
whole_plantext = secret_message + padding
|
||||
encrypted = cipher.encrypt(whole_plantext) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=whole_plantext
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
print()
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
|
||||
decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted
|
||||
|
||||
decrypted = decrypted[:-padding_len]
|
||||
|
||||
print("decrypted={}".format(decrypted))
|
||||
assert decrypted == secret_message
|
||||
@@ -20,8 +20,8 @@ message = b"message"
|
||||
|
||||
signer = DSS.new(private_key, mode='fips-186-3')
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
signature = signer.sign(hasher)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=DSA
|
||||
|
||||
print("signature={}".format(signature))
|
||||
|
||||
@@ -29,13 +29,13 @@ print()
|
||||
|
||||
verifier = DSS.new(public_key, mode='fips-186-3')
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=DSA
|
||||
print("Signature verified (as expected)")
|
||||
|
||||
try:
|
||||
hasher = SHA256.new(b"other message")
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message"
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=DSA
|
||||
raise Exception("Signature verified (unexpected)")
|
||||
except ValueError:
|
||||
print("Signature mismatch (as expected)")
|
||||
|
||||
@@ -17,8 +17,8 @@ message = b"message"
|
||||
|
||||
signer = DSS.new(private_key, mode='fips-186-3')
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
signature = signer.sign(hasher)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=ECDSA
|
||||
|
||||
print("signature={}".format(signature))
|
||||
|
||||
@@ -26,13 +26,13 @@ print()
|
||||
|
||||
verifier = DSS.new(public_key, mode='fips-186-3')
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature
|
||||
print("Signature verified (as expected)")
|
||||
|
||||
try:
|
||||
hasher = SHA256.new(b"other message")
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message"
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=ECDSA
|
||||
raise Exception("Signature verified (unexpected)")
|
||||
except ValueError:
|
||||
print("Signature mismatch (as expected)")
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
from Cryptodome.Hash import MD5
|
||||
|
||||
hasher = MD5.new(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
|
||||
|
||||
hasher = MD5.new() # $ CryptographicOperation CryptographicOperationAlgorithm=MD5
|
||||
hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5
|
||||
hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
@@ -0,0 +1,30 @@
|
||||
# https://pycryptodome.readthedocs.io/en/latest/src/cipher/arc4.html
|
||||
from Cryptodome.Cipher import ARC4
|
||||
|
||||
import os
|
||||
|
||||
key = os.urandom(256//8)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# encrypt/decrypt
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
print("encrypt/decrypt")
|
||||
|
||||
secret_message = b"secret message"
|
||||
|
||||
cipher = ARC4.new(key)
|
||||
encrypted = cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=secret_message
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
print()
|
||||
|
||||
cipher = ARC4.new(key)
|
||||
decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=encrypted
|
||||
|
||||
print("decrypted={}".format(decrypted))
|
||||
assert decrypted == secret_message
|
||||
@@ -23,7 +23,7 @@ secret_message = b"secret message"
|
||||
|
||||
encrypt_cipher = PKCS1_OAEP.new(public_key)
|
||||
|
||||
encrypted = encrypt_cipher.encrypt(secret_message)
|
||||
encrypted = encrypt_cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationInput=secret_message # MISSING: CryptographicOperationAlgorithm=RSA-OAEP?
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
@@ -31,9 +31,7 @@ print()
|
||||
|
||||
decrypt_cipher = PKCS1_OAEP.new(private_key)
|
||||
|
||||
decrypted = decrypt_cipher.decrypt(
|
||||
encrypted,
|
||||
)
|
||||
decrypted = decrypt_cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationInput=encrypted # MISSING: CryptographicOperationAlgorithm=RSA-OAEP?
|
||||
|
||||
print("decrypted={}".format(decrypted))
|
||||
assert decrypted == secret_message
|
||||
@@ -51,8 +49,8 @@ message = b"message"
|
||||
|
||||
signer = pss.new(private_key)
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
signature = signer.sign(hasher)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=RSA-PSS?
|
||||
|
||||
print("signature={}".format(signature))
|
||||
|
||||
@@ -60,14 +58,14 @@ print()
|
||||
|
||||
verifier = pss.new(public_key)
|
||||
|
||||
hasher = SHA256.new(message)
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=RSA-PSS?
|
||||
print("Signature verified (as expected)")
|
||||
|
||||
try:
|
||||
verifier = pss.new(public_key)
|
||||
hasher = SHA256.new(b"other message")
|
||||
verifier.verify(hasher, signature)
|
||||
hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message"
|
||||
verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=RSA-PSS?
|
||||
raise Exception("Signature verified (unexpected)")
|
||||
except ValueError:
|
||||
print("Signature mismatch (as expected)")
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes
|
||||
import os
|
||||
|
||||
key = os.urandom(256//8)
|
||||
iv = os.urandom(16)
|
||||
|
||||
algorithm = algorithms.AES(key)
|
||||
cipher = Cipher(algorithm, mode=modes.CBC(iv))
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# encrypt/decrypt
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# following https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.Cipher
|
||||
|
||||
print("encrypt/decrypt")
|
||||
|
||||
secret_message = b"secret message"
|
||||
|
||||
padding_len = 16 - (len(secret_message) % 16)
|
||||
padding = b"\0"*padding_len
|
||||
|
||||
encryptor = cipher.encryptor()
|
||||
print(padding_len)
|
||||
encrypted = encryptor.update(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=secret_message
|
||||
encrypted += encryptor.update(padding) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=padding
|
||||
encrypted += encryptor.finalize()
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
print()
|
||||
|
||||
decryptor = cipher.decryptor()
|
||||
decrypted = decryptor.update(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted
|
||||
decrypted += decryptor.finalize()
|
||||
|
||||
decrypted = decrypted[:-padding_len]
|
||||
|
||||
print("decrypted={}".format(decrypted))
|
||||
assert decrypted == secret_message
|
||||
@@ -0,0 +1,10 @@
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
|
||||
from binascii import hexlify
|
||||
|
||||
|
||||
hasher = hashes.Hash(hashes.MD5())
|
||||
hasher.update(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5
|
||||
|
||||
digest = hasher.finalize()
|
||||
print(hexlify(digest).decode('utf-8'))
|
||||
@@ -0,0 +1,32 @@
|
||||
from cryptography.hazmat.primitives.ciphers import algorithms, Cipher
|
||||
import os
|
||||
|
||||
key = os.urandom(256//8)
|
||||
|
||||
algorithm = algorithms.ARC4(key)
|
||||
cipher = Cipher(algorithm, mode=None)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# encrypt/decrypt
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# following https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption.html#cryptography.hazmat.primitives.ciphers.algorithms.ARC4
|
||||
|
||||
print("encrypt/decrypt")
|
||||
|
||||
secret_message = b"secret message"
|
||||
|
||||
encryptor = cipher.encryptor()
|
||||
encrypted = encryptor.update(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=secret_message
|
||||
encrypted += encryptor.finalize()
|
||||
|
||||
print("encrypted={}".format(encrypted))
|
||||
|
||||
print()
|
||||
|
||||
decryptor = cipher.decryptor()
|
||||
decrypted = decryptor.update(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=encrypted
|
||||
decrypted += decryptor.finalize()
|
||||
|
||||
print("decrypted={}".format(decrypted))
|
||||
assert decrypted == secret_message
|
||||
29
python/ql/test/library-tests/frameworks/stdlib/test_md5.py
Normal file
29
python/ql/test/library-tests/frameworks/stdlib/test_md5.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import hashlib
|
||||
|
||||
|
||||
hasher = hashlib.md5(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
|
||||
|
||||
hasher = hashlib.md5(string=b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
|
||||
|
||||
hasher = hashlib.md5()
|
||||
hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5
|
||||
hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
|
||||
|
||||
hasher = hashlib.new('md5', b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
|
||||
|
||||
hasher = hashlib.new('md5', data=b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
|
||||
|
||||
hasher = hashlib.new('md5')
|
||||
hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5
|
||||
hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5
|
||||
print(hasher.hexdigest())
|
||||
@@ -0,0 +1,2 @@
|
||||
| test_cryptodome.py:11:13:11:42 | ControlFlowNode for Attribute() | The cryptographic algorithm ARC4 is broken or weak, and should not be used. |
|
||||
| test_cryptography.py:13:13:13:44 | ControlFlowNode for Attribute() | The cryptographic algorithm ARC4 is broken or weak, and should not be used. |
|
||||
@@ -0,0 +1,3 @@
|
||||
Note that the tests in this directory are very shallow, and simply show that the query is able to produce alerts.
|
||||
|
||||
More in-depth tests can be found for the individual frameworks that we have modeled `Cryptography::CryptographicOperation` for.
|
||||
@@ -0,0 +1,13 @@
|
||||
# snippet from python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py
|
||||
from Cryptodome.Cipher import ARC4
|
||||
|
||||
import os
|
||||
|
||||
key = os.urandom(256//8)
|
||||
|
||||
secret_message = b"secret message"
|
||||
|
||||
cipher = ARC4.new(key)
|
||||
encrypted = cipher.encrypt(secret_message) # NOT OK
|
||||
|
||||
print(secret_message, encrypted)
|
||||
@@ -0,0 +1,16 @@
|
||||
# snippet from python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py
|
||||
from cryptography.hazmat.primitives.ciphers import algorithms, Cipher
|
||||
import os
|
||||
|
||||
key = os.urandom(256//8)
|
||||
|
||||
algorithm = algorithms.ARC4(key)
|
||||
cipher = Cipher(algorithm, mode=None)
|
||||
|
||||
secret_message = b"secret message"
|
||||
|
||||
encryptor = cipher.encryptor()
|
||||
encrypted = encryptor.update(secret_message) # NOT OK
|
||||
encrypted += encryptor.finalize()
|
||||
|
||||
print(secret_message, encrypted)
|
||||
@@ -0,0 +1,3 @@
|
||||
Note that the tests in this directory are very shallow, and simply show that the query is able to produce alerts.
|
||||
|
||||
More in-depth tests can be found for the individual frameworks that we have modeled `Cryptography::CryptographicOperation` for.
|
||||
@@ -0,0 +1,27 @@
|
||||
edges
|
||||
| test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous |
|
||||
| test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous |
|
||||
| test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous |
|
||||
| test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous |
|
||||
| test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous |
|
||||
| test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous |
|
||||
nodes
|
||||
| test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | semmle.label | ControlFlowNode for get_certificate() |
|
||||
| test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous |
|
||||
| test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
|
||||
| test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous |
|
||||
| test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
|
||||
| test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous |
|
||||
| test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | semmle.label | ControlFlowNode for get_certificate() |
|
||||
| test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous |
|
||||
| test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
|
||||
| test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous |
|
||||
| test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
|
||||
| test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous |
|
||||
#select
|
||||
| test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | Sensitive data (certificate) |
|
||||
| test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | Sensitive data (password) |
|
||||
| test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | Sensitive data (password) |
|
||||
| test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | Sensitive data (certificate) |
|
||||
| test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | Sensitive data (password) |
|
||||
| test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | Sensitive data (password) |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-327/WeakSensitiveDataHashing.ql
|
||||
@@ -0,0 +1,25 @@
|
||||
from Cryptodome.Hash import MD5, SHA256
|
||||
from my_module import get_password, get_certificate
|
||||
|
||||
|
||||
def get_badly_hashed_certificate():
|
||||
dangerous = get_certificate()
|
||||
hasher = MD5.new()
|
||||
hasher.update(dangerous) # NOT OK
|
||||
return hasher.hexdigest()
|
||||
|
||||
|
||||
def get_badly_hashed_password():
|
||||
dangerous = get_password()
|
||||
hasher = MD5.new()
|
||||
hasher.update(dangerous) # NOT OK
|
||||
return hasher.hexdigest()
|
||||
|
||||
|
||||
def get_badly_hashed_password2():
|
||||
dangerous = get_password()
|
||||
# Although SHA-256 is a strong cryptographic hash functions,
|
||||
# it is not suitable for password hashing.
|
||||
hasher = SHA256.new()
|
||||
hasher.update(dangerous) # NOT OK
|
||||
return hasher.hexdigest()
|
||||
@@ -0,0 +1,29 @@
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from binascii import hexlify
|
||||
from my_module import get_password, get_certificate
|
||||
|
||||
|
||||
def get_badly_hashed_certificate():
|
||||
dangerous = get_certificate()
|
||||
hasher = hashes.Hash(hashes.MD5())
|
||||
hasher.update(dangerous) # NOT OK
|
||||
digest = hasher.finalize()
|
||||
return hexlify(digest).decode("utf-8")
|
||||
|
||||
|
||||
def get_badly_hashed_password():
|
||||
dangerous = get_password()
|
||||
hasher = hashes.Hash(hashes.MD5())
|
||||
hasher.update(dangerous) # NOT OK
|
||||
digest = hasher.finalize()
|
||||
return hexlify(digest).decode("utf-8")
|
||||
|
||||
|
||||
def get_badly_hashed_password2():
|
||||
dangerous = get_password()
|
||||
# Although SHA-256 is a strong cryptographic hash functions,
|
||||
# it is not suitable for password hashing.
|
||||
hasher = hashes.Hash(hashes.SHA256())
|
||||
hasher.update(dangerous) # NOT OK
|
||||
digest = hasher.finalize()
|
||||
return hexlify(digest).decode("utf-8")
|
||||
@@ -1,8 +0,0 @@
|
||||
edges
|
||||
| test_cryptography.py:5:17:5:30 | a password | test_cryptography.py:8:29:8:37 | a password |
|
||||
| test_cryptography.py:5:17:5:30 | a password | test_cryptography.py:8:29:8:37 | a password |
|
||||
| test_pycrypto.py:5:17:5:30 | a password | test_pycrypto.py:7:27:7:35 | a password |
|
||||
| test_pycrypto.py:5:17:5:30 | a password | test_pycrypto.py:7:27:7:35 | a password |
|
||||
#select
|
||||
| test_cryptography.py:8:29:8:37 | dangerous | test_cryptography.py:5:17:5:30 | a password | test_cryptography.py:8:29:8:37 | a password | $@ is used in a broken or weak cryptographic algorithm. | test_cryptography.py:5:17:5:30 | get_password() | Sensitive data |
|
||||
| test_pycrypto.py:7:27:7:35 | dangerous | test_pycrypto.py:5:17:5:30 | a password | test_pycrypto.py:7:27:7:35 | a password | $@ is used in a broken or weak cryptographic algorithm. | test_pycrypto.py:5:17:5:30 | get_password() | Sensitive data |
|
||||
@@ -1,12 +0,0 @@
|
||||
| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:6:14:6:27 | test_pycrypto.py:6 | test_pycrypto.py:6:14:6:27 | Attribute() | |
|
||||
| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:7:12:7:17 | test_pycrypto.py:7 | test_pycrypto.py:7:12:7:17 | cipher | |
|
||||
| Taint cryptography.Cipher.RC4 | test_cryptography.py:6:14:6:47 | test_cryptography.py:6 | test_cryptography.py:6:14:6:47 | Cipher() | |
|
||||
| Taint cryptography.Cipher.RC4 | test_cryptography.py:7:17:7:22 | test_cryptography.py:7 | test_cryptography.py:7:17:7:22 | cipher | |
|
||||
| Taint cryptography.encryptor.RC4 | test_cryptography.py:7:17:7:34 | test_cryptography.py:7 | test_cryptography.py:7:17:7:34 | Attribute() | |
|
||||
| Taint cryptography.encryptor.RC4 | test_cryptography.py:8:12:8:20 | test_cryptography.py:8 | test_cryptography.py:8:12:8:20 | encryptor | |
|
||||
| Taint cryptography.encryptor.RC4 | test_cryptography.py:8:42:8:50 | test_cryptography.py:8 | test_cryptography.py:8:42:8:50 | encryptor | |
|
||||
| Taint sensitive.data.password | test_cryptography.py:5:17:5:30 | test_cryptography.py:5 | test_cryptography.py:5:17:5:30 | get_password() | |
|
||||
| Taint sensitive.data.password | test_cryptography.py:8:29:8:37 | test_cryptography.py:8 | test_cryptography.py:8:29:8:37 | dangerous | |
|
||||
| Taint sensitive.data.password | test_cryptography.py:8:42:8:50 | test_cryptography.py:8 | test_cryptography.py:8:42:8:50 | encryptor | |
|
||||
| Taint sensitive.data.password | test_pycrypto.py:5:17:5:30 | test_pycrypto.py:5 | test_pycrypto.py:5:17:5:30 | get_password() | |
|
||||
| Taint sensitive.data.password | test_pycrypto.py:7:27:7:35 | test_pycrypto.py:7 | test_pycrypto.py:7:27:7:35 | dangerous | |
|
||||
@@ -1,9 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import python
|
||||
import semmle.python.security.SensitiveData
|
||||
import semmle.python.security.Crypto
|
||||
|
||||
from TaintedNode n, AstNode src
|
||||
where src = n.getAstNode() and src.getLocation().getFile().getAbsolutePath().matches("%test%")
|
||||
select "Taint " + n.getTaintKind(), n.getLocation(), src, n.getContext()
|
||||
@@ -1,9 +0,0 @@
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
|
||||
from secrets_store import get_password
|
||||
|
||||
def get_badly_encrypted_password():
|
||||
dangerous = get_password()
|
||||
cipher = Cipher(algorithms.ARC4(key), _, _)
|
||||
encryptor = cipher.encryptor()
|
||||
return encryptor.update(dangerous) + encryptor.finalize()
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
from Crypto.Cipher import ARC4
|
||||
from secrets_store import get_password
|
||||
|
||||
def get_badly_encrypted_password():
|
||||
dangerous = get_password()
|
||||
cipher = ARC4.new(_, _)
|
||||
return cipher.encrypt(dangerous)
|
||||
|
||||
Reference in New Issue
Block a user