Updated DB_Helper by adding firebase methods.

This commit is contained in:
Batuhan Berk Başoğlu 2020-10-05 16:53:40 -04:00
parent 485cc3bbba
commit c82121d036
1810 changed files with 537281 additions and 1 deletions

View file

@ -0,0 +1,423 @@
#
# Signature/DSS.py : DSS.py
#
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
Digital Signature Standard (DSS), as specified in `FIPS PUB 186-3`__.
A sender signs a message in the following way:
>>> from Crypto.Hash import SHA256
>>> from Crypto.PublicKey import ECC
>>> from Crypto.Signature import DSS
>>>
>>> message = b'I give my permission to order #4355'
>>> key = ECC.import_key(open('privkey.der').read())
>>> h = SHA256.new(message)
>>> signer = DSS.new(key, 'fips-186-3')
>>> signature = signer.sign(h)
The receiver can verify authenticity of the message:
>>> key = ECC.import_key(open('pubkey.der').read())
>>> h = SHA256.new(received_message)
>>> verifier = DSS.new(key, 'fips-186-3')
>>> try:
>>> verifier.verify(h, signature):
>>> print "The message is authentic."
>>> except ValueError:
>>> print "The message is not authentic."
.. __: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
"""
__all__ = ['new', 'DssSigScheme']
from Crypto.Util.py3compat import bchr, b
from Crypto.Util.asn1 import DerSequence
from Crypto.Util.number import long_to_bytes
from Crypto.Math.Numbers import Integer
from Crypto.Hash import HMAC
from Crypto.PublicKey.ECC import _curve, EccKey
class DssSigScheme(object):
"""This signature scheme can perform DSS signature or verification.
:undocumented: __init__
"""
def __init__(self, key, encoding, order):
"""Create a new Digital Signature Standard (DSS) object.
Do not instantiate this object directly,
use `Crypto.Signature.DSS.new` instead.
"""
self._key = key
self._encoding = encoding
self._order = order
self._order_bits = self._order.size_in_bits()
self._order_bytes = (self._order_bits - 1) // 8 + 1
def can_sign(self):
"""Return True if this signature object can be used
for signing messages."""
return self._key.has_private()
def _compute_nonce(self, msg_hash):
raise NotImplementedError("To be provided by subclasses")
def _valid_hash(self, msg_hash):
raise NotImplementedError("To be provided by subclasses")
def sign(self, msg_hash):
"""Produce the DSS signature of a message.
:Parameters:
msg_hash : hash object
The hash that was carried out over the message.
The object belongs to the `Crypto.Hash` package.
Under mode *'fips-186-3'*, the hash must be a FIPS
approved secure hash (SHA-1 or a member of the SHA-2 family),
of cryptographic strength appropriate for the DSA key.
For instance, a 3072/256 DSA key can only be used
in combination with SHA-512.
:Return: The signature encoded as a byte string.
:Raise ValueError:
If the hash algorithm is incompatible to the DSA key.
:Raise TypeError:
If the DSA key has no private half.
"""
if not self._valid_hash(msg_hash):
raise ValueError("Hash is not sufficiently strong")
# Generate the nonce k (critical!)
nonce = self._compute_nonce(msg_hash)
# Perform signature using the raw API
z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes])
sig_pair = self._key._sign(z, nonce)
# Encode the signature into a single byte string
if self._encoding == 'binary':
output = b("").join([long_to_bytes(x, self._order_bytes)
for x in sig_pair])
else:
# Dss-sig ::= SEQUENCE {
# r OCTET STRING,
# s OCTET STRING
# }
output = DerSequence(sig_pair).encode()
return output
def verify(self, msg_hash, signature):
"""Verify that a certain DSS signature is authentic.
This function checks if the party holding the private half of the key
really signed the message.
:Parameters:
msg_hash : hash object
The hash that was carried out over the message.
This is an object belonging to the `Crypto.Hash` module.
Under mode *'fips-186-3'*, the hash must be a FIPS
approved secure hash (SHA-1 or a member of the SHA-2 family),
of cryptographic strength appropriate for the DSA key.
For instance, a 3072/256 DSA key can only be used in
combination with SHA-512.
signature : byte string
The signature that needs to be validated.
:Raise ValueError:
If the signature is not authentic.
"""
if not self._valid_hash(msg_hash):
raise ValueError("Hash does not belong to SHS")
if self._encoding == 'binary':
if len(signature) != (2 * self._order_bytes):
raise ValueError("The signature is not authentic (length)")
r_prime, s_prime = [Integer.from_bytes(x)
for x in (signature[:self._order_bytes],
signature[self._order_bytes:])]
else:
try:
der_seq = DerSequence().decode(signature)
except (ValueError, IndexError):
raise ValueError("The signature is not authentic (DER)")
if len(der_seq) != 2 or not der_seq.hasOnlyInts():
raise ValueError("The signature is not authentic (DER content)")
r_prime, s_prime = der_seq[0], der_seq[1]
if not (0 < r_prime < self._order) or not (0 < s_prime < self._order):
raise ValueError("The signature is not authentic (d)")
z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes])
result = self._key._verify(z, (r_prime, s_prime))
if not result:
raise ValueError("The signature is not authentic")
# Make PyCrypto code to fail
return False
class DeterministicDsaSigScheme(DssSigScheme):
# Also applicable to ECDSA
def __init__(self, key, encoding, order, private_key):
super(DeterministicDsaSigScheme, self).__init__(key, encoding, order)
self._private_key = private_key
def _bits2int(self, bstr):
"""See 2.3.2 in RFC6979"""
result = Integer.from_bytes(bstr)
q_len = self._order.size_in_bits()
b_len = len(bstr) * 8
if b_len > q_len:
result >>= (b_len - q_len)
return result
def _int2octets(self, int_mod_q):
"""See 2.3.3 in RFC6979"""
assert 0 < int_mod_q < self._order
return long_to_bytes(int_mod_q, self._order_bytes)
def _bits2octets(self, bstr):
"""See 2.3.4 in RFC6979"""
z1 = self._bits2int(bstr)
if z1 < self._order:
z2 = z1
else:
z2 = z1 - self._order
return self._int2octets(z2)
def _compute_nonce(self, mhash):
"""Generate k in a deterministic way"""
# See section 3.2 in RFC6979.txt
# Step a
h1 = mhash.digest()
# Step b
mask_v = bchr(1) * mhash.digest_size
# Step c
nonce_k = bchr(0) * mhash.digest_size
for int_oct in 0, 1:
# Step d/f
nonce_k = HMAC.new(nonce_k,
mask_v + bchr(int_oct) +
self._int2octets(self._private_key) +
self._bits2octets(h1), mhash).digest()
# Step e/g
mask_v = HMAC.new(nonce_k, mask_v, mhash).digest()
nonce = -1
while not (0 < nonce < self._order):
# Step h.C (second part)
if nonce != -1:
nonce_k = HMAC.new(nonce_k, mask_v + bchr(0),
mhash).digest()
mask_v = HMAC.new(nonce_k, mask_v, mhash).digest()
# Step h.A
mask_t = b("")
# Step h.B
while len(mask_t) < self._order_bytes:
mask_v = HMAC.new(nonce_k, mask_v, mhash).digest()
mask_t += mask_v
# Step h.C (first part)
nonce = self._bits2int(mask_t)
return nonce
def _valid_hash(self, msg_hash):
return True
class FipsDsaSigScheme(DssSigScheme):
#: List of L (bit length of p) and N (bit length of q) combinations
#: that are allowed by FIPS 186-3. The security level is provided in
#: Table 2 of FIPS 800-57 (rev3).
_fips_186_3_L_N = (
(1024, 160), # 80 bits (SHA-1 or stronger)
(2048, 224), # 112 bits (SHA-224 or stronger)
(2048, 256), # 128 bits (SHA-256 or stronger)
(3072, 256) # 256 bits (SHA-512)
)
def __init__(self, key, encoding, order, randfunc):
super(FipsDsaSigScheme, self).__init__(key, encoding, order)
self._randfunc = randfunc
L = Integer(key.p).size_in_bits()
if (L, self._order_bits) not in self._fips_186_3_L_N:
error = ("L/N (%d, %d) is not compliant to FIPS 186-3"
% (L, self._order_bits))
raise ValueError(error)
def _compute_nonce(self, msg_hash):
# hash is not used
return Integer.random_range(min_inclusive=1,
max_exclusive=self._order,
randfunc=self._randfunc)
def _valid_hash(self, msg_hash):
"""Verify that SHA-1, SHA-2 or SHA-3 are used"""
return (msg_hash.oid == "1.3.14.3.2.26" or
msg_hash.oid.startswith("2.16.840.1.101.3.4.2."))
class FipsEcDsaSigScheme(DssSigScheme):
def __init__(self, key, encoding, order, randfunc):
super(FipsEcDsaSigScheme, self).__init__(key, encoding, order)
self._randfunc = randfunc
def _compute_nonce(self, msg_hash):
return Integer.random_range(min_inclusive=1,
max_exclusive=_curve.order,
randfunc=self._randfunc)
def _valid_hash(self, msg_hash):
"""Verify that SHA-[23] (256|384|512) bits are used to
match the 128-bit security of P-256"""
approved = ("2.16.840.1.101.3.4.2.1",
"2.16.840.1.101.3.4.2.2",
"2.16.840.1.101.3.4.2.3",
"2.16.840.1.101.3.4.2.8",
"2.16.840.1.101.3.4.2.9",
"2.16.840.1.101.3.4.2.10")
return msg_hash.oid in approved
def new(key, mode, encoding='binary', randfunc=None):
"""Return a signature scheme object `DSS_SigScheme` that
can be used to perform DSS signature or verification.
:Parameters:
key : a `Crypto.PublicKey.DSA` or `Crypto.PublicKey.ECC` key object
If the key has got its private half, both signature and
verification are possible.
If it only has the public half, verification is possible
but not signature generation.
For DSA keys, let *L* and *N* be the bit lengths of the modules *p*
and *q*: the combination *(L,N)* must appear in the following list,
in compliance to section 4.2 of `FIPS-186`__:
- (1024, 160)
- (2048, 224)
- (2048, 256)
- (3072, 256)
mode : string
The parameter can take these values:
- *'fips-186-3'*. The signature generation is carried out
according to `FIPS-186`__: the nonce *k* is taken from the RNG.
- *'deterministic-rfc6979'*. The signature generation
process does not rely on a random generator.
See RFC6979_.
encoding : string
How the signature is encoded. This value determines the output of
``sign`` and the input of ``verify``.
The following values are accepted:
- *'binary'* (default), the signature is the raw concatenation
of *r* and *s*. The size in bytes of the signature is always
two times the size of *q*.
- *'der'*, the signature is a DER encoded SEQUENCE with two
INTEGERs, *r* and *s*. The size of the signature is variable.
randfunc : callable
The source of randomness. If ``None``, the internal RNG is used.
Only used for the *'fips-186-3'* mode.
.. __: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
.. __: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
.. _RFC6979: http://tools.ietf.org/html/rfc6979
"""
# The goal of the 'mode' parameter is to avoid to
# have the current version of the standard as default.
#
# Over time, such version will be superseded by (for instance)
# FIPS 186-4 and it will be odd to have -3 as default.
if encoding not in ('binary', 'der'):
raise ValueError("Unknown encoding '%s'" % encoding)
if isinstance(key, EccKey):
order = _curve.order
private_key_attr = 'd'
else:
order = Integer(key.q)
private_key_attr = 'x'
if key.has_private():
private_key = getattr(key, private_key_attr)
else:
private_key = None
if mode == 'deterministic-rfc6979':
return DeterministicDsaSigScheme(key, encoding, order, private_key)
elif mode == 'fips-186-3':
if isinstance(key, EccKey):
return FipsEcDsaSigScheme(key, encoding, order, randfunc)
else:
return FipsDsaSigScheme(key, encoding, order, randfunc)
else:
raise ValueError("Unknown DSS mode '%s'" % mode)

View file

@ -0,0 +1,55 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
Legacy module for PKCS#1 PSS signatures.
:undocumented: __package__
"""
import types
from Crypto.Signature import pss
def _pycrypto_verify(self, hash_object, signature):
try:
self._verify(hash_object, signature)
except (ValueError, TypeError):
return False
return True
def new(rsa_key, mgfunc=None, saltLen=None, randfunc=None):
pkcs1 = pss.new(rsa_key, mask_func=mgfunc,
salt_bytes=saltLen, rand_func=randfunc)
pkcs1._verify = pkcs1.verify
pkcs1.verify = types.MethodType(_pycrypto_verify, pkcs1)
return pkcs1

