Merge pull request #5075 from RasmusWL/crypto

Python: Port py/weak-crypto-key to use type-tracking
This commit is contained in:
yoff
2021-03-18 20:53:28 +01:00
committed by GitHub
32 changed files with 998 additions and 45 deletions

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -0,0 +1,41 @@
# DSA is a public-key algorithm for signing messages.
# Following example at https://pycryptodome.readthedocs.io/en/latest/src/signature/dsa.html
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from Crypto.Hash import SHA256
private_key = DSA.generate(2048) # $ PublicKeyGeneration keySize=2048
public_key = private_key.publickey()
# ------------------------------------------------------------------------------
# sign/verify
# ------------------------------------------------------------------------------
print("sign/verify")
message = b"message"
signer = DSS.new(private_key, mode='fips-186-3')
hasher = SHA256.new(message)
signature = signer.sign(hasher)
print("signature={}".format(signature))
print()
verifier = DSS.new(public_key, mode='fips-186-3')
hasher = SHA256.new(message)
verifier.verify(hasher, signature)
print("Signature verified (as expected)")
try:
hasher = SHA256.new(b"other message")
verifier.verify(hasher, signature)
raise Exception("Signature verified (unexpected)")
except ValueError:
print("Signature mismatch (as expected)")

View File

@@ -0,0 +1,38 @@
from Crypto.PublicKey import ECC
from Crypto.Signature import DSS
from Crypto.Hash import SHA256
private_key = ECC.generate(curve="P-256") # $ PublicKeyGeneration keySize=256
public_key = private_key.public_key()
# ------------------------------------------------------------------------------
# sign/verify
# ------------------------------------------------------------------------------
print("sign/verify")
message = b"message"
signer = DSS.new(private_key, mode='fips-186-3')
hasher = SHA256.new(message)
signature = signer.sign(hasher)
print("signature={}".format(signature))
print()
verifier = DSS.new(public_key, mode='fips-186-3')
hasher = SHA256.new(message)
verifier.verify(hasher, signature)
print("Signature verified (as expected)")
try:
hasher = SHA256.new(b"other message")
verifier.verify(hasher, signature)
raise Exception("Signature verified (unexpected)")
except ValueError:
print("Signature mismatch (as expected)")

View File

@@ -0,0 +1,73 @@
# RSA is a public-key algorithm for encrypting and signing messages.
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import pss
from Crypto.Hash import SHA256
private_key = RSA.generate(2048) # $ PublicKeyGeneration keySize=2048
# These 2 methods do the same
public_key = private_key.publickey()
public_key = private_key.public_key()
# ------------------------------------------------------------------------------
# encrypt/decrypt
# ------------------------------------------------------------------------------
print("encrypt/decrypt")
secret_message = b"secret message"
# Following example at https://pycryptodome.readthedocs.io/en/latest/src/examples.html#encrypt-data-with-rsa
encrypt_cipher = PKCS1_OAEP.new(public_key)
encrypted = encrypt_cipher.encrypt(secret_message)
print("encrypted={}".format(encrypted))
print()
decrypt_cipher = PKCS1_OAEP.new(private_key)
decrypted = decrypt_cipher.decrypt(
encrypted,
)
print("decrypted={}".format(decrypted))
assert decrypted == secret_message
print("\n---\n")
# ------------------------------------------------------------------------------
# sign/verify
# ------------------------------------------------------------------------------
print("sign/verify")
message = b"message"
signer = pss.new(private_key)
hasher = SHA256.new(message)
signature = signer.sign(hasher)
print("signature={}".format(signature))
print()
verifier = pss.new(public_key)
hasher = SHA256.new(message)
verifier.verify(hasher, signature)
print("Signature verified (as expected)")
try:
verifier = pss.new(public_key)
hasher = SHA256.new(b"other message")
verifier.verify(hasher, signature)
raise Exception("Signature verified (unexpected)")
except ValueError:
print("Signature mismatch (as expected)")

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -0,0 +1,41 @@
# DSA is a public-key algorithm for signing messages.
# Following example at https://pycryptodome.readthedocs.io/en/latest/src/signature/dsa.html
from Cryptodome.PublicKey import DSA
from Cryptodome.Signature import DSS
from Cryptodome.Hash import SHA256
private_key = DSA.generate(2048) # $ PublicKeyGeneration keySize=2048
public_key = private_key.publickey()
# ------------------------------------------------------------------------------
# sign/verify
# ------------------------------------------------------------------------------
print("sign/verify")
message = b"message"
signer = DSS.new(private_key, mode='fips-186-3')
hasher = SHA256.new(message)
signature = signer.sign(hasher)
print("signature={}".format(signature))
print()
verifier = DSS.new(public_key, mode='fips-186-3')
hasher = SHA256.new(message)
verifier.verify(hasher, signature)
print("Signature verified (as expected)")
try:
hasher = SHA256.new(b"other message")
verifier.verify(hasher, signature)
raise Exception("Signature verified (unexpected)")
except ValueError:
print("Signature mismatch (as expected)")

