Index: third_party/google-endpoints/jwkest/__init__.py |
diff --git a/third_party/google-endpoints/jwkest/__init__.py b/third_party/google-endpoints/jwkest/__init__.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2d0239d351a9f025bf2b4a075661e2cfd37dff40 |
--- /dev/null |
+++ b/third_party/google-endpoints/jwkest/__init__.py |
@@ -0,0 +1,234 @@ |
+"""JSON Web Token""" |
+import base64 |
+import logging |
+import re |
+import struct |
+import six |
+ |
+try: |
+ from builtins import zip |
+ from builtins import hex |
+ from builtins import str |
+except ImportError: |
+ pass |
+ |
+from binascii import unhexlify |
+ |
+__version__ = "1.0.9" |
+ |
+logger = logging.getLogger(__name__) |
+ |
+JWT_TYPES = (u"JWT", u"application/jws", u"JWS", u"JWE") |
+ |
+JWT_CLAIMS = {"iss": str, "sub": str, "aud": str, "exp": int, "nbf": int, |
+ "iat": int, "jti": str, "typ": str} |
+ |
+JWT_HEADERS = ["typ", "cty"] |
+ |
+ |
+class JWKESTException(Exception): |
+ pass |
+ |
+ |
+# XXX Should this be a subclass of ValueError? |
+class Invalid(JWKESTException): |
+ """The JWT is invalid.""" |
+ |
+ |
+class BadSyntax(Invalid): |
+ """The JWT could not be parsed because the syntax is invalid.""" |
+ |
+ def __init__(self, value, msg): |
+ Invalid.__init__(self) |
+ self.value = value |
+ self.msg = msg |
+ |
+ def __str__(self): |
+ return "%s: %r" % (self.msg, self.value) |
+ |
+ |
+class BadSignature(Invalid): |
+ """The signature of the JWT is invalid.""" |
+ |
+ |
+class Expired(Invalid): |
+ """The JWT claim has expired or is not yet valid.""" |
+ |
+ |
+class UnknownAlgorithm(Invalid): |
+ """The JWT uses an unknown signing algorithm""" |
+ |
+ |
+class BadType(Invalid): |
+ """The JWT has an unexpected "typ" value.""" |
+ |
+ |
+class MissingKey(JWKESTException): |
+ """ No usable key """ |
+ |
+ |
+class UnknownKeytype(Invalid): |
+ """An unknown key type""" |
+ |
+ |
+# --------------------------------------------------------------------------- |
+# Helper functions |
+ |
+ |
+def intarr2bin(arr): |
+ return unhexlify(''.join(["%02x" % byte for byte in arr])) |
+ |
+ |
+def long2hexseq(l): |
+ try: |
+ return unhexlify(hex(l)[2:]) |
+ except TypeError: |
+ return unhexlify(hex(l)[2:-1]) |
+ |
+ |
+def intarr2long(arr): |
+ return int(''.join(["%02x" % byte for byte in arr]), 16) |
+ |
+ |
+def long2intarr(long_int): |
+ _bytes = [] |
+ while long_int: |
+ long_int, r = divmod(long_int, 256) |
+ _bytes.insert(0, r) |
+ return _bytes |
+ |
+ |
+def long_to_base64(n): |
+ bys = long2intarr(n) |
+ data = struct.pack('%sB' % len(bys), *bys) |
+ if not len(data): |
+ data = '\x00' |
+ s = base64.urlsafe_b64encode(data).rstrip(b'=') |
+ return s.decode("ascii") |
+ |
+ |
+def base64_to_long(data): |
+ if isinstance(data, six.text_type): |
+ data = data.encode("ascii") |
+ |
+ # urlsafe_b64decode will happily convert b64encoded data |
+ _d = base64.urlsafe_b64decode(bytes(data) + b'==') |
+ return intarr2long(struct.unpack('%sB' % len(_d), _d)) |
+ |
+ |
+def base64url_to_long(data): |
+ """ |
+ Stricter then base64_to_long since it really checks that it's |
+ base64url encoded |
+ |
+ :param data: The base64 string |
+ :return: |
+ """ |
+ _d = base64.urlsafe_b64decode(bytes(data) + b'==') |
+ # verify that it's base64url encoded and not just base64 |
+ # that is no '+' and '/' characters and not trailing "="s. |
+ if [e for e in [b'+', b'/', b'='] if e in data]: |
+ raise ValueError("Not base64url encoded") |
+ return intarr2long(struct.unpack('%sB' % len(_d), _d)) |
+ |
+ |
+# ============================================================================= |
+ |
+def b64e(b): |
+ """Base64 encode some bytes. |
+ |
+ Uses the url-safe - and _ characters, and doesn't pad with = characters.""" |
+ return base64.urlsafe_b64encode(b).rstrip(b"=") |
+ |
+ |
+_b64_re = re.compile(b"^[A-Za-z0-9_-]*$") |
+ |
+ |
+def add_padding(b): |
+ # add padding chars |
+ m = len(b) % 4 |
+ if m == 1: |
+ # NOTE: for some reason b64decode raises *TypeError* if the |
+ # padding is incorrect. |
+ raise BadSyntax(b, "incorrect padding") |
+ elif m == 2: |
+ b += b"==" |
+ elif m == 3: |
+ b += b"=" |
+ return b |
+ |
+ |
+def b64d(b): |
+ """Decode some base64-encoded bytes. |
+ |
+ Raises BadSyntax if the string contains invalid characters or padding. |
+ |
+ :param b: bytes |
+ """ |
+ |
+ cb = b.rstrip(b"=") # shouldn't but there you are |
+ |
+ # Python's base64 functions ignore invalid characters, so we need to |
+ # check for them explicitly. |
+ if not _b64_re.match(cb): |
+ raise BadSyntax(cb, "base64-encoded data contains illegal characters") |
+ |
+ if cb == b: |
+ b = add_padding(b) |
+ |
+ return base64.urlsafe_b64decode(b) |
+ |
+ |
+def b64e_enc_dec(str, encode, decode): |
+ return base64.urlsafe_b64encode(str.encode(encode)).decode(decode) |
+ |
+ |
+def b64d_enc_dec(str, encode, decode): |
+ return base64.urlsafe_b64decode(str.encode(encode)).decode(decode) |
+ |
+ |
+# 'Stolen' from Werkzeug |
+def safe_str_cmp(a, b): |
+ """Compare two strings in constant time.""" |
+ if len(a) != len(b): |
+ return False |
+ r = 0 |
+ for c, d in zip(a, b): |
+ r |= ord(c) ^ ord(d) |
+ return r == 0 |
+ |
+ |
+def constant_time_compare(a, b): |
+ """Compare two strings in constant time.""" |
+ if len(a) != len(b): |
+ return False |
+ r = 0 |
+ for c, d in zip(a, b): |
+ r |= c ^ d |
+ return r == 0 |
+ |
+ |
+def as_bytes(s): |
+ """ |
+ Convert an unicode string to bytes. |
+ :param s: Unicode / bytes string |
+ :return: bytes string |
+ """ |
+ try: |
+ s = s.encode() |
+ except (AttributeError, UnicodeDecodeError): |
+ pass |
+ return s |
+ |
+ |
+def as_unicode(b): |
+ """ |
+ Convert a byte string to a unicode string |
+ :param b: byte string |
+ :return: unicode string |
+ """ |
+ try: |
+ b = b.decode() |
+ except (AttributeError, UnicodeDecodeError): |
+ pass |
+ return b |