# -*- coding: utf-8 -*-
#
#  SelfTest/Signature/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 signatures
#
# ===================================================================
# The contents of this file are dedicated to the public domain.  To
# the extent that dedication to the public domain is not available,
# everyone is granted a worldwide, perpetual, royalty-free,
# non-exclusive license to exercise all rights associated with the
# contents of this file for any purpose whatsoever.
# No rights are reserved.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ===================================================================

__revision__ = "$Id$"

import unittest

from Crypto.PublicKey import RSA
from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
from Crypto.Hash import *
from Crypto import Random
from Crypto.Signature import PKCS1_v1_5 as PKCS
from Crypto.Util.py3compat import *

def isStr(s):
        t = ''
        try:
                t += s
        except TypeError:
                return 0
        return 1

def rws(t):
    """Remove white spaces, tabs, and new lines from a string"""
    for c in ['\n', '\t', ' ']:
        t = t.replace(c,'')
    return t

def t2b(t):
    """Convert a text string with bytes in hex form to a byte string"""
    clean = b(rws(t))
    if len(clean)%2 == 1:
        raise ValueError("Even number of characters expected")
    return a2b_hex(clean)

class PKCS1_15_Tests(unittest.TestCase):

        # List of tuples with test data for PKCS#1 v1.5.
        # Each tuple is made up by:
        #       Item #0: dictionary with RSA key component, or key to import
        #       Item #1: data to hash and sign
        #       Item #2: signature of the data #1, done with the key #0, after
        #                hashing it with #3
        #       Item #3: hash object generator

        _testData = (

                #
                # RSA keypair generated with openssl
                #
                (
                """-----BEGIN RSA PRIVATE KEY-----
                MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII
                q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8
                Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI
                OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr
                +rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK
                JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9
                n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ==
                -----END RSA PRIVATE KEY-----""",
                "This is a test\x0a",
                #
                # PKCS#1 signature computed with openssl
                #
                '''4a700a16432a291a3194646952687d5316458b8b86fb0a25aa30e0dcecdb
                442676759ac63d56ec1499c3ae4c0013c2053cabd5b5804848994541ac16
                fa243a4d''',
                SHA
                ),

                #
                # Test vector from http://www.di-mgt.com.au/rsa_alg.html#signpkcs1
                #
                (
                {
                    'n':'''E08973398DD8F5F5E88776397F4EB005BB5383DE0FB7ABDC7DC775290D052E6D
                    12DFA68626D4D26FAA5829FC97ECFA82510F3080BEB1509E4644F12CBBD832CF
                    C6686F07D9B060ACBEEE34096A13F5F7050593DF5EBA3556D961FF197FC981E6
                    F86CEA874070EFAC6D2C749F2DFA553AB9997702A648528C4EF357385774575F''',
                    'e':'''010001''',
                    'd':'''00A403C327477634346CA686B57949014B2E8AD2C862B2C7D748096A8B91F736
                    F275D6E8CD15906027314735644D95CD6763CEB49F56AC2F376E1CEE0EBF282D
                    F439906F34D86E085BD5656AD841F313D72D395EFE33CBFF29E4030B3D05A28F
                    B7F18EA27637B07957D32F2BDE8706227D04665EC91BAF8B1AC3EC9144AB7F21'''
                },
                "abc",
                '''60AD5A78FB4A4030EC542C8974CD15F55384E836554CEDD9A322D5F4135C6267
                A9D20970C54E6651070B0144D43844C899320DD8FA7819F7EBC6A7715287332E
                C8675C136183B3F8A1F81EF969418267130A756FDBB2C71D9A667446E34E0EAD
                9CF31BFB66F816F319D0B7E430A5F2891553986E003720261C7E9022C0D9F11F''',
                SHA
                )

        )

        def testSign1(self):
                for i in range(len(self._testData)):
                        row = self._testData[i]
                        # Build the key
                        if isStr(row[0]):
                                key = RSA.importKey(row[0])
                        else:
                                comps = [ long(rws(row[0][x]),16) for x in ('n','e','d') ]
                                key = RSA.construct(comps)
                        h = row[3].new()
                        # Data to sign can either be in hex form or not
                        try:
                            h.update(t2b(row[1]))
                        except:
                            h.update(b(row[1]))
                        # The real test
                        signer = PKCS.new(key)
                        self.failUnless(signer.can_sign())
                        s = signer.sign(h)
                        self.assertEqual(s, t2b(row[2]))

        def testVerify1(self):
                for i in range(len(self._testData)):
                        row = self._testData[i]
                        # Build the key
                        if isStr(row[0]):
                                key = RSA.importKey(row[0]).publickey()
                        else:
                                comps = [ long(rws(row[0][x]),16) for x in ('n','e') ]
                                key = RSA.construct(comps)
                        h = row[3].new()
                        # Data to sign can either be in hex form or not
                        try:
                            h.update(t2b(row[1]))
                        except:
                            h.update(b(row[1]))
                        # The real test
                        verifier = PKCS.new(key)
                        self.failIf(verifier.can_sign())
                        result = verifier.verify(h, t2b(row[2]))
                        self.failUnless(result)

        def testSignVerify(self):
                        rng = Random.new().read
                        key = RSA.generate(1024, rng)

                        for hashmod in (MD5,SHA,SHA224,SHA256,SHA384,SHA512,RIPEMD):
                            h = hashmod.new()
                            h.update(b('blah blah blah'))

                            signer = PKCS.new(key)
                            s = signer.sign(h)
                            result = signer.verify(h, s)
                            self.failUnless(result)


def get_tests(config={}):
    tests = []
    tests += list_test_cases(PKCS1_15_Tests)
    return tests

if __name__ == '__main__':
    suite = lambda: unittest.TestSuite(get_tests())
    unittest.main(defaultTest='suite')

# vim:set ts=4 sw=4 sts=4 expandtab:
