11import base64
22import datetime
33import os
4+ import struct
45import tempfile
56import unittest
6- from unittest import TestCase
7+ from unittest import TestCase , mock
8+
9+ from cryptography .exceptions import UnsupportedAlgorithm
710
811from cryptography .hazmat .primitives .asymmetric .rsa import RSAPublicNumbers , RSAPrivateNumbers
912from pyasn1 .codec .der import decoder as der_decoder
254257 'seed_block_key' : bytes .fromhex ('01 02 02 01 03 05 05 03 09 08 08 09 11 33 33 11' .replace (' ' , '' )),
255258}
256259
260+ CERT_PUBKEY_DER = base64 .b64decode (TEST_CERT ['signCert' ])
261+ CERT_PRIKEY_DER = base64 .b64decode (TEST_CERT ['signPri' ])
262+ CERT_P12_DATA = base64 .b64decode (TEST_CERT ['pfx' ])
263+ CERT_PASSWORD = TEST_CERT ['signPw' ]
264+
257265
258266class TestPinkSign (TestCase ):
259267 def setUp (self ) -> None :
260268 """
261269 Load test certificate
262270 :return: None
263271 """
264- self .c = PinkSign (pubkey_data = base64 . b64decode ( TEST_CERT [ 'signCert' ]),
265- prikey_data = base64 . b64decode ( TEST_CERT [ 'signPri' ]) ,
266- prikey_password = TEST_CERT [ 'signPw' ]
267- )
268- pass
272+ self .c = PinkSign (
273+ pubkey_data = CERT_PUBKEY_DER ,
274+ prikey_data = CERT_PRIKEY_DER ,
275+ prikey_password = CERT_PASSWORD ,
276+ )
269277
270278 def test_load_pubkey (self ):
271279 cert = PinkSign ()
272- cert .load_pubkey (pubkey_data = base64 . b64decode ( TEST_CERT [ 'signCert' ]) )
280+ cert .load_pubkey (pubkey_data = CERT_PUBKEY_DER )
273281 expected = RSAPublicNumbers (e = 65537 , n = TEST_CERT ['n' ])
274282 self .assertEqual (expected , cert .pubkey .public_numbers ())
275283
276284 def test_load_prikey (self ):
277- cert = PinkSign (pubkey_data = base64 .b64decode (TEST_CERT ['signCert' ]),
278- prikey_data = base64 .b64decode (TEST_CERT ['signPri' ]),
279- prikey_password = TEST_CERT ['signPw' ]
280- )
285+ cert = PinkSign (
286+ pubkey_data = CERT_PUBKEY_DER ,
287+ prikey_data = CERT_PRIKEY_DER ,
288+ prikey_password = CERT_PASSWORD ,
289+ )
281290 cert .load_prikey ()
282291 expected_public_numbers = RSAPublicNumbers (e = 65537 , n = TEST_CERT ['n' ])
283292 expected = RSAPrivateNumbers (p = TEST_CERT ['p' ], q = TEST_CERT ['q' ], d = TEST_CERT ['d' ], dmp1 = TEST_CERT ['dmp1' ],
@@ -286,8 +295,7 @@ def test_load_prikey(self):
286295 self .assertEqual (expected , cert .prikey .private_numbers ())
287296
288297 def test_load_p12 (self ):
289- cert = PinkSign (p12_data = base64 .b64decode (TEST_CERT ['pfx' ]),
290- prikey_password = TEST_CERT ['signPw' ])
298+ cert = PinkSign (p12_data = CERT_P12_DATA , prikey_password = CERT_PASSWORD )
291299 expected_public_numbers = RSAPublicNumbers (e = 65537 , n = TEST_CERT ['n' ])
292300 self .assertEqual (cert .pubkey .public_numbers (), expected_public_numbers )
293301 expected = RSAPrivateNumbers (p = TEST_CERT ['p' ], q = TEST_CERT ['q' ], d = TEST_CERT ['d' ], dmp1 = TEST_CERT ['dmp1' ],
@@ -296,13 +304,13 @@ def test_load_p12(self):
296304 self .assertEqual (expected , cert .prikey .private_numbers ())
297305
298306 def test_load_12_file (self ):
299- f = tempfile .NamedTemporaryFile (delete = False )
300- f .write (base64 .b64decode (TEST_CERT ['pfx' ]))
301- f .close ()
302- cert = PinkSign (p12_path = f .name , prikey_password = TEST_CERT ['signPw' ])
307+ with tempfile .NamedTemporaryFile (delete = False ) as f :
308+ f .write (CERT_P12_DATA )
309+ temp_path = f .name
310+ self .addCleanup (lambda : os .path .exists (temp_path ) and os .unlink (temp_path ))
311+ cert = PinkSign (p12_path = temp_path , prikey_password = CERT_PASSWORD )
303312 signed = cert .sign (msg = b'1' )
304313 cert .verify (signature = signed , msg = b'1' )
305- os .unlink (f .name )
306314 expected_public_numbers = RSAPublicNumbers (e = 65537 , n = TEST_CERT ['n' ])
307315 self .assertEqual (cert .pubkey .public_numbers (), expected_public_numbers )
308316 expected = RSAPrivateNumbers (p = TEST_CERT ['p' ], q = TEST_CERT ['q' ], d = TEST_CERT ['d' ], dmp1 = TEST_CERT ['dmp1' ],
@@ -441,40 +449,49 @@ def test_seed_cbc_128_decrypt_pure(self):
441449 def test_seed_generator (self ):
442450 self .assertEqual (16 , len (seed_generator (16 )))
443451 self .assertIsInstance (seed_generator (16 ), bytes )
452+ self .assertTrue (all (b != 0 for b in seed_generator (64 )))
453+ self .assertEqual (b'' , seed_generator (0 ))
444454
445455 def test_separate_p12_into_npki (self ):
446456 expected = tuple ((base64 .b64decode (TEST_CERT ['signCert' ]), base64 .b64decode (TEST_CERT ['plainSignPri' ])))
447- self .assertEqual (expected , separate_p12_into_npki (base64 . b64decode ( TEST_CERT [ 'pfx' ]), TEST_CERT [ 'signPw' ] ))
457+ self .assertEqual (expected , separate_p12_into_npki (CERT_P12_DATA , CERT_PASSWORD ))
448458
449459 def test_inject_rand_in_plain_prikey (self ):
450460 expected = TEST_CERT ['plainSignPriFullB64' ]
451461 self .assertEqual (expected , inject_rand_in_plain_prikey (TEST_CERT ['plainSignPri' ], TEST_CERT ['r' ]))
452462
453463 def test_encrypt_decrypted_prikey (self ):
454464 expected = TEST_CERT ['signPri' ]
455- self .assertEqual (expected , encrypt_decrypted_prikey (TEST_CERT ['plainSignPriFullB64' ], TEST_CERT [ 'signPw' ] ,
465+ self .assertEqual (expected , encrypt_decrypted_prikey (TEST_CERT ['plainSignPriFullB64' ], CERT_PASSWORD ,
456466 TEST_CERT ['signPriSalt' ]))
457467
458468 def test_set_key (self ):
459469 expected = TEST_DATA ['seed_key' ]
460- self .assertEqual (expected , set_key (bytes (16 )), expected )
470+ self .assertEqual (expected , set_key (bytes (16 )))
461471
462472 def test_process_block (self ):
463- expected = bytearray (TEST_DATA ['seed_block_cipher' ])
464- self .assertEqual (expected , process_block (True , TEST_DATA ['seed_block_key' ], TEST_DATA ['seed_block_plain' ]),
465- expected )
466-
467- expected = bytearray (TEST_DATA ['seed_block_plain' ])
468- self .assertEqual (expected , process_block (False , TEST_DATA ['seed_block_key' ], TEST_DATA ['seed_block_cipher' ]),
469- expected )
473+ expected_cipher = bytearray (TEST_DATA ['seed_block_cipher' ])
474+ self .assertEqual (
475+ expected_cipher ,
476+ process_block (True , TEST_DATA ['seed_block_key' ], TEST_DATA ['seed_block_plain' ]),
477+ )
478+
479+ expected_plain = bytearray (TEST_DATA ['seed_block_plain' ])
480+ self .assertEqual (
481+ expected_plain ,
482+ process_block (False , TEST_DATA ['seed_block_key' ], TEST_DATA ['seed_block_cipher' ]),
483+ )
470484
471485 def test_process_block_openssl (self ):
472486 expected = TEST_DATA ['seed_block_cipher' ]
473- self .assertEqual (expected ,
474- seed_cbc_128_encrypt_openssl (
475- TEST_DATA ['seed_block_key' ],
476- TEST_DATA ['seed_block_plain' ],
477- bytes (16 ))[:16 ], expected )
487+ self .assertEqual (
488+ expected ,
489+ seed_cbc_128_encrypt_openssl (
490+ TEST_DATA ['seed_block_key' ],
491+ TEST_DATA ['seed_block_plain' ],
492+ bytes (16 ),
493+ )[:16 ],
494+ )
478495
479496 def test_pure_vs_openssl (self ):
480497 for i in range (100 ):
@@ -488,5 +505,90 @@ def test_pure_vs_openssl(self):
488505 self .assertEqual (dec , random_bytes )
489506
490507
508+ class TestPinkSignValidation (TestCase ):
509+ def test_load_pubkey_requires_source (self ):
510+ cert = PinkSign ()
511+ with self .assertRaises (ValueError ):
512+ cert .load_pubkey ()
513+
514+ def test_load_prikey_requires_pubkey (self ):
515+ cert = PinkSign ()
516+ with self .assertRaises (ValueError ):
517+ cert .load_prikey (prikey_path = 'dummy' , prikey_password = CERT_PASSWORD )
518+
519+ def test_load_prikey_requires_password (self ):
520+ cert = PinkSign (pubkey_data = CERT_PUBKEY_DER , prikey_data = CERT_PRIKEY_DER )
521+ with self .assertRaises (ValueError ):
522+ cert .load_prikey ()
523+
524+ def test_cert_type_oid_requires_pubkey (self ):
525+ cert = PinkSign ()
526+ with self .assertRaises (ValueError ):
527+ cert .cert_type_oid ()
528+
529+ def test_valid_date_requires_pubkey (self ):
530+ cert = PinkSign ()
531+ with self .assertRaises (ValueError ):
532+ cert .valid_date ()
533+
534+ def test_serialnum_requires_pubkey (self ):
535+ cert = PinkSign ()
536+ with self .assertRaises (ValueError ):
537+ cert .serialnum ()
538+
539+ def test_sign_requires_private_key (self ):
540+ cert = PinkSign (pubkey_data = CERT_PUBKEY_DER )
541+ with self .assertRaises (ValueError ):
542+ cert .sign (b'data' )
543+
544+ def test_decrypt_requires_private_key (self ):
545+ cert = PinkSign ()
546+ with self .assertRaises (ValueError ):
547+ cert .decrypt (b'data' )
548+
549+ def test_verify_requires_public_key (self ):
550+ cert = PinkSign ()
551+ with self .assertRaises (ValueError ):
552+ cert .verify (b'sig' , b'msg' )
553+
554+ def test_encrypt_requires_public_key (self ):
555+ cert = PinkSign ()
556+ with self .assertRaises (ValueError ):
557+ cert .encrypt (b'msg' )
558+
559+
560+ class TestSeedInternals (TestCase ):
561+ def test_process_block_requires_key (self ):
562+ with self .assertRaises (ValueError ):
563+ process_block (True , None , TEST_DATA ['seed_block_plain' ])
564+
565+ def test_process_block_requires_block_size (self ):
566+ with self .assertRaises (ValueError ):
567+ process_block (True , TEST_DATA ['seed_block_key' ], b'\x00 ' )
568+
569+ def test_set_key_requires_full_block (self ):
570+ with self .assertRaises (struct .error ):
571+ set_key (b'\x00 ' * 15 )
572+
573+
574+ class TestSeedFallbacks (TestCase ):
575+ def test_seed_encrypt_falls_back_to_pure (self ):
576+ expected = seed_cbc_128_encrypt_pure (TEST_DATA ['key' ], TEST_DATA ['plaintext' ], TEST_DATA ['iv' ])
577+ with mock .patch ('pypinksign.pypinksign.seed_cbc_128_encrypt_openssl' , side_effect = UnsupportedAlgorithm ("seed" )):
578+ self .assertEqual (
579+ expected ,
580+ seed_cbc_128_encrypt (TEST_DATA ['key' ], TEST_DATA ['plaintext' ], TEST_DATA ['iv' ]),
581+ )
582+
583+ def test_seed_decrypt_falls_back_to_pure (self ):
584+ ciphertext = seed_cbc_128_encrypt_pure (TEST_DATA ['key' ], TEST_DATA ['plaintext' ], TEST_DATA ['iv' ])
585+ expected = seed_cbc_128_decrypt_pure (TEST_DATA ['key' ], ciphertext , TEST_DATA ['iv' ])
586+ with mock .patch ('pypinksign.pypinksign.seed_cbc_128_decrypt_openssl' , side_effect = UnsupportedAlgorithm ("seed" )):
587+ self .assertEqual (
588+ expected ,
589+ seed_cbc_128_decrypt (TEST_DATA ['key' ], ciphertext , TEST_DATA ['iv' ]),
590+ )
591+
592+
491593if __name__ == '__main__' :
492594 unittest .main ()
0 commit comments