View file

@ -0,0 +1,53 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
Legacy module for PKCS#1 v1.5 signatures.
:undocumented: __package__
"""
import types
from Crypto.Signature import pkcs1_15
def _pycrypto_verify(self, hash_object, signature):
try:
self._verify(hash_object, signature)
except ValueError as TypeError:
return False
return True
def new(rsa_key):
pkcs1 = pkcs1_15.new(rsa_key)
pkcs1._verify = pkcs1.verify
pkcs1.verify = types.MethodType(_pycrypto_verify, pkcs1)
return pkcs1

View file

@ -0,0 +1,36 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""Digital signature protocols
A collection of standardized protocols to carry out digital signatures.
"""
__all__ = ['PKCS1_v1_5', 'PKCS1_PSS', 'DSS', 'pkcs1_15', 'pss']

View file

@ -0,0 +1,259 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
Module to create PKCS#1 v1.5 RSA signatures
See RFC3447__ or the `original RSA Labs specification`__.
This scheme is more properly called ``RSASSA-PKCS1-v1_5``.
For example, a sender can create the signature of a message using
its private RSA key:
>>> from Crypto.Signature import pkcs1_15
>>> from Crypto.Hash import SHA256
>>> from Crypto.PublicKey import RSA
>>>
>>> message = 'To be signed'
>>> key = RSA.importKey(open('private_key.der').read())
>>> h = SHA256.new(message)
>>> signature = pkcs1_15.new(key).sign(h)
At the other side, the receiver can verify the signature (and therefore
the authenticity of the message) using the public RSA key:
>>> key = RSA.importKey(open('public_key.der').read())
>>> h = SHA.new(message)
>>> try:
>>> pkcs1_15.new(key).verify(h, signature):
>>> print "The signature is valid."
>>> except (ValueError, TypeError):
>>> print "The signature is not valid."
:undocumented: __package__
.. __: http://www.ietf.org/rfc/rfc3447.txt
.. __: http://www.rsa.com/rsalabs/node.asp?id=2125
"""
from Crypto.Util.py3compat import b, bchr
import Crypto.Util.number
from Crypto.Util.number import ceil_div, bytes_to_long, long_to_bytes
from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString, DerObjectId
class PKCS115_SigScheme:
"""An instance of the PKCS#1 v1.5 signature scheme for a specific RSA key."""
def __init__(self, rsa_key):
"""Initialize this PKCS#1 v1.5 signature scheme object.
:Parameters:
rsa_key : an RSA key object
Creation of signatures is only possible if this is a *private*
RSA key. Verification of signatures is always possible.
"""
self._key = rsa_key
def can_sign(self):
"""Return True if this object can be used to sign messages."""
return self._key.has_private()
def sign(self, msg_hash):
"""Produce the PKCS#1 v1.5 signature of a message.
This function is named ``RSASSA-PKCS1-V1_5-SIGN``;
it is specified in section 8.2.1 of RFC3447.
:Parameters:
msg_hash : hash object
This is an object created with to the `Crypto.Hash` module.
It was used used to hash the message to sign.
:Return: The signature encoded as a byte string.
:Raise ValueError:
If the RSA key is not long enough when combined with the given
hash algorithm.
:Raise TypeError:
If the RSA key has no private half.
"""
# See 8.2.1 in RFC3447
modBits = Crypto.Util.number.size(self._key.n)
k = ceil_div(modBits,8) # Convert from bits to bytes
# Step 1
em = _EMSA_PKCS1_V1_5_ENCODE(msg_hash, k)
# Step 2a (OS2IP)
em_int = bytes_to_long(em)
# Step 2b (RSASP1)
m_int = self._key._decrypt(em_int)
# Step 2c (I2OSP)
signature = long_to_bytes(m_int, k)
return signature
def verify(self, msg_hash, signature):
"""Verify that a certain PKCS#1 v1.5 signature is valid.
This method checks if the message really originates from someone
that holds the RSA private key.
really signed the message.
This function is named ``RSASSA-PKCS1-V1_5-VERIFY``;
it is specified in section 8.2.2 of RFC3447.
:Parameters:
msg_hash : hash object
The hash that was carried out over the message. This is an object
belonging to the `Crypto.Hash` module.
signature : byte string
The signature that needs to be validated.
:Raise ValueError:
if the signature is not valid.
"""
# See 8.2.2 in RFC3447
modBits = Crypto.Util.number.size(self._key.n)
k = ceil_div(modBits, 8) # Convert from bits to bytes
# Step 1
if len(signature) != k:
raise ValueError("Invalid signature")
# Step 2a (O2SIP)
signature_int = bytes_to_long(signature)
# Step 2b (RSAVP1)
em_int = self._key._encrypt(signature_int)
# Step 2c (I2OSP)
em1 = long_to_bytes(em_int, k)
# Step 3
try:
possible_em1 = [ _EMSA_PKCS1_V1_5_ENCODE(msg_hash, k, True) ]
# MD2/4/5 hashes always require NULL params in AlgorithmIdentifier.
# For all others, it is optional.
try:
algorithm_is_md = msg_hash.oid.startswith('1.2.840.113549.2.')
except AttributeError:
algorithm_is_md = False
if not algorithm_is_md: # MD2/MD4/MD5
possible_em1.append(_EMSA_PKCS1_V1_5_ENCODE(msg_hash, k, False))
except ValueError:
raise ValueError("Invalid signature")
# Step 4
# By comparing the full encodings (as opposed to checking each
# of its components one at a time) we avoid attacks to the padding
# scheme like Bleichenbacher's (see http://www.mail-archive.com/cryptography@metzdowd.com/msg06537).
#
if em1 not in possible_em1:
raise ValueError("Invalid signature")
pass
def _EMSA_PKCS1_V1_5_ENCODE(msg_hash, emLen, with_hash_parameters=True):
"""
Implement the ``EMSA-PKCS1-V1_5-ENCODE`` function, as defined
in PKCS#1 v2.1 (RFC3447, 9.2).
``_EMSA-PKCS1-V1_5-ENCODE`` actually accepts the message ``M`` as input,
and hash it internally. Here, we expect that the message has already
been hashed instead.
:Parameters:
msg_hash : hash object
The hash object that holds the digest of the message being signed.
emLen : int
The length the final encoding must have, in bytes.
with_hash_parameters : bool
If True (default), include NULL parameters for the hash
algorithm in the ``digestAlgorithm`` SEQUENCE.
:attention: the early standard (RFC2313) stated that ``DigestInfo``
had to be BER-encoded. This means that old signatures
might have length tags in indefinite form, which
is not supported in DER. Such encoding cannot be
reproduced by this function.
:Return: An ``emLen`` byte long string that encodes the hash.
"""
# First, build the ASN.1 DER object DigestInfo:
#
# DigestInfo ::= SEQUENCE {
# digestAlgorithm AlgorithmIdentifier,
# digest OCTET STRING
# }
#
# where digestAlgorithm identifies the hash function and shall be an
# algorithm ID with an OID in the set PKCS1-v1-5DigestAlgorithms.
#
# PKCS1-v1-5DigestAlgorithms ALGORITHM-IDENTIFIER ::= {
# { OID id-md2 PARAMETERS NULL }|
# { OID id-md5 PARAMETERS NULL }|
# { OID id-sha1 PARAMETERS NULL }|
# { OID id-sha256 PARAMETERS NULL }|
# { OID id-sha384 PARAMETERS NULL }|
# { OID id-sha512 PARAMETERS NULL }
# }
#
# Appendix B.1 also says that for SHA-1/-2 algorithms, the parameters
# should be omitted. They may be present, but when they are, they shall
# have NULL value.
digestAlgo = DerSequence([ DerObjectId(msg_hash.oid).encode() ])
if with_hash_parameters:
digestAlgo.append(DerNull().encode())
digest = DerOctetString(msg_hash.digest())
digestInfo = DerSequence([
digestAlgo.encode(),
digest.encode()
]).encode()
# We need at least 11 bytes for the remaining data: 3 fixed bytes and
# at least 8 bytes of padding).
if emLen<len(digestInfo)+11:
raise TypeError("Selected hash algorith has a too long digest (%d bytes)." % len(digest))
PS = bchr(0xFF) * (emLen - len(digestInfo) - 3)
return b("\x00\x01") + PS + bchr(0x00) + digestInfo
def new(rsa_key):
"""Return a signature scheme object `PKCS115_SigScheme` that
can create or verify PKCS#1 v1.5 signatures.
:Parameters:
rsa_key : RSA key object
The RSA key to use to sign or verify the message.
This is a `Crypto.PublicKey.RSA` object.
Signing is only possible if ``rsa_key`` is a private RSA key.
"""
return PKCS115_SigScheme(rsa_key)

