Commit 816c2f20 authored by Arthur de Jong's avatar Arthur de Jong

Import python-pskc_0.4.orig.tar.gz

parent e9792624
This diff is collapsed.
changes from 0.3 to 0.4
-----------------------
* add support for writing encrypted PSKC files (with either a pre-shared key
or PBKDF2 password-based encryption)
* extend may_use() policy checking function to check for unknown policy
elements and key expiry
* add a number of tests for existing vendor PSKC files and have full line
coverage with tests
* be more lenient in handling a number of XML files (e.g. automatically
sanitise encryption algorithm URIs, ignore XML namespaces and support more
spellings of some properties)
* support reading password or key files in pskc2csv
* support Python 3 in the pskc2csv script (thanks Mathias Laurin)
* refactoring and clean-ups to be more easily extendible (thanks Mathias
Laurin)
changes from 0.2 to 0.3
-----------------------
......
Metadata-Version: 1.1
Name: python-pskc
Version: 0.3
Version: 0.4
Summary: Python module for handling PSKC files
Home-page: http://arthurdejong.org/python-pskc/
Author: Arthur de Jong
......@@ -13,13 +13,13 @@ Description: Python module for handling PSKC files
keys (seed files) to different types of crypto modules, commonly one-time
password tokens or other authentication devices.
The main goal of this module is to be able to extract keys from PSKC files
for use in an OTP authentication system.
This module can be used to extract keys from PSKC files for use in an OTP
authentication system. The module can also be used for authoring PSKC files.
The following prints all keys, decrypting using a password:
>>> from pskc import PSKC
>>> pskc = PSKC('tests/rfc6030-figure7.pskcxml')
>>> pskc = PSKC('tests/rfc6030/figure7.pskcxml')
>>> pskc.encryption.derive_key('qwerty')
>>> for key in pskc.keys:
... print('%s %s' % (key.serial, str(key.secret.decode())))
......@@ -42,5 +42,7 @@ Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
Classifier: Topic :: Text Processing :: Markup :: XML
......@@ -3,12 +3,12 @@ Python PSKC module
A Python module to handle Portable Symmetric Key Container (PSKC) files as
defined in `RFC 6030 <https://tools.ietf.org/html/rfc6030>`_. PSKC files are
used to transport and provision symmetric keys and key meta data to different
types of crypto modules. The format is commonly used for one-time password
tokens or other authentication devices.
used to transport and provision symmetric keys and key meta data (seed files)
to different types of crypto modules, commonly one-time password tokens or
other authentication devices.
The goal of this module is mainly to provide parsing of PSKC files in order
to extract secret keys for use in an OTP authentication system.
This module can be used to extract keys from PSKC files for use in an OTP
authentication system. The module can also be used for authoring PSKC files.
http://arthurdejong.org/python-pskc/
......@@ -19,10 +19,10 @@ API
The module provides a straightforward API that is mostly geared towards
parsing existing PSKC files.
Extracting key matarial from encrypted PSKC files is as simple as.
Extracting key material from encrypted PSKC files is as simple as.
>>> from pskc import PSKC
>>> pskc = PSKC('tests/rfc6030-figure7.pskcxml')
>>> pskc = PSKC('tests/rfc6030/figure7.pskcxml')
>>> pskc.encryption.derive_key('qwerty')
>>> for key in pskc.keys:
... print key.serial, key.secret
......@@ -44,7 +44,7 @@ private key material.
Copyright
---------
Copyright (C) 2014-2015 Arthur de Jong
Copyright (C) 2014-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
......@@ -60,3 +60,12 @@ 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
Development notes
-----------------
This package should use a mostly standard source code layout and support both
Python 2 (2.6 but 2.7 is recommended) and Python 3 (most recent versions
should work). The tests can be run with nosetests and the aim is to have
maximum code coverage.
......@@ -46,7 +46,7 @@ master_doc = 'index'
# General information about the project.
project = u'python-pskc'
copyright = u'2014-2015 Arthur de Jong'
copyright = u'2014-2016 Arthur de Jong'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
......
......@@ -3,15 +3,15 @@ PSKC encryption
.. module:: pskc.encryption
The keys (and some embedded data) in PSKC files can be encrypted with either
pre-shared keys, passphrase-based keys or asymmetric keys (asymmetric keys
are currently unimplemented).
Some of the information in PSKC files (e.g. key material) can be encrypted
with either pre-shared keys, passphrase-based keys or asymmetric keys
(asymmetric keys are currently unimplemented).
Embedded PSKC encryption is handled inside the :class:`Encryption` class that
defines encryption key or means of deriving keys. It is accessed from the
:attr:`~pskc.PSKC.encryption` attribute of a :class:`~pskc.PSKC` instance::
>>> rom binascii import a2b_hex
>>> from binascii import a2b_hex
>>> from pskc import PSKC
>>> pskc = PSKC('somefile.pskcxml')
>>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012')
......@@ -23,11 +23,23 @@ or::
Once the encryption key has been set up, any encrypted key values from the
PSKC file are available transparently.
If an incorrect key has been set up, upon accessing encrypted
If no key or an incorrect key has been configured, upon accessing encrypted
information (e.g. the :attr:`~pskc.key.Key.secret` attribute of a
:class:`~pskc.key.Key` instance) a :exc:`~pskc.exceptions.DecryptionError`
exception will be raised.
When writing out a PSKC file, encryption can be configured with the
:func:`~pskc.encryption.Encryption.setup_preshared_key()` or
:func:`~pskc.encryption.Encryption.setup_pbkdf2()` function::
>>> from pskc import PSKC
>>> pskc = PSKC()
>>> pskc.encryption.setup_preshared_key(algorithm='AES256-CBC')
or::
>>> pskc.encryption.setup_pbkdf2(password='verysecure')
The Encryption class
--------------------
......@@ -38,6 +50,13 @@ The Encryption class
Optional identifier of the encryption key.
.. attribute:: algorithm
A URI of the encryption algorithm used. Setting a value for this
attribute will result in an attempt to use the canonical URI for this
algorithm. For instance setting a `3DES-CBC` value will automatically
be converted to `http://www.w3.org/2001/04/xmlenc#aes128-cbc`.
.. attribute:: key_names
List of names provided for the encryption key.
......@@ -46,7 +65,7 @@ The Encryption class
Since usually only one name is defined for a key but the schema allows
for multiple names, this is a shortcut for accessing the first value of
:attr:`key_names`.
:attr:`key_names`. It will return ``None`` if no name is available.
.. attribute:: key
......@@ -64,3 +83,58 @@ The Encryption class
This function may raise a :exc:`~pskc.exceptions.KeyDerivationError`
exception if key derivation fails for some reason.
.. attribute:: fields
A list of :class:`~pskc.key.Key` instance field names that will be
encrypted when the PSKC file is written. List values can contain
``secret``, ``counter``, ``time_offset``, ``time_interval`` and
``time_drift``.
.. function:: setup_preshared_key(...)
Configure pre-shared key encryption.
:param binary key: the encryption key to use
:param str id: encryption key identifier
:param str algorithm: encryption algorithm
:param int key_length: encryption key length in bytes
:param str key_name: a name for the key
:param list key_names: a number of names for the key
:param list fields: a list of fields to encrypt
This is a utility function to easily set up encryption. Encryption can
also be set up by manually by setting the
:class:`~pskc.encryption.Encryption` properties.
This method will generate a key if required and set the passed values.
By default AES128-CBC encryption will be configured and unless a key is
specified one of the correct length will be generated. If the algorithm
does not provide integrity checks (e.g. CBC-mode algorithms) integrity
checking in the PSKC file will be set up using
:func:`~pskc.mac.MAC.setup()`.
By default only the :attr:`~pskc.key.Key.secret` property will be
encrypted when writing the file.
.. function:: setup_pbkdf2(...)
Configure password-based PSKC encryption.
:param str password: the password to use (required)
:param str id: encryption key identifier
:param str algorithm: encryption algorithm
:param int key_length: encryption key length in bytes
:param str key_name: a name for the key
:param list key_names: a number of names for the key
:param list fields: a list of fields to encrypt
:param binary salt: PBKDF2 salt
:param int salt_length: used when generating random salt
:param int iterations: number of PBKDF2 iterations
:param function prf: PBKDF2 pseudorandom function
Defaults for the above parameters are similar to those for
:func:`setup_preshared_key()` but the password parameter is required.
By default 12000 iterations will be used and a random salt with the
length of the to-be-generated encryption key will be used.
......@@ -38,3 +38,14 @@ The MAC class
MAC key is generated specifically for each PSKC file and encrypted with
the PSKC encryption key, so the PSKC file should be decrypted first
(see :doc:`encryption`).
.. function:: setup(...)
Configure an encrypted MAC key.
:param str algorithm: encryption algorithm
:param binary key: the encryption key to use
None of the arguments are required. By default HMAC-SHA1 will be used
as a MAC algorithm. If no key is configured a random key will be
generated with the length of the output of the configured hash.
......@@ -92,11 +92,11 @@ The Policy class
value is ``True`` to ensure that the lack of understanding of certain
extensions does not lead to unintended key usage.
.. function:: may_use(usage)
Check whether the key may be used for the provided purpose. See
:ref:`key-use-constants` below.
.. function:: may_use(usage=None, now=None)
Check whether the key may be used for the provided purpose. The key
:attr:`start_date` and :attr:`expiry_date` are also checked. The `now`
argument can be used to specify another point in time to check against.
.. _key-use-constants:
......
Basic usage
===========
The :mod:`pskc` module implements a simple and efficient API for parsing PSKC
files. The :class:`~pskc.PSKC` class is used to access the file as a whole
which provides access to a list of :class:`~pskc.key.Key` instances which
contain most of the useful information from the PSKC file.
The :mod:`pskc` module implements a simple and efficient API for parsing and
creating PSKC files. The :class:`~pskc.PSKC` class is used to access the file
as a whole which provides access to a list of :class:`~pskc.key.Key`
instances which contain most of the useful information of the PSKC file.
Opening a PSKC file
Reading a PSKC file
-------------------
Importing data from a PSKC file can be done by instantiating the
......@@ -50,16 +50,15 @@ adding keys with :func:`~pskc.PSKC.add_key()` and writing the result::
>>> from pskc import PSKC
>>> pskc = PSKC()
>>> key = pskc.add_key(id='456', manufacturer='Manufacturer')
>>> key.id
'456'
>>> key.secret = '987654321'
>>> key.algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:hotp'
>>> key = pskc.add_key(
... id='456', secret='987654321', manufacturer='Manufacturer',
... algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:hotp')
>>> pskc.write('output.pskcxml')
Writing the data in encrypted form in the PSKC file is not yet supported so
currently opening an encrypted PSKC file, providing the encryption key and
writing the file should result in the same file but with encryption removed.
By default an unencrypted PSKC file will be created but an encryption can be
configured using the
:func:`~pskc.encryption.Encryption.setup_preshared_key()` or
:func:`~pskc.encryption.Encryption.setup_pbkdf2()` function.
The PSKC class
......
# __init__.py - main module
# coding: utf-8
#
# Copyright (C) 2014-2015 Arthur de Jong
# Copyright (C) 2014-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
......@@ -25,13 +25,13 @@ defined in RFC 6030. PSKC files are used to transport and provision symmetric
keys (seed files) to different types of crypto modules, commonly one-time
password tokens or other authentication devices.
The main goal of this module is to be able to extract keys from PSKC files
for use in an OTP authentication system.
This module can be used to extract keys from PSKC files for use in an OTP
authentication system. The module can also be used for authoring PSKC files.
The following prints all keys, decrypting using a password:
>>> from pskc import PSKC
>>> pskc = PSKC('tests/rfc6030-figure7.pskcxml')
>>> pskc = PSKC('tests/rfc6030/figure7.pskcxml')
>>> pskc.encryption.derive_key('qwerty')
>>> for key in pskc.keys:
... print('%s %s' % (key.serial, str(key.secret.decode())))
......@@ -45,7 +45,7 @@ __all__ = ['PSKC', '__version__']
# the version number of the library
__version__ = '0.3'
__version__ = '0.4'
class PSKC(object):
......@@ -66,15 +66,16 @@ class PSKC(object):
from pskc.mac import MAC
self.version = None
self.id = None
self.encryption = Encryption()
self.encryption = Encryption(self)
self.mac = MAC(self)
self.keys = []
if filename is not None:
from pskc.xml import parse
from pskc.xml import parse, remove_namespaces
try:
tree = parse(filename)
except Exception:
raise ParseError('Error parsing XML')
remove_namespaces(tree)
self.parse(tree.getroot())
else:
self.version = '1.0'
......@@ -84,7 +85,7 @@ class PSKC(object):
from pskc.exceptions import ParseError
from pskc.key import Key
from pskc.xml import find, findall
if not container.tag.endswith('KeyContainer'):
if container.tag != 'KeyContainer':
raise ParseError('Missing KeyContainer')
# the version of the PSKC schema
self.version = container.get('Version')
......@@ -93,17 +94,19 @@ class PSKC(object):
# unique identifier for the container
self.id = container.get('Id')
# handle EncryptionKey entries
self.encryption.parse(find(container, 'pskc:EncryptionKey'))
self.encryption.parse(find(container, 'EncryptionKey'))
# handle MACMethod entries
self.mac.parse(find(container, 'pskc:MACMethod'))
self.mac.parse(find(container, 'MACMethod'))
# handle KeyPackage entries
for key_package in findall(container, 'pskc:KeyPackage'):
for key_package in findall(container, 'KeyPackage'):
self.keys.append(Key(self, key_package))
def make_xml(self):
from pskc.xml import mk_elem
container = mk_elem('pskc:KeyContainer', Version=self.version,
Id=self.id)
self.encryption.make_xml(container)
self.mac.make_xml(container)
for key in self.keys:
key.make_xml(container)
return container
......@@ -127,7 +130,12 @@ class PSKC(object):
"""Write the PSKC file to the provided file."""
from pskc.xml import tostring
if hasattr(filename, 'write'):
filename.write(tostring(self.make_xml()))
xml = tostring(self.make_xml())
try:
filename.write(xml)
except TypeError: # pragma: no cover (Python 3 specific)
# fall back to writing as string for Python 3
filename.write(xml.decode('utf-8'))
else:
with open(filename, 'wb') as output:
self.write(output)
This diff is collapsed.
This diff is collapsed.
# mac.py - module for checking value signatures
# coding: utf-8
#
# Copyright (C) 2014 Arthur de Jong
# Copyright (C) 2014-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
......@@ -29,58 +29,28 @@ with the PSKC encryption key.
"""
import base64
import re
_hmac_url_re = re.compile(r'^.*#hmac-(?P<hash>[a-z0-9]+)$')
def get_hmac(algorithm):
"""Return an HMAC function that takes a secret and a value and returns a
digest."""
def get_hash(algorithm):
"""Return the hash function for the specifies HMAC algorithm."""
import hashlib
import hmac
match = _hmac_url_re.search(algorithm)
if match:
digestmod = getattr(hashlib, match.group('hash'), None)
if digestmod is not None:
return lambda key, value: hmac.new(key, value, digestmod).digest()
return getattr(hashlib, match.group('hash'), None)
class ValueMAC(object):
"""Provide MAC checking ability to PSKC data values."""
def __init__(self, mac, value_mac=None):
self.mac = mac
self._value_mac = None
self.parse(value_mac)
def parse(self, value_mac):
"""Read MAC information from the <ValueMAC> XML tree."""
from pskc.xml import findbin
if value_mac is None:
return
self._value_mac = findbin(value_mac, '.')
def check(self, value):
"""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 value is None or self._value_mac is None:
return # no MAC present or nothing to check
key = self.mac.key
if key is None:
raise DecryptionError('No MAC key available')
hmacfn = get_hmac(self.mac.algorithm)
if hmacfn is None:
raise DecryptionError(
'Unsupported MAC algorithm: %r' % self.mac.algorithm)
if hmacfn(key, value) != self._value_mac:
raise DecryptionError('MAC value does not match')
return True
def get_hmac(algorithm):
"""Return an HMAC function that takes a secret and a value and returns a
digest."""
import hmac
digestmod = get_hash(algorithm)
if digestmod is not None:
return lambda key, value: hmac.new(key, value, digestmod).digest()
class MAC(object):
......@@ -92,22 +62,125 @@ class MAC(object):
key: the binary value of the MAC key if it can be decrypted
"""
def __init__(self, pskc, mac_method=None):
from pskc.encryption import EncryptedValue
self.algorithm = None
self._mac_key = EncryptedValue(pskc.encryption)
self.parse(mac_method)
def __init__(self, pskc):
self.pskc = pskc
self._algorithm = None
self.key_plain_value = None
self.key_cipher_value = None
self.key_algorithm = None
def parse(self, mac_method):
"""Read MAC information from the <MACMethod> XML tree."""
from pskc.xml import find, findtext
from pskc.xml import find, findtext, findbin
if mac_method is None:
return
self.algorithm = mac_method.get('Algorithm')
self._mac_key.parse(find(mac_method, 'pskc:MACKey'))
mac_key_reference = findtext(mac_method, 'pskc:MACKeyReference')
mac_key = find(mac_method, 'MACKey')
if mac_key is not None:
self.key_cipher_value = findbin(mac_key, 'CipherData/CipherValue')
encryption_method = find(mac_key, 'EncryptionMethod')
if encryption_method is not None:
self.key_algorithm = encryption_method.attrib.get('Algorithm')
mac_key_reference = findtext(mac_method, 'MACKeyReference')
def make_xml(self, container):
from pskc.xml import mk_elem
if not self.algorithm and not self.key:
return
mac_method = mk_elem(
container, 'pskc:MACMethod', Algorithm=self.algorithm, empty=True)
mac_key = mk_elem(mac_method, 'pskc:MACKey', empty=True)
mk_elem(
mac_key, 'xenc:EncryptionMethod',
Algorithm=self.pskc.encryption.algorithm)
cipher_data = mk_elem(mac_key, 'xenc:CipherData', empty=True)
if self.key_cipher_value:
mk_elem(
cipher_data, 'xenc:CipherValue',
base64.b64encode(self.key_cipher_value).decode())
elif self.key_plain_value:
mk_elem(
cipher_data, 'xenc:CipherValue', base64.b64encode(
self.pskc.encryption.encrypt_value(self.key_plain_value)
).decode())
@property
def key(self):
"""Provides access to the MAC key binary value if available."""
return self._mac_key.decrypt()
if self.key_plain_value:
return self.key_plain_value
elif self.key_cipher_value:
return self.pskc.encryption.decrypt_value(
self.key_cipher_value, self.key_algorithm)
@key.setter
def key(self, value):
self.key_plain_value = value
self.key_cipher_value = None
@property
def algorithm(self):
"""Provide the MAC algorithm used."""
if self._algorithm:
return self._algorithm
@algorithm.setter
def algorithm(self, value):
from pskc.encryption import normalise_algorithm
self._algorithm = normalise_algorithm(value)
@property
def algorithm_key_length(self):
"""Recommended minimal key length in bytes for the set algorithm."""
# https://tools.ietf.org/html/rfc2104#section-3
# an HMAC key should be at least as long as the hash output length
hashfn = get_hash(self.algorithm)
if hashfn is not None:
return int(hashfn().digest_size)
else:
return 16
def generate_mac(self, value):
"""Generate the MAC over the specified value."""
from pskc.exceptions import DecryptionError
key = self.key
if key is None:
raise DecryptionError('No MAC key available')
hmacfn = get_hmac(self.algorithm)
if hmacfn is None:
raise DecryptionError(
'Unsupported MAC algorithm: %r' % self.algorithm)
return hmacfn(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.
The following arguments may be supplied:
key: the MAC key to use
algorithm: MAC algorithm
None of the arguments are required, reasonable defaults will be
chosen for missing arguments.
"""
if key:
self.key = key
if algorithm:
self.algorithm = algorithm
# default to HMAC-SHA1
if not self.algorithm:
self.algorithm = 'hmac-sha1'
# generate an HMAC key
if not self.key:
from Crypto import Random
self.key = Random.get_random_bytes(self.algorithm_key_length)
# policy.py - module for handling PSKC policy information
# coding: utf-8
#
# Copyright (C) 2014-2015 Arthur de Jong
# Copyright (C) 2014-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
......@@ -21,6 +21,14 @@
"""Module that provides PSKC key policy information."""
def _make_aware(d):
"""Make tge specified datetime timezone aware."""
import dateutil.tz
if not d.tzinfo:
return d.replace(tzinfo=dateutil.tz.tzlocal())
return d
class Policy(object):
"""Representation of a policy that describes key and pin usage.
......@@ -114,14 +122,14 @@ class Policy(object):
if policy is None:
return
self.start_date = findtime(policy, 'pskc:StartDate')
self.expiry_date = findtime(policy, 'pskc:ExpiryDate')
self.start_date = findtime(policy, 'StartDate')
self.expiry_date = findtime(policy, 'ExpiryDate')
self.number_of_transactions = findint(
policy, 'pskc:NumberOfTransactions')
for key_usage in findall(policy, 'pskc:KeyUsage'):
policy, 'NumberOfTransactions')
for key_usage in findall(policy, 'KeyUsage'):
self.key_usage.append(findtext(key_usage, '.'))
pin_policy = find(policy, 'pskc:PINPolicy')
pin_policy = find(policy, 'PINPolicy')
if pin_policy is not None:
self.pin_key_id = pin_policy.get('PINKeyId')
self.pin_usage = pin_policy.get('PINUsageMode')
......@@ -130,11 +138,23 @@ class Policy(object):
self.pin_min_length = getint(pin_policy, 'MinLength')
self.pin_max_length = getint(pin_policy, 'MaxLength')
self.pin_encoding = pin_policy.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)
# check for child elements
if list(pin_policy):
self.unknown_policy_elements = True
# check for unknown attributes
known_attributes = set([
'PINKeyId', 'PINUsageMode', 'MaxFailedAttempts', 'MinLength',
'MaxLength', 'PINEncoding'])
if set(pin_policy.keys()) - known_attributes:
self.unknown_policy_elements = True
# check for other child elements
known_children = set([
'StartDate', 'ExpiryDate', 'NumberOfTransactions', 'KeyUsage',
'PINPolicy'])
for child in policy:
if child.tag not in known_children:
self.unknown_policy_elements = True
def make_xml(self, key):
from pskc.xml import mk_elem
......@@ -145,8 +165,6 @@ class Policy(object):
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)
......@@ -162,11 +180,25 @@ class Policy(object):
mk_elem(policy, 'pskc:NumberOfTransactions',
self.number_of_transactions)
def may_use(self, usage):
def may_use(self, usage=None, now=None):
"""Check whether the key may be used for the provided purpose."""
import datetime
import dateutil.tz
if self.unknown_policy_elements:
return False
return not self.key_usage or usage in self.key_usage
if usage is not None and self.key_usage:
if usage not in self.key_usage:
return False
# check start_date and expiry_date
if now is None:
now = datetime.datetime.now(dateutil.tz.tzlocal())
if self.start_date:
if _make_aware(self.start_date) > _make_aware(now):
return False # not-yet usable key
if self.expiry_date:
if _make_aware(self.expiry_date) < _make_aware(now):
return False # not-yet usable key
return True
@property
def pin_key(self):
......