mirror of
https://github.com/certbot/certbot.git
synced 2026-07-30 16:14:44 +02:00
JOSE Base64: encode str only, decode str or ascii unicode
This commit is contained in:
@@ -74,39 +74,42 @@ def unique_file(default_name, mode=0777):
|
|||||||
# - padding stripped
|
# - padding stripped
|
||||||
|
|
||||||
|
|
||||||
def jose_b64encode(data, encoding='utf-8'):
|
def jose_b64encode(data):
|
||||||
"""JOSE Base64 encode.
|
"""JOSE Base64 encode.
|
||||||
|
|
||||||
:param data: Data to be encoded.
|
:param data: Data to be encoded.
|
||||||
:type data: str or unicode
|
:type data: str or bytearray
|
||||||
|
|
||||||
:param encoding: Name of the encoding to be performed before
|
:raises: TypeError
|
||||||
Base64 encoding. If not None, then `data`
|
|
||||||
has to be unicode.
|
|
||||||
:type encoding: str or None
|
|
||||||
|
|
||||||
:returns: JOSE Base64 string.
|
:returns: JOSE Base64 string.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
encoded = data if encoding is None else data.encode(encoding)
|
if not isinstance(data, str):
|
||||||
return base64.urlsafe_b64encode(encoded).rstrip('=')
|
raise TypeError('argument should be str or bytearray')
|
||||||
|
return base64.urlsafe_b64encode(data).rstrip('=')
|
||||||
|
|
||||||
|
|
||||||
def jose_b64decode(arg, decoding='utf-8'):
|
def jose_b64decode(data):
|
||||||
"""JOSE Base64 decode.
|
"""JOSE Base64 decode.
|
||||||
|
|
||||||
:param arg: Base64 string to be decoded.
|
:param data: Base64 string to be decoded. If it's unicode, then
|
||||||
:type arg: str or unicode
|
only ASCII characters are allowed.
|
||||||
|
:type data: str or unicode
|
||||||
|
|
||||||
:param decoding: Name of the encoding to be performed after
|
:raises: ValueError, TypeError
|
||||||
Base64 decoding.
|
|
||||||
:type decoding: str or None
|
|
||||||
|
|
||||||
:returns: Decoded data. Unicode if `decoding` is not None.
|
:returns: Decoded data.
|
||||||
:rtype: str or unicode
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ascii = arg.encode('ascii') # equivalent to str(arg), for unicode input
|
if isinstance(data, unicode):
|
||||||
decoded = base64.urlsafe_b64decode(ascii + '=' * (4 - (len(ascii) % 4)))
|
try:
|
||||||
return decoded if decoding is None else decoded.decode(decoding)
|
data = data.encode('ascii')
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
raise ValueError(
|
||||||
|
'unicode argument should contain only ASCII characters')
|
||||||
|
elif not isinstance(data, str):
|
||||||
|
raise TypeError('argument should be a str or unicode')
|
||||||
|
|
||||||
|
return base64.urlsafe_b64decode(data + '=' * (4 - (len(data) % 4)))
|
||||||
|
|||||||
@@ -88,46 +88,46 @@ B64_URL_UNSAFE_EXAMPLES = {
|
|||||||
class JOSEB64EncodeTest(unittest.TestCase):
|
class JOSEB64EncodeTest(unittest.TestCase):
|
||||||
"""Tests for letsencrypt.client.le_util.jose_b64encode."""
|
"""Tests for letsencrypt.client.le_util.jose_b64encode."""
|
||||||
|
|
||||||
def _call(self, data, encoding):
|
def _call(self, data):
|
||||||
from letsencrypt.client.le_util import jose_b64encode
|
from letsencrypt.client.le_util import jose_b64encode
|
||||||
return jose_b64encode(data, encoding)
|
return jose_b64encode(data)
|
||||||
|
|
||||||
def test_unsafe_url(self):
|
def test_unsafe_url(self):
|
||||||
for text, b64 in B64_URL_UNSAFE_EXAMPLES.iteritems():
|
for text, b64 in B64_URL_UNSAFE_EXAMPLES.iteritems():
|
||||||
self.assertEqual(self._call(text, None), b64)
|
self.assertEqual(self._call(text), b64)
|
||||||
|
|
||||||
def test_different_paddings(self):
|
def test_different_paddings(self):
|
||||||
for text, (b64, _) in JOSE_B64_PADDING_EXAMPLES.iteritems():
|
for text, (b64, _) in JOSE_B64_PADDING_EXAMPLES.iteritems():
|
||||||
self.assertEqual(self._call(text, None), b64)
|
self.assertEqual(self._call(text), b64)
|
||||||
|
|
||||||
def test_with_encoding(self):
|
def test_unicode_fails_with_type_error(self):
|
||||||
self.assertEqual(self._call(u'\u0105', 'utf-8'), 'xIU')
|
self.assertRaises(TypeError, self._call, u'some unicode')
|
||||||
|
|
||||||
|
|
||||||
class JOSEB64DecodeTest(unittest.TestCase):
|
class JOSEB64DecodeTest(unittest.TestCase):
|
||||||
"""Tests for letsencrypt.client.le_util.jose_b64decode."""
|
"""Tests for letsencrypt.client.le_util.jose_b64decode."""
|
||||||
|
|
||||||
def _call(self, jose_b64_string, decoding):
|
def _call(self, data):
|
||||||
from letsencrypt.client.le_util import jose_b64decode
|
from letsencrypt.client.le_util import jose_b64decode
|
||||||
return jose_b64decode(jose_b64_string, decoding)
|
return jose_b64decode(data)
|
||||||
|
|
||||||
def test_unsafe_url(self):
|
def test_unsafe_url(self):
|
||||||
for text, b64 in B64_URL_UNSAFE_EXAMPLES.iteritems():
|
for text, b64 in B64_URL_UNSAFE_EXAMPLES.iteritems():
|
||||||
self.assertEqual(self._call(b64, None), text)
|
self.assertEqual(self._call(b64), text)
|
||||||
|
|
||||||
def test_input_without_padding(self):
|
def test_input_without_padding(self):
|
||||||
for text, (b64, _) in JOSE_B64_PADDING_EXAMPLES.iteritems():
|
for text, (b64, _) in JOSE_B64_PADDING_EXAMPLES.iteritems():
|
||||||
self.assertEqual(self._call(b64, None), text)
|
self.assertEqual(self._call(b64), text)
|
||||||
|
|
||||||
def test_input_with_padding(self):
|
def test_input_with_padding(self):
|
||||||
for text, (b64, pad) in JOSE_B64_PADDING_EXAMPLES.iteritems():
|
for text, (b64, pad) in JOSE_B64_PADDING_EXAMPLES.iteritems():
|
||||||
self.assertEqual(self._call(b64 + pad, None), text)
|
self.assertEqual(self._call(b64 + pad), text)
|
||||||
|
|
||||||
def test_with_encoding(self):
|
def test_unicode_with_ascii(self):
|
||||||
self.assertEqual(self._call('xIU=', 'utf-8'), u'\u0105')
|
self.assertEqual(self._call(u'YQ'), 'a')
|
||||||
|
|
||||||
def test_unicode(self):
|
def test_non_ascii_unicode_fails(self):
|
||||||
self.assertEqual(self._call(u'YQ', None), 'a')
|
self.assertRaises(ValueError, self._call, u'\u0105')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
Reference in New Issue
Block a user