View file

@ -0,0 +1,383 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""RSA digital signature protocol with appendix according to PKCS#1 PSS.
See RFC3447__ or the `original RSA Labs specification`__.
This scheme is more properly called ``RSASSA-PSS``.
The following example shows how the sender can create the signatue of
a message using their private key:
>>> from Crypto.Signature import pss
>>> from Crypto.Hash import SHA256
>>> from Crypto.PublicKey import RSA
>>> from Crypto import Random
>>>
>>> message = 'To be signed'
>>> key = RSA.importKey(open('privkey.der').read())
>>> h = SHA256.new(message)
>>> signature = pss.new(key).sign(h)
At the receiver side, verification can be done using the public RSA key:
>>> key = RSA.importKey(open('pubkey.der').read())
>>> h = SHA256.new(message)
>>> verifier = pss.new(key)
>>> try:
>>> verifier.verify(h, signature):
>>> print "The signature is authentic."
>>> except (ValueError, TypeError):
>>> print "The signature is not authentic."
:undocumented: __package__
.. __: http://www.ietf.org/rfc/rfc3447.txt
.. __: http://www.rsa.com/rsalabs/node.asp?id=2125
"""
from Crypto.Util.py3compat import b, bchr, bord
import Crypto.Util.number
from Crypto.Util.number import (ceil_div,
long_to_bytes,
bytes_to_long
)
from Crypto.Util.strxor import strxor
from Crypto import Random
class PSS_SigScheme:
"""An instance of the PKCS#1 PSS signature scheme for a specific RSA key."""
def __init__(self, key, mgfunc, saltLen, randfunc):
"""Initialize this PKCS#1 PSS signature scheme object.
:Parameters:
key : an RSA key object
If a private half is given, both signature and
verification are possible.
If a public half is given, only verification is possible.
mgfunc : callable
A mask generation function that accepts two parameters:
a string to use as seed, and the lenth of the mask to
generate, in bytes.
saltLen : integer
Length of the salt, in bytes.
randfunc : callable
A function that returns random bytes.
"""
self._key = key
self._saltLen = saltLen
self._mgfunc = mgfunc
self._randfunc = randfunc
def can_sign(self):
"""Return True if this cipher object can be used
or signing messages."""
return self._key.has_private()
def sign(self, msg_hash):
"""Produce the PKCS#1 PSS signature of a message.
This function is named ``RSASSA-PSS-SIGN``, and is specified in
section 8.1.1 of RFC3447.
:Parameters:
msg_hash : hash object
The hash that was carried out over the message. This is an object
belonging to the `Crypto.Hash` module.
:Return: The PSS signature encoded as a byte string.
:Raise ValueError:
If the RSA key length is not sufficiently long to deal
with the given hash algorithm.
:Raise TypeError:
If the RSA key has no private half.
:attention: Modify the salt length and the mask generation
function only if you know what you are doing.
The receiver must use the same parameters too.
"""
# Set defaults for salt length and mask generation function
if self._saltLen is None:
sLen = msg_hash.digest_size
else:
sLen = self._saltLen
if self._mgfunc is None:
mgf = lambda x, y: MGF1(x, y, msg_hash)
else:
mgf = self._mgfunc
modBits = Crypto.Util.number.size(self._key.n)
# See 8.1.1 in RFC3447
k = ceil_div(modBits, 8) # k is length in bytes of the modulus
# Step 1
em = _EMSA_PSS_ENCODE(msg_hash, modBits-1, self._randfunc, mgf, sLen)
# Step 2a (OS2IP)
em_int = bytes_to_long(em)
# Step 2b (RSASP1)
m_int = self._key._decrypt(em_int)
# Step 2c (I2OSP)
signature = long_to_bytes(m_int, k)
return signature
def verify(self, msg_hash, signature):
"""Verify that a certain PKCS#1 PSS signature is authentic.
This function checks if the party holding the private half
of the given RSA key has really signed the message.
This function is called ``RSASSA-PSS-VERIFY``, and is specified
in section 8.1.2 of RFC3447.
:Parameters:
msg_hash : hash object
The cryptographic hash computed over the message.
This is an object belonging to the `Crypto.Hash` module.
signature : byte string
The signature that needs to be validated.
:Raise ValueError:
if the signature is incorrect.
"""
# Set defaults for salt length and mask generation function
if self._saltLen is None:
sLen = msg_hash.digest_size
else:
sLen = self._saltLen
if self._mgfunc:
mgf = self._mgfunc
else:
mgf = lambda x, y: MGF1(x, y, msg_hash)
modBits = Crypto.Util.number.size(self._key.n)
# See 8.1.2 in RFC3447
k = ceil_div(modBits, 8) # Convert from bits to bytes
# Step 1
if len(signature) != k:
raise ValueError("Incorrect signature")
# Step 2a (O2SIP)
signature_int = bytes_to_long(signature)
# Step 2b (RSAVP1)
em_int = self._key._encrypt(signature_int)
# Step 2c (I2OSP)
emLen = ceil_div(modBits - 1, 8)
em = long_to_bytes(em_int, emLen)
# Step 3/4
_EMSA_PSS_VERIFY(msg_hash, em, modBits-1, mgf, sLen)
def MGF1(mgfSeed, maskLen, hash):
"""Mask Generation Function, described in B.2.1"""
T = b("")
for counter in range(ceil_div(maskLen, hash.digest_size)):
c = long_to_bytes(counter, 4)
hobj = hash.new()
hobj.update(mgfSeed + c)
T = T + hobj.digest()
assert(len(T) >= maskLen)
return T[:maskLen]
def _EMSA_PSS_ENCODE(mhash, emBits, randFunc, mgf, sLen):
"""
Implement the ``EMSA-PSS-ENCODE`` function, as defined
in PKCS#1 v2.1 (RFC3447, 9.1.1).
The original ``EMSA-PSS-ENCODE`` actually accepts the message ``M``
as input, and hash it internally. Here, we expect that the message
has already been hashed instead.
:Parameters:
mhash : hash object
The hash object that holds the digest of the message being signed.
emBits : int
Maximum length of the final encoding, in bits.
randFunc : callable
An RNG function that accepts as only parameter an int, and returns
a string of random bytes, to be used as salt.
mgf : callable
A mask generation function that accepts two parameters: a string to
use as seed, and the lenth of the mask to generate, in bytes.
sLen : int
Length of the salt, in bytes.
:Return: An ``emLen`` byte long string that encodes the hash
(with ``emLen = \ceil(emBits/8)``).
:Raise ValueError:
When digest or salt length are too big.
"""
emLen = ceil_div(emBits, 8)
# Bitmask of digits that fill up
lmask = 0
for i in range(8*emLen-emBits):
lmask = lmask >> 1 | 0x80
# Step 1 and 2 have been already done
# Step 3
if emLen < mhash.digest_size+sLen+2:
raise ValueError("Digest or salt length are too long"
" for given key size.")
# Step 4
salt = randFunc(sLen)
# Step 5
m_prime = bchr(0)*8 + mhash.digest() + salt
# Step 6
h = mhash.new()
h.update(m_prime)
# Step 7
ps = bchr(0)*(emLen-sLen-mhash.digest_size-2)
# Step 8
db = ps + bchr(1) + salt
# Step 9
dbMask = mgf(h.digest(), emLen-mhash.digest_size-1)
# Step 10
maskedDB = strxor(db, dbMask)
# Step 11
maskedDB = bchr(bord(maskedDB[0]) & ~lmask) + maskedDB[1:]
# Step 12
em = maskedDB + h.digest() + bchr(0xBC)
return em
def _EMSA_PSS_VERIFY(mhash, em, emBits, mgf, sLen):
"""
Implement the ``EMSA-PSS-VERIFY`` function, as defined
in PKCS#1 v2.1 (RFC3447, 9.1.2).
``EMSA-PSS-VERIFY`` actually accepts the message ``M`` as input,
and hash it internally. Here, we expect that the message has already
been hashed instead.
:Parameters:
mhash : hash object
The hash object that holds the digest of the message to be verified.
em : string
The signature to verify, therefore proving that the sender really
signed the message that was received.
emBits : int
Length of the final encoding (em), in bits.
mgf : callable
A mask generation function that accepts two parameters: a string to
use as seed, and the lenth of the mask to generate, in bytes.
sLen : int
Length of the salt, in bytes.
:Raise ValueError:
When the encoding is inconsistent, or the digest or salt lengths
are too big.
"""
emLen = ceil_div(emBits, 8)
# Bitmask of digits that fill up
lmask = 0
for i in range(8*emLen-emBits):
lmask = lmask >> 1 | 0x80
# Step 1 and 2 have been already done
# Step 3
if emLen < mhash.digest_size+sLen+2:
return False
# Step 4
if ord(em[-1:]) != 0xBC:
raise ValueError("Incorrect signature")
# Step 5
maskedDB = em[:emLen-mhash.digest_size-1]
h = em[emLen-mhash.digest_size-1:-1]
# Step 6
if lmask & bord(em[0]):
raise ValueError("Incorrect signature")
# Step 7
dbMask = mgf(h, emLen-mhash.digest_size-1)
# Step 8
db = strxor(maskedDB, dbMask)
# Step 9
db = bchr(bord(db[0]) & ~lmask) + db[1:]
# Step 10
if not db.startswith(bchr(0)*(emLen-mhash.digest_size-sLen-2) + bchr(1)):
raise ValueError("Incorrect signature")
# Step 11
if sLen > 0:
salt = db[-sLen:]
else:
salt = b("")
# Step 12
m_prime = bchr(0)*8 + mhash.digest() + salt
# Step 13
hobj = mhash.new()
hobj.update(m_prime)
hp = hobj.digest()
# Step 14
if h != hp:
raise ValueError("Incorrect signature")
def new(rsa_key, **kwargs):
"""Return a signature scheme object `PSS_SigScheme` that
can be used to perform PKCS#1 PSS signature or verification.
:Parameters:
rsa_key : RSA key object
The key to use to sign or verify the message.
This is a `Crypto.PublicKey.RSA` object.
Signing is only possible if *key* is a private RSA key.
:Keywords:
mask_func : callable
A mask generation function that accepts two parameters: a string to
use as seed, and the length of the mask in bytes to generate.
If not specified, the standard MGF1 is used.
salt_bytes : int
Length of the salt, in bytes.
If not specified, it matches the output size of the hash function.
If zero, the signature scheme becomes deterministic.
rand_func : callable
A function that returns random bytes.
The default is `Crypto.Random.get_random_bytes`.
"""
mask_func = kwargs.pop("mask_func", None)
salt_len = kwargs.pop("salt_bytes", None)
rand_func = kwargs.pop("rand_func", None)
if rand_func is None:
rand_func = Random.get_random_bytes
if kwargs:
raise ValueError("Unknown keywords: " + str(list(kwargs.keys())))
return PSS_SigScheme(rsa_key, mask_func, salt_len, rand_func)