lists.arthurdejong.org
RSS feed

python-pskc branch master updated. 0.2-6-g480e2d0

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

python-pskc branch master updated. 0.2-6-g480e2d0



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  480e2d0efeba8fb916b9d4e96a196786e59acad8 (commit)
       via  37dc64ade0ccff0ba9c33ccbf976935b73ffa7a1 (commit)
       via  865a755e28d0273b2edd7d192802e8ac216074d6 (commit)
       via  61a192f40e95cb986c1cebee4a7005aae83a2dc0 (commit)
       via  69aec9fd56f7aa67db1c2a24ff3551af5fc1f012 (commit)
       via  7591271f9d8bebd7a5fa7feaa6b3e84a263eb1ca (commit)
      from  09eb6b333c5a81392b70255db637be669d511654 (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=480e2d0efeba8fb916b9d4e96a196786e59acad8

commit 480e2d0efeba8fb916b9d4e96a196786e59acad8
Merge: 7591271 37dc64a
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat Jun 28 22:47:31 2014 +0200

    Support writing unencrypted PSKC files


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

commit 37dc64ade0ccff0ba9c33ccbf976935b73ffa7a1
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Jun 27 14:34:23 2014 +0200

    Add test for writing PSKC files
    
    This makes a simple doctest that checks the writing of the XML
    representation of the PSKC data.

diff --git a/tests/test_write.doctest b/tests/test_write.doctest
new file mode 100644
index 0000000..0d91eb6
--- /dev/null
+++ b/tests/test_write.doctest
@@ -0,0 +1,143 @@
+test_write.doctest - tests for writing PSKC files
+
+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
+
+>>> import datetime
+>>> import sys
+>>> import tempfile
+>>> from dateutil.tz import tzutc
+>>> utc = tzutc()
+
+>>> from pskc import PSKC
+
+
+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=utc)
+>>> key.expiry_date = datetime.datetime(2014, 5, 31, 0, 0, tzinfo=utc)
+>>> 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 = '4e1790ba272406ba309c5a31'.decode('hex')
+
+
+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=utc)
+>>> key.policy.expiry_date = datetime.datetime(2012, 6, 13, 0, 0, tzinfo=utc)
+>>> 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)
+>>> sys.stdout.write(open(f.name, 'rb').read())
+<?xml version="1.0" encoding="UTF-8"?>
+<pskc:KeyContainer Version="1.0" 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc">
+ <pskc:KeyPackage>
+  <pskc:DeviceInfo>
+   <pskc:Manufacturer>Manufacturer</pskc:Manufacturer>
+   <pskc:SerialNo>987654321</pskc:SerialNo>
+   <pskc:Model>Model</pskc:Model>
+   <pskc:IssueNo>2</pskc:IssueNo>
+   <pskc:StartDate>2006-05-01T00:00:00Z</pskc:StartDate>
+   <pskc:ExpiryDate>2014-05-31T00:00:00Z</pskc:ExpiryDate>
+   <pskc:UserId>uid=arthur, dc=arthurdejong, dc=org</pskc:UserId>
+  </pskc:DeviceInfo>
+  <pskc:CryptoModuleInfo>
+   <pskc:Id>CyrptoId</pskc:Id>
+  </pskc:CryptoModuleInfo>
+  <pskc:Key Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:totp" Id="123">
+   <pskc:Issuer>Issuer</pskc:Issuer>
+   <pskc:AlgorithmParameters>
+    <pskc:Suite>Clubs</pskc:Suite>
+    <pskc:ChallengeFormat CheckDigits="true" Encoding="DECIMAL" Max="8" 
Min="6"/>
+    <pskc:ResponseFormat CheckDigits="false" Encoding="DECIMAL" Length="8"/>
+   </pskc:AlgorithmParameters>
+   <pskc:KeyProfileId>key profile id</pskc:KeyProfileId>
+   <pskc:KeyReference>reference to some key</pskc:KeyReference>
+   <pskc:FriendlyName>a friendly key</pskc:FriendlyName>
+   <pskc:Data>
+    <pskc:Secret>
+     <pskc:PlainValue>TheQuickBrownFox</pskc:PlainValue>
+    </pskc:Secret>
+    <pskc:Counter>
+     <pskc:PlainValue>0</pskc:PlainValue>
+    </pskc:Counter>
+   </pskc:Data>
+   <pskc:UserId>cn=Arthur de Jong, dc=arthurdejong, dc=org</pskc:UserId>
+   <pskc:Policy>
+    <pskc:StartDate>2008-05-01T00:00:00Z</pskc:StartDate>
+    <pskc:ExpiryDate>2012-06-13T00:00:00Z</pskc:ExpiryDate>
+    <pskc:PINPolicy MaxFailedAttempts="3" MaxLength="4" MinLength="4" 
PINEncoding="DECIMAL" PINKeyId="pinID" PINUsageMode="Local"/>
+    <pskc:KeyUsage>OTP</pskc:KeyUsage>
+    <pskc:KeyUsage>Verify</pskc:KeyUsage>
+    <pskc:NumberOfTransactions>42</pskc:NumberOfTransactions>
+   </pskc:Policy>
+  </pskc:Key>
+ </pskc:KeyPackage>
+ <pskc:KeyPackage>
+  <pskc:Key Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:pin" Id="pinID">
+   <pskc:AlgorithmParameters>
+    <pskc:ResponseFormat Encoding="DECIMAL" Length="4"/>
+   </pskc:AlgorithmParameters>
+   <pskc:Data>
+    <pskc:Secret>
+     <pskc:PlainValue>MTIzNA==</pskc:PlainValue>
+    </pskc:Secret>
+   </pskc:Data>
+  </pskc:Key>
+ </pskc:KeyPackage>
+</pskc:KeyContainer>

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

