python-pskc branch master updated. 0.4-29-g2f7cb1a
[
Date Prev][Date Next]
[
Thread Prev][Thread Next]
python-pskc branch master updated. 0.4-29-g2f7cb1a
- From: Commits of the python-pskc project <python-pskc-commits [at] lists.arthurdejong.org>
- To: python-pskc-commits [at] lists.arthurdejong.org
- Reply-to: python-pskc-users [at] lists.arthurdejong.org
- Subject: python-pskc branch master updated. 0.4-29-g2f7cb1a
- Date: Wed, 21 Dec 2016 15:33:41 +0100 (CET)
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-pskc".
The branch, master has been updated
via 2f7cb1ab571e4415cb652fea095ccbaa052d689a (commit)
via 0b757ec62780365ee56e649a2d985c20e7e284c8 (commit)
via 09076f8cacc482317ac88953aaa0265d2a2526ee (commit)
via 46fa5f1f63123efc2a9cb6c890fe69b6a5a7c030 (commit)
via 047a2a9f904e587128102d450d7ae30874edeb24 (commit)
via bae70840d42c7d371c3e2499181dc57a0df4401d (commit)
from d864bc8da25767975271b50579fe5370eee1eda6 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
http://arthurdejong.org/git/python-pskc/commit/?id=2f7cb1ab571e4415cb652fea095ccbaa052d689a
commit 2f7cb1ab571e4415cb652fea095ccbaa052d689a
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Wed Dec 21 15:32:32 2016 +0100
Add all figures from RFC 6030 to test suite
Note that asymmetric encryption and digital signature checking has not
yet been implemented so the tests are pretty minimal.
diff --git a/tests/rfc6030/figure8.pskcxml b/tests/rfc6030/figure8.pskcxml
new file mode 100644
index 0000000..eb09012
--- /dev/null
+++ b/tests/rfc6030/figure8.pskcxml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ Figure 8 example from RFC 6030 that has a PSKC document using encryption
+ based on asymmetric keys.
+-->
+
+<KeyContainer
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
+ xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
+ id="KC0001"
+ Version="1.0">
+ <EncryptionKey>
+ <ds:X509Data>
+<ds:X509Certificate>MIIB5zCCAVCgAwIBAgIESZp/vDANBgkqhkiG9w0BAQUFADA4M
+Q0wCwYDVQQKEwRJRVRGMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIF
+Rlc3QwHhcNMDkwMjE3MDkxMzMyWhcNMTEwMjE3MDkxMzMyWjA4MQ0wCwYDVQQKEwRJRVR
+GMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIFRlc3QwgZ8wDQYJKoZI
+hvcNAQEBBQADgY0AMIGJAoGBALCWLDa2ItYJ6su80hd1gL4cggQYdyyKK17btt/aS6Q/e
+DsKjsPyFIODsxeKVV/uA3wLT4jQJM5euKJXkDajzGGOy92+ypfzTX4zDJMkh61SZwlHNJ
+xBKilAM5aW7C+BQ0RvCxvdYtzx2LTdB+X/KMEBA7uIYxLfXH2Mnub3WIh1AgMBAAEwDQY
+JKoZIhvcNAQEFBQADgYEAe875m84sYUJ8qPeZ+NG7REgTvlHTmoCdoByU0LBBLotUKuqf
+rnRuXJRMeZXaaEGmzY1kLonVjQGzjAkU4dJ+RPmiDlYuHLZS41Pg6VMwY+03lhk6I5A/w
+4rnqdkmwZX/NgXg06alnc2pBsXWhL4O7nk0S2ZrLMsQZ6HcsXgdmHo=
+</ds:X509Certificate>
+ </ds:X509Data>
+ </EncryptionKey>
+ <KeyPackage>
+ <DeviceInfo>
+ <Manufacturer>TokenVendorAcme</Manufacturer>
+ <SerialNo>987654321</SerialNo>
+ </DeviceInfo>
+ <Key
+ Id="MBK000000001"
+ Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
+ <Issuer>Example-Issuer</Issuer>
+ <AlgorithmParameters>
+ <ResponseFormat Length="6" Encoding="DECIMAL"/>
+ </AlgorithmParameters>
+ <Data>
+ <Secret>
+ <EncryptedValue>
+ <xenc:EncryptionMethod
+ Algorithm="http://www.w3.org/2001/04/xmlenc#rsa_1_5"/>
+ <xenc:CipherData>
+<xenc:CipherValue>hJ+fvpoMPMO9BYpK2rdyQYGIxiATYHTHC7e/sPLKYo5/r1v+4
+xTYG3gJolCWuVMydJ7Ta0GaiBPHcWa8ctCVYmHKfSz5fdeV5nqbZApe6dofTqhRwZK6
+Yx4ufevi91cjN2vBpSxYafvN3c3+xIgk0EnTV4iVPRCR0rBwyfFrPc4=
+</xenc:CipherValue>
+ </xenc:CipherData>
+ </EncryptedValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ </Key>
+ </KeyPackage>
+</KeyContainer>
diff --git a/tests/rfc6030/figure9.pskcxml b/tests/rfc6030/figure9.pskcxml
new file mode 100644
index 0000000..d3b38ce
--- /dev/null
+++ b/tests/rfc6030/figure9.pskcxml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Figure 9 example from RFC 6030 that has a digital signature.
+-->
+
+<KeyContainer
+ xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
+ Version="1.0">
+ <KeyPackage>
+ <DeviceInfo>
+ <Manufacturer>TokenVendorAcme</Manufacturer>
+ <SerialNo>0755225266</SerialNo>
+ </DeviceInfo>
+ <Key Id="123"
+ Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
+ <Issuer>Example-Issuer</Issuer>
+ <AlgorithmParameters>
+ <ResponseFormat Length="6" Encoding="DECIMAL"/>
+ </AlgorithmParameters>
+ <Data>
+ <Secret>
+ <PlainValue>
+ MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
+ </PlainValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ </Key>
+ </KeyPackage>
+ <Signature>
+ <ds:SignedInfo>
+ <ds:CanonicalizationMethod
+ Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ <ds:SignatureMethod
+ Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+ <ds:Reference URI="#Device">
+ <ds:DigestMethod
+ Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>
+ j6lwx3rvEPO0vKtMup4NbeVu8nk=
+ </ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue>
+ j6lwx3rvEPO0vKtMup4NbeVu8nk=
+ </ds:SignatureValue>
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509IssuerSerial>
+ <ds:X509IssuerName>
+ CN=Example.com,C=US
+ </ds:X509IssuerName>
+ <ds:X509SerialNumber>
+ 12345678
+ </ds:X509SerialNumber>
+ </ds:X509IssuerSerial>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </Signature>
+</KeyContainer>
diff --git a/tests/test_rfc6030.doctest b/tests/test_rfc6030.doctest
index 0220952..e872a95 100644
--- a/tests/test_rfc6030.doctest
+++ b/tests/test_rfc6030.doctest
@@ -30,7 +30,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>> from pskc import PSKC
-This tests Figure 2 from RFC 6030. It is a basic key container example with a
+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')
@@ -47,7 +47,7 @@ simple plain text secret key.
'1234'
-This tests Figure 3 from RFC 6030. Relative to Figure 2 this includes device,
+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')
@@ -80,7 +80,7 @@ cryptographic module and user identification as well as some
more parameters.
'UID=jsmith,DC=example-bank,DC=net'
-This tests Figure 4 from RFC 6030. In this case the key value itself is not
+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.
@@ -96,8 +96,7 @@ the meanings of key_profile and key_reference.
0
-This tests the key policy properties as illustrated in Figure 5 from RFC
-6030.
+This tests the key policy properties as illustrated in figure 5 of RFC 6030.
>>> pskc = PSKC('tests/rfc6030/figure5.pskcxml')
>>> len(pskc.keys)
@@ -149,8 +148,8 @@ False
'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
+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')
@@ -182,8 +181,7 @@ True
'Manufacturer'
-This tests a derived master key using PBKDF2 as seen in Figure 7 from RFC
-6030.
+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
@@ -210,7 +208,65 @@ True
'TokenVendorAcme'
-This tests bulk provisioning as shown in Figure 10 From RFC 6030.
+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. Note that python-pskc currently does not yet
+support checking this signature so this test is very limited.
+
+>>> pskc = PSKC('tests/rfc6030/figure9.pskcxml')
+>>> 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)
http://arthurdejong.org/git/python-pskc/commit/?id=0b757ec62780365ee56e649a2d985c20e7e284c8
commit 0b757ec62780365ee56e649a2d985c20e7e284c8
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Wed Dec 21 15:02:56 2016 +0100
Add support for older Internet Draft version
This adds support for parsing most examples from
draft-ietf-keyprov-pskc-02. That file uses a few other names for
elements and attributes of the PSKC file and a few other minor
differences.
The XML parsing has been changed to allow specifying multiple matches
and the find*() functions now return the first found match.
While all examples from draft-ietf-keyprov-pskc-02 are tested support
for verifying digital signatures and asymmetric keys have not yet been
implemented.
diff --git a/pskc/parser.py b/pskc/parser.py
index 03a8a8e..94d9849 100644
--- a/pskc/parser.py
+++ b/pskc/parser.py
@@ -46,16 +46,21 @@ class PSKCParser(object):
raise ParseError('Missing KeyContainer')
# the version of the PSKC schema
pskc.version = container.get('Version') or container.get('version')
- if pskc.version and pskc.version != '1.0':
+ if pskc.version and pskc.version not in ('1', '1.0'):
raise ParseError('Unsupported version %r' % pskc.version)
# unique identifier for the container
- pskc.id = container.get('Id')
+ pskc.id = (
+ container.get('Id') or container.get('ID') or container.get('id'))
# handle EncryptionKey entries
cls.parse_encryption(pskc.encryption, find(container, 'EncryptionKey'))
# handle MACMethod entries
cls.parse_mac_method(pskc.mac, find(container, 'MACMethod'))
+ # fall back to MACAlgorithm
+ mac_algorithm = findtext(container, 'MACAlgorithm')
+ if mac_algorithm:
+ pskc.mac.algorithm = mac_algorithm
# handle KeyPackage entries
- for key_package in findall(container, 'KeyPackage'):
+ for key_package in findall(container, 'KeyPackage', 'Device'):
cls.parse_key_package(pskc.add_device(), key_package)
@classmethod
@@ -64,9 +69,9 @@ class PSKCParser(object):
if key_info is None:
return
encryption.id = key_info.get('Id')
- for name in findall(key_info, 'KeyName'):
- encryption.key_names.append(findtext(name, '.'))
- for name in findall(key_info, 'DerivedKey/MasterKeyName'):
+ for name in findall(key_info,
+ 'KeyName', 'DerivedKey/MasterKeyName',
+ 'DerivedKey/CarriedKeyName'):
encryption.key_names.append(findtext(name, '.'))
cls.parse_key_derivation(encryption.derivation, find(
key_info, 'DerivedKey/KeyDerivationMethod'))
@@ -100,11 +105,8 @@ class PSKCParser(object):
mac.algorithm = mac_method.get('Algorithm')
mac_key = find(mac_method, 'MACKey')
if mac_key is not None:
- mac.key_cipher_value = findbin(mac_key, 'CipherData/CipherValue')
- encryption_method = find(mac_key, 'EncryptionMethod')
- if encryption_method is not None:
- mac.key_algorithm = encryption_method.attrib.get('Algorithm')
- mac_key_reference = findtext(mac_method, 'MACKeyReference')
+ mac.key_algorithm, mac.key_cipher_value = (
+ cls.parse_encrypted_value(mac_key))
@classmethod
def parse_key_package(cls, device, key_package):
@@ -129,8 +131,8 @@ class PSKCParser(object):
def parse_key(cls, key, key_elm):
"""Read key information from the provided <KeyPackage> tree."""
- key.id = key_elm.get('Id')
- key.algorithm = key_elm.get('Algorithm')
+ key.id = key_elm.get('Id') or key_elm.get('KeyId')
+ key.algorithm = key_elm.get('Algorithm') or key_elm.get('KeyAlgorithm')
data = find(key_elm, 'Data')
if data is not None:
@@ -151,7 +153,8 @@ class PSKCParser(object):
key_elm, 'AlgorithmParameters/Suite')
challenge_format = find(
- key_elm, 'AlgorithmParameters/ChallengeFormat')
+ key_elm,
+ 'AlgorithmParameters/ChallengeFormat', 'Usage/ResponseFormat')
if challenge_format is not None:
key.challenge_encoding = challenge_format.get('Encoding')
key.challenge_min_length = getint(challenge_format, 'Min')
@@ -161,7 +164,8 @@ class PSKCParser(object):
challenge_format, 'CheckDigit'))
response_format = find(
- key_elm, 'AlgorithmParameters/ResponseFormat')
+ key_elm,
+ 'AlgorithmParameters/ResponseFormat', 'Usage/ResponseFormat')
if response_format is not None:
key.response_encoding = response_format.get('Encoding')
key.response_length = getint(response_format, 'Length')
@@ -172,6 +176,20 @@ class PSKCParser(object):
cls.parse_policy(key.policy, find(key_elm, 'Policy'))
@classmethod
+ def parse_encrypted_value(cls, encrypted_value):
+ """Read encryption value from <EncryptedValue> element."""
+ algorithm = None
+ cipher_value = findbin(encrypted_value, 'CipherData/CipherValue')
+ encryption_method = find(encrypted_value, 'EncryptionMethod')
+ if encryption_method is not None:
+ algorithm = encryption_method.attrib.get('Algorithm')
+ encryption_scheme = find(
+ encrypted_value, 'EncryptionMethod/EncryptionScheme')
+ if encryption_scheme is not None:
+ algorithm = encryption_scheme.attrib.get('Algorithm') or algorithm
+ return (algorithm, cipher_value)
+
+ @classmethod
def parse_datatype(cls, dt, element):
"""Read information from the provided element.
@@ -187,14 +205,11 @@ class PSKCParser(object):
# read encrypted data from <EncryptedValue>
encrypted_value = find(element, 'EncryptedValue')
if encrypted_value is not None:
- dt.cipher_value = findbin(
- encrypted_value, 'CipherData/CipherValue')
- encryption_method = find(encrypted_value, 'EncryptionMethod')
- if encryption_method is not None:
- dt.algorithm = encryption_method.attrib.get('Algorithm')
- # store the found algorithm in the pskc.encryption property
- if not dt.pskc.encryption.algorithm and dt.algorithm:
- dt.pskc.encryption.algorithm = dt.algorithm
+ dt.algorithm, dt.cipher_value = cls.parse_encrypted_value(
+ encrypted_value)
+ # store the found algorithm in the pskc.encryption property
+ if not dt.pskc.encryption.algorithm and dt.algorithm:
+ dt.pskc.encryption.algorithm = dt.algorithm
# read MAC information from <ValueMAC>
value_mac = findbin(element, 'ValueMAC')
if value_mac is not None:
diff --git a/pskc/xml.py b/pskc/xml.py
index 1ecf2ca..d5288b3 100644
--- a/pskc/xml.py
+++ b/pskc/xml.py
@@ -65,44 +65,46 @@ def remove_namespaces(tree):
elem.tag = re.sub(r'^\{[^}]*\}', '', elem.tag)
-def findall(tree, match):
+def findall(tree, *matches):
"""Find the child elements."""
- return tree.findall(match, namespaces=namespaces)
+ for match in matches:
+ for element in tree.findall(match, namespaces=namespaces):
+ yield element
-def find(tree, match):
+def find(tree, *matches):
"""Find a child element that matches any of the patterns (or None)."""
try:
- return next(iter(findall(tree, match)))
+ return next(findall(tree, *matches))
except StopIteration:
pass
-def findtext(tree, match):
+def findtext(tree, *matches):
"""Get the text value of an element (or None)."""
- element = find(tree, match)
+ element = find(tree, *matches)
if element is not None:
return element.text.strip()
-def findint(tree, match):
+def findint(tree, *matches):
"""Return an element value as an int (or None)."""
- value = findtext(tree, match)
+ value = findtext(tree, *matches)
if value:
return int(value)
-def findtime(tree, match):
+def findtime(tree, *matches):
"""Return an element value as a datetime (or None)."""
- value = findtext(tree, match)
+ value = findtext(tree, *matches)
if value:
import dateutil.parser
return dateutil.parser.parse(value)
-def findbin(tree, match):
+def findbin(tree, *matches):
"""Return the binary element value base64 decoded."""
- value = findtext(tree, match)
+ value = findtext(tree, *matches)
if value:
import base64
return base64.b64decode(value)
diff --git a/tests/draft-ietf-keyprov-pskc-02/figure2.pskcxml
b/tests/draft-ietf-keyprov-pskc-02/figure2.pskcxml
new file mode 100644
index 0000000..daa45a7
--- /dev/null
+++ b/tests/draft-ietf-keyprov-pskc-02/figure2.pskcxml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Basic PSKC Key Container example from draft-ietf-keyprov-pskc-02 (Figure 2).
+-->
+
+<KeyContainer Version="1.0" id="exampleID1"
+xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>Manufacturer</Manufacturer>
+ <SerialNo>987654321</SerialNo>
+ </DeviceInfo>
+ <Key KeyId="12345678"
+ KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp">
+ <Issuer>Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="8" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <PlainValue>MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
+ </PlainValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ </Key>
+ </Device>
+</KeyContainer>
diff --git a/tests/draft-ietf-keyprov-pskc-02/figure3.pskcxml
b/tests/draft-ietf-keyprov-pskc-02/figure3.pskcxml
new file mode 100644
index 0000000..4784d78
--- /dev/null
+++ b/tests/draft-ietf-keyprov-pskc-02/figure3.pskcxml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Non-encrypted HOTP secret key protected by a PIN example from
+ draft-ietf-keyprov-pskc-02 (Figure 3).
+-->
+
+<KeyContainer Version="1.0" id="exampleID1"
+ xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>Manufacturer</Manufacturer>
+ <SerialNo>987654321</SerialNo>
+ </DeviceInfo>
+ <Key KeyId="12345678"
+ KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp">
+ <Issuer>Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="8" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <PlainValue>MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
+ </PlainValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ <Policy>
+ <PINPolicy MinLength="4" MaxLength="4"
+ PINKeyId="123456781" PINEncoding="DECIMAL"
+ PINUsageMode="Local"/>
+ <KeyUsage>OTP</KeyUsage>
+ </Policy>
+ </Key>
+ <Key KeyId="123456781"
+ KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#pin">
+ <Issuer>Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="4" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <PlainValue>MTIzNA==</PlainValue>
+ </Secret>
+ </Data>
+ </Key>
+ </Device>
+</KeyContainer>
+
+
diff --git a/tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml
b/tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml
new file mode 100644
index 0000000..bb89f7e
--- /dev/null
+++ b/tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ AES-128-CBC encrypted pre-shared secret key example from
+ draft-ietf-keyprov-pskc-02 (Figure 4). The value of the pre-shared key
+ is 12345678901234567890123456789012 which should result in a secret
+ of 3132333435363738393031323334353637383930. The value in CipherValue
+ was modified to correctly decrypt.
+-->
+
+<KeyContainer Version="1.0" xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
+xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
+ <EncryptionKey>
+ <ds:KeyName>Pre-shared-key</ds:KeyName>
+ </EncryptionKey>
+ <MACAlgorithm>http://www.w3.org/2000/09/xmldsig#hmac-sha1
+ </MACAlgorithm>
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>Manufacturer</Manufacturer>
+ <SerialNo>987654321</SerialNo>
+ </DeviceInfo>
+ <Key KeyId="12345678"
+ KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp">
+ <Issuer>Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="8" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <EncryptedValue>
+ <xenc:EncryptionMethod
+ Algorithm=
+ "http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
+ <xenc:CipherData>
+ <xenc:CipherValue>
+
pgznhXdDh4LJ2G3mOY2RL/e5cN9M3qjkBZJEE4w+NvVr64LbzkwRh9WHLAIkjXcW
+ </xenc:CipherValue>
+ </xenc:CipherData>
+ </EncryptedValue>
+ <ValueMAC>zdrZbGBj9BDZJzunbfAG3kyZyYc=
+ </ValueMAC>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ </Key>
+ </Device>
+</KeyContainer>
diff --git a/tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml
b/tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml
new file mode 100644
index 0000000..6662f9b
--- /dev/null
+++ b/tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ PSKC file using Encryption based on Passphrase-based Keys example from
+ draft-ietf-keyprov-pskc-02 (Figure 5). The passphrase to generate the
+ encryption key is "qwerty" which should result in a derived encryption key
+ of 651e63cd57008476af1ff6422cd02e41 and 12345678901234567890 as OTP secret.
+ The file was modified to remove the ns2 namespace declaration from ValueMAC
+ to make it valid XML. Note that the ValueMAC element is in the wrong place
+ in the tree so it is ignored, also a MACAlgorithm element is missing.
+-->
+
+<KeyContainer
+ xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
+ xmlns:pkcs5=
+ "http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#"
+ xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
+ Version="1.0">
+ <EncryptionKey>
+ <DerivedKey>
+ <CarriedKeyName>Passphrase1</CarriedKeyName>
+ <KeyDerivationMethod
+ Algorithm=
+"http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2">
+ <pkcs5:PBKDF2-params>
+ <pkcs5:Salt>
+ <pkcs5:Specified>Ej7/PEpyEpw=</pkcs5:Specified>
+ </pkcs5:Salt>
+ <pkcs5:IterationCount>1000</pkcs5:IterationCount>
+ <pkcs5:KeyLength>16</pkcs5:KeyLength>
+ <pkcs5:PRF/>
+ </pkcs5:PBKDF2-params>
+ </KeyDerivationMethod>
+ <xenc:ReferenceList>
+ <xenc:DataReference URI="#ED"/>
+ </xenc:ReferenceList>
+ </DerivedKey>
+ </EncryptionKey>
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>TokenVendorAcme</Manufacturer>
+ <SerialNo>987654321</SerialNo>
+ </DeviceInfo>
+ <Key KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp"
+ KeyId="123456">
+ <Issuer>Example-Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="8" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <EncryptedValue Id="ED">
+ <xenc:EncryptionMethod Algorithm=
+"http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5#pbes2">
+ <EncryptionScheme Algorithm=
+"http://www.w3.org/2001/04/xmlenc#aes128-cbc">
+ </EncryptionScheme>
+ </xenc:EncryptionMethod>
+ <xenc:CipherData>
+ <xenc:CipherValue>
+ oTvo+S22nsmS2Z/RtcoF8Hfh+jzMe0RkiafpoDpnoZTjPYZu6V+A4aEn032yCr4f
+ </xenc:CipherValue>
+ </xenc:CipherData>
+ <ValueMAC>cOpiQ/H7Zlj6ywiYWtwgz9cRaOA=
+ </ValueMAC>
+ </EncryptedValue>
+ </Secret>
+ </Data>
+ </Key>
+ </Device>
+</KeyContainer>
diff --git a/tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml
b/tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml
new file mode 100644
index 0000000..d791231
--- /dev/null
+++ b/tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ PSKC file using encryption based on asymmetric keys example from
+ draft-ietf-keyprov-pskc-02 (Figure 6).
+-->
+
+<KeyContainer
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
+ xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
+ id="KC0001"
+ Version="1.0">
+ <EncryptionKey>
+ <ds:X509Data>
+<ds:X509Certificate>MIIB5zCCAVCgAwIBAgIESZp/vDANBgkqhkiG9w0BAQUFADA4M
+Q0wCwYDVQQKEwRJRVRGMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIF
+Rlc3QwHhcNMDkwMjE3MDkxMzMyWhcNMTEwMjE3MDkxMzMyWjA4MQ0wCwYDVQQKEwRJRVR
+GMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIFRlc3QwgZ8wDQYJKoZI
+hvcNAQEBBQADgY0AMIGJAoGBALCWLDa2ItYJ6su80hd1gL4cggQYdyyKK17btt/aS6Q/e
+DsKjsPyFIODsxeKVV/uA3wLT4jQJM5euKJXkDajzGGOy92+ypfzTX4zDJMkh61SZwlHNJ
+xBKilAM5aW7C+BQ0RvCxvdYtzx2LTdB+X/KMEBA7uIYxLfXH2Mnub3WIh1AgMBAAEwDQY
+JKoZIhvcNAQEFBQADgYEAe875m84sYUJ8qPeZ+NG7REgTvlHTmoCdoByU0LBBLotUKuqf
+rnRuXJRMeZXaaEGmzY1kLonVjQGzjAkU4dJ+RPmiDlYuHLZS41Pg6VMwY+03lhk6I5A/w
+4rnqdkmwZX/NgXg06alnc2pBsXWhL4O7nk0S2ZrLMsQZ6HcsXgdmHo=
+</ds:X509Certificate>
+ </ds:X509Data>
+ </EncryptionKey>
+ <MACAlgorithm>
+ http://www.w3.org/2000/09/xmldsig#hmac-sha1
+ </MACAlgorithm>
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>TokenVendorAcme</Manufacturer>
+ <SerialNo>987654321</SerialNo>
+ </DeviceInfo>
+ <Key
+ KeyId="MBK000000001"
+ KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp">
+ <Issuer>Example-Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="6" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <EncryptedValue>
+ <xenc:EncryptionMethod
+ Algorithm="http://www.w3.org/2001/04/xmlenc#rsa_1_5"/>
+ <xenc:CipherData>
+<xenc:CipherValue>hJ+fvpoMPMO9BYpK2rdyQYGIxiATYHTHC7e/sPLKYo5/r1v+4
+xTYG3gJolCWuVMydJ7Ta0GaiBPHcWa8ctCVYmHKfSz5fdeV5nqbZApe6dofTqhRwZK6
+Yx4ufevi91cjN2vBpSxYafvN3c3+xIgk0EnTV4iVPRCR0rBwyfFrPc4=
+</xenc:CipherValue>
+ </xenc:CipherData>
+ </EncryptedValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ </Key>
+ </Device>
+</KeyContainer>
diff --git a/tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml
b/tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml
new file mode 100644
index 0000000..0ec00cc
--- /dev/null
+++ b/tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ PSKC file transmitting a HOTP key via key derivation values example from
+ draft-ietf-keyprov-pskc-02 (Figure 7). The key value will be derived using
+ the serialnumber and an external key.
+-->
+
+<KeyContainer Version="1" id="exampleID1"
+xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>Manufacturer</Manufacturer>
+ <SerialNo>987654321</SerialNo>
+ </DeviceInfo>
+ <Key KeyId="12345678"
+ KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp">
+ <Issuer>Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="8" Encoding="DECIMAL"/>
+ </Usage>
+ <KeyProfileId>keyProfile1</KeyProfileId>
+ <KeyReference>MasterKeyLabel</KeyReference>
+ <Data>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ <Policy>
+ <KeyUsage>OTP</KeyUsage>
+ </Policy>
+ </Key>
+ </Device>
+</KeyContainer>
diff --git a/tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml
b/tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml
new file mode 100644
index 0000000..6b0e6cf
--- /dev/null
+++ b/tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ PSKC file using a digital signature example from draft-ietf-keyprov-pskc-02
+ (Figure 8).
+-->
+
+<KeyContainer
+ xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
+ xmlns:pkcs5=
+ "http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
+ Version="1">
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>TokenVendorAcme</Manufacturer>
+ <SerialNo>0755225266</SerialNo>
+ </DeviceInfo>
+ <Key KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp"
+ KeyId="123">
+ <Issuer>Example-Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="6" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <PlainValue>
+ MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
+ </PlainValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ </Key>
+ </Device>
+ <Signature>
+ <ds:SignedInfo>
+ <ds:CanonicalizationMethod
+ Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ <ds:SignatureMethod
+ Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+ <ds:Reference URI="#Device">
+ <ds:DigestMethod
+ Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>
+ j6lwx3rvEPO0vKtMup4NbeVu8nk=
+ </ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue>
+ j6lwx3rvEPO0vKtMup4NbeVu8nk=
+ </ds:SignatureValue>
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509IssuerSerial>
+ <ds:X509IssuerName>
+ CN=Example.com,C=US
+ </ds:X509IssuerName>
+ <ds:X509SerialNumber>
+ 12345678
+ </ds:X509SerialNumber>
+ </ds:X509IssuerSerial>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </Signature>
+</KeyContainer>
diff --git a/tests/draft-ietf-keyprov-pskc-02/figure9.pskcxml
b/tests/draft-ietf-keyprov-pskc-02/figure9.pskcxml
new file mode 100644
index 0000000..f2ecbc4
--- /dev/null
+++ b/tests/draft-ietf-keyprov-pskc-02/figure9.pskcxml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ PSKC file showing bulkt provisioning with multiple devices and multiple
+ keys from draft-ietf-keyprov-pskc-02 (Figure 9).
+-->
+
+<KeyContainer Version="1"
+ xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>TokenVendorAcme</Manufacturer>
+ <SerialNo>654321</SerialNo>
+ </DeviceInfo>
+ <Key KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp"
+ KeyId="1">
+ <Issuer>Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="8" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <PlainValue>
+ MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
+ </PlainValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ <Policy>
+ <StartDate>2006-05-01T00:00:00Z</StartDate>
+ <ExpiryDate>2006-05-31T00:00:00Z</ExpiryDate>
+ </Policy>
+ </Key>
+ </Device>
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>TokenVendorAcme</Manufacturer>
+ <SerialNo>123456</SerialNo>
+ </DeviceInfo>
+ <Key KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp"
+ KeyId="2">
+ <Issuer>Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="8" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <PlainValue>
+ MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
+ </PlainValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ <Policy>
+ <StartDate>2006-05-01T00:00:00Z</StartDate>
+ <ExpiryDate>2006-05-31T00:00:00Z</ExpiryDate>
+ </Policy>
+ </Key>
+ </Device>
+ <Device>
+ <DeviceInfo>
+ <Manufacturer>TokenVendorAcme</Manufacturer>
+ <SerialNo>9999999</SerialNo>
+ </DeviceInfo>
+ <Key KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp"
+ KeyId="3">
+ <Issuer>Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="8" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <PlainValue>
+ MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
+ </PlainValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ <Policy>
+ <StartDate>2006-03-01T00:00:00Z</StartDate>
+ <ExpiryDate>2006-03-31T00:00:00Z</ExpiryDate>
+ </Policy>
+ </Key>
+ <Key KeyAlgorithm="urn:ietf:params:xml:ns:keyprov:pskc#hotp"
+ KeyId="4">
+ <Issuer>Issuer</Issuer>
+ <Usage>
+ <ResponseFormat Length="8" Encoding="DECIMAL"/>
+ </Usage>
+ <Data>
+ <Secret>
+ <PlainValue>
+ MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
+ </PlainValue>
+ </Secret>
+ <Counter>
+ <PlainValue>0</PlainValue>
+ </Counter>
+ </Data>
+ <Policy>
+ <StartDate>2006-04-01T00:00:00Z</StartDate>
+ <ExpiryDate>2006-04-30T00:00:00Z</ExpiryDate>
+ </Policy>
+ </Key>
+ </Device>
+</KeyContainer>
diff --git a/tests/test_draft_ietf_keyprov_pskc_02.doctest
b/tests/test_draft_ietf_keyprov_pskc_02.doctest
new file mode 100644
index 0000000..7a039d5
--- /dev/null
+++ b/tests/test_draft_ietf_keyprov_pskc_02.doctest
@@ -0,0 +1,305 @@
+test_draft_ietf_keyprov_pskc_02.doctest - test for examples from
+ draft-ietf-keyprov-pskc-02
+
+Copyright (C) 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)
+
+>>> from pskc import PSKC
+
+
+This tests figure 2 from draft-ietf-keyprov-pskc-02. It is a basic key
+container example with a simple plain text secret key.
+
+>>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure2.pskcxml')
+>>> pskc.id
+'exampleID1'
+>>> key = pskc.keys[0]
+>>> key.id
+'12345678'
+>>> key.manufacturer
+'Manufacturer'
+>>> key.serial
+'987654321'
+>>> 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
+
+This tests figure 3 from draft-ietf-keyprov-pskc-02. It is a basic key
+container example with a non-encrypted HOTP secret key protected by a PIN
+with some extra policy elements.
+
+>>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure3.pskcxml')
+>>> pskc.id
+'exampleID1'
+>>> device = pskc.devices[0]
+>>> device.manufacturer
+'Manufacturer'
+>>> device.serial
+'987654321'
+>>> key = pskc.keys[0]
+>>> 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.policy.pin_min_length
+4
+>>> key.policy.pin_max_length
+4
+>>> key.policy.pin_encoding
+'DECIMAL'
+>>> key.policy.pin_usage
+'Local'
+>>> key.policy.key_usage
+['OTP']
+>>> pinkey = key.policy.pin_key
+>>> pinkey == pskc.keys[1]
+True
+>>> pinkey.id
+'123456781'
+>>> pinkey.algorithm
+'urn:ietf:params:xml:ns:keyprov:pskc#pin'
+>>> pinkey.issuer
+'Issuer'
+>>> pinkey.response_encoding
+'DECIMAL'
+>>> pinkey.response_length
+4
+>>> tostr(pinkey.secret)
+'1234'
+
+
+This tests figure 4 from draft-ietf-keyprov-pskc-02. It is a basic key
+container example using a AES-128-CBC encrypted pre-shared secret key.
+
+>>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml')
+>>> pskc.encryption.key_name
+'Pre-shared-key'
+>>> pskc.encryption.algorithm
+'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
+>>> pskc.mac.algorithm
+'http://www.w3.org/2000/09/xmldsig#hmac-sha1'
+>>> key = pskc.keys[0]
+>>> key.manufacturer
+'Manufacturer'
+>>> key.serial
+'987654321'
+>>> key.id
+'12345678'
+>>> key.algorithm
+'urn:ietf:params:xml:ns:keyprov:pskc#hotp'
+>>> key.issuer
+'Issuer'
+>>> key.response_encoding
+'DECIMAL'
+>>> key.response_length
+8
+>>> key.counter
+0
+>>> b2a_hex(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL
+Traceback (most recent call last):
+ ...
+DecryptionError: No key available
+>>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012')
+>>> b2a_hex(key.secret)
+'3132333435363738393031323334353637383930'
+
+
+This tests figure 5 from draft-ietf-keyprov-pskc-02 which uses an encryption
+key that is derived from a passphrase. While the PSKC file contains a
+<ValueMAC> element, there is no MAC algorithm specified anywhere.
+
+>>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml')
+>>> pskc.encryption.key_name
+'Passphrase1'
+>>> pskc.encryption.algorithm
+'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
+>>> key = pskc.keys[0]
+>>> key.manufacturer
+'TokenVendorAcme'
+>>> key.serial
+'987654321'
+>>> key.id
+'123456'
+>>> key.algorithm
+'urn:ietf:params:xml:ns:keyprov:pskc#hotp'
+>>> key.issuer
+'Example-Issuer'
+>>> key.response_encoding
+'DECIMAL'
+>>> key.response_length
+8
+>>> tostr(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL
+Traceback (most recent call last):
+ ...
+DecryptionError: No key available
+>>> pskc.encryption.derive_key('qwerty')
+>>> b2a_hex(pskc.encryption.key)
+'651e63cd57008476af1ff6422cd02e41'
+>>> tostr(key.secret)
+'12345678901234567890'
+
+
+This tests figure 6 from draft-ietf-keyprov-pskc-02 which uses an encryption
+based on asymmetric keys. Note that python-pskc does not yet support
+asymmetric encryption so this test is really limited.
+
+>>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml')
+>>> pskc.id
+'KC0001'
+>>> pskc.encryption.algorithm
+'http://www.w3.org/2001/04/xmlenc#rsa_1_5'
+>>> pskc.mac.algorithm
+'http://www.w3.org/2000/09/xmldsig#hmac-sha1'
+>>> 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 figure 7 from draft-ietf-keyprov-pskc-02 which uses a reference to
+an external mechanism to construct a HOTP key.
+
+>>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml')
+>>> pskc.id
+'exampleID1'
+>>> key = pskc.keys[0]
+>>> key.manufacturer
+'Manufacturer'
+>>> key.serial
+'987654321'
+>>> key.id
+'12345678'
+>>> key.algorithm
+'urn:ietf:params:xml:ns:keyprov:pskc#hotp'
+>>> key.issuer
+'Issuer'
+>>> key.response_encoding
+'DECIMAL'
+>>> key.response_length
+8
+>>> key.key_profile
+'keyProfile1'
+>>> key.key_reference
+'MasterKeyLabel'
+>>> key.counter
+0
+>>> key.policy.key_usage
+['OTP']
+
+
+This tests figure 8 from draft-ietf-keyprov-pskc-02 which uses a a digital
+signature to sign the PSKC file. Note that python-pskc currently does not yet
+support checking this signature so this test is very limited.
+
+>>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml')
+>>> 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 figure 9 from draft-ietf-keyprov-pskc-02 which contains multiple
+devices and keys.
+
+>>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure9.pskcxml')
+>>> [device.manufacturer for device in pskc.devices]
+['TokenVendorAcme', 'TokenVendorAcme', 'TokenVendorAcme']
+>>> [device.serial for device in pskc.devices]
+['654321', '123456', '9999999']
+>>> [len(device.keys) for device in pskc.devices]
+[1, 1, 2]
+>>> [key.id for key in pskc.keys]
+['1', '2', '3', '4']
+>>> all(key.algorithm.endswith('#hotp') for key in pskc.keys)
+True
+>>> [key.issuer for key in pskc.keys]
+['Issuer', 'Issuer', 'Issuer', 'Issuer']
+>>> [bool(key.secret) for key in pskc.keys]
+[True, True, True, True]
+>>> [key.counter for key in pskc.keys]
+[0, 0, 0, 0]
+>>> 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[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())
http://arthurdejong.org/git/python-pskc/commit/?id=09076f8cacc482317ac88953aaa0265d2a2526ee
commit 09076f8cacc482317ac88953aaa0265d2a2526ee
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Mon Dec 19 23:37:17 2016 +0100
Fix typo in test
diff --git a/tests/test_encryption.doctest b/tests/test_encryption.doctest
index 2819256..fa567ef 100644
--- a/tests/test_encryption.doctest
+++ b/tests/test_encryption.doctest
@@ -160,11 +160,11 @@ Test decryption with tripledes-cbc and a specified IV.
>>> iv = a2b_hex('1010101010101010')
>>> key = a2b_hex('12345678901234567890123456789012')
->>> cyphertext = encrypt('#tripledes-cbc', key, b'FOOBAR', iv)
->>> cyphertext = cyphertext[8:] # strip IV
->>> tostr(decrypt('#tripledes-cbc', key, cyphertext, iv))
+>>> ciphertext = encrypt('#tripledes-cbc', key, b'FOOBAR', iv)
+>>> ciphertext = ciphertext[8:] # strip IV
+>>> tostr(decrypt('#tripledes-cbc', key, ciphertext, iv))
'FOOBAR'
->>> tostr(decrypt('#tripledes-cbc', key, iv + cyphertext))
+>>> tostr(decrypt('#tripledes-cbc', key, iv + ciphertext))
'FOOBAR'
http://arthurdejong.org/git/python-pskc/commit/?id=46fa5f1f63123efc2a9cb6c890fe69b6a5a7c030
commit 46fa5f1f63123efc2a9cb6c890fe69b6a5a7c030
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Tue Dec 20 21:00:30 2016 +0100
Fail tests on missing coverage
diff --git a/setup.cfg b/setup.cfg
index 33da187..d702dfb 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -9,9 +9,11 @@ doctest-options=+IGNORE_EXCEPTION_DETAIL
with-coverage=true
cover-branches=true
cover-package=pskc
+cover-inclusive=true
cover-erase=true
cover-html=true
cover-html-dir=coverage
+cover-min-percentage=100
[build_sphinx]
all_files = 1
http://arthurdejong.org/git/python-pskc/commit/?id=047a2a9f904e587128102d450d7ae30874edeb24
commit 047a2a9f904e587128102d450d7ae30874edeb24
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Tue Dec 20 20:36:15 2016 +0100
Allow MAC over plaintext or ciphertext
RFC 6030 implies that the MAC should be performed over the ciphertext
but some earlier drafts implied that the MAC should be performed on the
plaintext. This change accpets the MAC if either the plaintext or
ciphertext match.
Note that this change allows for a padding oracle attack when CBC
encryption modes are used because decryption (and unpadding) needs to be
done before MAC checking. However, this module is not expected to be
available to users to process arbitrary PSKC files repeatedly.
This removes the tests for a missing MAC key (and replaces it for tests
of missing EncryptionMethod) because falling back to using the
encryption key (implemented in a444f78) in combination with this change
means that decryption is performed before MAC checking and is no longer
possible to trigger a missing MAC key error.
diff --git a/pskc/key.py b/pskc/key.py
index 69f710f..0ff5edb 100644
--- a/pskc/key.py
+++ b/pskc/key.py
@@ -64,13 +64,20 @@ class DataType(object):
def get_value(self):
"""Provide the attribute value, decrypting as needed."""
+ from pskc.exceptions import DecryptionError
if self.value is not None:
return self.value
if self.cipher_value:
- # check MAC and decrypt
- self.check()
- return self._from_bin(self.pskc.encryption.decrypt_value(
- self.cipher_value, self.algorithm))
+ plaintext = self.pskc.encryption.decrypt_value(
+ self.cipher_value, self.algorithm)
+ # allow MAC over plaintext or cipertext
+ # (RFC6030 implies MAC over ciphertext but older draft used
+ # MAC over plaintext)
+ if self.value_mac and self.value_mac not in (
+ self.pskc.mac.generate_mac(self.cipher_value),
+ self.pskc.mac.generate_mac(plaintext)):
+ raise DecryptionError('MAC value does not match')
+ return self._from_bin(plaintext)
def set_value(self, value):
"""Set the unencrypted value."""
@@ -79,13 +86,6 @@ class DataType(object):
self.algorithm = None
self.value_mac = None
- def check(self):
- """Check whether the embedded MAC is correct."""
- # this checks the encrypted value
- if self.cipher_value and self.value_mac:
- return self.pskc.mac.check_value(
- self.cipher_value, self.value_mac)
-
class BinaryDataType(DataType):
"""Subclass of DataType for binary data (e.g. keys)."""
@@ -272,7 +272,6 @@ class Key(object):
def check(self):
"""Check if all MACs in the message are valid."""
if all(x is not False for x in (
- self._secret.check(), self._counter.check(),
- self._time_offset.check(), self._time_interval.check(),
- self._time_drift.check())):
+ self.secret, self.counter, self.time_offset,
+ self.time_interval, self.time_drift)):
return True
diff --git a/pskc/mac.py b/pskc/mac.py
index c5ac1ec..b051eb8 100644
--- a/pskc/mac.py
+++ b/pskc/mac.py
@@ -55,8 +55,6 @@ def get_hmac(algorithm):
def get_mac(algorithm, key, value):
"""Generate the MAC value over the specified value."""
from pskc.exceptions import DecryptionError
- if key is None:
- raise DecryptionError('No MAC key available')
if algorithm is None:
raise DecryptionError('No MAC algorithm set')
hmacfn = get_hmac(algorithm)
@@ -124,17 +122,6 @@ class MAC(object):
"""Generate the MAC over the specified value."""
return get_mac(self.algorithm, self.key, value)
- def check_value(self, value, value_mac):
- """Check if the provided value matches the MAC.
-
- This will return None if there is no MAC to be checked. It will
- return True if the MAC matches and raise an exception if it fails.
- """
- from pskc.exceptions import DecryptionError
- if self.generate_mac(value) != value_mac:
- raise DecryptionError('MAC value does not match')
- return True
-
def setup(self, key=None, algorithm=None):
"""Configure an encrypted MAC key.
diff --git a/tests/encryption/mac-over-plaintext.pskcxml
b/tests/encryption/mac-over-plaintext.pskcxml
new file mode 100644
index 0000000..5f12e91
--- /dev/null
+++ b/tests/encryption/mac-over-plaintext.pskcxml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Test that holds an aes128-cbc encrypted value. Key is
+ 12345678901234567890123456789012. The ValueMAC is a MAC over the plaintext
+ secret instead of the ciphertext.
+-->
+
+<KeyContainer Version="1.0"
+ xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
+ <EncryptionKey>
+ <ds:KeyName>Pre-shared-key</ds:KeyName>
+ </EncryptionKey>
+ <MACMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#hmac-sha224">
+ <MACKey>
+ <xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
+ <xenc:CipherData>
+
<xenc:CipherValue>SVZJVklWSVZJVklWSVZJViZS3d+rzbWqD74OQPuyiwrD+XlDXK7ef602mwOebfTR</xenc:CipherValue>
+ </xenc:CipherData>
+ </MACKey>
+ </MACMethod>
+ <KeyPackage>
+ <Key>
+ <Data>
+ <Secret>
+ <EncryptedValue>
+ <xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
+ <xenc:CipherData>
+
<xenc:CipherValue>AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv</xenc:CipherValue>
+ </xenc:CipherData>
+ </EncryptedValue>
+ <ValueMAC>RDATcSJh3n8TAvMDoPzKqobgOCPZSluA7Gmvpg==</ValueMAC>
+ </Secret>
+ </Data>
+ </Key>
+ </KeyPackage>
+</KeyContainer>
diff --git a/tests/invalid/mac-missing.pskcxml
b/tests/invalid/mac-missing.pskcxml
deleted file mode 100644
index 04b8ba7..0000000
--- a/tests/invalid/mac-missing.pskcxml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Based on figure 6 from RFC6030 but with a missing encryption element
- but MAC present.
--->
-
-<KeyContainer Version="1.0"
- xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
- xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
- xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
- <MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>
- <KeyPackage>
- <DeviceInfo>
- <Manufacturer>Manufacturer</Manufacturer>
- <SerialNo>987654321</SerialNo>
- </DeviceInfo>
- <CryptoModuleInfo>
- <Id>CM_ID_001</Id>
- </CryptoModuleInfo>
- <Key Id="12345678" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
- <Issuer>Issuer</Issuer>
- <AlgorithmParameters>
- <ResponseFormat Length="8" Encoding="DECIMAL"/>
- </AlgorithmParameters>
- <Data>
- <Secret>
- <EncryptedValue>
- <xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
- <xenc:CipherData>
-
<xenc:CipherValue>AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGz</xenc:CipherValue>
- </xenc:CipherData>
- </EncryptedValue>
- <ValueMAC>Su+NvtQfmvfJzF6bmQiJqoLRExc=</ValueMAC>
- </Secret>
- <Counter>
- <PlainValue>0</PlainValue>
- </Counter>
- </Data>
- </Key>
- </KeyPackage>
-</KeyContainer>
diff --git a/tests/invalid/mac-value.pskcxml b/tests/invalid/mac-value.pskcxml
index 26d335c..f7ff9d5 100644
--- a/tests/invalid/mac-value.pskcxml
+++ b/tests/invalid/mac-value.pskcxml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Based on figure 6 but with a modified CipherValue element.
+ Based on figure 6 from RFC 6030 but with a modified ValueMAC element.
-->
<KeyContainer Version="1.0"
@@ -37,10 +37,10 @@
<EncryptedValue>
<xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
<xenc:CipherData>
-
<xenc:CipherValue>AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGz</xenc:CipherValue>
+
<xenc:CipherValue>AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv</xenc:CipherValue>
</xenc:CipherData>
</EncryptedValue>
- <ValueMAC>Su+NvtQfmvfJzF6bmQiJqoLRExc=</ValueMAC>
+ <ValueMAC>Au+NvtQfmvfJzF6bmQiJqoLRExc=</ValueMAC>
</Secret>
<Counter>
<PlainValue>0</PlainValue>
diff --git a/tests/invalid/empty-mac-key.pskcxml
b/tests/invalid/missing-encryption-method.pskcxml
similarity index 66%
rename from tests/invalid/empty-mac-key.pskcxml
rename to tests/invalid/missing-encryption-method.pskcxml
index 16f5c4f..b8a1848 100644
--- a/tests/invalid/empty-mac-key.pskcxml
+++ b/tests/invalid/missing-encryption-method.pskcxml
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- An encrypted secret with a MAC but missing a global MAC key definition.
+ Based on the Figure 6 example, this file is missing the EncryptionMethod
+ element in both the encrypted value and the MAC key definition.
-->
<KeyContainer Version="1.0"
@@ -11,21 +12,19 @@
<EncryptionKey>
<ds:KeyName>Pre-shared-key</ds:KeyName>
</EncryptionKey>
- <MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-zha9">
+ <MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1">
<MACKey>
</MACKey>
</MACMethod>
<KeyPackage>
- <Key Id="12345678" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
+ <Key Id="45678901" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
<Data>
<Secret>
<EncryptedValue>
- xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
<xenc:CipherData>
<xenc:CipherValue>AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv</xenc:CipherValue>
</xenc:CipherData>
</EncryptedValue>
- <ValueMAC>LP6xMvjtypbfT9PdkJhBZ+D6O4w=</ValueMAC>
</Secret>
</Data>
</Key>
diff --git a/tests/test_encryption.doctest b/tests/test_encryption.doctest
index a04348a..2819256 100644
--- a/tests/test_encryption.doctest
+++ b/tests/test_encryption.doctest
@@ -147,6 +147,15 @@ PSKC format allowed using the encryption key for the HMAC
function.
'12345678901234567890'
+Older versions of the PSKC format allowed having the MAC go over the
+plaintext instead of the ciphertext.
+
+>>> pskc = PSKC('tests/encryption/mac-over-plaintext.pskcxml')
+>>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012')
+>>> tostr(pskc.keys[0].secret)
+'12345678901234567890'
+
+
Test decryption with tripledes-cbc and a specified IV.
>>> iv = a2b_hex('1010101010101010')
diff --git a/tests/test_invalid.doctest b/tests/test_invalid.doctest
index d110f34..9231c5c 100644
--- a/tests/test_invalid.doctest
+++ b/tests/test_invalid.doctest
@@ -88,6 +88,22 @@ DecryptionError: No algorithm specified
'3132333435363738393031323334353637383930'
+This PSKC file has a key without an encryption method specified.
+
+>>> pskc = PSKC('tests/invalid/missing-encryption-method.pskcxml')
+>>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012')
+>>> key = pskc.keys[0]
+>>> key.id
+'45678901'
+>>> b2a_hex(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL
+Traceback (most recent call last):
+ ...
+DecryptionError: No algorithm specified
+>>> pskc.encryption.algorithm = 'aes128-cbc'
+>>> b2a_hex(key.secret)
+'3132333435363738393031323334353637383930'
+
+
This PSKC file has an incomplete key derivation configuration.
>>> pskc = PSKC('tests/invalid/incomplete-derivation.pskcxml')
@@ -144,19 +160,6 @@ Traceback (most recent call last):
DecryptionError: No MAC algorithm set
-There is an empty MACKey in the PSKC file.
-
->>> pskc = PSKC('tests/invalid/empty-mac-key.pskcxml')
->>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012')
->>> key = pskc.keys[0]
->>> key.id
-'12345678'
->>> key.secret # doctest: +IGNORE_EXCEPTION_DETAIL
-Traceback (most recent call last):
- ...
-DecryptionError: No MAC key available
-
-
There is an unknown algorithm specified in MACMethod.
>>> pskc = PSKC('tests/invalid/mac-algorithm.pskcxml')
@@ -192,18 +195,6 @@ Traceback (most recent call last):
DecryptionError: MAC value does not match
-A MAC is specified but keys are missing.
-
->>> pskc = PSKC('tests/invalid/mac-missing.pskcxml')
->>> key = pskc.keys[0]
->>> key.id
-'12345678'
->>> key.secret # doctest: +IGNORE_EXCEPTION_DETAIL
-Traceback (most recent call last):
- ...
-DecryptionError: No MAC key available
-
-
Checks to see that invalid values are detected.
>>> pskc = PSKC('tests/invalid/not-integer.pskcxml') # doctest:
>>> +IGNORE_EXCEPTION_DETAIL
http://arthurdejong.org/git/python-pskc/commit/?id=bae70840d42c7d371c3e2499181dc57a0df4401d
commit bae70840d42c7d371c3e2499181dc57a0df4401d
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Mon Dec 19 23:33:43 2016 +0100
Add sanity checks to unpadding
diff --git a/pskc/crypto/__init__.py b/pskc/crypto/__init__.py
index af0d336..356e498 100644
--- a/pskc/crypto/__init__.py
+++ b/pskc/crypto/__init__.py
@@ -27,6 +27,12 @@ def pad(value, block_size):
return value + padding * chr(padding).encode('ascii')
-def unpad(value):
+def unpad(value, block_size):
"""Remove padding from the plaintext."""
- return value[0:-ord(value[-1:])]
+ from pskc.exceptions import DecryptionError
+ padding = ord(value[-1:])
+ # only unpad if all padding bytes are the same
+ if (padding > 0 and padding <= block_size and
+ value[-padding:] == padding * chr(padding).encode('ascii')):
+ return value[:-padding]
+ raise DecryptionError('Invalid padding')
diff --git a/pskc/encryption.py b/pskc/encryption.py
index a493e79..bbfcf2f 100644
--- a/pskc/encryption.py
+++ b/pskc/encryption.py
@@ -66,7 +66,7 @@ def decrypt(algorithm, key, ciphertext, iv=None):
iv = ciphertext[:AES.block_size]
ciphertext = ciphertext[AES.block_size:]
cipher = AES.new(key, AES.MODE_CBC, iv)
- return unpad(cipher.decrypt(ciphertext))
+ return unpad(cipher.decrypt(ciphertext), AES.block_size)
elif algorithm.endswith('#tripledes-cbc'):
from Crypto.Cipher import DES3
from pskc.crypto import unpad
@@ -74,7 +74,7 @@ def decrypt(algorithm, key, ciphertext, iv=None):
iv = ciphertext[:DES3.block_size]
ciphertext = ciphertext[DES3.block_size:]
cipher = DES3.new(key, DES3.MODE_CBC, iv)
- return unpad(cipher.decrypt(ciphertext))
+ return unpad(cipher.decrypt(ciphertext), DES3.block_size)
elif algorithm.endswith('#kw-aes128') or \
algorithm.endswith('#kw-aes192') or \
algorithm.endswith('#kw-aes256'):
diff --git a/tests/test_crypto.doctest b/tests/test_crypto.doctest
new file mode 100644
index 0000000..73cef48
--- /dev/null
+++ b/tests/test_crypto.doctest
@@ -0,0 +1,76 @@
+test_crypto.doctest - test various internal crypto helper functions
+
+Copyright (C) 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
+
+>>> import random
+>>> 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)
+
+>>> from pskc.crypto import pad, unpad
+
+
+Tests for padding and unpadding. This should follow PKCS#7 as defined in RFC
+5652 section 6.3.
+
+>>> b2a_hex(pad(a2b_hex('1234'), 16))
+'12340e0e0e0e0e0e0e0e0e0e0e0e0e0e'
+>>> b2a_hex(pad(a2b_hex('abcdefabcdefabcdefabcdefabcdef'), 16))
+'abcdefabcdefabcdefabcdefabcdef01'
+>>> b2a_hex(pad(bytearray(16 * [65]), 16))
+'4141414141414141414141414141414110101010101010101010101010101010'
+
+>>> b2a_hex(unpad(a2b_hex('12340e0e0e0e0e0e0e0e0e0e0e0e0e0e'), 16))
+'1234'
+>>> b2a_hex(unpad(a2b_hex('abcdefabcdefabcdefabcdefabcdef01'), 16))
+'abcdefabcdefabcdefabcdefabcdef'
+
+
+Padding should always work but there are some conditions where unpadding does
+not work. Specifically, the to be removed padding should all have the same
+bytes:
+
+>>> b2a_hex(unpad(a2b_hex('ffff0606060f0606'), 8)) # doctest:
+IGNORE_EXCEPTION_DETAIL
+Traceback (most recent call last):
+ ...
+DecryptionError: Invalid padding
+
+The unpadding should also refuse to remove more than a block size worth of
+data:
+
+>>> b2a_hex(unpad(a2b_hex('ffff0e0e0e0e0e0e0e0e0e0e0e0e0e0e'), 8)) # doctest:
+IGNORE_EXCEPTION_DETAIL
+Traceback (most recent call last):
+ ...
+DecryptionError: Invalid padding
+
+
+We generate a number of (non-cryptographically strong) random strings and
+ensure that padding and unpadding works for them.
+
+>>> for i in range(100):
+... l = random.randrange(1, 255)
+... r = bytearray(random.getrandbits(8) for x in range(l))
+... b = random.randrange(10, 100)
+... p = pad(r, b)
+... u = unpad(p, b)
+... if u != r:
+... raise ValueError('%s -> %s -> %s (%d)' % (
+... b2a_hex(r), b2a_hex(p), b2a_hex(u), b))
-----------------------------------------------------------------------
Summary of changes:
pskc/crypto/__init__.py | 10 +-
pskc/encryption.py | 4 +-
pskc/key.py | 27 +-
pskc/mac.py | 13 -
pskc/parser.py | 61 +++--
pskc/xml.py | 26 +-
setup.cfg | 2 +
.../figure2.pskcxml} | 25 +-
.../figure3.pskcxml} | 46 ++--
tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml | 51 ++++
tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml | 71 +++++
tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml | 63 +++++
tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml | 34 +++
tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml | 68 +++++
.../figure9.pskcxml} | 55 ++--
...s128-cbc.pskcxml => mac-over-plaintext.pskcxml} | 5 +-
tests/invalid/mac-missing.pskcxml | 42 ---
tests/invalid/mac-value.pskcxml | 6 +-
...y.pskcxml => missing-encryption-method.pskcxml} | 9 +-
tests/rfc6030/figure8.pskcxml | 60 ++++
tests/rfc6030/figure9.pskcxml | 65 +++++
tests/test_crypto.doctest | 76 +++++
tests/test_draft_ietf_keyprov_pskc_02.doctest | 305 +++++++++++++++++++++
tests/test_encryption.doctest | 17 +-
tests/test_invalid.doctest | 41 ++-
tests/test_rfc6030.doctest | 76 ++++-
26 files changed, 1027 insertions(+), 231 deletions(-)
copy tests/{rfc6030/figure3.pskcxml =>
draft-ietf-keyprov-pskc-02/figure2.pskcxml} (51%)
copy tests/{rfc6030/figure5.pskcxml =>
draft-ietf-keyprov-pskc-02/figure3.pskcxml} (50%)
create mode 100644 tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml
create mode 100644 tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml
create mode 100644 tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml
create mode 100644 tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml
create mode 100644 tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml
copy tests/{rfc6030/figure10.pskcxml =>
draft-ietf-keyprov-pskc-02/figure9.pskcxml} (75%)
copy tests/encryption/{aes128-cbc.pskcxml => mac-over-plaintext.pskcxml} (86%)
delete mode 100644 tests/invalid/mac-missing.pskcxml
rename tests/invalid/{empty-mac-key.pskcxml =>
missing-encryption-method.pskcxml} (66%)
create mode 100644 tests/rfc6030/figure8.pskcxml
create mode 100644 tests/rfc6030/figure9.pskcxml
create mode 100644 tests/test_crypto.doctest
create mode 100644 tests/test_draft_ietf_keyprov_pskc_02.doctest
hooks/post-receive
--
python-pskc
--
To unsubscribe send an email to
python-pskc-commits-unsubscribe@lists.arthurdejong.org or see
https://lists.arthurdejong.org/python-pskc-commits/
- python-pskc branch master updated. 0.4-29-g2f7cb1a,
Commits of the python-pskc project