Python: Model rsa

This commit is contained in:
Rasmus Wriedt Larsen
2021-06-11 11:23:06 +02:00
parent 40714c05b7
commit 6f29b01abc
5 changed files with 160 additions and 15 deletions

View File

@@ -16,6 +16,7 @@ private import semmle.python.frameworks.MysqlConnectorPython
private import semmle.python.frameworks.MySQLdb
private import semmle.python.frameworks.Psycopg2
private import semmle.python.frameworks.PyMySQL
private import semmle.python.frameworks.Rsa
private import semmle.python.frameworks.Simplejson
private import semmle.python.frameworks.Stdlib
private import semmle.python.frameworks.Tornado

View File

@@ -0,0 +1,141 @@
/**
* Provides classes modeling security-relevant aspects of the `rsa` PyPI package.
* See https://stuvel.eu/python-rsa-doc/.
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
/**
* Provides models for the `rsa` PyPI package.
* See https://stuvel.eu/python-rsa-doc/.
*/
private module Rsa {
/**
* A call to `rsa.newkeys`
*
* See https://stuvel.eu/python-rsa-doc/reference.html#rsa.newkeys
*/
class RsaNewkeysCall extends Cryptography::PublicKey::KeyGeneration::RsaRange,
DataFlow::CallCfgNode {
RsaNewkeysCall() { this = API::moduleImport("rsa").getMember("newkeys").getACall() }
override DataFlow::Node getKeySizeArg() {
result in [this.getArg(0), this.getArgByName("nbits")]
}
}
/**
* A call to `rsa.encrypt`
*
* See https://stuvel.eu/python-rsa-doc/reference.html#rsa.encrypt
*/
class RsaEncryptCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
RsaEncryptCall() { this = API::moduleImport("rsa").getMember("encrypt").getACall() }
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
override DataFlow::Node getAnInput() {
result in [this.getArg(0), this.getArgByName("message")]
}
}
/**
* A call to `rsa.decrypt`
*
* See https://stuvel.eu/python-rsa-doc/reference.html#rsa.decrypt
*/
class RsaDecryptCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
RsaDecryptCall() { this = API::moduleImport("rsa").getMember("decrypt").getACall() }
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("crypto")] }
}
/**
* A call to `rsa.sign`, which both hashes and signs in the input message.
*
* See https://stuvel.eu/python-rsa-doc/reference.html#rsa.sign
*/
class RsaSignCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
RsaSignCall() { this = API::moduleImport("rsa").getMember("sign").getACall() }
override Cryptography::CryptographicAlgorithm getAlgorithm() {
// signature part
result.getName() = "RSA"
or
// hashing part
exists(StrConst str, DataFlow::Node hashNameArg |
hashNameArg in [this.getArg(2), this.getArgByName("hash_method")] and
DataFlow::exprNode(str).(DataFlow::LocalSourceNode).flowsTo(hashNameArg) and
result.matchesName(str.getText())
)
}
override DataFlow::Node getAnInput() {
result in [this.getArg(0), this.getArgByName("message")]
}
}
/**
* A call to `rsa.verify`
*
* See https://stuvel.eu/python-rsa-doc/reference.html#rsa.verify
*/
class RsaVerifyCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
RsaVerifyCall() { this = API::moduleImport("rsa").getMember("verify").getACall() }
override Cryptography::CryptographicAlgorithm getAlgorithm() {
// note that technically there is also a hashing operation going on but we don't
// know what algorithm is used up front, since it is encoded in the signature
result.getName() = "RSA"
}
override DataFlow::Node getAnInput() {
result in [this.getArg(0), this.getArgByName("message")]
or
result in [this.getArg(1), this.getArgByName("signature")]
}
}
/**
* A call to `rsa.compute_hash`
*
* See https://stuvel.eu/python-rsa-doc/reference.html#rsa.compute_hash
*/
class RsaComputeHashCall extends Cryptography::CryptographicOperation::Range,
DataFlow::CallCfgNode {
RsaComputeHashCall() { this = API::moduleImport("rsa").getMember("compute_hash").getACall() }
override Cryptography::CryptographicAlgorithm getAlgorithm() {
exists(StrConst str, DataFlow::Node hashNameArg |
hashNameArg in [this.getArg(1), this.getArgByName("method_name")] and
DataFlow::exprNode(str).(DataFlow::LocalSourceNode).flowsTo(hashNameArg) and
result.matchesName(str.getText())
)
}
override DataFlow::Node getAnInput() {
result in [this.getArg(0), this.getArgByName("message")]
}
}
/**
* A call to `rsa.sign_hash`
*
* See https://stuvel.eu/python-rsa-doc/reference.html#rsa.sign_hash
*/
class RsaSignHashCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
RsaSignHashCall() { this = API::moduleImport("rsa").getMember("sign_hash").getACall() }
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
override DataFlow::Node getAnInput() {
result in [this.getArg(0), this.getArgByName("hash_value")]
}
}
}