commit 865a755e28d0273b2edd7d192802e8ac216074d6
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Jun 27 16:27:54 2014 +0200

    Add function for writing XML
    
    This provides a function for pretty-printing the generated XML document.

diff --git a/pskc/__init__.py b/pskc/__init__.py
index 4a33d42..7c9e2da 100644
--- a/pskc/__init__.py
+++ b/pskc/__init__.py
@@ -124,3 +124,12 @@ class PSKC(object):
                 raise AttributeError()
             setattr(key, k, v)
         return key
+
+    def write(self, filename):
+        """Write the PSKC file to the provided file."""
+        from pskc.parse import tostring
+        if hasattr(filename, 'write'):
+            filename.write(tostring(self.make_xml()))
+        else:
+            with open(filename, 'wb') as output:
+                self.write(output)
diff --git a/pskc/parse.py b/pskc/parse.py
index a853ce5..98839ad 100644
--- a/pskc/parse.py
+++ b/pskc/parse.py
@@ -148,3 +148,11 @@ def mk_elem(parent, tag=None, text=None, empty=False, 
**kwargs):
         if v is not None:
             element.set(k, _format(v))
     return element
+
+
+def tostring(element):
+    """Return a serialised XML document for the element tree."""
+    from xml.dom import minidom
+    xml = etree.tostring(element, encoding='UTF-8')
+    return minidom.parseString(xml).toprettyxml(
+        indent=' ', encoding='UTF-8').strip()

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

commit 61a192f40e95cb986c1cebee4a7005aae83a2dc0
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Jun 27 14:24:02 2014 +0200

    Construct XML document with basic PKSC information
    
    This introduces make_xml() functions to build an XML document that
    contains the basic PSKC information and keys. This currently only
    supports writing unencrypted PSKC files.

diff --git a/pskc/__init__.py b/pskc/__init__.py
index 61fa9fe..4a33d42 100644
--- a/pskc/__init__.py
+++ b/pskc/__init__.py
@@ -102,6 +102,14 @@ class PSKC(object):
         for key_package in findall(container, 'pskc:KeyPackage'):
             self.keys.append(Key(self, key_package))
 