View File

@@ -0,0 +1,38 @@
from Cryptodome.PublicKey import ECC
from Cryptodome.Signature import DSS
from Cryptodome.Hash import SHA256
private_key = ECC.generate(curve="P-256") # $ PublicKeyGeneration keySize=256
public_key = private_key.public_key()
# ------------------------------------------------------------------------------
# sign/verify
# ------------------------------------------------------------------------------
print("sign/verify")
message = b"message"
signer = DSS.new(private_key, mode='fips-186-3')
hasher = SHA256.new(message)
signature = signer.sign(hasher)
print("signature={}".format(signature))
print()
verifier = DSS.new(public_key, mode='fips-186-3')
hasher = SHA256.new(message)
verifier.verify(hasher, signature)
print("Signature verified (as expected)")
try:
hasher = SHA256.new(b"other message")
verifier.verify(hasher, signature)
raise Exception("Signature verified (unexpected)")
except ValueError:
print("Signature mismatch (as expected)")

View File

@@ -0,0 +1,73 @@
# RSA is a public-key algorithm for encrypting and signing messages.
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_OAEP
from Cryptodome.Signature import pss
from Cryptodome.Hash import SHA256
private_key = RSA.generate(2048) # $ PublicKeyGeneration keySize=2048
# These 2 methods do the same
public_key = private_key.publickey()
public_key = private_key.public_key()
# ------------------------------------------------------------------------------
# encrypt/decrypt
# ------------------------------------------------------------------------------
print("encrypt/decrypt")
secret_message = b"secret message"
# Following example at https://pycryptodome.readthedocs.io/en/latest/src/examples.html#encrypt-data-with-rsa
encrypt_cipher = PKCS1_OAEP.new(public_key)
encrypted = encrypt_cipher.encrypt(secret_message)
print("encrypted={}".format(encrypted))
print()
decrypt_cipher = PKCS1_OAEP.new(private_key)
decrypted = decrypt_cipher.decrypt(
encrypted,
)
print("decrypted={}".format(decrypted))
assert decrypted == secret_message
print("\n---\n")
# ------------------------------------------------------------------------------
# sign/verify
# ------------------------------------------------------------------------------
print("sign/verify")
message = b"message"
signer = pss.new(private_key)
hasher = SHA256.new(message)
signature = signer.sign(hasher)
print("signature={}".format(signature))
print()
verifier = pss.new(public_key)
hasher = SHA256.new(message)
verifier.verify(hasher, signature)
print("Signature verified (as expected)")
try:
verifier = pss.new(public_key)
hasher = SHA256.new(b"other message")
verifier.verify(hasher, signature)
raise Exception("Signature verified (unexpected)")
except ValueError:
print("Signature mismatch (as expected)")

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -0,0 +1,37 @@
# DSA is a public-key algorithm for signing messages.
# see https://cryptography.io/en/latest/hazmat/primitives/asymmetric/dsa.html
from cryptography.hazmat.primitives.asymmetric import dsa
from cryptography.hazmat.primitives import hashes
from cryptography.exceptions import InvalidSignature
HASH_ALGORITHM = hashes.SHA256()
private_key = dsa.generate_private_key(key_size=2048) # $ PublicKeyGeneration keySize=2048
public_key = private_key.public_key()
message = b"message"
# Following example at https://cryptography.io/en/latest/hazmat/primitives/asymmetric/dsa.html#signing
signature = private_key.sign(
message,
algorithm=HASH_ALGORITHM,
)
print("signature={}".format(signature))
print()
public_key.verify(
signature, message, algorithm=HASH_ALGORITHM
)
print("Signature verified (as expected)")
try:
public_key.verify(
signature, b"other message", algorithm=HASH_ALGORITHM
)
raise Exception("Signature verified (unexpected)")
except InvalidSignature:
print("Signature mismatch (as expected)")

