Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
path: root/pskc/key.py
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2016-03-27 17:53:26 +0200
committerArthur de Jong <arthur@arthurdejong.org>2016-03-27 17:53:26 +0200
commitb4a6c720cb202f44b07ad2d0f9d8812ab7212ea5 (patch)
treefe056384a6a3112dd6ca9f34f3d722413fe931e1 /pskc/key.py
parent8b5f6c27e0dde5f8b995b89dd2e3c9fa3caed3d5 (diff)
parent59aa65be6d3349e78d2f17e94ae34f6767113a87 (diff)
Implement writing encrypted files
This adds support for setting up encryption keys and password-based key derivation when writing PSKC files. Also MAC keys are set up when needed.
Diffstat (limited to 'pskc/key.py')
-rw-r--r--pskc/key.py63
1 files changed, 52 insertions, 11 deletions
diff --git a/pskc/key.py b/pskc/key.py
index c332efa..d7d1cb0 100644
--- a/pskc/key.py
+++ b/pskc/key.py
@@ -23,6 +23,7 @@
import array
import base64
+import binascii
from pskc.policy import Policy
@@ -91,18 +92,43 @@ class DataType(object):
"""Convert the value to an unencrypted string representation."""
raise NotImplementedError # pragma: no cover
- def make_xml(self, key, tag):
+ def make_xml(self, key, tag, field):
from pskc.xml import find, mk_elem
# skip empty values
- value = self.get_value()
- if value is None:
+ if self.value in (None, '') and not self.cipher_value:
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(value))
+ # see if we should encrypt
+ if field in self.pskc.encryption.fields and not self.cipher_value:
+ self.cipher_value = self.pskc.encryption.encrypt_value(
+ self._to_bin(self.value))
+ self.algorithm = self.pskc.encryption.algorithm
+ self.value = None
+ # write out value
+ if self.cipher_value:
+ encrypted_value = mk_elem(
+ element, 'pskc:EncryptedValue', empty=True)
+ mk_elem(
+ encrypted_value, 'xenc:EncryptionMethod',
+ Algorithm=self.algorithm)
+ cipher_data = mk_elem(
+ encrypted_value, 'xenc:CipherData', empty=True)
+ mk_elem(
+ cipher_data, 'xenc:CipherValue',
+ base64.b64encode(self.cipher_value).decode())
+ if self.value_mac:
+ mk_elem(element, 'pskc:ValueMAC', base64.b64encode(
+ self.value_mac).decode())
+ elif self.pskc.mac.key:
+ mk_elem(element, 'pskc:ValueMAC', base64.b64encode(
+ self.pskc.mac.generate_mac(self.cipher_value)
+ ).decode())
+ else:
+ mk_elem(element, 'pskc:PlainValue', self._to_text(self.value))
def get_value(self):
"""Provide the attribute value, decrypting as needed."""
@@ -150,6 +176,14 @@ class BinaryDataType(DataType):
value = value.encode() # pragma: no cover (Python 3 specific)
return base64.b64encode(value).decode()
+ @staticmethod
+ def _to_bin(value):
+ """Convert the value to binary representation for encryption."""
+ # force conversion to bytestring on Python 3
+ if not isinstance(value, type(b'')):
+ value = value.encode() # pragma: no cover (Python 3 specific)
+ return value
+
class IntegerDataType(DataType):
"""Subclass of DataType for integer types (e.g. counters)."""
@@ -182,6 +216,13 @@ class IntegerDataType(DataType):
"""Convert the value to an unencrypted string representation."""
return str(value)
+ @staticmethod
+ def _to_bin(value):
+ """Convert the value to binary representation for encryption."""
+ value = '%x' % value
+ n = len(value)
+ return binascii.unhexlify(value.zfill(n + (n & 1)))
+
class Key(object):
"""Representation of a single key from a PSKC file.
@@ -314,7 +355,7 @@ class Key(object):
self.challenge_max_length = getint(challenge_format, 'Max')
self.challenge_check = getbool(
challenge_format, 'CheckDigits', getbool(
- challenge_format, 'CheckDigit'))
+ challenge_format, 'CheckDigit'))
response_format = find(
key_package,
@@ -324,7 +365,7 @@ class Key(object):
self.response_length = getint(response_format, 'Length')
self.response_check = getbool(
response_format, 'CheckDigits', getbool(
- response_format, 'CheckDigit'))
+ response_format, 'CheckDigit'))
self.policy.parse(find(key_package, 'Key/Policy'))
@@ -373,11 +414,11 @@ class Key(object):
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')
+ self._secret.make_xml(key, 'pskc:Secret', 'secret')
+ self._counter.make_xml(key, 'pskc:Counter', 'counter')
+ self._time_offset.make_xml(key, 'pskc:Time', 'time_offset')
+ self._time_interval.make_xml(key, 'pskc:TimeInterval', 'time_interval')
+ self._time_drift.make_xml(key, 'pskc:TimeDrift', 'time_drif')
mk_elem(key, 'pskc:UserId', self.key_userid)
self.policy.make_xml(key)