+    def make_xml(self):
+        from pskc.parse import mk_elem
+        container = mk_elem('pskc:KeyContainer', Version=self.version,
+                            Id=self.id)
+        for key in self.keys:
+            key.make_xml(container)
+        return container
+
     def add_key(self, **kwargs):
         """Create a new key instance for the PSKC file.
 
diff --git a/pskc/key.py b/pskc/key.py
index 9ed79d4..1814a48 100644
--- a/pskc/key.py
+++ b/pskc/key.py
@@ -60,6 +60,19 @@ class DataType(object):
         self.encrypted_value.parse(find(element, 'pskc:EncryptedValue'))
         self.value_mac.parse(find(element, 'pskc:ValueMAC'))
 
+    def make_xml(self, key, tag):
+        from pskc.parse import find, mk_elem
+        # skip empty values
+        value = self.get_value()
+        if value is None:
+            return
+        # find the data tag and create our tag under it
+        data = find(key, 'pskc:Data')
+        if data is None:
+            data = mk_elem(key, 'pskc:Data', empty=True)
+        element = mk_elem(data, tag, empty=True)
+        mk_elem(element, 'pskc:PlainValue', self.to_text(self.value))
+
     def get_value(self):
         """Provide the raw binary value."""
         if self.value is not None:
@@ -87,6 +100,10 @@ class BinaryDataType(DataType):
         """Convert the plain value to native representation."""
         return base64.b64decode(value)
 
+    def to_text(self, value):
+        """Convert the value to an unencrypted string representation."""
+        return base64.b64encode(value)
+
     def from_bin(self, value):
         """Convert the unencrypted binary to native representation."""
         return value
@@ -99,6 +116,10 @@ class IntegerDataType(DataType):
         """Convert the plain value to native representation."""
         return int(value)
 
+    def to_text(self, value):
+        """Convert the value to an unencrypted string representation."""
+        return str(value)
+
     def from_bin(self, value):
         """Convert the unencrypted binary to native representation."""
         result = 0
@@ -256,6 +277,60 @@ class Key(object):
 
         self.policy.parse(find(key_package, 'pskc:Key/pskc:Policy'))
 
+    def make_xml(self, container):
+        from pskc.parse import mk_elem
+
+        key_package = mk_elem(container, 'pskc:KeyPackage', empty=True)
+
+        if any(x is not None
+               for x in (self.manufacturer, self.serial, self.model,
+                         self.issue_no, self.device_binding, self.start_date,
+                         self.expiry_date, self.device_userid)):
+            device_info = mk_elem(key_package, 'pskc:DeviceInfo', empty=True)
+            mk_elem(device_info, 'pskc:Manufacturer', self.manufacturer)
+            mk_elem(device_info, 'pskc:SerialNo', self.serial)
+            mk_elem(device_info, 'pskc:Model', self.model)
+            mk_elem(device_info, 'pskc:IssueNo', self.issue_no)
+            mk_elem(device_info, 'pskc:DeviceBinding', self.device_binding)
+            mk_elem(device_info, 'pskc:StartDate', self.start_date)
+            mk_elem(device_info, 'pskc:ExpiryDate', self.expiry_date)
+            mk_elem(device_info, 'pskc:UserId', self.device_userid)
+
+        if self.crypto_module is not None:
+            crypto_module = mk_elem(key_package, 'pskc:CryptoModuleInfo',
+                                    empty=True)
+            mk_elem(crypto_module, 'pskc:Id', self.crypto_module)
+
+        key = mk_elem(key_package, 'pskc:Key', empty=True, Id=self.id,
+                      Algorithm=self.algorithm, )
+        mk_elem(key, 'pskc:Issuer', self.issuer)
+
+        if any((self.algorithm_suite, self.challenge_encoding,
+                self.response_encoding, self.response_length)):
+            parameters = mk_elem(key, 'pskc:AlgorithmParameters', empty=True)
+            mk_elem(parameters, 'pskc:Suite', self.algorithm_suite)
+            mk_elem(parameters, 'pskc:ChallengeFormat',
+                    Encoding=self.challenge_encoding,
+                    Min=self.challenge_min_length,
+                    Max=self.challenge_max_length,
+                    CheckDigits=self.challenge_check)
+            mk_elem(parameters, 'pskc:ResponseFormat',
+                    Encoding=self.response_encoding,
+                    Length=self.response_length,
+                    CheckDigits=self.response_check)
+
+        mk_elem(key, 'pskc:KeyProfileId', self.key_profile)
+        mk_elem(key, 'pskc:KeyReference', self.key_reference)
+        mk_elem(key, 'pskc:FriendlyName', self.friendly_name)
+        self._secret.make_xml(key, 'pskc:Secret')
+        self._counter.make_xml(key, 'pskc:Counter')
+        self._time_offset.make_xml(key, 'pskc:Time')
+        self._time_interval.make_xml(key, 'pskc:TimeInterval')
+        self._time_drift.make_xml(key, 'pskc:TimeDrift')
+        mk_elem(key, 'pskc:UserId', self.key_userid)
+
+        self.policy.make_xml(key)
+
     secret = property(
         fget=lambda self: self._secret.get_value(),
         fset=lambda self, x: self._secret.set_value(x),
diff --git a/pskc/policy.py b/pskc/policy.py
index ee0095e..f1d976c 100644
--- a/pskc/policy.py
+++ b/pskc/policy.py
@@ -136,6 +136,32 @@ class Policy(object):
         # TODO: check if there are other children and make sure
         # policy rejects any key usage (set unknown_policy_elements)
 
+    def make_xml(self, key):
+        from pskc.parse import mk_elem
+        # check if any policy attribute is set
+        if not self.key_usage and all(x is None for x in (
+                self.start_date, self.expiry_date,
+                self.number_of_transactions, self.pin_key_id, self.pin_usage,
+                self.pin_max_failed_attemtps, self.pin_min_length,
+                self.pin_max_length, self.pin_encoding)):
+            return
+        # TODO: raise exception if unknown_policy_elements is set
+
+        policy = mk_elem(key, 'pskc:Policy', empty=True)
+        mk_elem(policy, 'pskc:StartDate', self.start_date)
+        mk_elem(policy, 'pskc:ExpiryDate', self.expiry_date)
+        mk_elem(policy, 'pskc:PINPolicy',
+                PINKeyId=self.pin_key_id,
+                PINUsageMode=self.pin_usage,
+                MaxFailedAttempts=self.pin_max_failed_attemtps,
+                MinLength=self.pin_min_length,
+                MaxLength=self.pin_max_length,
+                PINEncoding=self.pin_encoding)
+        for usage in self.key_usage:
+            mk_elem(policy, 'pskc:KeyUsage', usage)
+        mk_elem(policy, 'pskc:NumberOfTransactions',
+                self.number_of_transactions)
+
     def may_use(self, usage):
         """Check whether the key may be used for the provided purpose."""
         if self.unknown_policy_elements:

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

commit 69aec9fd56f7aa67db1c2a24ff3551af5fc1f012
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Jun 27 11:39:28 2014 +0200

    Introduce mk_elem() to create elements
    
    This introduces the mk_elem() function that can be used to create
    ElementTree elements for building XML documents. This function
    transparetly handles namespaces, translation of values into XML etc.

diff --git a/pskc/parse.py b/pskc/parse.py
index a08bfc1..a853ce5 100644
--- a/pskc/parse.py
+++ b/pskc/parse.py
@@ -45,6 +45,11 @@ namespaces = dict(
 )
 
 
+# register the namespaces so the correct short names will be used
+for ns, namespace in namespaces.items():
+    etree.register_namespace(ns, namespace)
+
+
 def findall(tree, match):
     """Find a child element (or None)."""
     return tree.findall(match, namespaces=namespaces)
@@ -100,3 +105,46 @@ def getbool(tree, attribute):
     value = tree.get(attribute)
     if value:
         return value.lower() == 'true'
+
+
+def _format(value):
+    import datetime
+    if isinstance(value, datetime.datetime):
+        value = value.isoformat()
+        if value.endswith('+00:00'):
+            value = value[:-6] + 'Z'
+        return value
+    elif value is True:
+        return 'true'
+    elif value is False:
+        return 'false'
+    return str(value)
+
+
+def mk_elem(parent, tag=None, text=None, empty=False, **kwargs):
+    """Add element as a child of parent."""
+    # special-case the top-level element
+    if tag is None:
+        tag = parent
+        parent = None
+        empty = True
+    # don't create empty elements
+    if not empty and text is None and \
+       all(x is None for x in kwargs.itervalues()):
+        return
+    # replace namespace identifier with URL
+    if ':' in tag:
+        ns, name = tag.split(':', 1)
+        tag = '{%s}%s' % (namespaces[ns], name)
+    if parent is None:
+        element = etree.Element(tag)
+    else:
+        element = etree.SubElement(parent, tag)
+    # set text of element
+    if text is not None:
+        element.text = _format(text)
+    # set kwargs as attributes
+    for k, v in kwargs.iteritems():
+        if v is not None:
+            element.set(k, _format(v))
+    return element

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

commit 7591271f9d8bebd7a5fa7feaa6b3e84a263eb1ca
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Jun 27 22:19:32 2014 +0200

    Simplify DataType value handling
    
    Only store the native value of the property, not the text
    representation. This also results in the BinaryDataType and
    IntegerDataType subclasses only needing from_text() and from_bin()
    functions.

diff --git a/pskc/key.py b/pskc/key.py
index 1eabdb1..9ed79d4 100644
--- a/pskc/key.py
+++ b/pskc/key.py
@@ -34,13 +34,13 @@ class DataType(object):
     This class is meant to be subclassed to provide typed access to stored
     values. Instances of this class provide the following attributes:
 
-      plain_value: raw unencrypted value if present (possibly base64 encoded)
+      value: unencrypted value if present
       encrypted_value: reference to an EncryptedValue instance
       value_mac: reference to a ValueMAC instance
     """
 
     def __init__(self, key, element=None):