View File

@@ -0,0 +1,43 @@
# see https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa.html
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes
from cryptography.exceptions import InvalidSignature
private_key = ec.generate_private_key(curve=ec.SECP384R1()) # $ PublicKeyGeneration keySize=384
public_key = private_key.public_key()
HASH_ALGORITHM = hashes.SHA256()
# ------------------------------------------------------------------------------
# sign/verify
# ------------------------------------------------------------------------------
print("sign/verify")
SIGNATURE_ALGORITHM = ec.ECDSA(HASH_ALGORITHM)
message = b"message"
signature = private_key.sign(
message,
signature_algorithm=SIGNATURE_ALGORITHM,
)
print("signature={}".format(signature))
print()
public_key.verify(
signature, message, signature_algorithm=SIGNATURE_ALGORITHM
)
print("Signature verified (as expected)")
try:
public_key.verify(
signature, b"other message", signature_algorithm=SIGNATURE_ALGORITHM
)
raise Exception("Signature verified (unexpected)")
except InvalidSignature:
print("Signature mismatch (as expected)")

View File

@@ -0,0 +1,80 @@
# RSA is a public-key algorithm for encrypting and signing messages.
# see https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa.html
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
from cryptography.exceptions import InvalidSignature
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) # $ PublicKeyGeneration keySize=2048
public_key = private_key.public_key()
HASH_ALGORITHM = hashes.SHA256()
# ------------------------------------------------------------------------------
# encrypt/decrypt
# ------------------------------------------------------------------------------
print("encrypt/decrypt")
ENCRYPT_PADDING = padding.OAEP(
mgf=padding.MGF1(algorithm=HASH_ALGORITHM),
algorithm=HASH_ALGORITHM,
label=None,
)
secret_message = b"secret message"
# Following example at https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa.html#encryption
encrypted = public_key.encrypt(secret_message, padding=ENCRYPT_PADDING)
print("encrypted={}".format(encrypted))
print()
decrypted = private_key.decrypt(
encrypted,
padding=ENCRYPT_PADDING
)
print("decrypted={}".format(decrypted))
assert decrypted == secret_message
print("\n---\n")
# ------------------------------------------------------------------------------
# sign/verify
# ------------------------------------------------------------------------------
print("sign/verify")
SIGN_PADDING = padding.PSS(
mgf=padding.MGF1(HASH_ALGORITHM),
salt_length=padding.PSS.MAX_LENGTH
)
message = b"message"
signature = private_key.sign(
message,
padding=SIGN_PADDING,
algorithm=HASH_ALGORITHM,
)
print("signature={}".format(signature))
print()
public_key.verify(
signature, message, padding=SIGN_PADDING, algorithm=HASH_ALGORITHM
)
print("Signature verified (as expected)")
try:
public_key.verify(
signature, b"other message", padding=SIGN_PADDING, algorithm=HASH_ALGORITHM
)
raise Exception("Signature verified (unexpected)")
except InvalidSignature:
print("Signature mismatch (as expected)")

