diff options
author | Legrandin <gooksankoo@hoiptorrow.mailexpire.com> | 2011-10-16 18:29:56 +0200 |
---|---|---|
committer | Legrandin <gooksankoo@hoiptorrow.mailexpire.com> | 2011-10-16 18:29:56 +0200 |
commit | e05362993e34be982bd08e5ffd076c3e4a351232 (patch) | |
tree | a01d1961add967ee67ecde5926ff7b3522a71d4d | |
parent | cee93d88d0c7283ff8c8c376ed47253900e5a49e (diff) | |
download | pycrypto-e05362993e34be982bd08e5ffd076c3e4a351232.tar.gz |
Restructure both PKCS#1 signature schemes as objects.
Fixed the can_sign() methods.
-rw-r--r-- | lib/Crypto/SelfTest/Signature/test_pkcs1_15.py | 15 | ||||
-rw-r--r-- | lib/Crypto/SelfTest/Signature/test_pkcs1_pss.py | 30 | ||||
-rw-r--r-- | lib/Crypto/Signature/PKCS1_PSS.py | 274 | ||||
-rw-r--r-- | lib/Crypto/Signature/PKCS1_v1_5.py | 201 |
4 files changed, 292 insertions, 228 deletions
diff --git a/lib/Crypto/SelfTest/Signature/test_pkcs1_15.py b/lib/Crypto/SelfTest/Signature/test_pkcs1_15.py index 9eec5ea..8dee59e 100644 --- a/lib/Crypto/SelfTest/Signature/test_pkcs1_15.py +++ b/lib/Crypto/SelfTest/Signature/test_pkcs1_15.py @@ -165,14 +165,16 @@ class PKCS1_15_Tests(unittest.TestCase): except: h.update(self._testData[i][1]) # The real test - s = PKCS.sign(h, key) + signer = PKCS.new(key) + self.failUnless(signer.can_sign()) + s = signer.sign(h) self.assertEqual(s, t2b(self._testData[i][2])) def testVerify1(self): for i in range(len(self._testData)): # Build the key if isStr(self._testData[i][0]): - key = RSA.importKey(self._testData[i][0]) + key = RSA.importKey(self._testData[i][0]).publickey() else: comps = [ long(rws(self._testData[i][0][x]),16) for x in ('n','e') ] key = RSA.construct(comps) @@ -183,7 +185,9 @@ class PKCS1_15_Tests(unittest.TestCase): except: h.update(self._testData[i][1]) # The real test - result = PKCS.verify(h, key, t2b(self._testData[i][2])) + verifier = PKCS.new(key) + self.failIf(verifier.can_sign()) + result = verifier.verify(h, t2b(self._testData[i][2])) self.failUnless(result) def testSignVerify(self): @@ -194,8 +198,9 @@ class PKCS1_15_Tests(unittest.TestCase): h = hashmod.new() h.update('blah blah blah') - s = PKCS.sign(h, key) - result = PKCS.verify(h, key, s) + signer = PKCS.new(key) + s = signer.sign(h) + result = signer.verify(h, s) self.failUnless(result) diff --git a/lib/Crypto/SelfTest/Signature/test_pkcs1_pss.py b/lib/Crypto/SelfTest/Signature/test_pkcs1_pss.py index 5f94b89..a770f06 100644 --- a/lib/Crypto/SelfTest/Signature/test_pkcs1_pss.py +++ b/lib/Crypto/SelfTest/Signature/test_pkcs1_pss.py @@ -65,6 +65,8 @@ class MyKey: return self._key._randfunc(N) def sign(self, m): return self._key.sign(m) + def has_private(self): + return self._key.has_private() def decrypt(self, m): return self._key.decrypt(m) def verify(self, m, p): @@ -353,7 +355,9 @@ class PKCS1_PSS_Tests(unittest.TestCase): test_salt = t2b(self._testData[i][3]) key._randfunc = lambda N: test_salt # The real test - s = PKCS.sign(h, key) + signer = PKCS.new(key) + self.failUnless(signer.can_sign()) + s = signer.sign(h) self.assertEqual(s, t2b(self._testData[i][2])) def testVerify1(self): @@ -369,7 +373,9 @@ class PKCS1_PSS_Tests(unittest.TestCase): test_salt = t2b(self._testData[i][3]) # The real test key._randfunc = lambda N: test_salt - result = PKCS.verify(h, key, t2b(self._testData[i][2])) + verifier = PKCS.new(key) + self.failIf(verifier.can_sign()) + result = verifier.verify(h, t2b(self._testData[i][2])) self.failUnless(result) def testSignVerify(self): @@ -394,8 +400,9 @@ class PKCS1_PSS_Tests(unittest.TestCase): # Verify that sign() asks for as many random bytes # as the hash output size key.asked = 0 - s = PKCS.sign(h, key) - self.failUnless(PKCS.verify(h, key, s)) + signer = PKCS.new(key) + s = signer.sign(h) + self.failUnless(signer.verify(h, s)) self.assertEqual(key.asked, h.digest_size) h = SHA1.new() @@ -404,24 +411,27 @@ class PKCS1_PSS_Tests(unittest.TestCase): # Verify that sign() uses a different salt length for sLen in (0,3,21): key.asked = 0 - s = PKCS.sign(h, key, saltLen=sLen) + signer = PKCS.new(key, saltLen=sLen) + s = signer.sign(h) self.assertEqual(key.asked, sLen) - self.failUnless(PKCS.verify(h, key, s, saltLen=sLen)) + self.failUnless(signer.verify(h, s)) # Verify that sign() uses the custom MGF mgfcalls = 0 - s = PKCS.sign(h, key, newMGF) + signer = PKCS.new(key, newMGF) + s = signer.sign(h) self.assertEqual(mgfcalls, 1) - self.failUnless(PKCS.verify(h, key, s, newMGF)) + self.failUnless(signer.verify(h, s)) # Verify that sign() does not call the RNG # when salt length is 0, even when a new MGF is provided key.asked = 0 mgfcalls = 0 - s = PKCS.sign(h, key, newMGF, 0) + signer = PKCS.new(key, newMGF, 0) + s = signer.sign(h) self.assertEqual(key.asked,0) self.assertEqual(mgfcalls, 1) - self.failUnless(PKCS.verify(h, key, s, newMGF, 0)) + self.failUnless(signer.verify(h, s)) def get_tests(config={}): tests = [] diff --git a/lib/Crypto/Signature/PKCS1_PSS.py b/lib/Crypto/Signature/PKCS1_PSS.py index 4b43655..31c10b2 100644 --- a/lib/Crypto/Signature/PKCS1_PSS.py +++ b/lib/Crypto/Signature/PKCS1_PSS.py @@ -38,7 +38,8 @@ this: >>> key = RSA.importKey(open('privkey.der').read()) >>> h = SHA.new() >>> h.update(message) - >>> signature = PKCS1_PSS.sign(h, key) + >>> signer = PKCS1_PSS.new(key) + >>> signature = PKCS1_PSS.sign(key) At the receiver side, verification can be done like using the public part of the RSA key: @@ -46,7 +47,8 @@ the RSA key: >>> key = RSA.importKey(open('pubkey.der').read()) >>> h = SHA.new() >>> h.update(message) - >>> if PKCS1_PSS.verify(h, key, signature): + >>> verifier = PKCS1_PSS.new(key) + >>> if verifier.verify(h, signature): >>> print "The signature is authentic." >>> else: >>> print "The signature is not authentic." @@ -62,134 +64,137 @@ the RSA key: from __future__ import nested_scopes __revision__ = "$Id$" -__all__ = [ 'sign', 'verify' ] +__all__ = [ 'new' ] import Crypto.Util.number from Crypto.Util.number import ceil_shift, ceil_div, long_to_bytes from Crypto.Util.strxor import strxor -def sign(mhash, key, mgfunc=None, saltLen=None): - """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: - mhash : hash object - The hash that was carried out over the message. This is an object - belonging to the `Crypto.Hash` module. - key : RSA key object - The key to use to sign the message. This is a `Crypto.PublicKey.RSA` - object and must have its private half. - 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. - If not specified, the standard MGF1 is used. - saltLen : int - Length of the salt, in bytes. If not specified, it matches the output - size of `mhash`. - - :Return: The PSS signature encoded as a 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. - """ - # TODO: Verify the key is RSA - - randfunc = key._randfunc - - # Set defaults for salt length and mask generation function - if saltLen == None: - sLen = mhash.digest_size - else: - sLen = saltLen - if mgfunc: - mgf = mgfunc - else: - mgf = lambda x,y: MGF1(x,y,mhash) - - modBits = Crypto.Util.number.size(key.n) - - # See 8.1.1 in RFC3447 - k = ceil_div(modBits,8) # Convert from bits to bytes - # Step 1 - em = EMSA_PSS_ENCODE(mhash, modBits-1, randfunc, mgf, sLen) - # Step 2a (OS2IP) and 2b (RSASP1) - m = key.decrypt(em) - # Step 2c (I2OSP) - S = '\x00'*(k-len(m)) + m - return S - -def verify(mhash, key, S, mgfunc=None, saltLen=None): - """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: - mhash : hash object - The hash that was carried out over the message. This is an object - belonging to the `Crypto.Hash` module. - key : RSA key object - The key to use to verify the message. This is a `Crypto.PublicKey.RSA` - object. - S : string - The signature that needs to be validated. - 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. - If not specified, the standard MGF1 is used. The sender must have - used the same function. - saltLen : int - Length of the salt, in bytes. If not specified, it matches the output - size of `mhash`. - - :Return: True if verification is correct. False otherwise. - """ - # TODO: Verify the key is RSA - - # Set defaults for salt length and mask generation function - if saltLen == None: - sLen = mhash.digest_size - else: - sLen = saltLen - if mgfunc: - mgf = mgfunc - else: - mgf = lambda x,y: MGF1(x,y,mhash) - - modBits = Crypto.Util.number.size(key.n) - - # See 8.1.2 in RFC3447 - k = ceil_div(modBits,8) # Convert from bits to bytes - # Step 1 - if len(S) != k: - return 0 - # Step 2a (O2SIP), 2b (RSAVP1), and partially 2c (I2OSP) - # Note that signature must be smaller than the module - # but RSA.py won't complain about it. - # TODO: Fix RSA object; don't do it here. - em = key.encrypt(S, 0)[0] - # Step 2c - emLen = ceil_div(modBits-1,8) - em = '\x00'*(emLen-len(em)) + em - # Step 3 - try: - result = EMSA_PSS_VERIFY(mhash, em, modBits-1, mgf, sLen) - except ValueError: - return 0 - # Step 4 - return result - +class PSS_SigScheme: + """This signature scheme can perform PKCS#1 PSS RSA signature or verification.""" + + def __init__(self, key, mgfunc, saltLen): + """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 : int + Length of the salt, in bytes. + """ + self._key = key + self._saltLen = saltLen + self._mgfunc = mgfunc + + def can_sign(self): + """Return True if this cipher object can be used for signing messages.""" + return self._key.has_private() + + def sign(self, mhash): + """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: + mhash : 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 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. + """ + # TODO: Verify the key is RSA + + randfunc = self._key._randfunc + + # Set defaults for salt length and mask generation function + if self._saltLen == None: + sLen = mhash.digest_size + else: + sLen = self._saltLen + if self._mgfunc: + mgf = self._mgfunc + else: + mgf = lambda x,y: MGF1(x,y,mhash) + + modBits = Crypto.Util.number.size(self._key.n) + + # See 8.1.1 in RFC3447 + k = ceil_div(modBits,8) # Convert from bits to bytes + # Step 1 + em = EMSA_PSS_ENCODE(mhash, modBits-1, randfunc, mgf, sLen) + # Step 2a (OS2IP) and 2b (RSASP1) + m = self._key.decrypt(em) + # Step 2c (I2OSP) + S = '\x00'*(k-len(m)) + m + return S + + def verify(self, mhash, S): + """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: + mhash : hash object + The hash that was carried out over the message. This is an object + belonging to the `Crypto.Hash` module. + S : string + The signature that needs to be validated. + + :Return: True if verification is correct. False otherwise. + """ + # TODO: Verify the key is RSA + + # Set defaults for salt length and mask generation function + if self._saltLen == None: + sLen = mhash.digest_size + else: + sLen = self._saltLen + if self._mgfunc: + mgf = self._mgfunc + else: + mgf = lambda x,y: MGF1(x,y,mhash) + + 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(S) != k: + return 0 + # Step 2a (O2SIP), 2b (RSAVP1), and partially 2c (I2OSP) + # Note that signature must be smaller than the module + # but RSA.py won't complain about it. + # TODO: Fix RSA object; don't do it here. + em = self._key.encrypt(S, 0)[0] + # Step 2c + emLen = ceil_div(modBits-1,8) + em = '\x00'*(emLen-len(em)) + em + # Step 3 + try: + result = EMSA_PSS_VERIFY(mhash, em, modBits-1, mgf, sLen) + except ValueError: + return 0 + # Step 4 + return result + def MGF1(mgfSeed, maskLen, hash): """Mask Generation Function, described in B.2.1""" T = "" @@ -326,3 +331,22 @@ def EMSA_PSS_VERIFY(mhash, em, emBits, mgf, sLen): return 0 return 1 +def new(key, mgfunc=None, saltLen=None): + """Return a signature scheme object `PSS_SigScheme` that + can be used to perform PKCS#1 PSS signature or verification. + + :Parameters: + 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. + 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. + If not specified, the standard MGF1 is used. + saltLen : int + Length of the salt, in bytes. If not specified, it matches the output + size of the hash function. + + """ + return PSS_SigScheme(key, mgfunc, saltLen) + diff --git a/lib/Crypto/Signature/PKCS1_v1_5.py b/lib/Crypto/Signature/PKCS1_v1_5.py index 6e0817d..b6e1cba 100644 --- a/lib/Crypto/Signature/PKCS1_v1_5.py +++ b/lib/Crypto/Signature/PKCS1_v1_5.py @@ -37,14 +37,16 @@ this: >>> message = 'To be signed' >>> key = RSA.importKey(open('privkey.der').read()) >>> h = SHA.new(message) - >>> signature = PKCS1_v1_5.sign(h, key) + >>> signer = PKCS1_v1_5.new(key) + >>> signature = signer.sign(h) At the receiver side, verification can be done using the public part of the RSA key: >>> key = RSA.importKey(open('pubkey.der').read()) >>> h = SHA.new(message) - >>> if PKCS.verify(h, key, signature): + >>> verifier = PKCS1_v1_5.new(key) + >>> if verifier.verify(h, signature): >>> print "The signature is authentic." >>> else: >>> print "The signature is not authentic." @@ -56,96 +58,107 @@ the RSA key: """ __revision__ = "$Id$" -__all__ = [ 'sign', 'verify' ] +__all__ = [ 'new' ] import Crypto.Util.number from Crypto.Util.number import ceil_div from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString -def sign(mhash, key): - """Produce the PKCS#1 v1.5 signature of a message. - - This function is named ``RSASSA-PKCS1-V1_5-SIGN``, and is specified in - section 8.2.1 of RFC3447. - - :Parameters: - mhash : hash object - The hash that was carried out over the message. This is an object - belonging to the `Crypto.Hash` module. - key : RSA key object - The key to use to sign the message. This is a `Crypto.PublicKey.RSA` - object and must have its private half. - - :Return: The signature encoded as a 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. - """ - # TODO: Verify the key is RSA - - # See 8.2.1 in RFC3447 - modBits = Crypto.Util.number.size(key.n) - k = ceil_div(modBits,8) # Convert from bits to bytes - - # Step 1 - em = EMSA_PKCS1_V1_5_ENCODE(mhash, k) - # Step 2a (OS2IP) and 2b (RSASP1) - m = key.decrypt(em) - # Step 2c (I2OSP) - S = '\x00'*(k-len(m)) + m - return S - -def verify(mhash, key, S): - """Verify that a certain PKCS#1 v1.5 signature is authentic. - - This function checks if the party holding the private half of the key - really signed the message. - - This function is named ``RSASSA-PKCS1-V1_5-VERIFY``, and is specified in - section 8.2.2 of RFC3447. - - :Parameters: - mhash : hash object - The hash that was carried out over the message. This is an object - belonging to the `Crypto.Hash` module. - key : RSA key object - The key to use to verify the message. This is a `Crypto.PublicKey.RSA` - object. - S : string - The signature that needs to be validated. - - :Return: True (1) if verification is correct. False (0) otherwise. - """ - # TODO: Verify the key is RSA - - # See 8.2.2 in RFC3447 - modBits = Crypto.Util.number.size(key.n) - k = ceil_div(modBits,8) # Convert from bits to bytes - - # Step 1 - if len(S) != k: - return 0 - # Step 2a (O2SIP) and 2b (RSAVP1) - # Note that signature must be smaller than the module - # but RSA.py won't complain about it. - # TODO: Fix RSA object; don't do it here. - m = key.encrypt(S, 0)[0] - # Step 2c (I2OSP) - em1 = '\x00'*(k-len(m)) + m - # Step 3 - try: - em2 = EMSA_PKCS1_V1_5_ENCODE(mhash, k) - except ValueError: - return 0 - # 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). - # - return em1==em2 - +class PKCS115_SigScheme: + """This signature scheme can perform PKCS#1 v1.5 RSA signature or verification.""" + + def __init__(self, key): + """Initialize this PKCS#1 v1.5 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. + """ + self._key = key + + def can_sign(self): + """Return True if this cipher object can be used for signing messages.""" + return self._key.has_private() + + def sign(self, mhash): + """Produce the PKCS#1 v1.5 signature of a message. + + This function is named ``RSASSA-PKCS1-V1_5-SIGN``, and is specified in + section 8.2.1 of RFC3447. + + :Parameters: + mhash : hash object + The hash that was carried out over the message. This is an object + belonging to the `Crypto.Hash` module. + + :Return: The signature encoded as a 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. + """ + # TODO: Verify the key is RSA + + # 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(mhash, k) + # Step 2a (OS2IP) and 2b (RSASP1) + m = self._key.decrypt(em) + # Step 2c (I2OSP) + S = '\x00'*(k-len(m)) + m + return S + + def verify(self, mhash, S): + """Verify that a certain PKCS#1 v1.5 signature is authentic. + + This function checks if the party holding the private half of the key + really signed the message. + + This function is named ``RSASSA-PKCS1-V1_5-VERIFY``, and is specified in + section 8.2.2 of RFC3447. + + :Parameters: + mhash : hash object + The hash that was carried out over the message. This is an object + belonging to the `Crypto.Hash` module. + S : string + The signature that needs to be validated. + + :Return: True if verification is correct. False otherwise. + """ + # TODO: Verify the key is RSA + + # 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(S) != k: + return 0 + # Step 2a (O2SIP) and 2b (RSAVP1) + # Note that signature must be smaller than the module + # but RSA.py won't complain about it. + # TODO: Fix RSA object; don't do it here. + m = self._key.encrypt(S, 0)[0] + # Step 2c (I2OSP) + em1 = '\x00'*(k-len(m)) + m + # Step 3 + try: + em2 = EMSA_PKCS1_V1_5_ENCODE(mhash, k) + except ValueError: + return 0 + # 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). + # + return em1==em2 + def EMSA_PKCS1_V1_5_ENCODE(hash, emLen): """ Implement the ``EMSA-PKCS1-V1_5-ENCODE`` function, as defined @@ -204,7 +217,19 @@ def EMSA_PKCS1_V1_5_ENCODE(hash, emLen): # 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 ValueError("Selected hash algorith has a too long digest (%d bytes)." % len(digest)) + raise ValueError("Selected hash algorith has a too long digest (%d bytes)." % len(digest)) PS = "\xFF" * (emLen - len(digestInfo) - 3) return "\x00" + "\x01" + PS + "\x00" + digestInfo +def new(key): + """Return a signature scheme object `PKCS115_SigScheme` that + can be used to perform PKCS#1 v1.5 signature or verification. + + :Parameters: + 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. + + """ + return PKCS115_SigScheme(key) + |