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