summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Thode <prometheanfire@gentoo.org>2013-06-20 14:40:22 +0000
committerMatt Thode <prometheanfire@gentoo.org>2013-06-20 14:40:22 +0000
commitaec25824af3bc95bf86c63538d5c96347e1e7f51 (patch)
treee3fbd8e0da1c99bd96114bbd017f6ed118395956 /dev-python/python-keystoneclient
parentVersion bump (diff)
downloadhistorical-aec25824af3bc95bf86c63538d5c96347e1e7f51.tar.gz
historical-aec25824af3bc95bf86c63538d5c96347e1e7f51.tar.bz2
historical-aec25824af3bc95bf86c63538d5c96347e1e7f51.zip
keystoneclient fix for bug 473914 CVE-2013-2166 CVE-2013-2167
Package-Manager: portage-2.1.11.62/cvs/Linux x86_64 Manifest-Sign-Key: 0x2471EB3E40AC5AC3
Diffstat (limited to 'dev-python/python-keystoneclient')
-rw-r--r--dev-python/python-keystoneclient/ChangeLog10
-rw-r--r--dev-python/python-keystoneclient/Manifest34
-rw-r--r--dev-python/python-keystoneclient/files/0.2.3-CVE-2013-2013.patch92
-rw-r--r--dev-python/python-keystoneclient/files/0.2.3-CVE-2013-2030.patch49
-rw-r--r--dev-python/python-keystoneclient/files/0.2.3-upstream-1181157.patch47
-rw-r--r--dev-python/python-keystoneclient/files/0.2.4-CVE-2013-2166-7.patch745
-rw-r--r--dev-python/python-keystoneclient/python-keystoneclient-0.2.4-r2.ebuild (renamed from dev-python/python-keystoneclient/python-keystoneclient-0.2.4-r1.ebuild)3
7 files changed, 772 insertions, 208 deletions
diff --git a/dev-python/python-keystoneclient/ChangeLog b/dev-python/python-keystoneclient/ChangeLog
index 81d552094835..20e5ea86f42f 100644
--- a/dev-python/python-keystoneclient/ChangeLog
+++ b/dev-python/python-keystoneclient/ChangeLog
@@ -1,6 +1,14 @@
# ChangeLog for dev-python/python-keystoneclient
# Copyright 1999-2013 Gentoo Foundation; Distributed under the GPL v2
-# $Header: /var/cvsroot/gentoo-x86/dev-python/python-keystoneclient/ChangeLog,v 1.11 2013/05/31 15:08:21 prometheanfire Exp $
+# $Header: /var/cvsroot/gentoo-x86/dev-python/python-keystoneclient/ChangeLog,v 1.12 2013/06/20 14:39:43 prometheanfire Exp $
+
+*python-keystoneclient-0.2.4-r2 (20 Jun 2013)
+
+ 20 Jun 2013; Matthew Thode <prometheanfire@gentoo.org>
+ +files/0.2.4-CVE-2013-2166-7.patch, +python-keystoneclient-0.2.4-r2.ebuild,
+ -files/0.2.3-CVE-2013-2013.patch, -files/0.2.3-CVE-2013-2030.patch,
+ -files/0.2.3-upstream-1181157.patch, -python-keystoneclient-0.2.4-r1.ebuild:
+ keystoneclient fix for bug 473914 CVE-2013-2166 CVE-2013-2167
*python-keystoneclient-0.2.4-r1 (31 May 2013)
diff --git a/dev-python/python-keystoneclient/Manifest b/dev-python/python-keystoneclient/Manifest
index f8cbcfd1808d..409dbd73ff12 100644
--- a/dev-python/python-keystoneclient/Manifest
+++ b/dev-python/python-keystoneclient/Manifest
@@ -1,28 +1,26 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
-AUX 0.2.3-CVE-2013-2013.patch 2927 SHA256 19a61c7453d1231bf2b90be2a95f3fe1c8fc65e381f52dc2c031a292bd9acf8e SHA512 573164b9d74e68c09052f4caf6b992c50b2410227ed299bc4248fb57acd4eb3086fe2c84c54d4739532ebb7a45567a50c9a43a414c0c4968494a76c77a1afed0 WHIRLPOOL ea78428bc2617492f00da08a0c9d5983f45534ad758f2d30e12c1530406866485dd4d880561980cfe9d6b80dda70eef7435f915cd1f65eab13a8f75968ad56a6
-AUX 0.2.3-CVE-2013-2030.patch 2267 SHA256 c2b9746339e1efef55fc768c36a9fb330165fcf0b2cc4f7568aa638e89f972f1 SHA512 e3f56d039449360196a5550f1a0a8db6e5373c30797842c996f71b1eaa620489fdd5b2681b8c940879c42fdcf2219eabaff8944dd42d7a3916102cc70004644c WHIRLPOOL 0a5cfeb10a4e520d6fab23a23637e2c13d598819643e1ba433487ad3aacc9eabb4dbc11e310ec68397e86077f007465701a122c2df301e68501961571ff35786
-AUX 0.2.3-upstream-1181157.patch 1732 SHA256 641d410662ae3259f8ed2772f39c29666f564eb7091f39b7d22522d42ce69c50 SHA512 d2f1bbfd96fec0542e8b1fea87f16288033de117df2ae45e2f19d1be7afe808f174ce527854ff1936f963e54895c5bc3e735e260c0acff7d3e8d61f471295ede WHIRLPOOL bc2e2eccb81dcbe542ea892bfa2a0ae84736d5e07f2a6f265c284799ee5eee3993c168e2e45e06563ac6c78abed1d41c9a19707676fe92943d6a745a81d84a71
+AUX 0.2.4-CVE-2013-2166-7.patch 30702 SHA256 a9863655ad76d7bd7076b8fa310411ac513f7b5b6f29f817c63c00c2116f34ef SHA512 fc0168eab4eda0c5286a6b8a9ab0976256a1da5b878c9fdbc5ebdcd9d1ef1bd41c95b7bce8f39716b6e27f6fad1d3e8b3d0c8c8f0c2f2306a3ae8f4073ebf9b1 WHIRLPOOL 8cb13c57409549475fa66277a85fe9f15d776f00472bc3de03b633877ceb2332136281b8012331e87118a271e84a43549f6ca53409b5d14ab4b6de31dca09517
DIST python-keystoneclient-0.2.4.tar.gz 194758 SHA256 1286a32cc08436410e00704177e3e5ced6ba88ac4eab62a6a55cbad7cadc6cc0 SHA512 e064e9fa7118f300467910b33fa693842db10326dd3c75701fba7edb76896c1fedbec8fbbe2f56e7260ecbfe35b4ee9fac463fcc0f93e47496a57516f708a472 WHIRLPOOL dad8fec545130c74780d1eff49642c8e0b48914a16e9ee4e7215a12d3e3a488c7b85c7d837d041caae8a7ca54570ba750f435e4c718479e8aa9abdaedf33ad6a
-EBUILD python-keystoneclient-0.2.4-r1.ebuild 2011 SHA256 beccac36f778c52c8ff5184d6974d7fd099902d66ef3c57e7888f53835bbc00f SHA512 9f571f4ceb9a14695c13187845fab4612ee4c7cd3eb16a4b0eda05d02d5aad5bd9838fd8831c2f8a59cb4cdb73164651904b4a0d911f40ab06ebc9b715b4a10f WHIRLPOOL 4d3adc2374688e5e4e1745f78d2b49d9ad356b12babfb5b309bfc9a58de8757a6149ec24c179c7f6f313e490f0487536d2ba87a36d7e2d73e425e607218b7f0d
+EBUILD python-keystoneclient-0.2.4-r2.ebuild 2054 SHA256 11e77804df0d43d63361b51f323b40982c1940c53c6cc22e99b5aa45edd69925 SHA512 784227bff389f210ca9df967aa0a1fa8c80ceb03c73c3e268edd0b7c6fcdf29ab5a5c3e408f9b183dc6d3f3d107c79318149809f8cfc7c8654db63edeeabda7d WHIRLPOOL 89f7f73d30ed2825923def6cee9040f0241e40b798ae7de9977db28d7fdfa5376305936f3dcbf4b7585b3108df76fbbfbd93e6a44f0d9338b0d5d6d37909de66
EBUILD python-keystoneclient-9999.ebuild 1597 SHA256 0062df58f82beaaa54dd279e7a95ae8540c51cc1a2bb32e4621ae96cf2508216 SHA512 6de16b203f9cdf88cad8c8c19c99bfd1f243de807971ca2292437074b147f02476d526974cab64b8f881afb6e2c8f19938966f5322eed50b95c5a95de25461e0 WHIRLPOOL 8d26907ba7ac0e570a3fa245b8d2e04d5285c4ff02c2ecafd7fa9cf9cd4b3a6b938abfe9efb925e02b5733320a4cb05b2fb1aca005ca5ffd0f19ab4d19db58a4
-MISC ChangeLog 3037 SHA256 f257619d906d2210aed348dafca68137d2e36514102905dee6e15871c69d152f SHA512 218213d1a0d1c106676b71743d6c89a2abdf0f255c027761df4fa93b7364d48dc57291108a34b59fec653c2a50ccacb3ec1d9734f521904c01e785a1e6726c90 WHIRLPOOL 8b7e2ffcfce1dd3a3d16e9a716ffa3e0e0cdb4c12d48e9799c40f92177ebc7b8ed2af79beeae694f2207f585c4df4d8d41480807cb055ae243e4091ca1635836
+MISC ChangeLog 3433 SHA256 e095b5bfd2dd1fa7b0b6cfe2d342a6093d21db310ca66d39273206fd54bf294e SHA512 ce0268a41f621ff939725e429d239a4069f776f912399c125db54e8d2ff2a49c4b072376b18ac76cd3cd94812cf9f5b1b9176f392c262c0357668a1b7bde7baa WHIRLPOOL 8d856cf253c380aac1bf1bf27f2f99a5a4c31ed2f479facb926350d6cbaef6c54a9a55763cabe8c1da51762b97290b03fdd2aadfcc537219ff9d95980ee72035
MISC metadata.xml 343 SHA256 716e96a66916d216f80e7ce0283db63bd6d18a95e2365d9d1a35964b2ef461f9 SHA512 fd28e5434f725af6a835205ae8cfae1354983f8e68f6c25f9a4f56ec17041ac9bb1a460ff8e82d0e165ab19c5b0f26e0f0c8c5124dfcc37723c786e12cd4d3bf WHIRLPOOL b486328b67aa7f6e4b68adc757bf5d3983077a894d7ded68be7723fe5655805324a73e5914fda0f81ae334e75db9557e2675a43e9437df633311590758466c5c
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.19 (GNU/Linux)
-iQIcBAEBCAAGBQJRqL0xAAoJECRx6z5ArFrDk18QAMxwiM1zy8RLSlg71qX/9j1P
-+Yl8K6Nt6T6zkD9NZhVQy6i1kjqffTpVs/TYzF7x284V3tO2aXM/e+mZnTW9i3MJ
-T3vugKo9DDa6gSzek7lNp3RTFql1KcaNGmXiZjjldnbx0k/r06x+rCu03nLxEM5j
-Lmc9EFMkX8v3oEjyUZzNfoYOMei0XA1v5DZva+PGVPEp5FtDYwSntkdyzwDgiCtj
-Nn76bwY8PmMrLI77SnVHqAF5Ag36lgvgTTb6q5Y5Tclurmo12tDuxqF+B9DIc/Mt
-j+iILaI5MUCQEfJgI5/+tXiBFn6VccV5IYwup6pcmj4jK3RcQPXa7afzwxmXAjHH
-Dw9891W/qKri/LveiHzC+CGfva3mBuMjNJYa7rm5nsG35NZAI4IbQm2YQm4ExfN0
-zklVAxkxmB3l00qyxvo8XTRBtrE/hw30nncy+OfV78sy/RbUrzUAtl9TWg67TYBx
-/SlwlliIVzTko1A1UlS0znu9WiQykv3WKzOhJU44hIZKgukh4iUaSU9z1T2xttQA
-ClbQfkR6rhriS3NkmmzW8Qp1P2T5mltXvm6sfa7STW+ilvgT+q31T0qvIYrLGQam
-4hI9RfGgttEojBkaL1ErH/gpdFdSAIF/iMpiPrz+5k3Zj/yD49WnNo+2UBP3GI9P
-HkVAGzMZanRccREjkHDI
-=WItb
+iQIcBAEBCAAGBQJRwxRaAAoJECRx6z5ArFrDC1sP/3/D1vfg7puyzTPMf/dYGQfg
+QzAKDmh9l7Ed3GfzfJYQToG4wk1LvstO2yOIXUr15v4Zdz6VteiEoj5jGwbCuqID
+1jdSyzcHUWsSJm7DanVA9ZS+PiCeAqkSwp9/4EyAa+JbFVVpXIGaW1HbytQo5Ubh
+rJSlV+n/ZIX5kqSFECRRUafBXchf+KHAqigK8Ty3XhyI7P/DHRBwP8TSpynQVsuo
+IJSTkisSgzh0PpysF8xxncBoul4utesdJ4yjX9vU6luw0iXh+UfJAyeE8C8rg8RC
+/0tSeZwkZvqBxhp4CqJtc9qIrDErh2OkJzU+3LS3xeli34i59lsbe9LFgOlve1ig
+EYXiOZBQAuMvGP4iBIw8V7zU6gNeZPH53++p8YQqIlwwYgXKQrQ8dFaafpF/dkaF
+8AOj85Nq2rURnEK2p7t2Bl/lxcXVLfbnC/RKyacF01DihAeoGwhqrdwtQaHKtTIy
+rUqPay3MKwqOIC4ao4sOOX9mVvhOsGIi9SRsX6Bk8fFKAsWawxH31rPI3lFLRyPR
+6PMZvHwR3HTKNWDe3wmGXnjXsRhPxuP5LYENvvrUWs3LzoBvEmeEGlzlwz1RNKEq
+mClAfvzqJVRe3IOzgI44nNNy3z55/yffMqFnwNcXqT6S2PxHSfzVCnsyWeb2KNgq
+yXVDU07YEaaptSwvRP2D
+=nJfd
-----END PGP SIGNATURE-----
diff --git a/dev-python/python-keystoneclient/files/0.2.3-CVE-2013-2013.patch b/dev-python/python-keystoneclient/files/0.2.3-CVE-2013-2013.patch
deleted file mode 100644
index 757f4c8c2302..000000000000
--- a/dev-python/python-keystoneclient/files/0.2.3-CVE-2013-2013.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From f2e0818bc97bfbeba83f6abbb07909a8debcad77 Mon Sep 17 00:00:00 2001
-From: Pradeep Kilambi <pkilambi@cisco.com>
-Date: Thu, 9 May 2013 09:29:02 -0700
-Subject: [PATCH] Allow secure user password update.
-
-This patch allows the ability for user password to be updated via
-a command prompt so the password doesnt show up in the bash history.
-The prompted password is asked twice to verify the match.
-If user cntl-D's the prompt a message appears suggesting user to use
-either of the options to update the password.
-
-Fixes: bug#938315
-
-Change-Id: I4271ae569b922f33c34f9b015a7ee6f760414e39
----
- keystoneclient/utils.py | 23 ++++++++++++++++++++++-
- keystoneclient/v2_0/shell.py | 10 ++++++++--
- 2 files changed, 30 insertions(+), 3 deletions(-)
-
-diff --git a/keystoneclient/utils.py b/keystoneclient/utils.py
-index 3d708ca..f45ec34 100644
---- a/keystoneclient/utils.py
-+++ b/keystoneclient/utils.py
-@@ -1,5 +1,7 @@
--import uuid
-+import getpass
- import hashlib
-+import sys
-+import uuid
-
- import prettytable
-
-@@ -128,3 +130,22 @@ def hash_signed_token(signed_text):
- hash_ = hashlib.md5()
- hash_.update(signed_text)
- return hash_.hexdigest()
-+
-+
-+def prompt_for_password():
-+ """
-+ Prompt user for password if not provided so the password
-+ doesn't show up in the bash history.
-+ """
-+ if not (hasattr(sys.stdin, 'isatty') and sys.stdin.isatty()):
-+ # nothing to do
-+ return
-+
-+ while True:
-+ try:
-+ new_passwd = getpass.getpass('New Password: ')
-+ rep_passwd = getpass.getpass('Repeat New Password: ')
-+ if new_passwd == rep_passwd:
-+ return new_passwd
-+ except EOFError:
-+ return
-diff --git a/keystoneclient/v2_0/shell.py b/keystoneclient/v2_0/shell.py
-index 4c53cf7..0c7c233 100755
---- a/keystoneclient/v2_0/shell.py
-+++ b/keystoneclient/v2_0/shell.py
-@@ -17,6 +17,7 @@
-
- import argparse
- import getpass
-+import sys
-
- from keystoneclient.v2_0 import client
- from keystoneclient import utils
-@@ -103,14 +104,19 @@ def do_user_update(kc, args):
- print 'Unable to update user: %s' % e
-
-
--@utils.arg('--pass', metavar='<password>', dest='passwd', required=True,
-+@utils.arg('--pass', metavar='<password>', dest='passwd', required=False,
- help='Desired new password')
- @utils.arg('user', metavar='<user>',
- help='Name or ID of user to update password')
- def do_user_password_update(kc, args):
- """Update user password"""
- user = utils.find_resource(kc.users, args.user)
-- kc.users.update_password(user, args.passwd)
-+ new_passwd = args.passwd or utils.prompt_for_password()
-+ if new_passwd is None:
-+ msg = ("\nPlease specify password using the --pass option "
-+ "or using the prompt")
-+ sys.exit(msg)
-+ kc.users.update_password(user, new_passwd)
-
-
- @utils.arg('--current-password', metavar='<current-password>',
---
-1.8.1.5
-
diff --git a/dev-python/python-keystoneclient/files/0.2.3-CVE-2013-2030.patch b/dev-python/python-keystoneclient/files/0.2.3-CVE-2013-2030.patch
deleted file mode 100644
index a1248d7787af..000000000000
--- a/dev-python/python-keystoneclient/files/0.2.3-CVE-2013-2030.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-From 1736e2ffb12f70eeebed019448bc14def48aa036 Mon Sep 17 00:00:00 2001
-From: Dolph Mathews <dolph.mathews@gmail.com>
-Date: Wed, 8 May 2013 10:49:20 -0500
-Subject: [PATCH] Securely create signing_dir (bug 1174608)
-
-Also verifies the security of an existing signing_dir.
-
-Change-Id: I0685b4274a94ad3974a2b2a7ab3f45830d3934bb
----
- keystoneclient/middleware/auth_token.py | 23 ++++++++++++++---------
- 1 file changed, 14 insertions(+), 9 deletions(-)
-
-diff --git a/keystoneclient/middleware/auth_token.py b/keystoneclient/middleware/auth_token.py
-index 0d0e124..e6cf99f 100644
---- a/keystoneclient/middleware/auth_token.py
-+++ b/keystoneclient/middleware/auth_token.py
-@@ -296,15 +296,20 @@ class AuthProtocol(object):
- self.signing_dirname = self._conf_get('signing_dir')
- self.LOG.info('Using %s as cache directory for signing certificate' %
- self.signing_dirname)
-- if (os.path.exists(self.signing_dirname) and
-- not os.access(self.signing_dirname, os.W_OK)):
-- raise ConfigurationError("unable to access signing dir %s" %
-- self.signing_dirname)
--
-- if not os.path.exists(self.signing_dirname):
-- os.makedirs(self.signing_dirname)
-- #will throw IOError if it cannot change permissions
-- os.chmod(self.signing_dirname, stat.S_IRWXU)
-+ if os.path.exists(self.signing_dirname):
-+ if not os.access(self.signing_dirname, os.W_OK):
-+ raise ConfigurationError(
-+ 'unable to access signing_dir %s' % self.signing_dirname)
-+ if os.stat(self.signing_dirname).st_uid != os.getuid():
-+ self.LOG.warning(
-+ 'signing_dir is not owned by %s' % os.getlogin())
-+ current_mode = stat.S_IMODE(os.stat(self.signing_dirname).st_mode)
-+ if current_mode != stat.S_IRWXU:
-+ self.LOG.warning(
-+ 'signing_dir mode is %s instead of %s' %
-+ (oct(current_mode), oct(stat.S_IRWXU)))
-+ else:
-+ os.makedirs(self.signing_dirname, stat.S_IRWXU)
-
- val = '%s/signing_cert.pem' % self.signing_dirname
- self.signing_cert_file_name = val
---
-1.8.1.5
-
diff --git a/dev-python/python-keystoneclient/files/0.2.3-upstream-1181157.patch b/dev-python/python-keystoneclient/files/0.2.3-upstream-1181157.patch
deleted file mode 100644
index a94a88a9d174..000000000000
--- a/dev-python/python-keystoneclient/files/0.2.3-upstream-1181157.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 03012e641d6c2a98fbfe3780102e28a65d11a887 Mon Sep 17 00:00:00 2001
-From: Dolph Mathews <dolph.mathews@gmail.com>
-Date: Fri, 17 May 2013 10:38:25 -0500
-Subject: [PATCH] Default signing_dir to secure temp dir (bug 1181157)
-
-Change-Id: I1a29f50b07a60de3d0519bf40074dbea92fa8656
----
- keystoneclient/middleware/auth_token.py | 8 +++++---
- 1 file changed, 5 insertions(+), 3 deletions(-)
-
-diff --git a/keystoneclient/middleware/auth_token.py b/keystoneclient/middleware/auth_token.py
-index e6cf99f..befa79e 100644
---- a/keystoneclient/middleware/auth_token.py
-+++ b/keystoneclient/middleware/auth_token.py
-@@ -150,6 +150,7 @@ import json
- import logging
- import os
- import stat
-+import tempfile
- import time
- import urllib
- import webob.exc
-@@ -211,8 +212,7 @@ opts = [
- cfg.StrOpt('cache', default=None), # env key for the swift cache
- cfg.StrOpt('certfile'),
- cfg.StrOpt('keyfile'),
-- cfg.StrOpt('signing_dir',
-- default=os.path.expanduser('~/keystone-signing')),
-+ cfg.StrOpt('signing_dir'),
- cfg.ListOpt('memcache_servers'),
- cfg.IntOpt('token_cache_time', default=300),
- cfg.IntOpt('revocation_cache_time', default=1),
-@@ -292,8 +292,10 @@ class AuthProtocol(object):
- self.cert_file = self._conf_get('certfile')
- self.key_file = self._conf_get('keyfile')
-
-- #signing
-+ # signing
- self.signing_dirname = self._conf_get('signing_dir')
-+ if self.signing_dirname is None:
-+ self.signing_dirname = tempfile.mkdtemp(prefix='keystone-signing-')
- self.LOG.info('Using %s as cache directory for signing certificate' %
- self.signing_dirname)
- if os.path.exists(self.signing_dirname):
---
-1.8.1.5
-
diff --git a/dev-python/python-keystoneclient/files/0.2.4-CVE-2013-2166-7.patch b/dev-python/python-keystoneclient/files/0.2.4-CVE-2013-2166-7.patch
new file mode 100644
index 000000000000..4d5b160a95d8
--- /dev/null
+++ b/dev-python/python-keystoneclient/files/0.2.4-CVE-2013-2166-7.patch
@@ -0,0 +1,745 @@
+From eeefb784f24c37d5f56a421e1ccc911cace9385e Mon Sep 17 00:00:00 2001
+From: "Bryan D. Payne" <bdpayne@acm.org>
+Date: Fri, 7 Jun 2013 09:34:25 -0700
+Subject: [PATCH] Fix memcache encryption middleware
+
+This fixes lp1175367 and lp1175368 by redesigning the memcache crypt
+middleware to not do dangerous things. It is forward compatible, but
+will invalidate any existing ephemeral encrypted or signed memcache
+entries.
+
+Change-Id: Ice8724949a48bfad3b8b7c41b5f50a18a9ad9f42
+Signed-off-by: Bryan D. Payne <bdpayne@acm.org>
+---
+ doc/source/middlewarearchitecture.rst | 37 +++---
+ keystoneclient/middleware/auth_token.py | 131 +++++++++---------
+ keystoneclient/middleware/memcache_crypt.py | 197 +++++++++++++++++-----------
+ tests/test_auth_token_middleware.py | 89 +++----------
+ tests/test_memcache_crypt.py | 96 ++++++++------
+ 5 files changed, 277 insertions(+), 273 deletions(-)
+
+diff --git a/doc/source/middlewarearchitecture.rst b/doc/source/middlewarearchitecture.rst
+index 803fbd9..894d40d 100644
+--- a/doc/source/middlewarearchitecture.rst
++++ b/doc/source/middlewarearchitecture.rst
+@@ -1,5 +1,5 @@
+ ..
+- Copyright 2011-2012 OpenStack, LLC
++ Copyright 2011-2013 OpenStack, LLC
+ All Rights Reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
+@@ -188,7 +188,8 @@ Configuration Options
+ the timeout when validating token by http).
+ * ``auth_port``: (optional, default `35357`) the port used to validate tokens
+ * ``auth_protocol``: (optional, default `https`)
+-* ``auth_uri``: (optional, defaults to `auth_protocol`://`auth_host`:`auth_port`)
++* ``auth_uri``: (optional, defaults to
++ `auth_protocol`://`auth_host`:`auth_port`)
+ * ``certfile``: (required, if Keystone server requires client cert)
+ * ``keyfile``: (required, if Keystone server requires client cert) This can be
+ the same as the certfile if the certfile includes the private key.
+@@ -232,22 +233,24 @@ Memcache Protection
+ ===================
+
+ When using memcached, we are storing user tokens and token validation
+-information into the cache as raw data. Which means anyone who have access
+-to the memcache servers can read and modify data stored there. To mitigate
+-this risk, ``auth_token`` middleware provides an option to either encrypt
+-or authenticate the token data stored in the cache.
+-
+-* ``memcache_security_strategy``: (optional) if defined, indicate whether token
+- data should be encrypted or authenticated. Acceptable values are ``ENCRYPT``
+- or ``MAC``. If ``ENCRYPT``, token data is encrypted in the cache. If
+- ``MAC``, token data is authenticated (with HMAC) in the cache. If its value
+- is neither ``MAC`` nor ``ENCRYPT``, ``auth_token`` will raise an exception
+- on initialization.
++information into the cache as raw data. Which means that anyone who
++has access to the memcache servers can read and modify data stored
++there. To mitigate this risk, ``auth_token`` middleware provides an
++option to authenticate and optionally encrypt the token data stored in
++the cache.
++
++* ``memcache_security_strategy``: (optional) if defined, indicate
++ whether token data should be authenticated or authenticated and
++ encrypted. Acceptable values are ``MAC`` or ``ENCRYPT``. If ``MAC``,
++ token data is authenticated (with HMAC) in the cache. If
++ ``ENCRYPT``, token data is encrypted and authenticated in the
++ cache. If the value is not one of these options or empty,
++ ``auth_token`` will raise an exception on initialization.
+ * ``memcache_secret_key``: (optional, mandatory if
+- ``memcache_security_strategy`` is defined) if defined,
+- a random string to be used for key derivation. If
+- ``memcache_security_strategy`` is defined and ``memcache_secret_key`` is
+- absent, ``auth_token`` will raise an exception on initialization.
++ ``memcache_security_strategy`` is defined) this string is used for
++ key derivation. If ``memcache_security_strategy`` is defined and
++ ``memcache_secret_key`` is absent, ``auth_token`` will raise an
++ exception on initialization.
+
+ Exchanging User Information
+ ===========================
+diff --git a/keystoneclient/middleware/auth_token.py b/keystoneclient/middleware/auth_token.py
+index 7e3012c..e50f723 100644
+--- a/keystoneclient/middleware/auth_token.py
++++ b/keystoneclient/middleware/auth_token.py
+@@ -222,6 +222,7 @@ opts = [
+ CONF.register_opts(opts, group='keystone_authtoken')
+
+ LIST_OF_VERSIONS_TO_ATTEMPT = ['v2.0', 'v3.0']
++CACHE_KEY_TEMPLATE = 'tokens/%s'
+
+
+ def will_expire_soon(expiry):
+@@ -847,91 +848,81 @@ class AuthProtocol(object):
+ env_key = self._header_to_env_var(key)
+ return env.get(env_key, default)
+
+- def _protect_cache_value(self, token, data):
+- """ Encrypt or sign data if necessary. """
+- try:
+- if self._memcache_security_strategy == 'ENCRYPT':
+- return memcache_crypt.encrypt_data(token,
+- self._memcache_secret_key,
+- data)
+- elif self._memcache_security_strategy == 'MAC':
+- return memcache_crypt.sign_data(token, data)
+- else:
+- return data
+- except:
+- msg = 'Failed to encrypt/sign cache data.'
+- self.LOG.exception(msg)
+- return data
+-
+- def _unprotect_cache_value(self, token, data):
+- """ Decrypt or verify signed data if necessary. """
+- if data is None:
+- return data
+-
+- try:
+- if self._memcache_security_strategy == 'ENCRYPT':
+- return memcache_crypt.decrypt_data(token,
+- self._memcache_secret_key,
+- data)
+- elif self._memcache_security_strategy == 'MAC':
+- return memcache_crypt.verify_signed_data(token, data)
+- else:
+- return data
+- except:
+- msg = 'Failed to decrypt/verify cache data.'
+- self.LOG.exception(msg)
+- # this should have the same effect as data not found in cache
+- return None
+-
+- def _get_cache_key(self, token):
+- """ Return the cache key.
+-
+- Do not use clear token as key if memcache protection is on.
+-
+- """
+- htoken = token
+- if self._memcache_security_strategy in ('ENCRYPT', 'MAC'):
+- derv_token = token + self._memcache_secret_key
+- htoken = memcache_crypt.hash_data(derv_token)
+- return 'tokens/%s' % htoken
+-
+- def _cache_get(self, token):
++ def _cache_get(self, token, ignore_expires=False):
+ """Return token information from cache.
+
+ If token is invalid raise InvalidUserToken
+ return token only if fresh (not expired).
+ """
++
+ if self._cache and token:
+- key = self._get_cache_key(token)
+- cached = self._cache.get(key)
+- cached = self._unprotect_cache_value(token, cached)
++ if self._memcache_security_strategy is None:
++ key = CACHE_KEY_TEMPLATE % token
++ serialized = self._cache.get(key)
++ else:
++ keys = memcache_crypt.derive_keys(
++ token,
++ self._memcache_secret_key,
++ self._memcache_security_strategy)
++ cache_key = CACHE_KEY_TEMPLATE % (
++ memcache_crypt.get_cache_key(keys))
++ raw_cached = self._cache.get(cache_key)
++ try:
++ # unprotect_data will return None if raw_cached is None
++ serialized = memcache_crypt.unprotect_data(keys,
++ raw_cached)
++ except Exception:
++ msg = 'Failed to decrypt/verify cache data'
++ self.LOG.exception(msg)
++ # this should have the same effect as data not
++ # found in cache
++ serialized = None
++
++ if serialized is None:
++ return None
++
++ # Note that 'invalid' and (data, expires) are the only
++ # valid types of serialized cache entries, so there is not
++ # a collision with json.loads(serialized) == None.
++ cached = json.loads(serialized)
+ if cached == 'invalid':
+ self.LOG.debug('Cached Token %s is marked unauthorized', token)
+ raise InvalidUserToken('Token authorization failed')
+- if cached:
+- data, expires = cached
+- if time.time() < float(expires):
+- self.LOG.debug('Returning cached token %s', token)
+- return data
+- else:
+- self.LOG.debug('Cached Token %s seems expired', token)
+-
+- def _cache_store(self, token, data, expires=None):
+- """ Store value into memcache. """
+- key = self._get_cache_key(token)
+- data = self._protect_cache_value(token, data)
+- data_to_store = data
+- if expires:
+- data_to_store = (data, expires)
++
++ data, expires = cached
++ if ignore_expires or time.time() < float(expires):
++ self.LOG.debug('Returning cached token %s', token)
++ return data
++ else:
++ self.LOG.debug('Cached Token %s seems expired', token)
++
++ def _cache_store(self, token, data):
++ """ Store value into memcache.
++
++ data may be the string 'invalid' or a tuple like (data, expires)
++
++ """
++ serialized_data = json.dumps(data)
++ if self._memcache_security_strategy is None:
++ cache_key = CACHE_KEY_TEMPLATE % token
++ data_to_store = serialized_data
++ else:
++ keys = memcache_crypt.derive_keys(
++ token,
++ self._memcache_secret_key,
++ self._memcache_security_strategy)
++ cache_key = CACHE_KEY_TEMPLATE % memcache_crypt.get_cache_key(keys)
++ data_to_store = memcache_crypt.protect_data(keys, serialized_data)
++
+ # we need to special-case set() because of the incompatibility between
+ # Swift MemcacheRing and python-memcached. See
+ # https://bugs.launchpad.net/swift/+bug/1095730
+ if self._use_keystone_cache:
+- self._cache.set(key,
++ self._cache.set(cache_key,
+ data_to_store,
+ time=self.token_cache_time)
+ else:
+- self._cache.set(key,
++ self._cache.set(cache_key,
+ data_to_store,
+ timeout=self.token_cache_time)
+
+@@ -959,7 +950,7 @@ class AuthProtocol(object):
+ """
+ if self._cache:
+ self.LOG.debug('Storing %s token in memcache', token)
+- self._cache_store(token, data, expires)
++ self._cache_store(token, (data, expires))
+
+ def _cache_store_invalid(self, token):
+ """Store invalid token in cache."""
+diff --git a/keystoneclient/middleware/memcache_crypt.py b/keystoneclient/middleware/memcache_crypt.py
+index 91e261d..6cadf3a 100755
+--- a/keystoneclient/middleware/memcache_crypt.py
++++ b/keystoneclient/middleware/memcache_crypt.py
+@@ -1,6 +1,6 @@
+ # vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+-# Copyright 2010-2012 OpenStack LLC
++# Copyright 2010-2013 OpenStack LLC
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+@@ -18,33 +18,34 @@
+ """
+ Utilities for memcache encryption and integrity check.
+
+-Data is serialized before been encrypted or MACed. Encryption have a
+-dependency on the pycrypto. If pycrypto is not available,
+-CryptoUnabailableError will be raised.
++Data should be serialized before entering these functions. Encryption
++has a dependency on the pycrypto. If pycrypto is not available,
++CryptoUnavailableError will be raised.
+
+-Encrypted data stored in memcache are prefixed with '{ENCRYPT:AES256}'.
+-
+-MACed data stored in memcache are prefixed with '{MAC:SHA1}'.
++This module will not be called unless signing or encryption is enabled
++in the config. It will always validate signatures, and will decrypt
++data if encryption is enabled. It is not valid to mix protection
++modes.
+
+ """
+
+ import base64
+ import functools
+ import hashlib
+-import json
++import hmac
++import math
+ import os
+
+-# make sure pycrypt is available
++# make sure pycrypto is available
+ try:
+ from Crypto.Cipher import AES
+ except ImportError:
+ AES = None
+
+-
+-# prefix marker indicating data is HMACed (signed by a secret key)
+-MAC_MARKER = '{MAC:SHA1}'
+-# prefix marker indicating data is encrypted
+-ENCRYPT_MARKER = '{ENCRYPT:AES256}'
++HASH_FUNCTION = hashlib.sha384
++DIGEST_LENGTH = HASH_FUNCTION().digest_size
++DIGEST_SPLIT = DIGEST_LENGTH // 3
++DIGEST_LENGTH_B64 = 4 * int(math.ceil(DIGEST_LENGTH / 3.0))
+
+
+ class InvalidMacError(Exception):
+@@ -81,77 +82,121 @@ def assert_crypto_availability(f):
+ return wrapper
+
+
+-def generate_aes_key(token, secret):
+- """ Generates and returns a 256 bit AES key, based on sha256 hash. """
+- return hashlib.sha256(token + secret).digest()
+-
+-
+-def compute_mac(token, serialized_data):
+- """ Computes and returns the base64 encoded MAC. """
+- return hash_data(serialized_data + token)
++def constant_time_compare(first, second):
++ """ Returns True if both string inputs are equal, otherwise False
+
++ This function should take a constant amount of time regardless of
++ how many characters in the strings match.
+
+-def hash_data(data):
+- """ Return the base64 encoded SHA1 hash of the data. """
+- return base64.b64encode(hashlib.sha1(data).digest())
+-
+-
+-def sign_data(token, data):
+- """ MAC the data using SHA1. """
+- mac_data = {}
+- mac_data['serialized_data'] = json.dumps(data)
+- mac = compute_mac(token, mac_data['serialized_data'])
+- mac_data['mac'] = mac
+- md = MAC_MARKER + base64.b64encode(json.dumps(mac_data))
+- return md
++ """
++ if len(first) != len(second):
++ return False
++ result = 0
++ for x, y in zip(first, second):
++ result |= ord(x) ^ ord(y)
++ return result == 0
++
++
++def derive_keys(token, secret, strategy):
++ """ Derives keys for MAC and ENCRYPTION from the user-provided
++ secret. The resulting keys should be passed to the protect and
++ unprotect functions.
++
++ As suggested by NIST Special Publication 800-108, this uses the
++ first 128 bits from the sha384 KDF for the obscured cache key
++ value, the second 128 bits for the message authentication key and
++ the remaining 128 bits for the encryption key.
++
++ This approach is faster than computing a separate hmac as the KDF
++ for each desired key.
++ """
++ digest = hmac.new(secret, token + strategy, HASH_FUNCTION).digest()
++ return {'CACHE_KEY': digest[:DIGEST_SPLIT],
++ 'MAC': digest[DIGEST_SPLIT: 2 * DIGEST_SPLIT],
++ 'ENCRYPTION': digest[2 * DIGEST_SPLIT:],
++ 'strategy': strategy}
+
+
+-def verify_signed_data(token, data):
+- """ Verify data integrity by ensuring MAC is valid. """
+- if data.startswith(MAC_MARKER):
+- try:
+- data = data[len(MAC_MARKER):]
+- mac_data = json.loads(base64.b64decode(data))
+- mac = compute_mac(token, mac_data['serialized_data'])
+- if mac != mac_data['mac']:
+- raise InvalidMacError('invalid MAC; expect=%s, actual=%s' %
+- (mac_data['mac'], mac))
+- return json.loads(mac_data['serialized_data'])
+- except:
+- raise InvalidMacError('invalid MAC; data appeared to be corrupted')
+- else:
+- # doesn't appear to be MACed data
+- return data
++def sign_data(key, data):
++ """ Sign the data using the defined function and the derived key"""
++ mac = hmac.new(key, data, HASH_FUNCTION).digest()
++ return base64.b64encode(mac)
+
+
+ @assert_crypto_availability
+-def encrypt_data(token, secret, data):
+- """ Encryptes the data with the given secret key. """
++def encrypt_data(key, data):
++ """ Encrypt the data with the given secret key.
++
++ Padding is n bytes of the value n, where 1 <= n <= blocksize.
++ """
+ iv = os.urandom(16)
+- aes_key = generate_aes_key(token, secret)
+- cipher = AES.new(aes_key, AES.MODE_CFB, iv)
+- data = json.dumps(data)
+- encoded_data = base64.b64encode(iv + cipher.encrypt(data))
+- encoded_data = ENCRYPT_MARKER + encoded_data
+- return encoded_data
++ cipher = AES.new(key, AES.MODE_CBC, iv)
++ padding = 16 - len(data) % 16
++ return iv + cipher.encrypt(data + chr(padding) * padding)
+
+
+ @assert_crypto_availability
+-def decrypt_data(token, secret, data):
++def decrypt_data(key, data):
+ """ Decrypt the data with the given secret key. """
+- if data.startswith(ENCRYPT_MARKER):
+- try:
+- # encrypted data
+- encoded_data = data[len(ENCRYPT_MARKER):]
+- aes_key = generate_aes_key(token, secret)
+- decoded_data = base64.b64decode(encoded_data)
+- iv = decoded_data[:16]
+- encrypted_data = decoded_data[16:]
+- cipher = AES.new(aes_key, AES.MODE_CFB, iv)
+- decrypted_data = cipher.decrypt(encrypted_data)
+- return json.loads(decrypted_data)
+- except:
+- raise DecryptError('data appeared to be corrupted')
+- else:
+- # doesn't appear to be encrypted data
+- return data
++ iv = data[:16]
++ cipher = AES.new(key, AES.MODE_CBC, iv)
++ try:
++ result = cipher.decrypt(data[16:])
++ except Exception:
++ raise DecryptError('Encrypted data appears to be corrupted.')
++
++ # Strip the last n padding bytes where n is the last value in
++ # the plaintext
++ padding = ord(result[-1])
++ return result[:-1 * padding]
++
++
++def protect_data(keys, data):
++ """ Given keys and serialized data, returns an appropriately
++ protected string suitable for storage in the cache.
++
++ """
++ if keys['strategy'] == 'ENCRYPT':
++ data = encrypt_data(keys['ENCRYPTION'], data)
++
++ encoded_data = base64.b64encode(data)
++
++ signature = sign_data(keys['MAC'], encoded_data)
++ return signature + encoded_data
++
++
++def unprotect_data(keys, signed_data):
++ """ Given keys and cached string data, verifies the signature,
++ decrypts if necessary, and returns the original serialized data.
++
++ """
++ # cache backends return None when no data is found. We don't mind
++ # that this particular special value is unsigned.
++ if signed_data is None:
++ return None
++
++ # First we calculate the signature
++ provided_mac = signed_data[:DIGEST_LENGTH_B64]
++ calculated_mac = sign_data(
++ keys['MAC'],
++ signed_data[DIGEST_LENGTH_B64:])
++
++ # Then verify that it matches the provided value
++ if not constant_time_compare(provided_mac, calculated_mac):
++ raise InvalidMacError('Invalid MAC; data appears to be corrupted.')
++
++ data = base64.b64decode(signed_data[DIGEST_LENGTH_B64:])
++
++ # then if necessary decrypt the data
++ if keys['strategy'] == 'ENCRYPT':
++ data = decrypt_data(keys['ENCRYPTION'], data)
++
++ return data
++
++
++def get_cache_key(keys):
++ """ Given keys generated by derive_keys(), returns a base64
++ encoded value suitable for use as a cache key in memcached.
++
++ """
++ return base64.b64encode(keys['CACHE_KEY'])
+diff --git a/tests/test_auth_token_middleware.py b/tests/test_auth_token_middleware.py
+index 06054d0..a428504 100644
+--- a/tests/test_auth_token_middleware.py
++++ b/tests/test_auth_token_middleware.py
+@@ -28,7 +28,6 @@ import webob
+ from keystoneclient.common import cms
+ from keystoneclient import utils
+ from keystoneclient.middleware import auth_token
+-from keystoneclient.middleware import memcache_crypt
+ from keystoneclient.openstack.common import memorycache
+ from keystoneclient.openstack.common import jsonutils
+ from keystoneclient.openstack.common import timeutils
+@@ -1013,9 +1012,7 @@ class AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest):
+ def _get_cached_token(self, token):
+ token_id = cms.cms_hash_token(token)
+ # NOTE(vish): example tokens are expired so skip the expiration check.
+- key = self.middleware._get_cache_key(token_id)
+- cached = self.middleware._cache.get(key)
+- return self.middleware._unprotect_cache_value(token, cached)
++ return self.middleware._cache_get(token_id, ignore_expires=True)
+
+ def test_memcache(self):
+ req = webob.Request.blank('/')
+@@ -1036,7 +1033,8 @@ class AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest):
+ token = 'invalid-token'
+ req.headers['X-Auth-Token'] = token
+ self.middleware(req.environ, self.start_fake_response)
+- self.assertEqual(self._get_cached_token(token), "invalid")
++ self.assertRaises(auth_token.InvalidUserToken,
++ self._get_cached_token, token)
+
+ def test_memcache_set_expired(self):
+ token_cache_time = 10
+@@ -1096,18 +1094,11 @@ class AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest):
+ 'memcache_secret_key': 'mysecret'
+ }
+ self.set_middleware(conf=conf)
+- encrypted_data = self.middleware._protect_cache_value(
+- 'token', TOKEN_RESPONSES[self.token_dict['uuid_token_default']])
+- self.assertEqual('{ENCRYPT:AES256}', encrypted_data[:16])
+- self.assertEqual(
+- TOKEN_RESPONSES[self.token_dict['uuid_token_default']],
+- self.middleware._unprotect_cache_value('token', encrypted_data))
+- # should return None if unable to decrypt
+- self.assertIsNone(
+- self.middleware._unprotect_cache_value(
+- 'token', '{ENCRYPT:AES256}corrupted'))
+- self.assertIsNone(
+- self.middleware._unprotect_cache_value('mykey', encrypted_data))
++ token = 'my_token'
++ data = ('this_data', 10e100)
++ self.middleware._init_cache({})
++ self.middleware._cache_store(token, data)
++ self.assertEqual(self.middleware._cache_get(token), data[0])
+
+ def test_sign_cache_data(self):
+ conf = {
+@@ -1119,19 +1110,11 @@ class AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest):
+ 'memcache_secret_key': 'mysecret'
+ }
+ self.set_middleware(conf=conf)
+- signed_data = self.middleware._protect_cache_value(
+- 'mykey', TOKEN_RESPONSES[self.token_dict['uuid_token_default']])
+- expected = '{MAC:SHA1}'
+- self.assertEqual(
+- signed_data[:10],
+- expected)
+- self.assertEqual(
+- TOKEN_RESPONSES[self.token_dict['uuid_token_default']],
+- self.middleware._unprotect_cache_value('mykey', signed_data))
+- # should return None on corrupted data
+- self.assertIsNone(
+- self.middleware._unprotect_cache_value('mykey',
+- '{MAC:SHA1}corrupted'))
++ token = 'my_token'
++ data = ('this_data', 10e100)
++ self.middleware._init_cache({})
++ self.middleware._cache_store(token, data)
++ self.assertEqual(self.middleware._cache_get(token), data[0])
+
+ def test_no_memcache_protection(self):
+ conf = {
+@@ -1142,47 +1125,11 @@ class AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest):
+ 'memcache_secret_key': 'mysecret'
+ }
+ self.set_middleware(conf=conf)
+- data = self.middleware._protect_cache_value('mykey',
+- 'This is a test!')
+- self.assertEqual(data, 'This is a test!')
+- self.assertEqual(
+- 'This is a test!',
+- self.middleware._unprotect_cache_value('mykey', data))
+-
+- def test_get_cache_key(self):
+- conf = {
+- 'auth_host': 'keystone.example.com',
+- 'auth_port': 1234,
+- 'auth_admin_prefix': '/testadmin',
+- 'memcache_servers': ['localhost:11211'],
+- 'memcache_secret_key': 'mysecret'
+- }
+- self.set_middleware(conf=conf)
+- self.assertEqual(
+- 'tokens/mytoken',
+- self.middleware._get_cache_key('mytoken'))
+- conf = {
+- 'auth_host': 'keystone.example.com',
+- 'auth_port': 1234,
+- 'auth_admin_prefix': '/testadmin',
+- 'memcache_servers': ['localhost:11211'],
+- 'memcache_security_strategy': 'mac',
+- 'memcache_secret_key': 'mysecret'
+- }
+- self.set_middleware(conf=conf)
+- expected = 'tokens/' + memcache_crypt.hash_data('mytoken' + 'mysecret')
+- self.assertEqual(self.middleware._get_cache_key('mytoken'), expected)
+- conf = {
+- 'auth_host': 'keystone.example.com',
+- 'auth_port': 1234,
+- 'auth_admin_prefix': '/testadmin',
+- 'memcache_servers': ['localhost:11211'],
+- 'memcache_security_strategy': 'Encrypt',
+- 'memcache_secret_key': 'abc!'
+- }
+- self.set_middleware(conf=conf)
+- expected = 'tokens/' + memcache_crypt.hash_data('mytoken' + 'abc!')
+- self.assertEqual(self.middleware._get_cache_key('mytoken'), expected)
++ token = 'my_token'
++ data = ('this_data', 10e100)
++ self.middleware._init_cache({})
++ self.middleware._cache_store(token, data)
++ self.assertEqual(self.middleware._cache_get(token), data[0])
+
+ def test_assert_valid_memcache_protection_config(self):
+ # test missing memcache_secret_key
+diff --git a/tests/test_memcache_crypt.py b/tests/test_memcache_crypt.py
+index b2281d9..524cd21 100644
+--- a/tests/test_memcache_crypt.py
++++ b/tests/test_memcache_crypt.py
+@@ -4,48 +4,66 @@ from keystoneclient.middleware import memcache_crypt
+
+
+ class MemcacheCryptPositiveTests(testtools.TestCase):
+- def test_generate_aes_key(self):
+- self.assertEqual(
+- len(memcache_crypt.generate_aes_key('Gimme Da Key', 'hush')), 32)
++ def _setup_keys(self, strategy):
++ return memcache_crypt.derive_keys('token', 'secret', strategy)
+
+- def test_compute_mac(self):
+- self.assertEqual(
+- memcache_crypt.compute_mac('mykey', 'This is a test!'),
+- 'tREu41yR5tEgeBWIuv9ag4AeKA8=')
++ def test_constant_time_compare(self):
++ # make sure it works as a compare, the "constant time" aspect
++ # isn't appropriate to test in unittests
++ ctc = memcache_crypt.constant_time_compare
++ self.assertTrue(ctc('abcd', 'abcd'))
++ self.assertTrue(ctc('', ''))
++ self.assertFalse(ctc('abcd', 'efgh'))
++ self.assertFalse(ctc('abc', 'abcd'))
++ self.assertFalse(ctc('abc', 'abc\x00'))
++ self.assertFalse(ctc('', 'abc'))
++
++ def test_derive_keys(self):
++ keys = memcache_crypt.derive_keys('token', 'secret', 'strategy')
++ self.assertEqual(len(keys['ENCRYPTION']),
++ len(keys['CACHE_KEY']))
++ self.assertEqual(len(keys['CACHE_KEY']),
++ len(keys['MAC']))
++ self.assertNotEqual(keys['ENCRYPTION'],
++ keys['MAC'])
++ self.assertIn('strategy', keys.keys())
++
++ def test_key_strategy_diff(self):
++ k1 = self._setup_keys('MAC')
++ k2 = self._setup_keys('ENCRYPT')
++ self.assertNotEqual(k1, k2)
+
+ def test_sign_data(self):
+- expected = '{MAC:SHA1}eyJtYWMiOiAiM0FrQmdPZHRybGo1RFFESHA1eUxqcDVq' +\
+- 'Si9BPSIsICJzZXJpYWxpemVkX2RhdGEiOiAiXCJUaGlzIGlzIGEgdG' +\
+- 'VzdCFcIiJ9'
+- self.assertEqual(
+- memcache_crypt.sign_data('mykey', 'This is a test!'),
+- expected)
+-
+- def test_verify_signed_data(self):
+- signed = memcache_crypt.sign_data('mykey', 'Testz')
+- self.assertEqual(
+- memcache_crypt.verify_signed_data('mykey', signed),
+- 'Testz')
+- self.assertEqual(
+- memcache_crypt.verify_signed_data('aasSFWE13WER', 'not MACed'),
+- 'not MACed')
+-
+- def test_encrypt_data(self):
+- expected = '{ENCRYPT:AES256}'
+- self.assertEqual(
+- memcache_crypt.encrypt_data('mykey', 'mysecret',
+- 'This is a test!')[:16],
+- expected)
+-
+- def test_decrypt_data(self):
+- encrypted = memcache_crypt.encrypt_data('mykey', 'mysecret', 'Testz')
+- self.assertEqual(
+- memcache_crypt.decrypt_data('mykey', 'mysecret', encrypted),
+- 'Testz')
+- self.assertEqual(
+- memcache_crypt.decrypt_data('mykey', 'mysecret',
+- 'Not Encrypted!'),
+- 'Not Encrypted!')
++ keys = self._setup_keys('MAC')
++ sig = memcache_crypt.sign_data(keys['MAC'], 'data')
++ self.assertEqual(len(sig), memcache_crypt.DIGEST_LENGTH_B64)
++
++ def test_encryption(self):
++ keys = self._setup_keys('ENCRYPT')
++ # what you put in is what you get out
++ for data in ['data', '1234567890123456', '\x00\xFF' * 13
++ ] + [chr(x % 256) * x for x in range(768)]:
++ crypt = memcache_crypt.encrypt_data(keys['ENCRYPTION'], data)
++ decrypt = memcache_crypt.decrypt_data(keys['ENCRYPTION'], crypt)
++ self.assertEqual(data, decrypt)
++ self.assertRaises(memcache_crypt.DecryptError,
++ memcache_crypt.decrypt_data,
++ keys['ENCRYPTION'], crypt[:-1])
++
++ def test_protect_wrappers(self):
++ data = 'My Pretty Little Data'
++ for strategy in ['MAC', 'ENCRYPT']:
++ keys = self._setup_keys(strategy)
++ protected = memcache_crypt.protect_data(keys, data)
++ self.assertNotEqual(protected, data)
++ if strategy == 'ENCRYPT':
++ self.assertNotIn(data, protected)
++ unprotected = memcache_crypt.unprotect_data(keys, protected)
++ self.assertEqual(data, unprotected)
++ self.assertRaises(memcache_crypt.InvalidMacError,
++ memcache_crypt.unprotect_data,
++ keys, protected[:-1])
++ self.assertIsNone(memcache_crypt.unprotect_data(keys, None))
+
+ def test_no_pycrypt(self):
+ aes = memcache_crypt.AES
+--
+1.8.1.5
+
diff --git a/dev-python/python-keystoneclient/python-keystoneclient-0.2.4-r1.ebuild b/dev-python/python-keystoneclient/python-keystoneclient-0.2.4-r2.ebuild
index 79af10deb4d6..6b9be04110b1 100644
--- a/dev-python/python-keystoneclient/python-keystoneclient-0.2.4-r1.ebuild
+++ b/dev-python/python-keystoneclient/python-keystoneclient-0.2.4-r2.ebuild
@@ -1,6 +1,6 @@
# Copyright 1999-2013 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
-# $Header: /var/cvsroot/gentoo-x86/dev-python/python-keystoneclient/python-keystoneclient-0.2.4-r1.ebuild,v 1.1 2013/05/31 15:08:21 prometheanfire Exp $
+# $Header: /var/cvsroot/gentoo-x86/dev-python/python-keystoneclient/python-keystoneclient-0.2.4-r2.ebuild,v 1.1 2013/06/20 14:39:43 prometheanfire Exp $
EAPI=5
#restricted due to packages missing and bad depends in the test ==webob-1.0.8
@@ -48,6 +48,7 @@ RDEPEND=">=dev-python/d2to1-0.2.10[${PYTHON_USEDEP}]
virtual/python-argparse[${PYTHON_USEDEP}]"
PATCHES=(
+ "${FILESDIR}/0.2.4-CVE-2013-2166-7.patch"
)
# "${FILESDIR}/0.2.3-CVE-2013-2104.patch"