2019-05-24 14:40:10 +02:00
|
|
|
from cryptography.exceptions import InvalidSignature
|
|
|
|
from cryptography.hazmat.backends import default_backend
|
|
|
|
from cryptography.hazmat.primitives import hashes
|
|
|
|
from cryptography.hazmat.primitives.asymmetric import rsa, padding
|
|
|
|
from cryptography.hazmat.primitives.serialization import load_der_private_key, load_der_public_key, \
|
|
|
|
Encoding, PrivateFormat, PublicFormat, NoEncryption
|
|
|
|
|
2019-06-24 12:05:12 -06:00
|
|
|
from allmydata.crypto.error import BadSignature
|
2019-05-24 14:40:10 +02:00
|
|
|
|
|
|
|
|
2019-06-24 12:27:09 -06:00
|
|
|
# This is the value that was used by `pycryptopp`, and we must continue to use it for
|
|
|
|
# both backwards compatibility and interoperability.
|
|
|
|
#
|
|
|
|
# The docs for `cryptography` suggest to use the constant defined at
|
|
|
|
# `cryptography.hazmat.primitives.asymmetric.padding.PSS.MAX_LENGTH`, but this causes old
|
|
|
|
# signatures to fail to validate.
|
2019-06-12 15:44:35 -06:00
|
|
|
RSA_PSS_SALT_LENGTH = 32
|
|
|
|
|
|
|
|
|
|
|
|
def create_signing_keypair(key_size):
|
|
|
|
"""
|
2019-06-17 19:16:39 -06:00
|
|
|
Create a new RSA signing (private) keypair from scratch. Can be used with
|
2019-06-12 15:44:35 -06:00
|
|
|
`sign_data` function.
|
|
|
|
|
|
|
|
:param int key_size: length of key in bits
|
|
|
|
|
|
|
|
:returns: 2-tuple of (private_key, public_key)
|
|
|
|
"""
|
2019-06-23 00:28:41 -06:00
|
|
|
# Tahoe's original use of pycryptopp would use cryptopp's default
|
|
|
|
# public_exponent, which is 17
|
|
|
|
#
|
|
|
|
# Thus, we are using 17 here as well. However, there are other
|
|
|
|
# choices; see this for more discussion:
|
|
|
|
# https://security.stackexchange.com/questions/2335/should-rsa-public-exponent-be-only-in-3-5-17-257-or-65537-due-to-security-c
|
|
|
|
#
|
|
|
|
# Another popular choice is 65537. See:
|
2019-06-17 22:16:50 -06:00
|
|
|
# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key
|
2019-06-23 11:25:59 -06:00
|
|
|
# https://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html
|
2019-06-12 15:44:35 -06:00
|
|
|
priv_key = rsa.generate_private_key(
|
2019-06-23 00:28:41 -06:00
|
|
|
public_exponent=17,
|
2019-06-12 15:44:35 -06:00
|
|
|
key_size=key_size,
|
|
|
|
backend=default_backend()
|
|
|
|
)
|
|
|
|
return priv_key, priv_key.public_key()
|
|
|
|
|
|
|
|
|
|
|
|
def create_signing_keypair_from_string(private_key_der):
|
|
|
|
"""
|
2019-06-17 19:16:39 -06:00
|
|
|
Create an RSA signing (private) key from a previously serialized
|
|
|
|
private key.
|
2019-06-12 15:44:35 -06:00
|
|
|
|
|
|
|
:returns: 2-tuple of (private_key, public_key)
|
|
|
|
"""
|
|
|
|
priv_key = load_der_private_key(
|
|
|
|
private_key_der,
|
|
|
|
password=None,
|
|
|
|
backend=default_backend(),
|
|
|
|
)
|
|
|
|
return priv_key, priv_key.public_key()
|
|
|
|
|
|
|
|
|
|
|
|
def der_string_from_signing_key(private_key):
|
|
|
|
"""
|
|
|
|
Serializes a given RSA private key to a DER string
|
|
|
|
"""
|
|
|
|
_validate_private_key(private_key)
|
|
|
|
return private_key.private_bytes(
|
|
|
|
encoding=Encoding.DER,
|
|
|
|
format=PrivateFormat.PKCS8,
|
|
|
|
encryption_algorithm=NoEncryption(),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def der_string_from_verifying_key(public_key):
|
|
|
|
"""
|
2019-06-17 19:16:39 -06:00
|
|
|
Serializes a given RSA public key to a DER string
|
2019-06-12 15:44:35 -06:00
|
|
|
"""
|
|
|
|
_validate_public_key(public_key)
|
|
|
|
return public_key.public_bytes(
|
|
|
|
encoding=Encoding.DER,
|
|
|
|
format=PublicFormat.SubjectPublicKeyInfo,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def create_verifying_key_from_string(public_key_der):
|
|
|
|
"""
|
2019-06-12 16:02:18 -06:00
|
|
|
Create an RSA verifying key from a previously serialized public key
|
2019-06-12 15:44:35 -06:00
|
|
|
"""
|
|
|
|
pub_key = load_der_public_key(
|
|
|
|
public_key_der,
|
|
|
|
backend=default_backend(),
|
|
|
|
)
|
|
|
|
return pub_key
|
|
|
|
|
|
|
|
|
|
|
|
def sign_data(private_key, data):
|
|
|
|
"""
|
|
|
|
:param private_key: the private part of a keypair returned from
|
|
|
|
`create_signing_keypair_from_string` or `create_signing_keypair`
|
|
|
|
|
|
|
|
:param bytes data: the bytes to sign
|
|
|
|
|
|
|
|
:returns: bytes which are a signature of the bytes given as `data`.
|
|
|
|
"""
|
|
|
|
_validate_private_key(private_key)
|
|
|
|
return private_key.sign(
|
|
|
|
data,
|
|
|
|
padding.PSS(
|
|
|
|
mgf=padding.MGF1(hashes.SHA256()),
|
|
|
|
salt_length=RSA_PSS_SALT_LENGTH,
|
|
|
|
),
|
|
|
|
hashes.SHA256(),
|
|
|
|
)
|
|
|
|
|
|
|
|
def verify_signature(public_key, alleged_signature, data):
|
|
|
|
"""
|
|
|
|
:param public_key: a verifying key, returned from `create_verifying_key_from_string` or `create_verifying_key_from_private_key`
|
|
|
|
|
|
|
|
:param bytes alleged_signature: the bytes of the alleged signature
|
|
|
|
|
|
|
|
:param bytes data: the data which was allegedly signed
|
|
|
|
"""
|
2019-06-17 21:56:06 -06:00
|
|
|
_validate_public_key(public_key)
|
2019-06-12 15:44:35 -06:00
|
|
|
try:
|
|
|
|
public_key.verify(
|
|
|
|
alleged_signature,
|
2019-05-24 14:40:10 +02:00
|
|
|
data,
|
|
|
|
padding.PSS(
|
|
|
|
mgf=padding.MGF1(hashes.SHA256()),
|
2019-06-12 15:44:35 -06:00
|
|
|
salt_length=RSA_PSS_SALT_LENGTH,
|
2019-05-24 14:40:10 +02:00
|
|
|
),
|
|
|
|
hashes.SHA256(),
|
|
|
|
)
|
2019-06-12 15:44:35 -06:00
|
|
|
except InvalidSignature:
|
|
|
|
raise BadSignature
|
|
|
|
|
|
|
|
|
|
|
|
def _validate_public_key(public_key):
|
|
|
|
"""
|
|
|
|
Internal helper. Checks that `public_key` is a valid cryptography
|
|
|
|
object
|
|
|
|
"""
|
|
|
|
if not isinstance(public_key, rsa.RSAPublicKey):
|
|
|
|
raise ValueError(
|
2019-06-17 21:56:06 -06:00
|
|
|
"public_key must be an RSAPublicKey"
|
2019-05-24 14:40:10 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2019-06-12 15:44:35 -06:00
|
|
|
def _validate_private_key(private_key):
|
|
|
|
"""
|
|
|
|
Internal helper. Checks that `public_key` is a valid cryptography
|
|
|
|
object
|
|
|
|
"""
|
|
|
|
if not isinstance(private_key, rsa.RSAPrivateKey):
|
|
|
|
raise ValueError(
|
2019-06-17 21:56:06 -06:00
|
|
|
"private_key must be an RSAPrivateKey"
|
2019-06-12 15:44:35 -06:00
|
|
|
)
|