test_write.doctest - tests for writing PSKC files Copyright (C) 2014-2016 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from pskc import PSKC >>> import datetime >>> import sys >>> import tempfile >>> from Crypto import Random >>> from binascii import a2b_hex >>> from dateutil.tz import tzutc Build a PSKC structure. >>> pskc = PSKC() Add a key with all attributes set. >>> key = pskc.add_key(id='456', manufacturer='Manufacturer') >>> key.id = '123' >>> key.serial = '987654321' >>> key.model = 'Model' >>> key.issue_no = 2 >>> key.start_date = datetime.datetime(2006, 5, 1, 0, 0, tzinfo=tzutc()) >>> key.expiry_date = datetime.datetime(2014, 5, 31, 0, 0, tzinfo=tzutc()) >>> key.device_userid = 'uid=arthur, dc=arthurdejong, dc=org' >>> key.crypto_module = 'CyrptoId' >>> key.algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:totp' >>> key.issuer = 'Issuer' >>> key.key_profile = 'key profile id' >>> key.key_reference = 'reference to some key' >>> key.friendly_name = 'a friendly key' >>> key.key_userid = 'cn=Arthur de Jong, dc=arthurdejong, dc=org' >>> key.algorithm_suite = 'Clubs' >>> key.challenge_encoding = 'DECIMAL' >>> key.challenge_min_length = 6 >>> key.challenge_max_length = 8 >>> key.challenge_check = True >>> key.response_encoding = 'DECIMAL' >>> key.response_length = 8 >>> key.response_check = False >>> key.counter = 0 >>> key.secret = a2b_hex('4e1790ba272406ba309c5a31') Add policy information and a PIN. >>> key.policy.key_usage.append('OTP') >>> key.policy.key_usage.append(key.policy.KEY_USE_VERIFY) >>> key.policy.start_date = datetime.datetime(2008, 5, 1, 0, 0, tzinfo=tzutc()) >>> key.policy.expiry_date = datetime.datetime(2012, 6, 13, 0, 0, tzinfo=tzutc()) >>> key.policy.number_of_transactions = 42 >>> key.policy.pin_key_id = 'pinID' >>> key.policy.pin_usage = 'Local' >>> key.policy.pin_max_failed_attemtps = 3 >>> key.policy.pin_min_length = 4 >>> key.policy.pin_max_length = 4 >>> key.policy.pin_encoding = 'DECIMAL' >>> pin_key = pskc.add_key(id='pinID', secret='1234', ... algorithm='urn:ietf:params:xml:ns:keyprov:pskc:pin', ... response_encoding='DECIMAL', response_length=4) Write the PSKC file (use temporary file to test passing file name as argument). >>> f = tempfile.NamedTemporaryFile() >>> pskc.write(f.name) >>> x = sys.stdout.write(open(f.name, 'r').read()) #doctest: +REPORT_UDIFF Manufacturer 987654321 Model 2 2006-05-01T00:00:00Z 2014-05-31T00:00:00Z uid=arthur, dc=arthurdejong, dc=org CyrptoId Issuer Clubs key profile id reference to some key a friendly key TheQuickBrownFox 0 cn=Arthur de Jong, dc=arthurdejong, dc=org 2008-05-01T00:00:00Z 2012-06-13T00:00:00Z OTP Verify 42 MTIzNA== Read an encrypted PSKC file and write it out as an unencrypted file. >>> pskc = PSKC('tests/encryption/kw-aes128.pskcxml') >>> pskc.encryption.key = a2b_hex('000102030405060708090a0b0c0d0e0f') >>> for key in pskc.keys: ... key.secret = key.secret # force decryption of values >>> pskc.encryption.key = None >>> pskc.encryption.key_name = None >>> pskc.write(sys.stdout) #doctest: +REPORT_UDIFF ABEiM0RVZneImaq7zN3u/w== Read an encrypted PSKC file and write it out as-is. This does not require providing the encryption key. >>> pskc = PSKC('tests/rfc6030/figure6.pskcxml') >>> pskc.write(sys.stdout) #doctest: +REPORT_UDIFF Pre-shared-key ESIzRFVmd4iZABEiM0RVZgKn6WjLaTC1sbeBMSvIhRejN9vJa2BOlSaMrR7I5wSX Manufacturer 987654321 CM_ID_001 Issuer AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv Su+NvtQfmvfJzF6bmQiJqoLRExc= 0 Set up an encrypted PSKC file and generate a pre-shared key for it. >>> pskc = PSKC() >>> key = pskc.add_key( ... id='1', serial='123456', secret='1234', counter=42) >>> pskc.encryption.setup_preshared_key( ... key_name='Pre-shared KEY', fields = ['secret', 'counter']) >>> f = tempfile.NamedTemporaryFile() >>> pskc.write(f.name) >>> x = sys.stdout.write(open(f.name, 'r').read()) #doctest: +ELLIPSIS +REPORT_UDIFF Pre-shared KEY ... 123456 ... ... ... ... Read the generated file back in and verify that it matches the original data. >>> newpskc = PSKC(f.name) >>> newpskc.encryption.algorithm == pskc.encryption.algorithm True >>> newpskc.encryption.key = pskc.encryption.key >>> all(newkey.check() for newkey in newpskc.keys) True >>> key = pskc.keys[0] >>> newkey = newpskc.keys[0] >>> newkey.secret == key.secret True >>> newkey.counter == key.counter True Use PBKDF2 to derive a key instead of using a pre-shared key. >>> pskc = PSKC() >>> key = pskc.add_key( ... id='1', serial='123456', secret='1234', counter=42) >>> pskc.encryption.setup_pbkdf2( ... 'passphrase', key_name='Passphrase') >>> pskc.write(sys.stdout) #doctest: +ELLIPSIS +REPORT_UDIFF ... 12000 16 Passphrase ... 123456 ... ... 42 Test encryption and decryption of the generated file to test encryption/ decryption combinations. >>> def test_algorithm(algorithm): ... f = tempfile.NamedTemporaryFile() ... pskc1 = PSKC() ... pskc1.add_key(secret=Random.get_random_bytes(16)) ... pskc1.encryption.setup_preshared_key(algorithm=algorithm) ... pskc1.write(f.name) ... pskc2 = PSKC(f.name) ... pskc2.encryption.key = pskc1.encryption.key ... assert pskc1.keys[0].secret == pskc2.keys[0].secret ... return (pskc1, pskc2) >>> pskc1, pskc2 = test_algorithm('aes192-cbc') >>> len(pskc1.encryption.key) 24 >>> pskc1, pskc2 = test_algorithm('aes256-cbc') >>> len(pskc1.encryption.key) 32 >>> pskc1, pskc2 = test_algorithm('tripledes-cbc') >>> len(pskc1.encryption.key) 24 >>> pskc1, pskc2 = test_algorithm('kw-aes128') >>> len(pskc1.encryption.key) 16 >>> pskc1, pskc2 = test_algorithm('kw-aes192') >>> len(pskc1.encryption.key) 24 >>> pskc1, pskc2 = test_algorithm('kw-aes256') >>> len(pskc1.encryption.key) 32 >>> pskc1, pskc2 = test_algorithm('kw-tripledes') >>> len(pskc1.encryption.key) 24 Not having a key and trying encryption will fail. >>> f = tempfile.NamedTemporaryFile() >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_preshared_key() >>> pskc.encryption.key = None >>> pskc.write(f.name) Traceback (most recent call last): ... EncryptionError: No key available >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_preshared_key() >>> pskc.encryption.algorithm = None >>> pskc.write(f.name) Traceback (most recent call last): ... EncryptionError: No algorithm specified >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_preshared_key() >>> pskc.encryption.algorithm = 'FOOBAR' >>> pskc.write(f.name) Traceback (most recent call last): ... DecryptionError: Unsupported algorithm: 'FOOBAR' >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_preshared_key() >>> pskc.encryption.algorithm = 'aes256-cbc' >>> pskc.write(f.name) Traceback (most recent call last): ... EncryptionError: Invalid key length