View File

@@ -319,3 +319,25 @@ class SafeAccessCheckTest extends InlineExpectationsTest {
)
}
}
class PublicKeyGenerationTest extends InlineExpectationsTest {
PublicKeyGenerationTest() { this = "PublicKeyGenerationTest" }
override string getARelevantTag() { result in ["PublicKeyGeneration", "keySize"] }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
exists(Cryptography::PublicKey::KeyGeneration keyGen |
location = keyGen.getLocation() and
(
element = keyGen.toString() and
value = "" and
tag = "PublicKeyGeneration"
or
element = keyGen.toString() and
value = keyGen.getKeySizeWithOrigin(_).toString() and
tag = "keySize"
)
)
}
}

View File

@@ -1,8 +0,0 @@
| weak_crypto.py:67:1:67:30 | ControlFlowNode for dsa_gen_key() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:68:1:68:28 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:21:11:21:33 | ControlFlowNode for FakeWeakEllipticCurve() | 160 |
| weak_crypto.py:69:1:69:37 | ControlFlowNode for rsa_gen_key() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:71:1:71:39 | ControlFlowNode for dsa_gen_key() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:72:1:72:34 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:21:11:21:33 | ControlFlowNode for FakeWeakEllipticCurve() | 160 |
| weak_crypto.py:73:1:73:46 | ControlFlowNode for rsa_gen_key() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:75:1:75:22 | ControlFlowNode for Attribute() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:76:1:76:22 | ControlFlowNode for Attribute() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |

View File

@@ -1 +0,0 @@
Security/CWE-326/WeakCrypto.ql

View File

@@ -0,0 +1,9 @@
| weak_crypto.py:68:1:68:21 | ControlFlowNode for dsa_gen_key() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:16:12:16:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:69:1:69:19 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:22:11:22:24 | ControlFlowNode for Attribute() | 163 |
| weak_crypto.py:70:1:70:28 | ControlFlowNode for rsa_gen_key() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:72:1:72:30 | ControlFlowNode for dsa_gen_key() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:16:12:16:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:73:1:73:25 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:22:11:22:24 | ControlFlowNode for Attribute() | 163 |
| weak_crypto.py:74:1:74:37 | ControlFlowNode for rsa_gen_key() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:76:1:76:22 | ControlFlowNode for Attribute() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:16:12:16:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:77:1:77:22 | ControlFlowNode for Attribute() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |
| weak_crypto.py:84:12:84:29 | ControlFlowNode for Attribute() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 |

View File

@@ -0,0 +1 @@
Security/CWE-326/WeakCryptoKey.ql

View File

@@ -1 +0,0 @@
semmle-extractor-options: -p ../lib/ --max-import-depth=3

View File

@@ -0,0 +1,9 @@
from Cryptodome.PublicKey import RSA
from weak_crypto import only_used_by_test
def test_example():
# This is technically not ok, but since it's in a test, we don't want to alert on it
RSA.generate(1024)
only_used_by_test(1024)

View File

