test_rfc6030.doctest - test for examples from RFC 6030 Copyright (C) 2014-2017 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 of 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 of 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.is_encrypted True >>> pskc.encryption.key_name 'Pre-shared-key' >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> pskc.encryption.is_encrypted False >>> 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 of RFC 6030. >>> pskc = PSKC('tests/rfc6030/figure7.pskcxml') >>> pskc.encryption.key_name 'My Password 1' >>> pskc.encryption.is_encrypted True >>> pskc.encryption.derive_key('qwerty') >>> pskc.encryption.is_encrypted False >>> 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 a PSKC file that uses asymmetric encryption as seen in figure 8 of RFC 6030. Note thet python-pskc does not yet support asymmetric encryption so this test is really limited. >>> pskc = PSKC('tests/rfc6030/figure8.pskcxml') >>> pskc.id 'KC0001' >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#rsa_1_5' >>> key = pskc.keys[0] >>> key.manufacturer 'TokenVendorAcme' >>> key.serial '987654321' >>> key.id 'MBK000000001' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.issuer 'Example-Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> tostr(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No key available >>> key.counter 0 This tests a PSKC file that uses digital signature to sign the PSKC file as seen in figure 8 of RFC 6030. Since the RFC does not supply the certificate that was used in the signature (which is likely invalid) we cannot verify it in this example and can only get some signature properties. >>> pskc = PSKC('tests/rfc6030/figure9.pskcxml') >>> pskc.signature.is_signed True >>> pskc.signature.algorithm 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' >>> pskc.signature.canonicalization_method 'http://www.w3.org/2001/10/xml-exc-c14n#' >>> pskc.signature.digest_algorithm 'http://www.w3.org/2000/09/xmldsig#sha1' >>> pskc.signature.issuer 'CN=Example.com,C=US' >>> pskc.signature.serial '12345678' >>> key = pskc.keys[0] >>> key.manufacturer 'TokenVendorAcme' >>> key.serial '0755225266' >>> key.id '123' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.issuer 'Example-Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> tostr(key.secret) '12345678901234567890' >>> key.counter 0 This tests bulk provisioning as shown in figure 10 of 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())