-        self.plain_value = None
+        self.value = None
         self.encrypted_value = EncryptedValue(key.pskc.encryption)
         self.value_mac = ValueMAC(key.pskc.mac)
         self.parse(element)
@@ -54,13 +54,24 @@ class DataType(object):
         from pskc.parse import find, findtext
         if element is None:
             return
-        self.plain_value = findtext(element, 'pskc:PlainValue')
+        value = findtext(element, 'pskc:PlainValue')
+        if value is not None:
+            self.value = self.from_text(value)
         self.encrypted_value.parse(find(element, 'pskc:EncryptedValue'))
         self.value_mac.parse(find(element, 'pskc:ValueMAC'))
 
+    def get_value(self):
+        """Provide the raw binary value."""
+        if self.value is not None:
+            return self.value
+        if self.encrypted_value.cipher_value:
+            # check MAC and decrypt
+            self.check()
+            return self.from_bin(self.encrypted_value.decrypt())
+
     def set_value(self, value):
-        """Save the plain value."""
-        self.plain_value = self.to_plain(value)
+        """Set the unencrypted value."""
+        self.value = value
         self.encrypted_value.cipher_value = None
 
     def check(self):
@@ -72,45 +83,28 @@ class DataType(object):
 class BinaryDataType(DataType):
     """Subclass of DataType for binary data (e.g. keys)."""
 
