1#!/usr/bin/env python
2
3"""
4(C) 2015,2017,2018,2019 Jack Lloyd
5
6Botan is released under the Simplified BSD License (see license.txt)
7"""
8
9import unittest
10import binascii
11import botan2
12
13def hex_encode(buf):
14    return binascii.hexlify(buf).decode('ascii')
15
16def hex_decode(buf):
17    return binascii.unhexlify(buf.encode('ascii'))
18
19class BotanPythonTests(unittest.TestCase):
20    # pylint: disable=too-many-public-methods,too-many-locals
21
22    def test_version(self):
23        version_str = botan2.version_string()
24        self.assertTrue(version_str.startswith('Botan '))
25
26        self.assertEqual(botan2.version_major(), 2)
27        self.assertGreaterEqual(botan2.version_minor(), 8)
28
29        self.assertGreaterEqual(botan2.ffi_api_version(), 20180713)
30
31    def test_compare(self):
32
33        x = "1234"
34        y = "1234"
35        z = "1233"
36        self.assertTrue(botan2.const_time_compare(x, y))
37        self.assertFalse(botan2.const_time_compare(x, z))
38        self.assertFalse(botan2.const_time_compare(x, x + z))
39
40    def test_block_cipher(self):
41        aes = botan2.BlockCipher("AES-128")
42        self.assertEqual(aes.algo_name(), "AES-128")
43        self.assertEqual(aes.block_size(), 16)
44        self.assertEqual(aes.minimum_keylength(), 16)
45        self.assertEqual(aes.maximum_keylength(), 16)
46
47        aes.set_key(hex_decode("000102030405060708090a0b0c0d0e0f"))
48        ct = aes.encrypt(hex_decode("00112233445566778899aabbccddeeff"))
49
50        self.assertEqual(hex_encode(ct), "69c4e0d86a7b0430d8cdb78070b4c55a")
51
52        pt = aes.decrypt(ct)
53
54        self.assertEqual(hex_encode(pt), "00112233445566778899aabbccddeeff")
55
56    def test_kdf(self):
57
58        secret = hex_decode('6FD4C3C0F38E5C7A6F83E99CD9BD')
59        salt = hex_decode('DBB986')
60        label = hex_decode('')
61        expected = hex_decode('02AEB40A3D4B66FBA540F9D4B20006F2046E0F3A029DEAB201FC692B79EB27CEF7E16069046A')
62
63        produced = botan2.kdf('KDF2(SHA-1)', secret, 38, salt, label)
64
65        self.assertEqual(hex_encode(produced), hex_encode(expected))
66
67    def test_pbkdf(self):
68
69        (salt, iterations, pbkdf) = botan2.pbkdf('PBKDF2(SHA-1)', '', 32, 10000, hex_decode('0001020304050607'))
70
71        self.assertEqual(iterations, 10000)
72        self.assertEqual(hex_encode(pbkdf),
73                         '59b2b1143b4cb1059ec58d9722fb1c72471e0d85c6f7543ba5228526375b0127')
74
75        (salt, iterations, pbkdf) = botan2.pbkdf_timed('PBKDF2(SHA-256)', 'xyz', 32, 200)
76
77        cmp_pbkdf = botan2.pbkdf('PBKDF2(SHA-256)', 'xyz', 32, iterations, salt)[2]
78
79        self.assertEqual(pbkdf, cmp_pbkdf)
80
81    def test_scrypt(self):
82        scrypt = botan2.scrypt(10, '', '', 16, 1, 1)
83        self.assertEqual(hex_encode(scrypt), "77d6576238657b203b19")
84
85        scrypt = botan2.scrypt(32, 'password', 'NaCl', 1024, 8, 16)
86        self.assertEqual(hex_encode(scrypt), "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162")
87
88    def test_bcrypt(self):
89        r = botan2.RandomNumberGenerator()
90        phash = botan2.bcrypt('testing', r)
91        self.assertTrue(isinstance(phash, str))
92        self.assertTrue(phash.startswith("$2a$"))
93
94        self.assertTrue(botan2.check_bcrypt('testing', phash))
95        self.assertFalse(botan2.check_bcrypt('live fire', phash))
96
97        self.assertTrue(botan2.check_bcrypt('test', '$2a$04$wjen1fAA.UW6UxthpKK.huyOoxvCR7ATRCVC4CBIEGVDOCtr8Oj1C'))
98
99    def test_mac(self):
100
101        hmac = botan2.MsgAuthCode('HMAC(SHA-256)')
102        self.assertEqual(hmac.algo_name(), 'HMAC(SHA-256)')
103        self.assertEqual(hmac.minimum_keylength(), 0)
104        self.assertEqual(hmac.maximum_keylength(), 4096)
105        hmac.set_key(hex_decode('0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20'))
106        hmac.update(hex_decode('616263'))
107
108        expected = hex_decode('A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181')
109        produced = hmac.final()
110
111        self.assertEqual(hex_encode(expected), hex_encode(produced))
112
113    def test_rng(self):
114        user_rng = botan2.RandomNumberGenerator("user")
115
116        output1 = user_rng.get(32)
117        output2 = user_rng.get(32)
118
119        self.assertEqual(len(output1), 32)
120        self.assertEqual(len(output2), 32)
121        self.assertNotEqual(output1, output2)
122
123        output3 = user_rng.get(1021)
124        self.assertEqual(len(output3), 1021)
125
126        system_rng = botan2.RandomNumberGenerator('system')
127
128        user_rng.reseed_from_rng(system_rng, 256)
129
130        user_rng.add_entropy('seed material...')
131
132    def test_hash(self):
133
134        try:
135            _h = botan2.HashFunction('NoSuchHash')
136        except botan2.BotanException as e:
137            self.assertEqual(str(e), "botan_hash_init failed: -40 (Not implemented)")
138
139        sha256 = botan2.HashFunction('SHA-256')
140        self.assertEqual(sha256.algo_name(), 'SHA-256')
141        self.assertEqual(sha256.output_length(), 32)
142        self.assertEqual(sha256.block_size(), 64)
143        sha256.update('ignore this please')
144        sha256.clear()
145        sha256.update('a')
146        hash1 = sha256.final()
147
148        self.assertEqual(hex_encode(hash1), "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb")
149
150        sha256.update(hex_decode('61'))
151        sha256_2 = sha256.copy_state()
152        sha256.update(hex_decode('6263'))
153        h2 = sha256.final()
154        self.assertEqual(hex_encode(h2), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
155
156        self.assertEqual(hex_encode(sha256_2.final()), hex_encode(hash1))
157
158    def test_cipher(self):
159        for mode in ['AES-128/CTR-BE', 'Serpent/GCM', 'ChaCha20Poly1305']:
160            enc = botan2.SymmetricCipher(mode, encrypt=True)
161
162            if mode == 'AES-128/CTR-BE':
163                self.assertEqual(enc.algo_name(), 'CTR-BE(AES-128)')
164            elif mode == 'Serpent/GCM':
165                self.assertEqual(enc.algo_name(), 'Serpent/GCM(16)')
166            else:
167                self.assertEqual(enc.algo_name(), mode)
168
169            (kmin, kmax) = enc.key_length()
170
171            self.assertLessEqual(kmin, kmax)
172
173            rng = botan2.RandomNumberGenerator()
174            iv = rng.get(enc.default_nonce_length())
175            key = rng.get(kmax)
176            pt = rng.get(21)
177
178            enc.set_key(key)
179            enc.start(iv)
180
181            update_result = enc.update('')
182            assert not update_result
183
184            ct = enc.finish(pt)
185
186            dec = botan2.SymmetricCipher(mode, encrypt=False)
187            dec.set_key(key)
188            dec.start(iv)
189            decrypted = dec.finish(ct)
190
191            self.assertEqual(decrypted, pt)
192
193
194    def test_mceliece(self):
195        rng = botan2.RandomNumberGenerator()
196        mce_priv = botan2.PrivateKey.create('McEliece', '2960,57', rng)
197        mce_pub = mce_priv.get_public_key()
198        self.assertEqual(mce_pub.estimated_strength(), 128)
199
200        mce_plaintext = rng.get(16)
201        mce_ad = rng.get(48)
202        mce_ciphertext = botan2.mceies_encrypt(mce_pub, rng, 'ChaCha20Poly1305', mce_plaintext, mce_ad)
203
204        mce_decrypt = botan2.mceies_decrypt(mce_priv, 'ChaCha20Poly1305', mce_ciphertext, mce_ad)
205
206        self.assertEqual(mce_plaintext, mce_decrypt)
207
208    def test_rsa_load_store(self):
209
210        rsa_priv_pem = """-----BEGIN PRIVATE KEY-----
211MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALWtiBjcofJW/4+r
212CIjQZn2V3yCYsNIBpMdVkNPr36FZ3ZHGSv2ggmCe+IWy0fTcBVyP+fo3HC8zmOC2
213EsYDFRExyB2zIsjRXlPrVrTfcyXwUEaInLJQId5CguFrmyj1y7K43ezg+OTop39n
214TyaukrciCSCh++Q/UQOanHnR8ctrAgMBAAECgYBPfKySgBmk31ZyA7k4rsFgye01
215JEkcoNZ41iGG7ujJffl4maLew9a3MmZ2jI3azVbVMDMFPA5rQm5tRowBMYEJ5oBc
216LP4AP41Lujfa+vua6l3t94bAV+CufZiY0297FcPbGqNu+xSQ2Bol2uHh9mrcgQUs
217fevA50KOLR9hv4zH6QJBAPCOKiExONtVhJn8qVPCBlJ8Vjjnt9Uno5EzMBAKMbZi
218OySkGwo9/9LUWO03r7tjrGSy5jJk+iOrcLeDl6zETfkCQQDBV6PpD/3ccQ1IfWcw
219jG8yik0bIuXgrD0uW4g8Cvj+05wrv7RYPHuFtj3Rtb94YjtgYn7QvjH7y88XmTC4
2202k2DAkEA4E9Ae7kBUoz42/odDswyxwHICMIRyoJu5Ht9yscmufH5Ql6AFFnhzf9S
221eMjfZfY4j6G+Q6mjElXQAl+DtIdMSQJBAJzdMkuBggI8Zv6NYA9voThsJSsDIWcr
22212epM9sjO+nkXizQmM2OJNnThkyDHRna+Tm2MBXEemFEdn06+ODBnWkCQQChAbG4
223255RiCuYdrfiTPF/WLtvRyGd1LRwHcYIW4mJFPzxYAMTwQKbppLAnxw73vyef/zC
2242BgXEW02tjRBtgZ+
225-----END PRIVATE KEY-----
226"""
227
228        rsa_pub_pem = """-----BEGIN PUBLIC KEY-----
229MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1rYgY3KHyVv+PqwiI0GZ9ld8g
230mLDSAaTHVZDT69+hWd2Rxkr9oIJgnviFstH03AVcj/n6NxwvM5jgthLGAxURMcgd
231syLI0V5T61a033Ml8FBGiJyyUCHeQoLha5so9cuyuN3s4Pjk6Kd/Z08mrpK3Igkg
232ofvkP1EDmpx50fHLawIDAQAB
233-----END PUBLIC KEY-----
234"""
235
236        rsapriv = botan2.PrivateKey.load(rsa_priv_pem)
237
238        self.assertEqual(rsapriv.to_pem(), rsa_priv_pem)
239
240        rsapub = rsapriv.get_public_key()
241        self.assertEqual(rsapub.to_pem(), rsa_pub_pem)
242
243        rsapub = botan2.PublicKey.load(rsa_pub_pem)
244        self.assertEqual(rsapub.to_pem(), rsa_pub_pem)
245
246        n = 0xB5AD8818DCA1F256FF8FAB0888D0667D95DF2098B0D201A4C75590D3EBDFA159DD91C64AFDA082609EF885B2D1F4DC055C8FF9FA371C2F3398E0B612C603151131C81DB322C8D15E53EB56B4DF7325F05046889CB25021DE4282E16B9B28F5CBB2B8DDECE0F8E4E8A77F674F26AE92B7220920A1FBE43F51039A9C79D1F1CB6B # pylint: disable=line-too-long
247        e = 0x10001
248
249        rsapub2 = botan2.PublicKey.load_rsa(n, e)
250        self.assertEqual(rsapub2.to_pem(), rsa_pub_pem)
251
252        self.assertEqual(rsapub2.get_field("n"), n)
253        self.assertEqual(rsapub2.get_field("e"), e)
254
255    def test_key_crypto(self):
256        rng = botan2.RandomNumberGenerator()
257        priv = botan2.PrivateKey.create('RSA', '1024', rng)
258        passphrase = "super secret tell noone"
259
260        for is_pem in [True, False]:
261            ref_val = priv.export(is_pem)
262
263            enc1 = priv.export_encrypted(passphrase, rng, True, msec=10)
264            dec1 = botan2.PrivateKey.load(enc1, passphrase)
265            self.assertEqual(dec1.export(is_pem), ref_val)
266
267            pem2 = priv.export_encrypted(passphrase, rng, True, msec=10, cipher="AES-128/SIV")
268            dec2 = botan2.PrivateKey.load(pem2, passphrase)
269            self.assertEqual(dec2.export(is_pem), ref_val)
270
271            pem3 = priv.export_encrypted(passphrase, rng, True, msec=10, cipher="AES-128/GCM", pbkdf="Scrypt")
272            dec3 = botan2.PrivateKey.load(pem3, passphrase)
273            self.assertEqual(dec3.export(is_pem), ref_val)
274
275    def test_check_key(self):
276        # valid (if rather small) RSA key
277        n = 273279220906618527352827457840955116141
278        e = 0x10001
279
280        rng = botan2.RandomNumberGenerator()
281
282        rsapub = botan2.PublicKey.load_rsa(n, e)
283        self.assertTrue(rsapub.check_key(rng))
284
285        # invalid
286        try:
287            rsapub = botan2.PublicKey.load_rsa(n - 1, e)
288        except botan2.BotanException as e:
289            self.assertEqual(str(e), "botan_pubkey_load_rsa failed: -1 (Invalid input)")
290
291    def test_rsa(self):
292        # pylint: disable=too-many-locals
293        rng = botan2.RandomNumberGenerator()
294        rsapriv = botan2.PrivateKey.create('RSA', '1024', rng)
295        self.assertEqual(rsapriv.algo_name(), 'RSA')
296
297        priv_pem = rsapriv.to_pem()
298        priv_der = rsapriv.to_der()
299
300        self.assertEqual(priv_pem[0:28], "-----BEGIN PRIVATE KEY-----\n")
301        self.assertGreater(len(priv_pem), len(priv_der))
302
303        rsapub = rsapriv.get_public_key()
304        self.assertEqual(rsapub.algo_name(), 'RSA')
305        self.assertEqual(rsapub.estimated_strength(), 80)
306
307        pub_pem = rsapub.to_pem()
308        pub_der = rsapub.to_der()
309
310        self.assertEqual(pub_pem[0:27], "-----BEGIN PUBLIC KEY-----\n")
311        self.assertGreater(len(pub_pem), len(pub_der))
312
313        enc = botan2.PKEncrypt(rsapub, "OAEP(SHA-256)")
314        dec = botan2.PKDecrypt(rsapriv, "OAEP(SHA-256)")
315
316        symkey = rng.get(32)
317        ctext = enc.encrypt(symkey, rng)
318
319        ptext = dec.decrypt(ctext)
320
321        self.assertEqual(ptext, symkey)
322
323        signer = botan2.PKSign(rsapriv, 'EMSA4(SHA-384)')
324
325        signer.update('messa')
326        signer.update('ge')
327        sig = signer.finish(botan2.RandomNumberGenerator())
328
329        verify = botan2.PKVerify(rsapub, 'EMSA4(SHA-384)')
330
331        verify.update('mess')
332        verify.update('age')
333        self.assertTrue(verify.check_signature(sig))
334
335        verify.update('mess of things')
336        verify.update('age')
337        self.assertFalse(verify.check_signature(sig))
338
339        verify.update('message')
340        self.assertTrue(verify.check_signature(sig))
341
342    def test_ecdsa(self):
343        rng = botan2.RandomNumberGenerator()
344
345        hash_fn = 'EMSA1(SHA-256)'
346        group = 'secp256r1'
347        msg = 'test message'
348
349        priv = botan2.PrivateKey.create('ECDSA', group, rng)
350        pub = priv.get_public_key()
351        self.assertEqual(pub.get_field('public_x'), priv.get_field('public_x'))
352        self.assertEqual(pub.get_field('public_y'), priv.get_field('public_y'))
353
354        signer = botan2.PKSign(priv, hash_fn, True)
355        signer.update(msg)
356        signature = signer.finish(rng)
357
358        verifier = botan2.PKVerify(pub, hash_fn)
359        verifier.update(msg)
360        #fails because DER/not-DER mismatch
361        self.assertFalse(verifier.check_signature(signature))
362
363        verifier = botan2.PKVerify(pub, hash_fn, True)
364        verifier.update(msg)
365        self.assertTrue(verifier.check_signature(signature))
366
367        pub_x = pub.get_field('public_x')
368        pub_y = priv.get_field('public_y')
369        pub2 = botan2.PublicKey.load_ecdsa(group, pub_x, pub_y)
370        verifier = botan2.PKVerify(pub2, hash_fn, True)
371        verifier.update(msg)
372        self.assertTrue(verifier.check_signature(signature))
373
374        priv2 = botan2.PrivateKey.load_ecdsa(group, priv.get_field('x'))
375        signer = botan2.PKSign(priv2, hash_fn, True)
376        # sign empty message
377        signature = signer.finish(rng)
378
379        # verify empty message
380        self.assertTrue(verifier.check_signature(signature))
381
382    def test_sm2(self):
383        rng = botan2.RandomNumberGenerator()
384
385        hash_fn = 'EMSA1(SM3)'
386        group = 'sm2p256v1'
387        msg = 'test message'
388
389        priv = botan2.PrivateKey.create('SM2', group, rng)
390        pub = priv.get_public_key()
391        self.assertEqual(pub.get_field('public_x'), priv.get_field('public_x'))
392        self.assertEqual(pub.get_field('public_y'), priv.get_field('public_y'))
393
394        signer = botan2.PKSign(priv, hash_fn)
395        signer.update(msg)
396        signature = signer.finish(rng)
397
398        verifier = botan2.PKVerify(pub, hash_fn)
399        verifier.update(msg)
400        self.assertTrue(verifier.check_signature(signature))
401
402        pub_x = pub.get_field('public_x')
403        pub_y = priv.get_field('public_y')
404        pub2 = botan2.PublicKey.load_sm2(group, pub_x, pub_y)
405        verifier = botan2.PKVerify(pub2, hash_fn)
406        verifier.update(msg)
407        self.assertTrue(verifier.check_signature(signature))
408
409        priv2 = botan2.PrivateKey.load_sm2(group, priv.get_field('x'))
410        signer = botan2.PKSign(priv2, hash_fn)
411        # sign empty message
412        signature = signer.finish(rng)
413
414        # verify empty message
415        self.assertTrue(verifier.check_signature(signature))
416
417    def test_ecdh(self):
418        # pylint: disable=too-many-locals
419        a_rng = botan2.RandomNumberGenerator('user')
420        b_rng = botan2.RandomNumberGenerator('user')
421
422        kdf = 'KDF2(SHA-384)'
423
424        for grp in ['secp256r1', 'secp384r1', 'brainpool256r1']:
425            a_priv = botan2.PrivateKey.create('ECDH', grp, a_rng)
426            b_priv = botan2.PrivateKey.create('ECDH', grp, b_rng)
427
428            a_op = botan2.PKKeyAgreement(a_priv, kdf)
429            b_op = botan2.PKKeyAgreement(b_priv, kdf)
430
431            a_pub = a_op.public_value()
432            b_pub = b_op.public_value()
433
434            salt = a_rng.get(8) + b_rng.get(8)
435
436            a_key = a_op.agree(b_pub, 32, salt)
437            b_key = b_op.agree(a_pub, 32, salt)
438
439            self.assertEqual(a_key, b_key)
440
441            a_pem = a_priv.to_pem()
442
443            a_priv_x = a_priv.get_field('x')
444
445            new_a = botan2.PrivateKey.load_ecdh(grp, a_priv_x)
446
447            self.assertEqual(a_pem, new_a.to_pem())
448
449    def test_certs(self):
450        # pylint: disable=too-many-statements
451        cert = botan2.X509Cert(filename="src/tests/data/x509/ecc/CSCA.CSCA.csca-germany.1.crt")
452        pubkey = cert.subject_public_key()
453
454        self.assertEqual(pubkey.algo_name(), 'ECDSA')
455        self.assertEqual(pubkey.estimated_strength(), 112)
456
457        self.assertEqual(cert.fingerprint("SHA-1"),
458                         "32:42:1C:C3:EC:54:D7:E9:43:EC:51:F0:19:23:BD:85:1D:F2:1B:B9")
459
460        self.assertEqual(hex_encode(cert.serial_number()), "01")
461        self.assertEqual(hex_encode(cert.authority_key_id()),
462                         "0096452de588f966c4ccdf161dd1f3f5341b71e7")
463
464        self.assertEqual(cert.subject_dn('Name', 0), 'csca-germany')
465        self.assertEqual(cert.subject_dn('Email', 0), 'csca-germany@bsi.bund.de')
466        self.assertEqual(cert.subject_dn('Organization', 0), 'bund')
467        self.assertEqual(cert.subject_dn('Organizational Unit', 0), 'bsi')
468        self.assertEqual(cert.subject_dn('Country', 0), 'DE')
469
470        self.assertTrue(cert.to_string().startswith("Version: 3"))
471
472        self.assertEqual(cert.issuer_dn('Name', 0), 'csca-germany')
473        self.assertEqual(cert.issuer_dn('Organization', 0), 'bund')
474        self.assertEqual(cert.issuer_dn('Organizational Unit', 0), 'bsi')
475        self.assertEqual(cert.issuer_dn('Country', 0), 'DE')
476
477        self.assertTrue(cert.hostname_match('csca-germany'))
478        self.assertFalse(cert.hostname_match('csca-slovakia'))
479
480        self.assertEqual(cert.not_before(), 1184858838)
481        self.assertEqual(cert.not_after(), 1831907880)
482
483        self.assertTrue(cert.allowed_usage(["CRL_SIGN", "KEY_CERT_SIGN"]))
484        self.assertTrue(cert.allowed_usage(["KEY_CERT_SIGN"]))
485        self.assertFalse(cert.allowed_usage(["DIGITAL_SIGNATURE"]))
486        self.assertFalse(cert.allowed_usage(["DIGITAL_SIGNATURE", "CRL_SIGN"]))
487
488        root = botan2.X509Cert("src/tests/data/x509/nist/root.crt")
489
490        int09 = botan2.X509Cert("src/tests/data/x509/nist/test09/int.crt")
491        end09 = botan2.X509Cert("src/tests/data/x509/nist/test09/end.crt")
492        self.assertEqual(end09.verify([int09], [root]), 2001)
493
494        end04 = botan2.X509Cert("src/tests/data/x509/nist/test04/end.crt")
495        int04_1 = botan2.X509Cert("src/tests/data/x509/nist/test04/int1.crt")
496        int04_2 = botan2.X509Cert("src/tests/data/x509/nist/test04/int2.crt")
497        self.assertEqual(end04.verify([int04_1, int04_2], [], "src/tests/data/x509/nist/", required_strength=80), 0)
498        self.assertEqual(end04.verify([int04_1, int04_2], [], required_strength=80), 3000)
499        self.assertEqual(end04.verify([int04_1, int04_2], [root], required_strength=80, hostname="User1-CP.02.01"), 0)
500        self.assertEqual(end04.verify([int04_1, int04_2], [root], required_strength=80, hostname="invalid"), 4008)
501        self.assertEqual(end04.verify([int04_1, int04_2], [root], required_strength=80, reference_time=1), 2000)
502
503        self.assertEqual(botan2.X509Cert.validation_status(0), 'Verified')
504        self.assertEqual(botan2.X509Cert.validation_status(3000), 'Certificate issuer not found')
505        self.assertEqual(botan2.X509Cert.validation_status(4008), 'Certificate does not match provided name')
506
507        rootcrl = botan2.X509CRL("src/tests/data/x509/nist/root.crl")
508
509        end01 = botan2.X509Cert("src/tests/data/x509/nist/test01/end.crt")
510        self.assertEqual(end01.verify([], [root], required_strength=80, crls=[rootcrl]), 0)
511
512        int20 = botan2.X509Cert("src/tests/data/x509/nist/test20/int.crt")
513        end20 = botan2.X509Cert("src/tests/data/x509/nist/test20/end.crt")
514        int20crl = botan2.X509CRL("src/tests/data/x509/nist/test20/int.crl")
515
516        self.assertEqual(end20.verify([int20], [root], required_strength=80, crls=[int20crl, rootcrl]), 5000)
517        self.assertEqual(botan2.X509Cert.validation_status(5000), 'Certificate is revoked')
518
519        int21 = botan2.X509Cert("src/tests/data/x509/nist/test21/int.crt")
520        end21 = botan2.X509Cert("src/tests/data/x509/nist/test21/end.crt")
521        int21crl = botan2.X509CRL("src/tests/data/x509/nist/test21/int.crl")
522        self.assertEqual(end21.verify([int21], [root], required_strength=80, crls=[int21crl, rootcrl]), 5000)
523
524        self.assertTrue(int20.is_revoked(rootcrl))
525        self.assertFalse(int04_1.is_revoked(rootcrl))
526        self.assertTrue(end21.is_revoked(int21crl))
527
528
529    def test_mpi(self):
530        # pylint: disable=too-many-statements,too-many-locals
531        z = botan2.MPI()
532        self.assertEqual(z.bit_count(), 0)
533        five = botan2.MPI('5')
534        self.assertEqual(five.bit_count(), 3)
535        big = botan2.MPI('0x85839682368923476892367235')
536        self.assertEqual(big.bit_count(), 104)
537        small = botan2.MPI(0xDEADBEEF)
538        radix = botan2.MPI("DEADBEEF", 16)
539
540        self.assertEqual(hex_encode(small.to_bytes()), "deadbeef")
541        self.assertEqual(hex_encode(big.to_bytes()), "85839682368923476892367235")
542
543        self.assertEqual(int(small), 0xDEADBEEF)
544        self.assertEqual(int(radix), int(small))
545
546        self.assertEqual(int(small >> 16), 0xDEAD)
547
548        small >>= 15
549
550        self.assertEqual(int(small), 0x1BD5B)
551
552        small <<= 15
553
554        self.assertEqual(int(small), 0xDEAD8000)
555
556        ten = botan2.MPI(10)
557
558        self.assertEqual(ten, five + five)
559        self.assertNotEqual(ten, five)
560        self.assertLess(five, ten)
561        self.assertLessEqual(five, ten)
562
563        x = botan2.MPI(five)
564
565        self.assertEqual(x, five)
566
567        x += botan2.MPI(1)
568        self.assertNotEqual(x, five)
569
570        self.assertEqual(int(x * five), 30)
571
572        x *= five
573        x *= five
574        self.assertEqual(int(x), 150)
575
576        self.assertTrue(not x.is_negative())
577
578        x.flip_sign()
579        self.assertTrue(x.is_negative())
580        self.assertEqual(int(x), -150)
581
582        x.flip_sign()
583
584        x.set_bit(0)
585        self.assertTrue(int(x), 151)
586        self.assertTrue(x.get_bit(0))
587        self.assertTrue(x.get_bit(4))
588        self.assertFalse(x.get_bit(6))
589
590        x.clear_bit(4)
591        self.assertEqual(int(x), 135)
592
593        rng = botan2.RandomNumberGenerator()
594        self.assertFalse(x.is_prime(rng))
595
596        two = botan2.MPI(2)
597
598        x += two
599        self.assertTrue(x.is_prime(rng))
600
601        mod = x + two
602
603        inv = x.inverse_mod(mod)
604        self.assertEqual(int(inv), 69)
605        self.assertEqual(int((inv * x) % mod), 1)
606
607        p = inv.pow_mod(botan2.MPI(46), mod)
608        self.assertEqual(int(p), 42)
609
610        one = botan2.MPI(1)
611        twelve = botan2.MPI("C", 16)
612        eight = botan2.MPI(8)
613
614        mul = twelve.mod_mul(eight, inv)
615        self.assertEqual(int(mul), 27)
616
617        gcd = one.gcd(one)
618        self.assertEqual(one, gcd)
619        gcd = one.gcd(twelve)
620        self.assertEqual(one, gcd)
621        gcd = twelve.gcd(eight)
622        self.assertEqual(4, int(gcd))
623
624    def test_mpi_random(self):
625        rng = botan2.RandomNumberGenerator()
626
627        u = botan2.MPI.random(rng, 512)
628        self.assertEqual(u.bit_count(), 512)
629
630        l = u >> 32
631        self.assertEqual(l.bit_count(), 512-32)
632
633        for _i in range(10):
634            x = botan2.MPI.random_range(rng, l, u)
635            self.assertLess(x, u)
636            self.assertGreater(x, l)
637
638    def test_fpe(self):
639
640        modulus = botan2.MPI('1000000000')
641        key = b'001122334455'
642
643        fpe = botan2.FormatPreservingEncryptionFE1(modulus, key)
644
645        value = botan2.MPI('392910392')
646        tweak = 'tweak value'
647
648        ctext = fpe.encrypt(value, tweak)
649
650        ptext = fpe.decrypt(ctext, tweak)
651
652        self.assertEqual(value, ptext)
653
654    def test_keywrap(self):
655        key = hex_decode('00112233445566778899aabbccddeeff')
656        kek = hex_decode('000102030405060708090a0b0c0d0e0f')
657
658        wrapped = botan2.nist_key_wrap(kek, key)
659        self.assertEqual(hex_encode(wrapped), '1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5')
660
661        self.assertEqual(len(wrapped), 16+8)
662        unwrapped = botan2.nist_key_unwrap(kek, wrapped)
663        self.assertEqual(hex_encode(unwrapped), '00112233445566778899aabbccddeeff')
664
665    def test_hotp(self):
666
667        hotp = botan2.HOTP(b'12345678901234567890')
668
669        self.assertEqual(hotp.generate(0), 755224)
670        self.assertEqual(hotp.generate(1), 287082)
671        self.assertEqual(hotp.generate(9), 520489)
672
673        self.assertEqual(hotp.check(520489, 8), (False, 8))
674        self.assertEqual(hotp.check(520489, 8, 1), (True, 10))
675        self.assertEqual(hotp.check(520489, 7, 2), (True, 10))
676        self.assertEqual(hotp.check(520489, 0, 9), (True, 10))
677
678    def test_totp(self):
679
680        totp = botan2.TOTP(b'12345678901234567890', digest="SHA-1", digits=8)
681
682        self.assertEqual(totp.generate(59), 94287082)
683        self.assertEqual(totp.generate(1111111109), 7081804)
684        self.assertEqual(totp.generate(1111111111), 14050471)
685        self.assertEqual(totp.generate(1234567890), 89005924)
686        self.assertEqual(totp.generate(1234567890), 89005924)
687        self.assertEqual(totp.generate(2000000000), 69279037)
688
689        self.assertTrue(totp.check(7081804, 1111111109))
690        self.assertTrue(totp.check(7081804, 1111111109 - 29))
691        self.assertFalse(totp.check(7081804, 1111111109 + 1))
692        self.assertTrue(totp.check(7081804, 1111111109 + 30, 1))
693
694if __name__ == '__main__':
695    unittest.main()
696