lists.arthurdejong.org
RSS feed

python-pskc branch master updated. b952b935314e77a01cd71448730fe029978d1627

[Date Prev][Date Next] [Thread Prev][Thread Next]

python-pskc branch master updated. b952b935314e77a01cd71448730fe029978d1627



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  b952b935314e77a01cd71448730fe029978d1627 (commit)
       via  e939a961c421997a5605f64da784d26d2f0cd55a (commit)
       via  8c9ac8cb824587099632de259b5446564531662c (commit)
      from  6446f7d30ea4d6b5c4f38ce99e071bb9add25ce1 (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=b952b935314e77a01cd71448730fe029978d1627

commit b952b935314e77a01cd71448730fe029978d1627
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Apr 11 17:53:23 2014 +0200

    Add test for Figure 5 from RFC6030
    
    This test extraction of key policy information and cross-key references.

diff --git a/tests/rfc6030-figure5.pskc b/tests/rfc6030-figure5.pskc
new file mode 100644
index 0000000..88f7c6e
--- /dev/null
+++ b/tests/rfc6030-figure5.pskc
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Non-Encrypted HOTP Secret Key with PIN example from RFC6030 (Figure 5).
+-->
+
+<KeyContainer Version="1.0"
+    Id="exampleID1"
+    xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
+    <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>
+                    <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>
+    </KeyPackage>
+    <KeyPackage>
+        <DeviceInfo>
+            <Manufacturer>Manufacturer</Manufacturer>
+            <SerialNo>987654321</SerialNo>
+        </DeviceInfo>
+        <CryptoModuleInfo>
+            <Id>CM_ID_001</Id>
+        </CryptoModuleInfo>
+        <Key Id="123456781"
+            Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:pin">
+            <Issuer>Issuer</Issuer>
+            <AlgorithmParameters>
+                <ResponseFormat Length="4" Encoding="DECIMAL"/>
+            </AlgorithmParameters>
+            <Data>
+                <Secret>
+                    <PlainValue>MTIzNA==</PlainValue>
+                </Secret>
+            </Data>
+        </Key>
+    </KeyPackage>
+</KeyContainer>
diff --git a/tests/test_rfc6030.doctest b/tests/test_rfc6030.doctest
index 5e131e0..049f3b1 100644
--- a/tests/test_rfc6030.doctest
+++ b/tests/test_rfc6030.doctest
@@ -86,3 +86,55 @@ on the meanings of key_profile and key_reference.
 'MasterKeyLabel'
 >>> key.counter
 0
+
+
+This tests the key policy properties as illustrated in Figure 5 from RFC6030.
+
+>>> pskc = PSKC('tests/rfc6030-figure5.pskc')
+>>> 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'
+>>> 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')
+True
+>>> key1.policy.may_use('Encrypt')
+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'
+>>> key2.secret
+'1234'
+>>> key1.policy.pin
+'1234'

http://arthurdejong.org/git/python-pskc/commit/?id=e939a961c421997a5605f64da784d26d2f0cd55a

commit e939a961c421997a5605f64da784d26d2f0cd55a
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Apr 11 17:50:41 2014 +0200

    Implement key policy parsing
    
    This parses key policy from PSKC files and provides a few utility
    methods to help with policy validation.

diff --git a/pskc/parse.py b/pskc/parse.py
index 78e1cf9..dc980af 100644
--- a/pskc/parse.py
+++ b/pskc/parse.py
@@ -23,6 +23,8 @@ import base64
 
 import dateutil.parser
 
+from pskc.policy import Policy
+
 
 # the relevant XML namespaces for PSKC
 namespaces = dict(
@@ -97,7 +99,9 @@ class IntegerDataType(DataType):
 
 class Key(object):
 
-    def __init__(self, key_package):
+    def __init__(self, pskc, key_package):
+
+        self.pskc = pskc
 
         self.manufacturer = g_e_v(key_package, 
'pskc:DeviceInfo/pskc:Manufacturer')
         self.serial = g_e_v(key_package, 'pskc:DeviceInfo/pskc:SerialNo')
@@ -183,6 +187,9 @@ class Key(object):
             time_drift = IntegerDataType(data.find('pskc:TimeDrift', 
namespaces=namespaces))
             self.time_drift = time_drift
 
+        self.policy = Policy(self, key_package.find(
+            'pskc:Key/pskc:Policy', namespaces=namespaces))
+
 
 class PSKC(object):
 
@@ -196,4 +203,4 @@ class PSKC(object):
         # handle KeyPackage entries
         self.keys = []
         for package in container.findall('pskc:KeyPackage', 
namespaces=namespaces):
-            self.keys.append(Key(package))
+            self.keys.append(Key(self, package))
diff --git a/pskc/policy.py b/pskc/policy.py
new file mode 100644
index 0000000..7a15ced
--- /dev/null
+++ b/pskc/policy.py
@@ -0,0 +1,116 @@
+# policy.py - module for handling PSKC policy information
+# coding: utf-8
+#
+# Copyright (C) 2014 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
+
+
+class Policy(object):
+    """Representation of a policy that describes key and pin usage.
+
+    Instances of this class provide attributes that describe limits that
+    are placed on key usage and requirements for key PIN protection. The
+    policy provides the following attributes:
+
+      start_date: the key MUST not be used before this datetime
+      expiry_date: the key MUST not be used after this datetime
+      number_of_transactions: maximum number of times the key may be used
+      key_usage: list of valid usage scenarios for the key (e.g. OTP)
+      pin_key_id: id of to the key that holds the PIN
+      pin_key: reference to the key that holds the PIN
+      pin: value of the PIN to use
+      pin_usage: define how the PIN is used in relation to the key
+      pin_max_failed_attemtps: max. number of times a wrong PIN may be entered
+      pin_min_length: minimum length of a PIN that may be set
+      pin_max_length: maximum length of a PIN that may be set
+      pin_encoding: DECIMAL/HEXADECIMAL/ALPHANUMERIC/BASE64/BINARY
+      unknown_policy_elements: True if the policy contains unsupported rules
+
+    If unknown_policy_elements is True the recipient MUST assume that key
+    usage is not permitted.
+    """
+
+    def __init__(self, key=None, policy=None):
+        """Create a new policy, optionally linked to the key and parsed."""
+        self.start_date = None
+        self.expiry_date = None
+        self.number_of_transactions = None
+        self.key_usage = []
+        self.pin_key_id = None
+        self.pin_usage = None
+        self.pin_max_failed_attemtps = None
+        self.pin_min_length = None
+        self.pin_max_length = None
+        self.pin_encoding = None
+        self.unknown_policy_elements = False
+        self.key = key
+        if policy is not None:
+            self.parse(policy)
+
+    def parse(self, policy):
+        """Read key policy information from the provided KeyPackage tree."""
+        from pskc.parse import g_e_v, g_e_i, g_e_d, namespaces
+
+        self.start_date = g_e_d(policy, 'pskc:StartDate')
+        self.expiry_date = g_e_d(policy, 'pskc:ExpiryDate')
+        self.number_of_transactions = g_e_i(
+            policy, 'pskc:NumberOfTransactions')
+        for key_usage in policy.findall(
+                'pskc:KeyUsage', namespaces=namespaces):
+            self.key_usage.append(g_e_v(key_usage, '.'))
+
+        pin_policy = policy.find(
+            'pskc:PINPolicy', namespaces=namespaces)
+        if pin_policy is not None:
+            self.pin_key_id = pin_policy.attrib.get('PINKeyId')
+            self.pin_usage = pin_policy.attrib.get('PINUsageMode')
+            v = pin_policy.attrib.get('MaxFailedAttempts')
+            if v:
+                self.pin_max_failed_attemtps = int(v)
+            v = pin_policy.attrib.get('MinLength')
+            if v:
+                self.pin_min_length = int(v)
+            v = pin_policy.attrib.get('MaxLength')
+            if v:
+                self.pin_max_length = int(v)
+            self.pin_encoding = pin_policy.attrib.get('PINEncoding')
+            # TODO: check if there are any other attributes set for PINPolicy
+            # of if there are any children and set unknown_policy_elementss
+
+        # TODO: check if there are other children and make sure
+        # policy rejects any key usage (set unknown_policy_elements)
+
+    def may_use(self, usage):
+        """Check whether the key may be used for the provided purpose."""
+        if self.unknown_policy_elements:
+            return False
+        return not self.key_usage or usage in self.key_usage
+
+    @property
+    def pin_key(self):
+        """Reference to the PSKC Key that holds the PIN (if any)."""
+        if self.pin_key_id and self.key and self.key.pskc:
+            for key in self.key.pskc.keys:
+                if key.id == self.pin_key_id:
+                    return key
+
+    @property
+    def pin(self):
+        """PIN value referenced by PINKeyId if any."""
+        key = self.pin_key
+        if key:
+            return key.secret

http://arthurdejong.org/git/python-pskc/commit/?id=8c9ac8cb824587099632de259b5446564531662c

commit 8c9ac8cb824587099632de259b5446564531662c
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Apr 11 16:19:43 2014 +0200

    Support parsing date and integer values

diff --git a/pskc/parse.py b/pskc/parse.py
index 1c91054..78e1cf9 100644
--- a/pskc/parse.py
+++ b/pskc/parse.py
@@ -21,6 +21,8 @@
 from xml.etree import ElementTree
 import base64
 
+import dateutil.parser
+
 
 # the relevant XML namespaces for PSKC
 namespaces = dict(
@@ -37,10 +39,25 @@ namespaces = dict(
 )
 
 
-def g_e_v(element, match):
-    value = element.find(match, namespaces=namespaces)
-    if value is not None:
-        return value.text.strip()
+def g_e_v(tree, match):
+    """Get the text value of an element (or None)."""
+    element = tree.find(match, namespaces=namespaces)
+    if element is not None:
+        return element.text.strip()
+
+
+def g_e_i(tree, match):
+    """Return an element value as an int (or None)."""
+    element = tree.find(match, namespaces=namespaces)
+    if element is not None:
+        return int(element.text.strip())
+
+
+def g_e_d(tree, match):
+    """Return an element value as a datetime (or None)."""
+    element = tree.find(match, namespaces=namespaces)
+    if element is not None:
+        return dateutil.parser.parse(element.text.strip())
 
 
 class DataType(object):
@@ -87,10 +104,8 @@ class Key(object):
         self.model = g_e_v(key_package, 'pskc:DeviceInfo/pskc:Model')
         self.issue_no = g_e_v(key_package, 'pskc:DeviceInfo/pskc:IssueNo')
         self.device_binding = g_e_v(key_package, 
'pskc:DeviceInfo/pskc:DeviceBinding')
-        self.start_date = g_e_v(key_package, 'pskc:DeviceInfo/pskc:StartDate')
-        # TODO: handle <StartDate> as datetime
-        self.expiry_date = g_e_v(key_package, 
'pskc:DeviceInfo/pskc:ExpiryDate')
-        # TODO: handle <ExpiryDate> as datetime
+        self.start_date = g_e_d(key_package, 'pskc:DeviceInfo/pskc:StartDate')
+        self.expiry_date = g_e_d(key_package, 
'pskc:DeviceInfo/pskc:ExpiryDate')
         self.device_userid = g_e_v(key_package, 'pskc:DeviceInfo/pskc:UserId')
 
         self.crypto_module = g_e_v(key_package, 
'pskc:CryptoModuleInfo/pskc:Id')

-----------------------------------------------------------------------

Summary of changes:
 pskc/parse.py              |   42 ++++++++++++----
 pskc/policy.py             |  116 ++++++++++++++++++++++++++++++++++++++++++++
 tests/rfc6030-figure5.pskc |   62 +++++++++++++++++++++++
 tests/test_rfc6030.doctest |   52 ++++++++++++++++++++
 4 files changed, 262 insertions(+), 10 deletions(-)
 create mode 100644 pskc/policy.py
 create mode 100644 tests/rfc6030-figure5.pskc


hooks/post-receive
-- 
python-pskc
-- 
To unsubscribe send an email to
python-pskc-commits-unsubscribe@lists.arthurdejong.org or see
http://lists.arthurdejong.org/python-pskc-commits/