-    def get_value(self):
-        """Provide the raw binary value."""
-        # plain value is base64 encoded
-        if self.plain_value is not None:
-            return base64.b64decode(self.plain_value)
-        # check MAC if present
-        self.check()
-        # encrypted value is in correct format
-        return self.encrypted_value.decrypt()
+    def from_text(self, value):
+        """Convert the plain value to native representation."""
+        return base64.b64decode(value)
 
-    def to_plain(self, value):
-        """Convert the value to an unencrypted string representation."""
-        if value:
-            return base64.b64encode(value)
+    def from_bin(self, value):
+        """Convert the unencrypted binary to native representation."""
+        return value
 
 
 class IntegerDataType(DataType):
     """Subclass of DataType for integer types (e.g. counters)."""
 
-    def get_value(self):
-        """Provide the raw integer value."""
-        # plain value is a string representation of the number
-        if self.plain_value:
-            return int(self.plain_value)
-        # check MAC if present
-        self.check()
-        # decrypted value is big endian encoded
-        value = self.encrypted_value.decrypt()
-        if value is not None:
-            # Python3 has int.from_bytes(value, byteorder='big')
-            v = 0
-            for x in value:
-                v = (v << 8) + ord(x)
-            return v
-
-    def to_plain(self, value):
-        """Convert the value to an unencrypted string representation."""
-        if value not in (None, ''):
-            return str(value)
+    def from_text(self, value):
+        """Convert the plain value to native representation."""
+        return int(value)
+
+    def from_bin(self, value):
+        """Convert the unencrypted binary to native representation."""
+        result = 0
+        for x in value:
+            result = (result << 8) + ord(x)
+        return result
 
 
 class Key(object):

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

Summary of changes:
 pskc/__init__.py         |   17 ++++++
 pskc/key.py              |  141 +++++++++++++++++++++++++++++++++------------
 pskc/parse.py            |   56 ++++++++++++++++++
 pskc/policy.py           |   26 +++++++++
 tests/test_write.doctest |  143 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 347 insertions(+), 36 deletions(-)
 create mode 100644 tests/test_write.doctest


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/