'''
:Date: Jun 17, 2011
:Authors: Gary Belvin
'''
import unittest
from charm.toolbox.paddingschemes import OAEPEncryptionPadding, MGF1, hashFunc, PSSPadding, PKCS7Padding
from binascii import a2b_hex
debug = False
[docs]class PaddingSchemesTest(unittest.TestCase):
[docs] def testOEAPVector1(self):
# OAEP Test vector taken from Appendix C
#ftp://ftp.rsa.com/pub/rsalabs/rsa_algorithm/rsa-oaep_spec.pdf
# --------------------------------------------------------------------------------
# Message:
m = a2b_hex(bytes('d4 36 e9 95 69 fd 32 a7 c8 a0 5b bc 90 d3 2c 49'.replace(' ',''),'utf-8'))
label = ""
lhash = a2b_hex(bytes("da 39 a3 ee 5e 6b 4b 0d 32 55 bf ef 95 60 18 90 af d8 07 09".replace(' ',""),'utf-8'))
DB = a2b_hex(bytes("da 39 a3 ee 5e 6b 4b 0d 32 55 bf ef 95 60 18 90 af d8 07 09 00 00 00 00\
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 d4 36 e9 95 69\
fd 32 a7 c8 a0 5b bc 90 d3 2c 49".replace(" ", ""),'utf-8'))
seed = a2b_hex(bytes("aa fd 12 f6 59 ca e6 34 89 b4 79 e5 07 6d de c2 f0 6c b5 8f".replace(' ' ,''),'utf-8'))
#dbmask = dbMask = MGF (seed , 107):
dbmask= a2b_hex(bytes("06 e1 de b2 36 9a a5 a5 c7 07 d8 2c 8e 4e 93 24 8a c7 83 de e0 b2 c0 46\
26 f5 af f9 3e dc fb 25 c9 c2 b3 ff 8a e1 0e 83 9a 2d db 4c dc fe 4f f4\
77 28 b4 a1 b7 c1 36 2b aa d2 9a b4 8d 28 69 d5 02 41 21 43 58 11 59 1b\
e3 92 f9 82 fb 3e 87 d0 95 ae b4 04 48 db 97 2f 3a c1 4e af f4 9c 8c 3b\
7c fc 95 1a 51 ec d1 dd e6 12 64".replace(" ",""),'utf-8'))
#maskedDB
#seedMask = M GF (maskedDB, 20):
seedMask = a2b_hex(bytes("41 87 0b 5a b0 29 e6 57 d9 57 50 b5 4c 28 3c 08 72 5d be a9".replace(' ',''),'utf-8'))
maskedSeed= a2b_hex(bytes("eb 7a 19 ac e9 e3 00 63 50 e3 29 50 4b 45 e2 ca 82 31 0b 26".replace(' ',''),'utf-8'))
#EM = maskedSeed maskedDB
EM = a2b_hex(bytes("00 eb 7a 19 ac e9 e3 00 63 50 e3 29 50 4b 45 e2 ca 82 31 0b 26 dc d8 7d 5c\
68 f1 ee a8 f5 52 67 c3 1b 2e 8b b4 25 1f 84 d7 e0 b2 c0 46 26 f5 af f9\
3e dc fb 25 c9 c2 b3 ff 8a e1 0e 83 9a 2d db 4c dc fe 4f f4 77 28 b4 a1\
b7 c1 36 2b aa d2 9a b4 8d 28 69 d5 02 41 21 43 58 11 59 1b e3 92 f9 82\
fb 3e 87 d0 95 ae b4 04 48 db 97 2f 3a c1 4f 7b c2 75 19 52 81 ce 32 d2\
f1 b7 6d 4d 35 3e 2d".replace(" ",''),'utf-8'))
if debug:
print("Test Vector 1:")
print("mesg =>", m)
print("label =>", label)
print("lhash =>", lhash) #Correct
print("DB =>", DB) #Correct
print("DBMask=>", dbmask) #Correct
print("seedMask=>", seedMask) #Correct
print("maskedseed=>", maskedSeed)
c = OAEPEncryptionPadding()
E = c.encode(m, 128,"",seed)
self.assertEqual(EM, E)
[docs] def testOAEPRoundTripEquiv(self):
oaep = OAEPEncryptionPadding()
m = b'This is a test message'
ct = oaep.encode(m, 64)
pt = oaep.decode(ct)
self.assertEqual(m, pt, 'Decoded message is not equal to encoded message\n'\
'ct: %s\nm: %s\npt: %s' % (ct, m, pt))
[docs] @unittest.skip("Unnecessary length test")
def testMFGLength(self):
seed = ""
hashFn = OAEPEncryptionPadding().hashFn
hLen = OAEPEncryptionPadding().hashFnOutputBytes
for mbytes in range(100):
a = MGF1(seed, mbytes, hashFn, hLen)
self.assertEqual(len(a), mbytes, 'MFG output wrong size')
[docs] def testMFGvector(self):
hashFn = OAEPEncryptionPadding().hashFn
hLen = OAEPEncryptionPadding().hashFnOutputBytes
seed = a2b_hex(bytes("aa fd 12 f6 59 ca e6 34 89 b4 79 e5 07 6d de c2 f0 6c b5 8f".replace(' ' ,''),'utf-8'))
#dbmask = dbMask = MGF (seed , 107):
dbmask= a2b_hex(bytes("06 e1 de b2 36 9a a5 a5 c7 07 d8 2c 8e 4e 93 24 8a c7 83 de e0 b2 c0 46\
26 f5 af f9 3e dc fb 25 c9 c2 b3 ff 8a e1 0e 83 9a 2d db 4c dc fe 4f f4\
77 28 b4 a1 b7 c1 36 2b aa d2 9a b4 8d 28 69 d5 02 41 21 43 58 11 59 1b\
e3 92 f9 82 fb 3e 87 d0 95 ae b4 04 48 db 97 2f 3a c1 4e af f4 9c 8c 3b\
7c fc 95 1a 51 ec d1 dd e6 12 64".replace(" ",""),'utf-8'))
a = MGF1(seed, 107, hashFn, hLen)
self.assertEqual(dbmask, a)
[docs] def testSHA1Vector(self):
hashFn = hashFunc('sha1')
V0 = (b"", a2b_hex(bytes("da39a3ee5e6b4b0d3255bfef95601890afd80709",'utf-8')))
V1 = (bytes("The quick brown fox jumps over the lazy dog", 'utf-8'), a2b_hex(bytes("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",'utf-8'))) #ASCII encoding
V2 = (b'The quick brown fox jumps over the lazy dog', a2b_hex(bytes("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",'utf-8'))) #binary data
#print("str => ", V2[0])
#print("H(s)=> ", hashFn(V2[0]))
#print("stnd=> ", V2[1])
self.assertEqual(hashFn(V0[0]), V0[1], 'empty string')
self.assertEqual(hashFn(V1[0]), V1[1], 'quick fox')
self.assertEqual(hashFn(V2[0]), V2[1])
[docs] def testPSSRountTripEquiv(self):
pss = PSSPadding()
m = b'This is a test message'
em = pss.encode(m)
self.assertTrue(pss.verify(m, em))
[docs] def testPSSTestVector(self):
# Test vector taken from http://www.rsa.com/rsalabs/node.asp?id=2125
# ---------------------------------
# Step-by-step RSASSA-PSS Signature
# ---------------------------------
# Message M to be signed:
m = a2b_hex(bytes('85 9e ef 2f d7 8a ca 00 30 8b dc 47 11 93 bf 55\
bf 9d 78 db 8f 8a 67 2b 48 46 34 f3 c9 c2 6e 64\
78 ae 10 26 0f e0 dd 8c 08 2e 53 a5 29 3a f2 17\
3c d5 0c 6d 5d 35 4f eb f7 8b 26 02 1c 25 c0 27\
12 e7 8c d4 69 4c 9f 46 97 77 e4 51 e7 f8 e9 e0\
4c d3 73 9c 6b bf ed ae 48 7f b5 56 44 e9 ca 74\
ff 77 a5 3c b7 29 80 2f 6e d4 a5 ff a8 ba 15 98\
90 fc'.replace(" ", ""),'utf-8'))
# mHash = Hash(M)
# salt = random string of octets
# M' = Padding || mHash || salt
# H = Hash(M')
# DB = Padding || salt
# dbMask = MGF(H, length(DB))
# maskedDB = DB xor dbMask (leftmost bit set to
# zero)
# EM = maskedDB || H || 0xbc
# mHash:
mHash = a2b_hex(bytes('37 b6 6a e0 44 58 43 35 3d 47 ec b0 b4 fd 14 c1\
10 e6 2d 6a'.replace(" ", ""),'utf-8'))
# salt:
salt = a2b_hex(bytes('e3 b5 d5 d0 02 c1 bc e5 0c 2b 65 ef 88 a1 88 d8\
3b ce 7e 61'.replace(" ", ""),'utf-8'))
# M':
mPrime = a2b_hex(bytes('00 00 00 00 00 00 00 00 37 b6 6a e0 44 58 43 35\
3d 47 ec b0 b4 fd 14 c1 10 e6 2d 6a e3 b5 d5 d0\
02 c1 bc e5 0c 2b 65 ef 88 a1 88 d8 3b ce 7e 61'.replace(" ", ""),'utf-8'))
# H:
H = a2b_hex(bytes('df 1a 89 6f 9d 8b c8 16 d9 7c d7 a2 c4 3b ad 54\
6f be 8c fe'.replace(" ", ""),'utf-8'))
# DB:
DB = a2b_hex(bytes('00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
00 00 00 00 00 00 01 e3 b5 d5 d0 02 c1 bc e5 0c\
2b 65 ef 88 a1 88 d8 3b ce 7e 61'.replace(" ", ""),'utf-8'))
# dbMask:
dbMask = a2b_hex(bytes('66 e4 67 2e 83 6a d1 21 ba 24 4b ed 65 76 b8 67\
d9 a4 47 c2 8a 6e 66 a5 b8 7d ee 7f bc 7e 65 af\
50 57 f8 6f ae 89 84 d9 ba 7f 96 9a d6 fe 02 a4\
d7 5f 74 45 fe fd d8 5b 6d 3a 47 7c 28 d2 4b a1\
e3 75 6f 79 2d d1 dc e8 ca 94 44 0e cb 52 79 ec\
d3 18 3a 31 1f c8 97 39 a9 66 43 13 6e 8b 0f 46\
5e 87 a4 53 5c d4 c5 9b 10 02 8d'.replace(" ", ""),'utf-8'))
# maskedDB:
maskedDB = a2b_hex(bytes('66 e4 67 2e 83 6a d1 21 ba 24 4b ed 65 76 b8 67\
d9 a4 47 c2 8a 6e 66 a5 b8 7d ee 7f bc 7e 65 af\
50 57 f8 6f ae 89 84 d9 ba 7f 96 9a d6 fe 02 a4\
d7 5f 74 45 fe fd d8 5b 6d 3a 47 7c 28 d2 4b a1\
e3 75 6f 79 2d d1 dc e8 ca 94 44 0e cb 52 79 ec\
d3 18 3a 31 1f c8 96 da 1c b3 93 11 af 37 ea 4a\
75 e2 4b db fd 5c 1d a0 de 7c ec'.replace(" ", ""),'utf-8'))
# Encoded message EM:
EM = a2b_hex(bytes('66 e4 67 2e 83 6a d1 21 ba 24 4b ed 65 76 b8 67\
d9 a4 47 c2 8a 6e 66 a5 b8 7d ee 7f bc 7e 65 af\
50 57 f8 6f ae 89 84 d9 ba 7f 96 9a d6 fe 02 a4\
d7 5f 74 45 fe fd d8 5b 6d 3a 47 7c 28 d2 4b a1\
e3 75 6f 79 2d d1 dc e8 ca 94 44 0e cb 52 79 ec\
d3 18 3a 31 1f c8 96 da 1c b3 93 11 af 37 ea 4a\
75 e2 4b db fd 5c 1d a0 de 7c ec df 1a 89 6f 9d\
8b c8 16 d9 7c d7 a2 c4 3b ad 54 6f be 8c fe bc'.replace(" ", ""),'utf-8'))
if debug:
print("PSS Test Vector:")
print("M =>", m)
print("Mlen =>", len(m))
print("mHash =>", mHash)
print("salt =>", salt)
print("M' =>", mPrime)
print("H =>", H)
print("DB =>", DB)
print("dbmask=>", dbMask)
print("masked=>", maskedDB)
print("EM =>", EM)
print("EMLen =>", len(EM))
pss = PSSPadding()
realEM = pss.encode(m,len(EM)*8,salt)
self.assertEqual(EM, realEM)
[docs] @classmethod
def suite(self):
suite = unittest.TestLoader().loadTestsFromTestCase(Test)
return suite
[docs]class Pkcs7PaddingTest(unittest.TestCase):
[docs] def setUp(self):
self.padder = PKCS7Padding()
[docs] def encodecode(self,text):
_bytes = bytes(text,'utf-8')
padded = self.padder.encode(_bytes)
assert _bytes == self.padder.decode(padded), 'o: =>%s\nm: =>%s' % (_bytes,padded)
assert len(padded) % 16 == 0 , 'invalid padding length: %s' % (len(padded))
assert len(padded) > 0, 'invalid padding length: %s' % (len(padded))
assert len(padded) > len(_bytes), 'message must allways have padding'
[docs] def testBasic(self):
self.encodecode("asd")
[docs] def testEmpty(self):
self.encodecode("")
[docs] def testFull(self):
self.encodecode("sixteen byte msg")
[docs] def testLarge(self):
self.encodecode("sixteen byte msg +3")
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()