Fix JOSE encoding mess

This commit is contained in:
Jakub Warmuz 2014-11-23 02:37:34 +01:00
parent 6f32c41da3
commit af4d955806
2 changed files with 39 additions and 40 deletions

View file

@ -65,48 +65,47 @@ def unique_file(default_name, mode=0777):
count += 1
def _to_utf8(arg):
"""Normalize to UTF-8 string."""
return arg.encode('utf-8') if isinstance(arg, unicode) else arg
# https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-37#appendix-C
#
# Jose Base64:
#
# - URL-safe Base64
#
# - padding stripped
def jose_b64encode(arg):
def jose_b64encode(data, encoding='utf-8'):
"""JOSE Base64 encode.
JOSE Base64:
- URL-safe Base64
- padding stripped
:param data: Data to be encoded.
:type data: str or unicode
https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-37#appendix-C
:param arg: String to be encoded. Unicode input will be encoded
to UTF-8 before Base64 encoding.
:type arg: str or unicode
:param encoding: Name of the encoding to be performed before
Base64 encoding. If not None, then `data`
has to be unicode.
:type encoding: str or None
:returns: JOSE Base64 string.
:rtype: str
"""
return base64.urlsafe_b64encode(_to_utf8(arg)).rstrip('=')
encoded = data if encoding is None else data.encode(encoding)
return base64.urlsafe_b64encode(encoded).rstrip('=')
def jose_b64decode(arg):
def jose_b64decode(arg, decoding='utf-8'):
"""JOSE Base64 decode.
Jose Base64:
- URL-safe Base64
- padding stripped
:param arg: Base64 string to be decoded.
:type arg: str
https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-37#appendix-C
:param decoding: Name of the encoding to be performed after
Base64 decoding.
:type decoding: str or None
:param arg: Base64 string to be decoded. Unicode input will be
encoded to UTF-8 before Base64 decoding.
:type arg: str or unicode
:returns: Decoded string.
:rtype: str
:returns: Decoded data. Unicode if `decoding` is not None.
:rtype: str or unicode
"""
normalized = _to_utf8(arg)
return base64.urlsafe_b64decode(
normalized + '=' * (4 - (len(normalized) % 4)))
decoded = base64.urlsafe_b64decode(arg + '=' * (4 - (len(arg) % 4)))
return decoded if decoding is None else decoded.decode(decoding)

View file

@ -72,32 +72,32 @@ class CheckPermissionsTest(unittest.TestCase):
class JOSEB64EncodeTest(unittest.TestCase):
"""Tests for letsencrypt.client.le_util.jose_b64encode."""
def _call(self, arg):
def _call(self, data, encoding):
from letsencrypt.client.le_util import jose_b64encode
return jose_b64encode(arg)
return jose_b64encode(data, encoding)
def test_str(self):
self.assertEqual(self._call('foo'), 'Zm9v')
def test_without_encoding(self):
self.assertEqual(self._call('foo', None), 'Zm9v')
def test_unicode(self):
self.assertEqual(self._call(u'\u0105'), 'xIU')
def test_with_encoding(self):
self.assertEqual(self._call(u'\u0105', 'utf-8'), 'xIU')
class JOSEB64DecodeTest(unittest.TestCase):
"""Tests for letsencrypt.client.le_util.jose_b64decode."""
def _call(self, arg):
def _call(self, jose_b64_string, decoding):
from letsencrypt.client.le_util import jose_b64decode
return jose_b64decode(arg)
return jose_b64decode(jose_b64_string, decoding)
def test_str(self):
self.assertEqual(self._call('Zm9v='), 'foo')
def test_without_decoding(self):
self.assertEqual(self._call('Zm9v=', None), 'foo')
def test_unicode(self):
self.assertEqual(self._call(u'XIU='), '\\\x85')
def test_with_encoding(self):
self.assertEqual(self._call('xIU=', 'utf-8'), u'\u0105')
def test_fills_padding(self):
self.assertEqual(self._call('Zm9v'), 'foo')
self.assertEqual(self._call('Zm9v', None), 'foo')
if __name__ == '__main__':