@@ -1,7 +1,7 @@
from cryptography.hazmat import backends
from cryptography.hazmat.primitives.asymmetric import ec, dsa, rsa
#Crypto and Cryptodome have same API
# Crypto and Cryptodome have same API
if random():
from Crypto.PublicKey import DSA
from Crypto.PublicKey import RSA
@@ -12,13 +12,14 @@ else:
RSA_WEAK = 1024
RSA_OK = 2048
RSA_STRONG = 3076
DSA_WEAK = 1024
DSA_OK = 2048
DSA_STRONG = 3076
BIG = 10000
class FakeWeakEllipticCurve:
name = "fake"
key_size = 160
EC_WEAK = FakeWeakEllipticCurve()
EC_WEAK = ec.SECT163K1() # has key size of 163
EC_OK = ec.SECP224R1()
EC_STRONG = ec.SECP384R1()
EC_BIG = ec.SECT571R1()
@@ -27,50 +28,68 @@ dsa_gen_key = dsa.generate_private_key
ec_gen_key = ec.generate_private_key
rsa_gen_key = rsa.generate_private_key
default = backends.default_backend()
#Strong and OK keys.
dsa_gen_key(key_size=RSA_OK, backend=default)
dsa_gen_key(key_size=RSA_STRONG, backend=default)
dsa_gen_key(key_size=BIG, backend=default)
ec_gen_key(curve=EC_OK, backend=default)
ec_gen_key(curve=EC_STRONG, backend=default)
ec_gen_key(curve=EC_BIG, backend=default)
rsa_gen_key(public_exponent=65537, key_size=RSA_OK, backend=default)
rsa_gen_key(public_exponent=65537, key_size=RSA_STRONG, backend=default)
rsa_gen_key(public_exponent=65537, key_size=BIG, backend=default)
# Strong and OK keys.
dsa_gen_key(key_size=DSA_OK)
dsa_gen_key(key_size=DSA_STRONG)
dsa_gen_key(key_size=BIG)
ec_gen_key(curve=EC_OK)
ec_gen_key(curve=EC_STRONG)
ec_gen_key(curve=EC_BIG)
rsa_gen_key(public_exponent=65537, key_size=RSA_OK)
rsa_gen_key(public_exponent=65537, key_size=RSA_STRONG)
rsa_gen_key(public_exponent=65537, key_size=BIG)
DSA.generate(bits=RSA_OK)
DSA.generate(bits=RSA_STRONG)
RSA.generate(bits=RSA_OK)
RSA.generate(bits=RSA_STRONG)
dsa_gen_key(RSA_OK, default)
dsa_gen_key(RSA_STRONG, default)
dsa_gen_key(BIG, default)
ec_gen_key(EC_OK, default)
ec_gen_key(EC_STRONG, default)
ec_gen_key(EC_BIG, default)
rsa_gen_key(65537, RSA_OK, default)
rsa_gen_key(65537, RSA_STRONG, default)
rsa_gen_key(65537, BIG, default)
dsa_gen_key(DSA_OK)
dsa_gen_key(DSA_STRONG)
dsa_gen_key(BIG)
ec_gen_key(EC_OK)
ec_gen_key(EC_STRONG)
ec_gen_key(EC_BIG)
rsa_gen_key(65537, RSA_OK)
rsa_gen_key(65537, RSA_STRONG)
rsa_gen_key(65537, BIG)
DSA.generate(RSA_OK)
DSA.generate(RSA_STRONG)
DSA.generate(DSA_OK)
DSA.generate(DSA_STRONG)
RSA.generate(RSA_OK)
RSA.generate(RSA_STRONG)
# Weak keys
dsa_gen_key(RSA_WEAK, default)
ec_gen_key(EC_WEAK, default)
rsa_gen_key(65537, RSA_WEAK, default)
dsa_gen_key(DSA_WEAK)
ec_gen_key(EC_WEAK)
rsa_gen_key(65537, RSA_WEAK)
dsa_gen_key(key_size=RSA_WEAK, default)
ec_gen_key(curve=EC_WEAK, default)
rsa_gen_key(65537, key_size=RSA_WEAK, default)
dsa_gen_key(key_size=DSA_WEAK)
ec_gen_key(curve=EC_WEAK)
rsa_gen_key(65537, key_size=RSA_WEAK)
DSA.generate(RSA_WEAK)
DSA.generate(DSA_WEAK)
RSA.generate(RSA_WEAK)
# ------------------------------------------------------------------------------
# Through function calls
def make_new_rsa_key_weak(bits):
return RSA.generate(bits) # NOT OK
make_new_rsa_key_weak(RSA_WEAK)
def make_new_rsa_key_strong(bits):
return RSA.generate(bits) # OK
make_new_rsa_key_strong(RSA_STRONG)
def only_used_by_test(bits):
# Although this call will technically not be ok, since it's only used in a test, we don't want to alert on it.
return RSA.generate(bits)