test_rfc6030.doctest - test for examples from RFC 6030

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 binascii import a2b_hex, b2a_hex
>>> def tostr(x):
...     return str(x.decode())
>>> def decode(f):
...     return lambda x: tostr(f(x))
>>> b2a_hex = decode(b2a_hex)
>>> import datetime
>>> now = datetime.datetime(2016, 3, 23, 0, 0, 0)

>>> from pskc import PSKC


This tests Figure 2 from RFC 6030. It is a basic key container example with a
simple plain text secret key.

>>> pskc = PSKC('tests/rfc6030/figure2.pskcxml')
>>> [tostr(key.secret) for key in pskc.keys]
['1234']
>>> key = pskc.keys[0]
>>> key.id
'12345678'
>>> key.algorithm
'urn:ietf:params:xml:ns:keyprov:pskc:hotp'
>>> key.issuer
'Issuer-A'
>>> tostr(key.secret)
'1234'


This tests Figure 3 from RFC 6030. Relative to Figure 2 this includes device,
cryptographic module and user identification as well as some more parameters.

>>> pskc = PSKC('tests/rfc6030/figure3.pskcxml')
>>> pskc.id
'exampleID1'
>>> key = pskc.keys[0]
>>> key.manufacturer
'Manufacturer'
>>> key.serial
'987654321'
>>> key.device_userid
'DC=example-bank,DC=net'
>>> key.crypto_module
'CM_ID_001'
>>> key.id
'12345678'
>>> key.algorithm
'urn:ietf:params:xml:ns:keyprov:pskc:hotp'
>>> key.issuer
'Issuer'
>>> key.response_encoding
'DECIMAL'
>>> key.response_length
8
>>> tostr(key.secret)
'12345678901234567890'
>>> key.counter
0
>>> key.key_userid
'UID=jsmith,DC=example-bank,DC=net'


This tests Figure 4 from RFC 6030. In this case the key value itself is not
contained but can be derived using the serial and out-of-band agreements on
the meanings of key_profile and key_reference.

>>> pskc = PSKC('tests/rfc6030/figure4.pskcxml')
>>> key = pskc.keys[0]
>>> key.serial
'987654321'
>>> key.key_profile
'keyProfile1'
>>> key.key_reference
'MasterKeyLabel'
>>> key.counter
0


This tests the key policy properties as illustrated in Figure 5 from RFC
6030.

>>> pskc = PSKC('tests/rfc6030/figure5.pskcxml')
>>> len(pskc.keys)
2
>>> key1, key2 = pskc.keys
>>> key1.serial
'987654321'
>>> key.algorithm
'urn:ietf:params:xml:ns:keyprov:pskc:hotp'
>>> key.response_length
8
>>> key.response_encoding
'DECIMAL'
>>> tostr(key1.secret)
'12345678901234567890'
>>> key1.counter
0
>>> key1.policy.pin_min_length
4
>>> key1.policy.pin_max_length
4
>>> key1.policy.pin_key_id
'123456781'
>>> key1.policy.pin_encoding
'DECIMAL'
>>> key1.policy.pin_usage
'Local'
>>> key1.policy.key_usage
['OTP']
>>> key1.policy.may_use('OTP', now)
True
>>> key1.policy.may_use('Encrypt', now)
False
>>> key1.policy.unknown_policy_elements
False
>>> key2.id
'123456781'
>>> key2.serial
'987654321'
>>> key2.algorithm
'urn:ietf:params:xml:ns:keyprov:pskc:pin'
>>> key2.response_length
4
>>> key2.response_encoding
'DECIMAL'
>>> tostr(key2.secret)
'1234'
>>> key1.policy.pin
'1234'


This tests key encryption based on pre-shared keys as illustrated in Figure 6
from RFC 6030. The first attempt at extracting the key will fail due to the
encryption.

>>> pskc = PSKC('tests/rfc6030/figure6.pskcxml')
>>> key = pskc.keys[0]
>>> key.id
'12345678'
>>> key.secret  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
    ...
DecryptionError: No key available
>>> pskc.encryption.key_name
'Pre-shared-key'
>>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012')
>>> pskc.encryption.algorithm
'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
>>> b2a_hex(pskc.mac.key)
'1122334455667788990011223344556677889900'
>>> pskc.mac.algorithm
'http://www.w3.org/2000/09/xmldsig#hmac-sha1'
>>> b2a_hex(key.secret)
'3132333435363738393031323334353637383930'
>>> key.check()
True
>>> key.algorithm
'urn:ietf:params:xml:ns:keyprov:pskc:hotp'
>>> key.response_length
8
>>> key.manufacturer
'Manufacturer'


This tests a derived master key using PBKDF2 as seen in Figure 7 from RFC
6030.

>>> pskc = PSKC('tests/rfc6030/figure7.pskcxml')
>>> pskc.encryption.key_name
'My Password 1'
>>> pskc.encryption.derive_key('qwerty')
>>> b2a_hex(pskc.encryption.key)
'651e63cd57008476af1ff6422cd02e41'
>>> pskc.encryption.algorithm
'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
>>> b2a_hex(pskc.mac.key)
'bdaab8d648e850d25a3289364f7d7eaaf53ce581'
>>> pskc.mac.algorithm
'http://www.w3.org/2000/09/xmldsig#hmac-sha1'
>>> key = pskc.keys[0]
>>> tostr(key.secret)
'12345678901234567890'
>>> key.check()
True
>>> key.algorithm
'urn:ietf:params:xml:ns:keyprov:pskc:hotp'
>>> key.response_length
8
>>> key.manufacturer
'TokenVendorAcme'


This tests bulk provisioning as shown in Figure 10 From RFC 6030.

>>> pskc = PSKC('tests/rfc6030/figure10.pskcxml')
>>> all(key.manufacturer == 'TokenVendorAcme' for key in pskc.keys)
True
>>> [key.serial for key in pskc.keys]
['654321', '123456', '9999999', '9999999']
>>> all(key.algorithm == 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' for key in pskc.keys)
True
>>> all(key.issuer == 'Issuer' for key in pskc.keys)
True
>>> all(key.response_length == 8 for key in pskc.keys)
True
>>> all(key.response_encoding == 'DECIMAL' for key in pskc.keys)
True
>>> all(key.secret == b'12345678901234567890' for key in pskc.keys)
True
>>> all(key.counter == 0 for key in pskc.keys)
True
>>> pskc.keys[0].policy.start_date
datetime.datetime(2006, 5, 1, 0, 0, tzinfo=tzutc())
>>> pskc.keys[0].policy.expiry_date
datetime.datetime(2006, 5, 31, 0, 0, tzinfo=tzutc())
>>> pskc.keys[1].policy.start_date
datetime.datetime(2006, 5, 1, 0, 0, tzinfo=tzutc())
>>> pskc.keys[1].policy.expiry_date
datetime.datetime(2006, 5, 31, 0, 0, tzinfo=tzutc())
>>> pskc.keys[2].policy.start_date
datetime.datetime(2006, 3, 1, 0, 0, tzinfo=tzutc())
>>> pskc.keys[2].policy.expiry_date
datetime.datetime(2006, 3, 31, 0, 0, tzinfo=tzutc())
>>> pskc.keys[3].policy.start_date
datetime.datetime(2006, 4, 1, 0, 0, tzinfo=tzutc())
>>> pskc.keys[3].policy.expiry_date
datetime.datetime(2006, 4, 30, 0, 0, tzinfo=tzutc())