python-pskc branch master updated. 0.4-9-g713d106
[
Date Prev][
Date Next]
[
Thread Prev][
Thread Next]
python-pskc branch master updated. 0.4-9-g713d106
- 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-9-g713d106
- Date: Sun, 11 Sep 2016 23:48:30 +0200 (CEST)
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 713d10620107a0d38a90b8110a31a856fca36a85 (commit)
via ff811c9041312c2ae5eaa3bb47b96e3ea5f6f9db (commit)
via fa07aa588d8c0b5932bab2b056e07ed9c11dd7eb (commit)
via a444f78959cc8f160f9a1b0c1c630e42c42ae407 (commit)
via 9b76135997517c325d9847ea28b176a12d2cb5ad (commit)
via d53f05b1a8be02d62b29a3890e3af92f11eaf463 (commit)
from 5dbfefdd05133448c91f1ef014f71f3dc001dcd7 (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=713d10620107a0d38a90b8110a31a856fca36a85
commit 713d10620107a0d38a90b8110a31a856fca36a85
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Sun Sep 11 22:15:55 2016 +0200
Support specifying PRF in setup_pbkdf2()
This also ensures that the PRF URL is normalised.
diff --git a/pskc/encryption.py b/pskc/encryption.py
index fd8dd49..4ce3f24 100644
--- a/pskc/encryption.py
+++ b/pskc/encryption.py
@@ -160,7 +160,8 @@ class KeyDerivation(object):
# pseudorandom function used
prf = find(pbkdf2, 'PRF')
if prf is not None:
- self.pbkdf2_prf = prf.get('Algorithm')
+ from pskc.algorithms import normalise_algorithm
+ self.pbkdf2_prf = normalise_algorithm(prf.get('Algorithm'))
def make_xml(self, encryption_key, key_names):
from pskc.xml import mk_elem
@@ -220,6 +221,8 @@ class KeyDerivation(object):
self.pbkdf2_iterations = 12 * 1000
if key_length:
self.pbkdf2_key_length = key_length
+ if prf:
+ self.pbkdf2_prf = normalise_algorithm(prf)
return self.derive_pbkdf2(password)
diff --git a/tests/test_encryption.doctest b/tests/test_encryption.doctest
index eed76a1..22bb118 100644
--- a/tests/test_encryption.doctest
+++ b/tests/test_encryption.doctest
@@ -210,10 +210,12 @@ reasonable defaults.
All properties can also be manually specified.
+>>> pskc = PSKC()
>>> pskc.encryption.setup_pbkdf2(
... 'qwerty', iterations=1000, algorithm='aes256-cbc', key_length=24,
... salt=base64.b64decode('Ej7/PEpyEpw='),
-... key_name='PBKDF2 passphrase')
+... key_name='PBKDF2 passphrase',
+... prf='hmac-md5')
>>> pskc.encryption.derivation.algorithm
'http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2'
>>> pskc.encryption.derivation.pbkdf2_iterations
@@ -222,9 +224,11 @@ All properties can also be manually specified.
'123eff3c4a72129c'
>>> pskc.encryption.derivation.pbkdf2_key_length
24
+>>> pskc.encryption.derivation.pbkdf2_prf
+'http://www.w3.org/2001/04/xmldsig-more#hmac-md5'
>>> pskc.encryption.algorithm
'http://www.w3.org/2001/04/xmlenc#aes256-cbc'
>>> pskc.encryption.key_name
'PBKDF2 passphrase'
>>> b2a_hex(pskc.encryption.key)
-'651e63cd57008476af1ff6422cd02e41a13be8f92db69ec9'
+'e8c5fecfb2a5cbb80ff791782ff5e125cc375bb6ba113071'
http://arthurdejong.org/git/python-pskc/commit/?id=ff811c9041312c2ae5eaa3bb47b96e3ea5f6f9db
commit ff811c9041312c2ae5eaa3bb47b96e3ea5f6f9db
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Sun Sep 11 21:41:37 2016 +0200
Fix bug in passing explicit key to setup_preshared_key()
diff --git a/pskc/encryption.py b/pskc/encryption.py
index 16cc565..fd8dd49 100644
--- a/pskc/encryption.py
+++ b/pskc/encryption.py
@@ -335,8 +335,8 @@ class Encryption(object):
chosen for missing arguments.
"""
self._setup_encryption(kwargs)
- key = kwargs.pop('key', self.key)
- if not key:
+ self.key = kwargs.pop('key', self.key)
+ if not self.key:
from Crypto import Random
self.key = Random.get_random_bytes(kwargs.pop(
'key_length', self.algorithm_key_lengths[-1]))
http://arthurdejong.org/git/python-pskc/commit/?id=fa07aa588d8c0b5932bab2b056e07ed9c11dd7eb
commit fa07aa588d8c0b5932bab2b056e07ed9c11dd7eb
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Sun Sep 11 16:28:55 2016 +0200
Clarify encryption.setup_*() documentation
This tries to make it clearer that the setup_preshared_key() and
setup_pbkdf2() functions are meant to be used when writing out PSKC
files.
diff --git a/docs/encryption.rst b/docs/encryption.rst
index cafdb74..59242dd 100644
--- a/docs/encryption.rst
+++ b/docs/encryption.rst
@@ -93,7 +93,7 @@ The Encryption class
.. function:: setup_preshared_key(...)
- Configure pre-shared key encryption.
+ Configure pre-shared key encryption when writing the file.
:param binary key: the encryption key to use
:param str id: encryption key identifier
@@ -119,7 +119,7 @@ The Encryption class
.. function:: setup_pbkdf2(...)
- Configure password-based PSKC encryption.
+ Configure password-based PSKC encryption when writing the file.
:param str password: the password to use (required)
:param str id: encryption key identifier
diff --git a/pskc/encryption.py b/pskc/encryption.py
index 4ca2042..16cc565 100644
--- a/pskc/encryption.py
+++ b/pskc/encryption.py
@@ -320,7 +320,7 @@ class Encryption(object):
self.pskc.mac.setup()
def setup_preshared_key(self, **kwargs):
- """Configure pre-shared key encryption.
+ """Configure pre-shared key encryption when writing the file.
The following arguments may be supplied:
key: the encryption key to use
@@ -342,7 +342,7 @@ class Encryption(object):
'key_length', self.algorithm_key_lengths[-1]))
def setup_pbkdf2(self, password, **kwargs):
- """Configure password-based PSKC encryption.
+ """Configure password-based PSKC encryption when writing the file.
The following arguments may be supplied:
password: the password to use (required)
http://arthurdejong.org/git/python-pskc/commit/?id=a444f78959cc8f160f9a1b0c1c630e42c42ae407
commit a444f78959cc8f160f9a1b0c1c630e42c42ae407
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Sat Apr 23 19:06:20 2016 +0200
Fall back to encryption key for MAC
This uses the encryption key also as MAC key if no MAC key has been
specified in the PSKC file. Earlier versions of the PSKC draft specified
this behaviour.
diff --git a/pskc/key.py b/pskc/key.py
index 56120ea..8ae0fda 100644
--- a/pskc/key.py
+++ b/pskc/key.py
@@ -122,7 +122,7 @@ class DataType(object):
if self.value_mac:
mk_elem(element, 'pskc:ValueMAC', base64.b64encode(
self.value_mac).decode())
- elif self.pskc.mac.key:
+ elif self.pskc.mac.algorithm:
mk_elem(element, 'pskc:ValueMAC', base64.b64encode(
self.pskc.mac.generate_mac(self.cipher_value)
).decode())
diff --git a/pskc/mac.py b/pskc/mac.py
index b9dc94b..4517d43 100644
--- a/pskc/mac.py
+++ b/pskc/mac.py
@@ -58,6 +58,8 @@ def get_mac(algorithm, key, 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)
if hmacfn is None:
raise DecryptionError(
@@ -124,6 +126,8 @@ class MAC(object):
elif self.key_cipher_value:
return self.pskc.encryption.decrypt_value(
self.key_cipher_value, self.key_algorithm)
+ # fall back to encryption key
+ return self.pskc.encryption.key
@key.setter
def key(self, value):
diff --git a/tests/encryption/no-mac-key.pskcxml
b/tests/encryption/no-mac-key.pskcxml
new file mode 100644
index 0000000..d880238
--- /dev/null
+++ b/tests/encryption/no-mac-key.pskcxml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Simple test that uses encryption and a MAC but the MAC key is absent. Older
+ versions of the format allowed a fallback to using the encryption key for
+ the HMAC function.
+-->
+
+<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-sha1"/>
+ <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>N8Agsfe5A7WQFswmdvL/MHdd3y0=</ValueMAC>
+ </Secret>
+ </Data>
+ </Key>
+ </KeyPackage>
+</KeyContainer>
diff --git a/tests/invalid/mac-missing.pskcxml
b/tests/invalid/mac-missing.pskcxml
new file mode 100644
index 0000000..04b8ba7
--- /dev/null
+++ b/tests/invalid/mac-missing.pskcxml
@@ -0,0 +1,42 @@
+<?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/test_encryption.doctest b/tests/test_encryption.doctest
index 92ce728..eed76a1 100644
--- a/tests/test_encryption.doctest
+++ b/tests/test_encryption.doctest
@@ -135,6 +135,17 @@ The IV can also be specified globally.
'MacMacMacMacMacMacMa'
+If the PSKC file does not have a MAC key configured, older versions of the
+PSKC format allowed using the encryption key for the HMAC function.
+
+>>> pskc = PSKC('tests/encryption/no-mac-key.pskcxml')
+>>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012')
+>>> b2a_hex(pskc.mac.key)
+'12345678901234567890123456789012'
+>>> tostr(pskc.keys[0].secret)
+'12345678901234567890'
+
+
MAC key and algorithm will use useful defaults but can also be manually
specified.
diff --git a/tests/test_invalid.doctest b/tests/test_invalid.doctest
index d8f697d..66fa241 100644
--- a/tests/test_invalid.doctest
+++ b/tests/test_invalid.doctest
@@ -158,6 +158,18 @@ 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=9b76135997517c325d9847ea28b176a12d2cb5ad
commit 9b76135997517c325d9847ea28b176a12d2cb5ad
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Sat Apr 23 17:49:53 2016 +0200
Allow global specification of IV
In older versions of the PSKC standard it was allowed to have a global
initialization vector for CBC based encryption algorithms. It is
probably not a good idea to re-use an IV in general.
diff --git a/pskc/encryption.py b/pskc/encryption.py
index cf29506..4ca2042 100644
--- a/pskc/encryption.py
+++ b/pskc/encryption.py
@@ -64,15 +64,17 @@ def decrypt(algorithm, key, ciphertext, iv=None):
algorithm.endswith('#aes256-cbc'):
from Crypto.Cipher import AES
from pskc.crypto import unpad
- iv = ciphertext[:AES.block_size]
- ciphertext = ciphertext[AES.block_size:]
+ if not iv:
+ iv = ciphertext[:AES.block_size]
+ ciphertext = ciphertext[AES.block_size:]
cipher = AES.new(key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(ciphertext))
elif algorithm.endswith('#tripledes-cbc'):
from Crypto.Cipher import DES3
from pskc.crypto import unpad
- iv = ciphertext[:DES3.block_size]
- ciphertext = ciphertext[DES3.block_size:]
+ if not iv:
+ iv = ciphertext[:DES3.block_size]
+ ciphertext = ciphertext[DES3.block_size:]
cipher = DES3.new(key, DES3.MODE_CBC, iv)
return unpad(cipher.decrypt(ciphertext))
elif algorithm.endswith('#kw-aes128') or \
@@ -100,14 +102,14 @@ def encrypt(algorithm, key, plaintext, iv=None):
from Crypto import Random
from Crypto.Cipher import AES
from pskc.crypto import pad
- iv = Random.get_random_bytes(AES.block_size)
+ iv = iv or Random.get_random_bytes(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(pad(plaintext, AES.block_size))
elif algorithm.endswith('#tripledes-cbc'):
from Crypto import Random
from Crypto.Cipher import DES3
from pskc.crypto import pad
- iv = Random.get_random_bytes(DES3.block_size)
+ iv = iv or Random.get_random_bytes(DES3.block_size)
cipher = DES3.new(key, DES3.MODE_CBC, iv)
return iv + cipher.encrypt(pad(plaintext, DES3.block_size))
elif algorithm.endswith('#kw-aes128') or \
@@ -233,6 +235,7 @@ class Encryption(object):
key_names: list of names for the key
key_name: (first) name of the key (usually there is only one)
key: the key value itself (binary form)
+ iv: optional initialization vector for CBC based encryption
fields: a list of Key fields that will be encrypted on writing
The key can either be assigned to the key property or derived using the
@@ -242,9 +245,10 @@ class Encryption(object):
def __init__(self, pskc):
self.pskc = pskc
self.id = None
+ self._algorithm = None
self.key_names = []
self.key = None
- self._algorithm = None
+ self.iv = None
self.derivation = KeyDerivation()
self.fields = []
@@ -369,8 +373,8 @@ class Encryption(object):
def decrypt_value(self, cipher_value, algorithm=None):
"""Decrypt the cipher_value and return the plaintext value."""
return decrypt(
- algorithm or self.algorithm, self.key, cipher_value)
+ algorithm or self.algorithm, self.key, cipher_value, self.iv)
def encrypt_value(self, plaintext):
"""Encrypt the provided value and return the cipher_value."""
- return encrypt(self.algorithm, self.key, plaintext)
+ return encrypt(self.algorithm, self.key, plaintext, self.iv)
diff --git a/tests/encryption/aes128-cbc-noiv.pskcxml
b/tests/encryption/aes128-cbc-noiv.pskcxml
new file mode 100644
index 0000000..1fba51b
--- /dev/null
+++ b/tests/encryption/aes128-cbc-noiv.pskcxml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Test that holds an aes128-cbc encrypted value. Key is
+ 12345678901234567890123456789012. The IV is not part of the CipherValue.
+-->
+
+<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>Diahu/VzjP5IbRYxRgNYT+YQcIa03s5FLMnHjTM0rSQ=</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>5wgci2UHdatrUNSnFW87HaV5/VWY08IyXyPBmohSoa8=</xenc:CipherValue>
+ </xenc:CipherData>
+ </EncryptedValue>
+ <ValueMAC>mNUFNm7a8VqhdmoYDX95B/V7HY36hHOKr6F9jQ==</ValueMAC>
+ </Secret>
+ </Data>
+ </Key>
+ </KeyPackage>
+</KeyContainer>
diff --git a/tests/test_encryption.doctest b/tests/test_encryption.doctest
index aba2014..92ce728 100644
--- a/tests/test_encryption.doctest
+++ b/tests/test_encryption.doctest
@@ -124,6 +124,17 @@ DecryptionError: Invalid key length
'2923bf85e06dd6ae529149f1f1bae9eab3a7da3d860d3e98'
+The IV can also be specified globally.
+
+>>> pskc = PSKC('tests/encryption/aes128-cbc-noiv.pskcxml')
+>>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012')
+>>> pskc.encryption.iv = a2b_hex('000102030405060708090a0b0c0d0e0f')
+>>> tostr(pskc.keys[0].secret)
+'12345678901234567890'
+>>> tostr(pskc.mac.key)
+'MacMacMacMacMacMacMa'
+
+
MAC key and algorithm will use useful defaults but can also be manually
specified.
http://arthurdejong.org/git/python-pskc/commit/?id=d53f05b1a8be02d62b29a3890e3af92f11eaf463
commit d53f05b1a8be02d62b29a3890e3af92f11eaf463
Author: Arthur de Jong <arthur@arthurdejong.org>
Date: Sat Apr 23 17:29:39 2016 +0200
Move crypto to functions
This makes it much easier to test the encryption, decryption and HMAC
processing separate from the PSKC parsing.
diff --git a/pskc/encryption.py b/pskc/encryption.py
index f63bf7b..cf29506 100644
--- a/pskc/encryption.py
+++ b/pskc/encryption.py
@@ -29,6 +29,97 @@ The encryption key can be derived using the KeyDerivation
class.
import base64
+def algorithm_key_lengths(algorithm):
+ """Return the possible key lengths for the configured algorithm."""
+ from pskc.exceptions import DecryptionError
+ if algorithm is None:
+ raise DecryptionError('No algorithm specified')
+ elif algorithm.endswith('#aes128-cbc') or \
+ algorithm.endswith('#aes192-cbc') or \
+ algorithm.endswith('#aes256-cbc'):
+ return [int(algorithm[-7:-4]) // 8]
+ elif algorithm.endswith('#tripledes-cbc') or \
+ algorithm.endswith('#kw-tripledes'):
+ from Crypto.Cipher import DES3
+ return list(DES3.key_size)
+ elif algorithm.endswith('#kw-aes128') or \
+ algorithm.endswith('#kw-aes192') or \
+ algorithm.endswith('#kw-aes256'):
+ return [int(algorithm[-3:]) // 8]
+ else:
+ raise DecryptionError('Unsupported algorithm: %r' % algorithm)
+
+
+def decrypt(algorithm, key, ciphertext, iv=None):
+ """Decrypt the ciphertext and return the plaintext value."""
+ from pskc.exceptions import DecryptionError
+ if key is None:
+ raise DecryptionError('No key available')
+ if algorithm is None:
+ raise DecryptionError('No algorithm specified')
+ if len(key) not in algorithm_key_lengths(algorithm):
+ raise DecryptionError('Invalid key length')
+ if algorithm.endswith('#aes128-cbc') or \
+ algorithm.endswith('#aes192-cbc') or \
+ algorithm.endswith('#aes256-cbc'):
+ from Crypto.Cipher import AES
+ from pskc.crypto import unpad
+ iv = ciphertext[:AES.block_size]
+ ciphertext = ciphertext[AES.block_size:]
+ cipher = AES.new(key, AES.MODE_CBC, iv)
+ return unpad(cipher.decrypt(ciphertext))
+ elif algorithm.endswith('#tripledes-cbc'):
+ from Crypto.Cipher import DES3
+ from pskc.crypto import unpad
+ iv = ciphertext[:DES3.block_size]
+ ciphertext = ciphertext[DES3.block_size:]
+ cipher = DES3.new(key, DES3.MODE_CBC, iv)
+ return unpad(cipher.decrypt(ciphertext))
+ elif algorithm.endswith('#kw-aes128') or \
+ algorithm.endswith('#kw-aes192') or \
+ algorithm.endswith('#kw-aes256'):
+ from pskc.crypto.aeskw import unwrap
+ return unwrap(ciphertext, key)
+ elif algorithm.endswith('#kw-tripledes'):
+ from pskc.crypto.tripledeskw import unwrap
+ return unwrap(ciphertext, key)
+
+
+def encrypt(algorithm, key, plaintext, iv=None):
+ """Encrypt the provided value with the key using the algorithm."""
+ from pskc.exceptions import EncryptionError
+ if key is None:
+ raise EncryptionError('No key available')
+ if algorithm is None:
+ raise EncryptionError('No algorithm specified')
+ if len(key) not in algorithm_key_lengths(algorithm):
+ raise EncryptionError('Invalid key length')
+ if algorithm.endswith('#aes128-cbc') or \
+ algorithm.endswith('#aes192-cbc') or \
+ algorithm.endswith('#aes256-cbc'):
+ from Crypto import Random
+ from Crypto.Cipher import AES
+ from pskc.crypto import pad
+ iv = Random.get_random_bytes(AES.block_size)
+ cipher = AES.new(key, AES.MODE_CBC, iv)
+ return iv + cipher.encrypt(pad(plaintext, AES.block_size))
+ elif algorithm.endswith('#tripledes-cbc'):
+ from Crypto import Random
+ from Crypto.Cipher import DES3
+ from pskc.crypto import pad
+ iv = Random.get_random_bytes(DES3.block_size)
+ cipher = DES3.new(key, DES3.MODE_CBC, iv)
+ return iv + cipher.encrypt(pad(plaintext, DES3.block_size))
+ elif algorithm.endswith('#kw-aes128') or \
+ algorithm.endswith('#kw-aes192') or \
+ algorithm.endswith('#kw-aes256'):
+ from pskc.crypto.aeskw import wrap
+ return wrap(plaintext, key)
+ elif algorithm.endswith('#kw-tripledes'):
+ from pskc.crypto.tripledeskw import wrap
+ return wrap(plaintext, key)
+
+
class KeyDerivation(object):
"""Handle key derivation.
@@ -273,93 +364,13 @@ class Encryption(object):
@property
def algorithm_key_lengths(self):
"""Provide the possible key lengths for the configured algorithm."""
- from pskc.exceptions import DecryptionError
- algorithm = self.algorithm
- if algorithm is None:
- raise DecryptionError('No algorithm specified')
- elif algorithm.endswith('#aes128-cbc') or \
- algorithm.endswith('#aes192-cbc') or \
- algorithm.endswith('#aes256-cbc'):
- return [int(algorithm[-7:-4]) // 8]
- elif algorithm.endswith('#tripledes-cbc') or \
- algorithm.endswith('#kw-tripledes'):
- from Crypto.Cipher import DES3
- return list(DES3.key_size)
- elif algorithm.endswith('#kw-aes128') or \
- algorithm.endswith('#kw-aes192') or \
- algorithm.endswith('#kw-aes256'):
- return [int(algorithm[-3:]) // 8]
- else:
- raise DecryptionError('Unsupported algorithm: %r' % algorithm)
+ return algorithm_key_lengths(self.algorithm)
def decrypt_value(self, cipher_value, algorithm=None):
"""Decrypt the cipher_value and return the plaintext value."""
- from pskc.exceptions import DecryptionError
- key = self.key
- if key is None:
- raise DecryptionError('No key available')
- algorithm = algorithm or self.algorithm
- if algorithm is None:
- raise DecryptionError('No algorithm specified')
- if len(key) not in self.algorithm_key_lengths:
- raise DecryptionError('Invalid key length')
- if algorithm.endswith('#aes128-cbc') or \
- algorithm.endswith('#aes192-cbc') or \
- algorithm.endswith('#aes256-cbc'):
- from Crypto.Cipher import AES
- from pskc.crypto import unpad
- iv = cipher_value[:AES.block_size]
- ciphertext = cipher_value[AES.block_size:]
- cipher = AES.new(key, AES.MODE_CBC, iv)
- return unpad(cipher.decrypt(ciphertext))
- elif algorithm.endswith('#tripledes-cbc'):
- from Crypto.Cipher import DES3
- from pskc.crypto import unpad
- iv = cipher_value[:DES3.block_size]
- ciphertext = cipher_value[DES3.block_size:]
- cipher = DES3.new(key, DES3.MODE_CBC, iv)
- return unpad(cipher.decrypt(ciphertext))
- elif algorithm.endswith('#kw-aes128') or \
- algorithm.endswith('#kw-aes192') or \
- algorithm.endswith('#kw-aes256'):
- from pskc.crypto.aeskw import unwrap
- return unwrap(cipher_value, key)
- elif algorithm.endswith('#kw-tripledes'):
- from pskc.crypto.tripledeskw import unwrap
- return unwrap(cipher_value, key)
+ return decrypt(
+ algorithm or self.algorithm, self.key, cipher_value)
def encrypt_value(self, plaintext):
"""Encrypt the provided value and return the cipher_value."""
- from pskc.exceptions import EncryptionError
- key = self.key
- if key is None:
- raise EncryptionError('No key available')
- algorithm = self.algorithm
- if algorithm is None:
- raise EncryptionError('No algorithm specified')
- if len(key) not in self.algorithm_key_lengths:
- raise EncryptionError('Invalid key length')
- if algorithm.endswith('#aes128-cbc') or \
- algorithm.endswith('#aes192-cbc') or \
- algorithm.endswith('#aes256-cbc'):
- from Crypto import Random
- from Crypto.Cipher import AES
- from pskc.crypto import pad
- iv = Random.get_random_bytes(AES.block_size)
- cipher = AES.new(key, AES.MODE_CBC, iv)
- return iv + cipher.encrypt(pad(plaintext, AES.block_size))
- elif algorithm.endswith('#tripledes-cbc'):
- from Crypto import Random
- from Crypto.Cipher import DES3
- from pskc.crypto import pad
- iv = Random.get_random_bytes(DES3.block_size)
- cipher = DES3.new(key, DES3.MODE_CBC, iv)
- return iv + cipher.encrypt(pad(plaintext, DES3.block_size))
- elif algorithm.endswith('#kw-aes128') or \
- algorithm.endswith('#kw-aes192') or \
- algorithm.endswith('#kw-aes256'):
- from pskc.crypto.aeskw import wrap
- return wrap(plaintext, key)
- elif algorithm.endswith('#kw-tripledes'):
- from pskc.crypto.tripledeskw import wrap
- return wrap(plaintext, key)
+ return encrypt(self.algorithm, self.key, plaintext)
diff --git a/pskc/mac.py b/pskc/mac.py
index 56e8cfa..b9dc94b 100644
--- a/pskc/mac.py
+++ b/pskc/mac.py
@@ -53,6 +53,18 @@ def get_hmac(algorithm):
return lambda key, value: hmac.new(key, value, digestmod).digest()
+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')
+ hmacfn = get_hmac(algorithm)
+ if hmacfn is None:
+ raise DecryptionError(
+ 'Unsupported MAC algorithm: %r' % algorithm)
+ return hmacfn(key, value)
+
+
class MAC(object):
"""Class describing the MAC algorithm to use and how to get the key.
@@ -142,15 +154,7 @@ class MAC(object):
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)
+ return get_mac(self.algorithm, self.key, value)
def check_value(self, value, value_mac):
"""Check if the provided value matches the MAC.
-----------------------------------------------------------------------
Summary of changes:
docs/encryption.rst | 4 +-
pskc/encryption.py | 198 +++++++++++----------
pskc/key.py | 2 +-
pskc/mac.py | 26 ++-
...{aes128-cbc.pskcxml => aes128-cbc-noiv.pskcxml} | 8 +-
.../{aes128-cbc.pskcxml => no-mac-key.pskcxml} | 16 +-
.../{mac-value.pskcxml => mac-missing.pskcxml} | 15 +-
tests/test_encryption.doctest | 30 +++-
tests/test_invalid.doctest | 12 ++
9 files changed, 180 insertions(+), 131 deletions(-)
copy tests/encryption/{aes128-cbc.pskcxml => aes128-cbc-noiv.pskcxml} (73%)
copy tests/encryption/{aes128-cbc.pskcxml => no-mac-key.pskcxml} (63%)
copy tests/invalid/{mac-value.pskcxml => mac-missing.pskcxml} (74%)
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-9-g713d106,
Commits of the python-pskc project