View File

@@ -2,8 +2,8 @@
import rsa
# using a rather low keysize, since otherwise it takes quite long to run.
(public_key, private_key) = rsa.newkeys(512) # $ MISSING: PublicKeyGeneration keySize=2048
(public_key, private_key) = rsa.newkeys(nbits=512) # $ MISSING: PublicKeyGeneration keySize=2048
(public_key, private_key) = rsa.newkeys(512) # $ PublicKeyGeneration keySize=512
(public_key, private_key) = rsa.newkeys(nbits=512) # $ PublicKeyGeneration keySize=512
# ------------------------------------------------------------------------------
@@ -16,15 +16,15 @@ print("encrypt/decrypt")
secret_message = b"secret message"
encrypted = rsa.encrypt(secret_message, public_key) # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=secret_message
encrypted = rsa.encrypt(message=secret_message, pub_key=public_key) # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=secret_message
encrypted = rsa.encrypt(secret_message, public_key) # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=secret_message
encrypted = rsa.encrypt(message=secret_message, pub_key=public_key) # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=secret_message
print("encrypted={}".format(encrypted))
print()
decrypted = rsa.decrypt(encrypted, private_key) # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=encrypted
decrypted = rsa.decrypt(crypto=encrypted, priv_key=private_key) # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=encrypted
decrypted = rsa.decrypt(encrypted, private_key) # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=encrypted
decrypted = rsa.decrypt(crypto=encrypted, priv_key=private_key) # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=encrypted
print("decrypted={}".format(decrypted))
assert decrypted == secret_message
@@ -42,13 +42,13 @@ print("sign/verify")
message = b"message"
other_message = b"other message"
hash = rsa.compute_hash(message, "SHA-256") # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
hash = rsa.compute_hash(message=message, method_name="SHA-256") # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
signature_from_hash = rsa.sign_hash(hash, private_key, "SHA-256") # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=hash
signature_from_hash = rsa.sign_hash(hash_value=hash, priv_key=private_key, hash_method="SHA-256") # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=hash
hash = rsa.compute_hash(message, "SHA-256") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
hash = rsa.compute_hash(message=message, method_name="SHA-256") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
signature_from_hash = rsa.sign_hash(hash, private_key, "SHA-256") # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=hash
signature_from_hash = rsa.sign_hash(hash_value=hash, priv_key=private_key, hash_method="SHA-256") # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=hash
signature = rsa.sign(message, private_key, "SHA-256") # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
signature = rsa.sign(message=message, priv_key=private_key, hash_method="SHA-256") # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
signature = rsa.sign(message, private_key, "SHA-256") # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
signature = rsa.sign(message=message, priv_key=private_key, hash_method="SHA-256") # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message
assert signature == signature_from_hash
@@ -56,13 +56,13 @@ print("signature={}".format(signature))
print()
rsa.verify(message, signature, public_key) # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=message CryptographicOperationInput=signature
rsa.verify(message=message, signature=signature, pub_key=public_key) # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=message CryptographicOperationInput=signature
rsa.verify(message, signature, public_key) # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=message CryptographicOperationInput=signature
rsa.verify(message=message, signature=signature, pub_key=public_key) # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=message CryptographicOperationInput=signature
print("Signature verified (as expected)")
try:
rsa.verify(other_message, signature, public_key) # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=other_message CryptographicOperationInput=signature
rsa.verify(other_message, signature, public_key) # $ CryptographicOperation CryptographicOperationAlgorithm=RSA CryptographicOperationInput=other_message CryptographicOperationInput=signature
raise Exception("Signature verified (unexpected)")
except rsa.VerificationError:
print("Signature mismatch (as expected)")