def test_jwe_09_a3(): #Example JWE using AES Key Wrap and AES GCM msg = "Live long and prosper." header = '{"alg":"A128KW","enc":"A128CBC-HS256"}' b64_header = b64e(header) assert b64_header == "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" cek = intarr2str([4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106, 206, 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156, 44, 207]) shared_key = [25, 172, 32, 130, 225, 114, 26, 181, 138, 106, 254, 192, 95, 133, 74, 82] jek = aes_wrap_key(intarr2str(shared_key), cek) assert str2intarr(jek) == [ 232, 160, 123, 211, 183, 76, 245, 132, 200, 128, 123, 75, 190, 216, 22, 67, 201, 138, 193, 186, 9, 91, 122, 31, 246, 90, 28, 139, 57, 3, 76, 124, 193, 11, 98, 37, 173, 61, 104, 57] b64_jek = b64e(jek) assert b64_jek == "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ" iv = intarr2str([3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101]) b64_iv = b64e(iv) assert b64_iv == "AxY8DCtDaGlsbGljb3RoZQ" aadp = b64_header assert str2intarr(aadp) == [101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 66, 77, 84, 73, 52, 83, 49, 99, 105, 76, 67, 74, 108, 98, 109, 77, 105, 79, 105, 74, 66, 77, 84, 73, 52, 81, 48, 74, 68, 76, 85, 104, 84, 77, 106, 85, 50, 73, 110, 48] _jwe = JWe() ctxt, tag, key = _jwe.enc_setup("A128CBC-HS256", msg, aadp, cek, iv=iv) print str2intarr(ctxt) assert str2intarr(ctxt) == [ 40, 57, 83, 181, 119, 33, 133, 148, 198, 185, 243, 24, 152, 230, 6, 75, 129, 223, 127, 19, 210, 82, 183, 230, 168, 33, 215, 104, 143, 112, 56, 102] assert str2intarr(tag) == [83, 73, 191, 98, 104, 205, 211, 128, 201, 189, 199, 133, 32, 38, 194, 85] enc_cipher_text = b64e(ctxt) assert enc_cipher_text == "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY" enc_authn_tag = b64e(tag) assert enc_authn_tag == "U0m_YmjN04DJvceFICbCVQ"
def test_a_1_1c(): hmac = jwkest.intarr2bin(HMAC_KEY) signer = SIGNER_ALGS["HS256"] header = b'{"typ":"JWT",\r\n "alg":"HS256"}' payload = b'{"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}' sign_input = b64e(header) + b'.' + b64e(payload) sig = signer.sign(sign_input, hmac) assert b64e(sig) == b'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'
def test_a_1_1c(): hmac = jwkest.intarr2bin(HMAC_KEY) signer = SIGNER_ALGS["HS256"] header = '{"typ":"JWT",\r\n "alg":"HS256"}' payload = '{"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}' sign_input = b64e(header) + '.' + b64e(payload) sig = signer.sign(sign_input, hmac) assert b64e(sig) == "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
def left_hash(msg, func="HS256"): """ 128 bits == 16 bytes """ if func == 'HS256': return b64e(sha256_digest(msg)[:16]) elif func == 'HS384': return b64e(sha384_digest(msg)[:24]) elif func == 'HS512': return b64e(sha512_digest(msg)[:32])
def pack(b64_header, jek, iv, ctxt, tag): res = b'.'.join( [b64_header, b64e(jek), b64e(iv), b64e(ctxt), b64e(tag)]) return res
def left_hash(msg, func="HS256"): """ 128 bits == 16 bytes """ if func == 'HS256': return as_unicode(b64e(sha256_digest(msg)[:16])) elif func == 'HS384': return as_unicode(b64e(sha384_digest(msg)[:24])) elif func == 'HS512': return as_unicode(b64e(sha512_digest(msg)[:32]))
def b64encode_item(item): if isinstance(item, bytes): return b64e(item) elif isinstance(item, str): return b64e(item.encode("utf-8")) elif isinstance(item, int): return b64e(item) else: return b64e( json.dumps(b2s_conv(item), separators=(",", ":")).encode("utf-8"))
def b64encode_item(item): if isinstance(item, bytes): return b64e(item) elif isinstance(item, str): return b64e(item.encode("utf-8")) elif isinstance(item, int): return b64e(item) else: return b64e(json.dumps(b2s_conv(item), separators=(",", ":")).encode("utf-8"))
def test_a_1_1b(): payload = b'{"iss":"joe",\r\n "exp":1300819380,' \ b'\r\n "http://example.com/is_root":true}' val = b64e(payload) assert val == ( b'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9' b'leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ')
def encryption_key(self, alg, **kwargs): """ Return an encryption key as per http://openid.net/specs/openid-connect-core-1_0.html#Encryption :param alg: encryption algorithm :param kwargs: :return: encryption key as byte string """ if not self.key: self.deserialize() tsize = ALG2KEYLEN[alg] #_keylen = len(self.key) if tsize <= 32: # SHA256 _enc_key = sha256_digest(self.key)[:tsize] elif tsize <= 48: # SHA384 _enc_key = sha384_digest(self.key)[:tsize] elif tsize <= 64: # SHA512 _enc_key = sha512_digest(self.key)[:tsize] else: raise JWKException("No support for symmetric keys > 512 bits") logger.debug('Symmetric encryption key: {}'.format( as_unicode(b64e(_enc_key)))) return _enc_key
def test_client_secret_jwt(self, client): client.token_endpoint = "https://example.com/token" client.provider_info = { 'issuer': 'https://example.com/', 'token_endpoint': "https://example.com/token" } csj = ClientSecretJWT(client) cis = AccessTokenRequest() csj.construct(cis, algorithm="HS256", authn_endpoint='userinfo') assert cis["client_assertion_type"] == JWT_BEARER assert "client_assertion" in cis cas = cis["client_assertion"] _jwt = JWT().unpack(cas) jso = _jwt.payload() assert _eq(jso.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert _jwt.headers == {'alg': 'HS256'} _rj = JWS() info = _rj.verify_compact( cas, [SYMKey(k=b64e(as_bytes(client.client_secret)))]) assert _eq(info.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert info['aud'] == [client.provider_info['issuer']]
def add_code_challenge(self): """ PKCE RFC 7636 support. :return: """ try: cv_len = self.config["code_challenge"]["length"] except KeyError: cv_len = 64 # Use default code_verifier = unreserved(cv_len) _cv = code_verifier.encode("ascii") try: _method = self.config["code_challenge"]["method"] except KeyError: _method = "S256" try: _h = CC_METHOD[_method](_cv).digest() code_challenge = b64e(_h).decode("ascii") except KeyError: raise Unsupported("PKCE Transformation method:{}".format(_method)) # TODO store code_verifier return ( {"code_challenge": code_challenge, "code_challenge_method": _method}, code_verifier, )
def sign_compact(self, keys=None): """ Produce a JWS using the JWS Compact Serialization :param keys: A dictionary of keys :return: """ enc_head = self._encoded_header() enc_payload = self._encoded_payload() _alg = self["alg"] if not _alg or _alg.lower() == "none": return enc_head + b"." + enc_payload + b"." try: _signer = SIGNER_ALGS[_alg] except KeyError: raise UnknownAlgorithm(_alg) if keys: keys = self._pick_keys(keys, use="sig") else: keys = self._pick_keys(self._get_keys()) if keys: key = keys[0] else: raise NoSuitableSigningKeys(_alg) _input = b".".join([enc_head, enc_payload]) sig = _signer.sign(_input, key.get_key(private=True)) return b".".join([enc_head, enc_payload, b64e(sig)])
def add_code_challenge(self): """ PKCE RFC 7636 support :return: """ try: cv_len = self.config['code_challenge']['length'] except KeyError: cv_len = 64 # Use default code_verifier = unreserved(cv_len) _cv = code_verifier.encode() try: _method = self.config['code_challenge']['method'] except KeyError: _method = 'S256' try: _h = CC_METHOD[_method](_cv).hexdigest() code_challenge = b64e(_h.encode()).decode() except KeyError: raise Unsupported('PKCE Transformation method:{}'.format(_method)) # TODO store code_verifier return { "code_challenge": code_challenge, "code_challenge_method": _method }, code_verifier
def add_code_challenge(self): try: cv_len = self.config['code_challenge']['length'] except KeyError: cv_len = 64 # Use default code_verifier = unreserved(cv_len) _cv = code_verifier.encode() try: _method = self.config['code_challenge']['method'] except KeyError: _method = 'S256' try: _h = CC_METHOD[_method](_cv).hexdigest() code_challenge = b64e(_h.encode()).decode() except KeyError: raise Unsupported( 'PKCE Transformation method:{}'.format(_method)) # TODO store code_verifier return {"code_challenge": code_challenge, "code_challenge_method": _method}, code_verifier
def encryption_key(self, alg, **kwargs): """ Return an encryption key as per http://openid.net/specs/openid-connect-core-1_0.html#Encryption :param alg: encryption algorithm :param kwargs: :return: encryption key as byte string """ if not self.key: self.deserialize() tsize = ALG2KEYLEN[alg] # _keylen = len(self.key) if tsize <= 32: # SHA256 _enc_key = sha256_digest(self.key)[:tsize] elif tsize <= 48: # SHA384 _enc_key = sha384_digest(self.key)[:tsize] elif tsize <= 64: # SHA512 _enc_key = sha512_digest(self.key)[:tsize] else: raise JWKException("No support for symmetric keys > 512 bits") logger.debug('Symmetric encryption key: {}'.format( as_unicode(b64e(_enc_key)))) return _enc_key
def sign_compact(self, keys=None): """ Produce a JWS using the JWS Compact Serialization :param keys: A dictionary of keys :return: """ enc_head = self._encoded_header() enc_payload = self._encoded_payload() _alg = self["alg"] if not _alg or _alg.lower() == "none": return enc_head + b"." + enc_payload + b"." try: _signer = SIGNER_ALGS[_alg] except KeyError: raise UnknownAlgorithm(_alg) if keys: keys = self._pick_keys(keys) else: keys = self._pick_keys(self._get_keys()) if keys: key = keys[0] else: raise NoSuitableSigningKeys(_alg) _input = b".".join([enc_head, enc_payload]) sig = _signer.sign(_input, key.key) return b".".join([enc_head, enc_payload, b64e(sig)])
def enc_setup(self, msg, auth_data, key=None, **kwargs): encrypted_key = "" # Generate the input parameters try: apu = b64d(kwargs["apu"]) except KeyError: apu = b64d(Random.get_random_bytes(16)) try: apv = b64d(kwargs["apv"]) except KeyError: apv = b64d(Random.get_random_bytes(16)) # Generate an ephemeral key pair curve = NISTEllipticCurve.by_name(key.crv) if "epk" in kwargs: epk = ECKey(key=kwargs["epk"], private=False) eprivk = ECKey(kwargs["epk"], private=True) else: (eprivk, epk) = curve.key_pair() # Derive the KEK and encrypt params = { "apu": b64e(apu), "apv": b64e(apv), #"epk": exportKey(epk, "EC", curve) } cek, iv = self._generate_key_and_iv(self.enc) if self.alg == "ECDH-ES": try: dk_len = KEYLEN[self.enc] except KeyError: raise Exception("Unknown key length for algorithm %s" % self.enc) cek = ecdh_derive_key(curve, eprivk, key, apu, apv, self.enc, dk_len) elif self.alg in [ "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW" ]: _pre, _post = self.alg.split("+") klen = int(_post[1:4]) kek = ecdh_derive_key(curve, eprivk, key, apu, apv, _post, klen) encrypted_key = aes_wrap_key(kek, cek) else: raise Exception("Unsupported algorithm %s" % self.alg) return cek, encrypted_key, iv, params
def party_value(pv): if pv: s = b64e(pv) r = int2bigendian(len(s)) r.extend(s) return r else: return [0, 0, 0, 0]
def store_key(self, key): kb = KeyBundle() kb.do_keys([key]) # Store key with thumbprint as key key_thumbprint = b64e(kb.keys()[0].thumbprint("SHA-256")).decode("utf8") self.thumbprint2key[key_thumbprint] = key return key_thumbprint
def build_keyjar(key_conf, kid_template="", keyjar=None, kidd=None): """ Configuration of the type: keys = [ {"type": "RSA", "key": "cp_keys/key.pem", "use": ["enc", "sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["enc"]} ] :param key_conf: The key configuration :param kid_template: A template by which to build the kids :return: a tuple consisting of a JWKS dictionary, a KeyJar instance and a representation of which kids that can be used for what. Note the JWKS contains private key information !! """ if keyjar is None: keyjar = KeyJar() if kidd is None: kidd = {"sig": {}, "enc": {}} kid = 0 jwks = {"keys": []} for spec in key_conf: typ = spec["type"].upper() if typ == "RSA": if "key" in spec: try: kb = KeyBundle(source="file://%s" % spec["key"], fileformat="der", keytype=typ, keyusage=spec["use"]) except FileNotFoundError: if 'name' not in spec: spec['name'] = spec['key'] kb = rsa_init(spec) else: kb = rsa_init(spec) elif typ == "EC": kb = ec_init(spec) for k in kb.keys(): if kid_template: k.kid = kid_template % kid kid += 1 else: k.kid = b64e(k.thumbprint('SHA-256')).decode('utf8') kidd[k.use][k.kty] = k.kid jwks["keys"].extend( [k.serialize() for k in kb.keys() if k.kty != 'oct']) keyjar.add_kb("", kb) return jwks, keyjar, kidd
def enc_setup(self, msg, auth_data, key=None, **kwargs): encrypted_key = "" # Generate the input parameters try: apu = b64d(kwargs["apu"]) except KeyError: apu = b64d(Random.get_random_bytes(16)) try: apv = b64d(kwargs["apv"]) except KeyError: apv = b64d(Random.get_random_bytes(16)) # Generate an ephemeral key pair curve = NISTEllipticCurve.by_name(key.crv) if "epk" in kwargs: epk = ECKey(key=kwargs["epk"], private=False) eprivk = ECKey(kwargs["epk"], private=True) else: (eprivk, epk) = curve.key_pair() # Derive the KEK and encrypt params = { "apu": b64e(apu), "apv": b64e(apv), #"epk": exportKey(epk, "EC", curve) } cek, iv = self._generate_key_and_iv(self.enc) if self.alg == "ECDH-ES": try: dk_len = KEYLEN[self.enc] except KeyError: raise Exception( "Unknown key length for algorithm %s" % self.enc) cek = ecdh_derive_key(curve, eprivk, key, apu, apv, self.enc, dk_len) elif self.alg in ["ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"]: _pre, _post = self.alg.split("+") klen = int(_post[1:4]) kek = ecdh_derive_key(curve, eprivk, key, apu, apv, _post, klen) encrypted_key = aes_wrap_key(kek, cek) else: raise Exception("Unsupported algorithm %s" % self.alg) return cek, encrypted_key, iv, params
def store_key(self, key): kb = KeyBundle() kb.do_keys([key]) # Store key with thumbprint as key key_thumbprint = b64e(kb.keys()[0].thumbprint('SHA-256')).decode( 'utf8') self.thumbprint2key[key_thumbprint] = key return key_thumbprint
def test_thumbprint_7638_example(): key = RSAKey( n="0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", e="AQAB", alg="RS256", kid="2011-04-29", ) thumbprint = key.thumbprint("SHA-256") assert b64e(thumbprint) == b"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
def test_thumbprint_7638_example(): key = RSAKey( n= '0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw', e='AQAB', alg='RS256', kid='2011-04-29') thumbprint = key.thumbprint('SHA-256') assert b64e(thumbprint) == b'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs'
def add_symmetric(self, issuer, key, usage=None): if issuer not in self.issuer_keys: self.issuer_keys[issuer] = [] _key = b64e(as_bytes(key)) if usage is None: self.issuer_keys[issuer].append(self.keybundle_cls([{"kty": "oct", "k": _key}])) else: for use in usage: self.issuer_keys[issuer].append(self.keybundle_cls([{"kty": "oct", "k": _key, "use": use}]))
def sign_compact(self, keys=None): """ Produce a JWS using the JWS Compact Serialization :param keys: A dictionary of keys :return: """ try: _alg = self["alg"] except KeyError: self["alg"] = _alg = "none" else: if not _alg: self["alg"] = _alg = "none" if keys: keys = self._pick_keys(keys, use="sig", alg=_alg) else: keys = self._pick_keys(self._get_keys(), use="sig", alg=_alg) xargs = {} if keys: key = keys[0] if key.kid: xargs = {"kid": key.kid} elif not _alg or _alg.lower() == "none": key = None else: if "kid" in self: raise NoSuitableSigningKeys( "No key for algorithm: %s with kid: %s" % (_alg, self["kid"])) else: raise NoSuitableSigningKeys("No key for algorithm: %s" % _alg) enc_head = self._encoded_header(xargs) enc_payload = self._encoded_payload() # Signing with alg == "none" if not _alg or _alg.lower() == "none": return enc_head + b"." + enc_payload + b"." # All other cases try: _signer = SIGNER_ALGS[_alg] except KeyError: raise UnknownAlgorithm(_alg) _input = b".".join([enc_head, enc_payload]) sig = _signer.sign(_input, key.get_key(alg=_alg, private=True)) logger.debug("Signed message using key with kid=%s" % key.kid) return b".".join([enc_head, enc_payload, b64e(sig)])
def encrypt(self, key, iv="", cek=""): """ :param key: Shared symmetric key :param iv: :param cek: :return: """ _msg = self.msg b64_header = self._encoded_header() # content master key 256 bit if not cek: cek = os.urandom(32) jek = aes_wrap_key(intarr2str(key), cek) auth_data = b64_header _enc = self["enc"] if _enc == "A256GCM": if not iv: iv = os.urandom(12) # 96 bits ctxt, tag = gcm_encrypt(cek, iv, _msg, auth_data) elif _enc.startswith("A128CBC-") or _enc.startswith("A256CBC-"): assert _enc in SUPPORTED["enc"] ealg, hashf = _enc.split("-") if not iv: if ealg == "A128CBC": iv = os.urandom(16) # 128 bits else: # ealg == "A256CBC" iv = os.urandom(32) # 256 bits ctxt, tag = ciphertext_and_authentication_tag(cek, _msg, auth_data, iv, algo="A128CBC-HS256") else: raise NotSupportedAlgorithm(_enc) res = b".".join([b64_header, b64e(jek), b64e(iv), b64e(ctxt), b64e(tag)]) return res
def test_encryption_key(): sk = SYMKey(key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') _enc = sk.encryption_key(alg='A128KW') _v = as_unicode(b64e(_enc)) assert _v == 'xCo9VhtommCTGMWi-RyWBw' sk = SYMKey(key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') _enc = sk.encryption_key(alg='A192KW') _v = as_unicode(b64e(_enc)) assert _v == 'xCo9VhtommCTGMWi-RyWB14GQqHAGC86' sk = SYMKey(key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') _enc = sk.encryption_key(alg='A256KW') _v = as_unicode(b64e(_enc)) assert _v == 'xCo9VhtommCTGMWi-RyWB14GQqHAGC86vweU_Pi62X8' ek = sha256_digest( 'YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY' )[:16] assert as_unicode(b64e(ek)) == 'yf_UUkAFZ8Pn_prxPPgu9w' sk = SYMKey( key= 'YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY' ) _enc = sk.encryption_key(alg='A128KW') _v = as_unicode(b64e(_enc)) assert _v == as_unicode(b64e(ek))
def test_encryption_key(): sk = SYMKey(key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') _enc = sk.encryption_key(alg='A128KW') _v = as_unicode(b64e(_enc)) assert _v == 'xCo9VhtommCTGMWi-RyWBw' sk = SYMKey(key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') _enc = sk.encryption_key(alg='A192KW') _v = as_unicode(b64e(_enc)) assert _v == 'xCo9VhtommCTGMWi-RyWB14GQqHAGC86' sk = SYMKey(key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') _enc = sk.encryption_key(alg='A256KW') _v = as_unicode(b64e(_enc)) assert _v == 'xCo9VhtommCTGMWi-RyWB14GQqHAGC86vweU_Pi62X8' ek = sha256_digest( 'YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY')[ :16] assert as_unicode(b64e(ek)) == 'yf_UUkAFZ8Pn_prxPPgu9w' sk = SYMKey( key='YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY') _enc = sk.encryption_key(alg='A128KW') _v = as_unicode(b64e(_enc)) assert _v == as_unicode(b64e(ek))
def test_sign_json_hs256(): payload = "Please take a moment to register today" keys = [SYMKey(key=jwkest.intarr2bin(HMAC_KEY))] _jws = JWS(payload, alg="HS256") _sig = {'alg': 'HS256'} _jwt = _jws.sign_json(per_signature_head=[_sig], keys=keys, alg='HS256') _jwt_sig = "%s.%s.%s" % (_jwt['signatures'][0]['header'], b64e(_jwt['payload']), _jwt['signatures'][0]['signature']) info = _jws.verify_compact(_jwt_sig, keys) assert info == payload
def add_symmetric(self, issuer, key, usage=None): if issuer not in self.issuer_keys: self.issuer_keys[issuer] = [] _key = b64e(as_bytes(key)) if usage is None: self.issuer_keys[issuer].append( self.keybundle_cls([{"kty": "oct", "k": _key}])) else: for use in usage: self.issuer_keys[issuer].append( self.keybundle_cls([{"kty": "oct", "k": _key, "use": use}]))
def test_sign_json_hs256(): payload = "Please take a moment to register today" keys = [SYMKey(key=jwkest.intarr2bin(HMAC_KEY))] _jws = JWS(payload, alg="HS256") _sig = { 'alg': 'HS256' } _jwt = _jws.sign_json(per_signature_head=[_sig], keys=keys, alg='HS256') _jwt_sig = "%s.%s.%s" % ( _jwt['signatures'][0]['header'], b64e(_jwt['payload']), _jwt['signatures'][0]['signature'] ) info = _jws.verify_compact(_jwt_sig, keys) assert info == payload
def modified_idtoken_hint(oper, arg): """ Context: EndSession Action: Sets the 'id_token_hint' argument in a end_session request. The value of the argument is a incorrect signed JWT. Example: "create_idtoken_hint_other_issuer": null """ res = get_signed_id_tokens(oper.conv) if res: _jws = jws_factory(res[-1]) header = as_unicode(b64e(as_bytes(json.dumps({'alg': 'none'})))) oper.req_args["id_token_hint"] = '.'.join( [header, as_unicode(_jws.jwt.b64part[1]), ''])
def sign_compact(self, keys=None): """ Produce a JWS using the JWS Compact Serialization :param keys: A dictionary of keys :return: """ _alg = self["alg"] if keys: keys = self._pick_keys(keys, use="sig", alg=_alg) else: keys = self._pick_keys(self._get_keys(), use="sig", alg=_alg) xargs = {} if keys: key = keys[0] if key.kid: xargs = {"kid": key.kid} elif _alg == "none": key = None elif _alg: raise NoSuitableSigningKeys(_alg) else: raise NoSuitableSigningKeys("None") enc_head = self._encoded_header(xargs) enc_payload = self._encoded_payload() # Signing with alg == "none" if not _alg or _alg.lower() == "none": return enc_head + b"." + enc_payload + b"." # All other cases try: _signer = SIGNER_ALGS[_alg] except KeyError: raise UnknownAlgorithm(_alg) _input = b".".join([enc_head, enc_payload]) sig = _signer.sign(_input, key.get_key(alg=_alg, private=True)) logger.debug("Signed message using key with kid=%s" % key.kid) return b".".join([enc_head, enc_payload, b64e(sig)])
def verify_code_challenge( code_verifier, code_challenge, code_challenge_method="S256" ): """ Verify a PKCE (RFC7636) code challenge. :param code_verifier: The origin :param code_challenge: The transformed verifier used as challenge :return: """ _h = CC_METHOD[code_challenge_method](code_verifier.encode("ascii")).digest() _cc = b64e(_h) if _cc.decode("ascii") != code_challenge: logger.error("PCKE Code Challenge check failed") err = TokenErrorResponse( error="invalid_request", error_description="PCKE check failed" ) return Response(err.to_json(), content="application/json", status_code=401) return True
def verify_code_challenge(code_verifier, code_challenge, code_challenge_method='S256'): """ Verify a PKCE (RFC7636) code challenge :param code_verifier: The origin :param code_challenge: The transformed verifier used as challenge :return: """ _h = CC_METHOD[code_challenge_method]( code_verifier.encode()).hexdigest() _cc = b64e(_h.encode()) if _cc.decode() != code_challenge: logger.error('PCKE Code Challenge check failed') err = TokenErrorResponse(error="invalid_request", error_description="PCKE check failed") return Response(err.to_json(), content="application/json", status="401 Unauthorized") return True
def test_client_secret_jwt(self, client): client.token_endpoint = "https://example.com/token" client.provider_info = {'issuer': 'https://example.com/', 'token_endpoint': "https://example.com/token"} csj = ClientSecretJWT(client) cis = AccessTokenRequest() csj.construct(cis, algorithm="HS256", authn_endpoint='userinfo') assert cis["client_assertion_type"] == JWT_BEARER assert "client_assertion" in cis cas = cis["client_assertion"] _jwt = JWT().unpack(cas) jso = _jwt.payload() assert _eq(jso.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert _jwt.headers == {'alg': 'HS256'} _rj = JWS() info = _rj.verify_compact( cas, [SYMKey(k=b64e(as_bytes(client.client_secret)))]) assert _eq(info.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert info['aud'] == [client.provider_info['issuer']]
def add_symmetric(self, owner, key, usage=None): """ Add a symmetric key. This is done by wrapping it in a key bundle cloak since KeyJar does not handle keys directly but only through key bundles. :param issuer: Owner of the key :param key: The key :param usage: What the key can be used for signing/signature verification (sig) and/or encryption/decryption (enc) """ if owner not in self.issuer_keys: self.issuer_keys[owner] = [] _key = b64e(as_bytes(key)) if usage is None: self.issuer_keys[owner].append( self.keybundle_cls([{"kty": "oct", "k": _key}])) else: for use in usage: self.issuer_keys[owner].append( self.keybundle_cls([{"kty": "oct", "k": _key, "use": use}]))
def serialize(self, private=True): res = self.common() res["k"] = as_unicode(b64e(bytes(self.key))) return res
def add_kid(self): self.kid = b64e(self.thumbprint('SHA-256')).decode('utf8')
def test_thumbprint(): keyl = KEYS() keyl.load_dict(JWKS) for key in keyl: txt = key.thumbprint("SHA-256") assert b64e(txt) in EXPECTED
from jwkest import b64e import json # A specific JWT is a sequence of URL-safe parts separated by a period '.' character. # All JWT parts are always base64 encoded. A JWT can have different claims (similar to attributes in LDAP world): # - iss (Issuer) who issued the token # - sub (Subject) who is described by the token # - aud (Audience) who the token intended for # - exp (Expiration Time) # - nbf (Not Before) # - iat (Issued At) # - jti (JWT Id) # # An unsecured JWT is the simplest JWT possible. It can be created as follow: # - create a JOSE header (the simplest possible has only the "alg" specification set to "none") # - create a claims set (which means create a JSON object) # - base64 encode the claims set (message and header) and encode them in UTF-8 # - create the JWS by joining the claims set with the period '.' character as a separator header = {"alg": "none"} htxt = json.dumps(header) hdr = b64e(htxt) claims_set = {"foo": "bar"} txt = json.dumps(claims_set) msg = b64e(txt) jws = ".".join([hdr, msg, ""]) print "jwt:", jws
def test_jwe_09_a3(): #Example JWE using AES Key Wrap and AES GCM msg = b'Live long and prosper.' header = b'{"alg":"A128KW","enc":"A128CBC-HS256"}' b64_header = b64e(header) assert b64_header == b'eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0' cek = intarr2bytes([ 4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106, 206, 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156, 44, 207 ]) shared_key = [ 25, 172, 32, 130, 225, 114, 26, 181, 138, 106, 254, 192, 95, 133, 74, 82 ] jek = aes_wrap_key(intarr2bytes(shared_key), cek) assert to_intarr(jek) == [ 232, 160, 123, 211, 183, 76, 245, 132, 200, 128, 123, 75, 190, 216, 22, 67, 201, 138, 193, 186, 9, 91, 122, 31, 246, 90, 28, 139, 57, 3, 76, 124, 193, 11, 98, 37, 173, 61, 104, 57 ] b64_jek = b64e(jek) assert b64_jek == b'6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ' iv = intarr2bytes([ 3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101 ]) b64_iv = b64e(iv) assert b64_iv == b'AxY8DCtDaGlsbGljb3RoZQ' aadp = b64_header assert to_intarr(aadp) == [ 101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 66, 77, 84, 73, 52, 83, 49, 99, 105, 76, 67, 74, 108, 98, 109, 77, 105, 79, 105, 74, 66, 77, 84, 73, 52, 81, 48, 74, 68, 76, 85, 104, 84, 77, 106, 85, 50, 73, 110, 48 ] _jwe = JWe() ctxt, tag, key = _jwe.enc_setup("A128CBC-HS256", msg, aadp, cek, iv=iv) print(to_intarr(ctxt)) assert to_intarr(ctxt) == [ 40, 57, 83, 181, 119, 33, 133, 148, 198, 185, 243, 24, 152, 230, 6, 75, 129, 223, 127, 19, 210, 82, 183, 230, 168, 33, 215, 104, 143, 112, 56, 102 ] assert to_intarr(tag) == [ 83, 73, 191, 98, 104, 205, 211, 128, 201, 189, 199, 133, 32, 38, 194, 85 ] enc_cipher_text = b64e(ctxt) assert enc_cipher_text == b'KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY' enc_authn_tag = b64e(tag) assert enc_authn_tag == b'U0m_YmjN04DJvceFICbCVQ'
def test_b64_encode_decode(): data = "abcd".encode("utf-8") assert b64d(b64e(data)) == data
def serialize(self): res = self.common() res["k"] = b64e(bytes(self.key)) return res
def test_a_1_1a(): header = '{"typ":"JWT",\r\n "alg":"HS256"}' val = b64e(header) assert val == "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"
def serialize(self): self.k = b64e(str(self.key))
def test_jwe_09_a1(): # RSAES OAEP and AES GCM msg = b"The true sign of intelligence is not knowledge but imagination." # A.1.1 header = b'{"alg":"RSA-OAEP","enc":"A256GCM"}' b64_header = b64e(header) # A.1.2 assert b64_header == b"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ" # A.1.3 cek = intarr2long([ 177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154, 212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122, 234, 64, 252 ]) # A.1.4 Key Encryption enc_key = [ 56, 163, 154, 192, 58, 53, 222, 4, 105, 218, 136, 218, 29, 94, 203, 22, 150, 92, 129, 94, 211, 232, 53, 89, 41, 60, 138, 56, 196, 216, 82, 98, 168, 76, 37, 73, 70, 7, 36, 8, 191, 100, 136, 196, 244, 220, 145, 158, 138, 155, 4, 117, 141, 230, 199, 247, 173, 45, 182, 214, 74, 177, 107, 211, 153, 11, 205, 196, 171, 226, 162, 128, 171, 182, 13, 237, 239, 99, 193, 4, 91, 219, 121, 223, 107, 167, 61, 119, 228, 173, 156, 137, 134, 200, 80, 219, 74, 253, 56, 185, 91, 177, 34, 158, 89, 154, 205, 96, 55, 18, 138, 43, 96, 218, 215, 128, 124, 75, 138, 243, 85, 25, 109, 117, 140, 26, 155, 249, 67, 167, 149, 231, 100, 6, 41, 65, 214, 251, 232, 87, 72, 40, 182, 149, 154, 168, 31, 193, 126, 215, 89, 28, 111, 219, 125, 182, 139, 235, 195, 197, 23, 234, 55, 58, 63, 180, 68, 202, 206, 149, 75, 205, 248, 176, 67, 39, 178, 60, 98, 193, 32, 238, 122, 96, 158, 222, 57, 183, 111, 210, 55, 188, 215, 206, 180, 166, 150, 166, 106, 250, 55, 229, 72, 40, 69, 214, 216, 104, 23, 40, 135, 212, 28, 127, 41, 80, 175, 174, 168, 115, 171, 197, 89, 116, 92, 103, 246, 83, 216, 182, 176, 84, 37, 147, 35, 45, 219, 172, 99, 226, 233, 73, 37, 124, 42, 72, 49, 242, 35, 127, 184, 134, 117, 114, 135, 206 ] b64_ejek = b'ApfOLCaDbqs_JXPYy2I937v_xmrzj-Iss1mG6NAHmeJViM6j2l0MHvfseIdHVyU2BIoGVu9ohvkkWiRq5DL2jYZTPA9TAdwq3FUIVyoH-Pedf6elHIVFi2KGDEspYMtQARMMSBcS7pslx6flh1Cfh3GBKysztVMEhZ_maFkm4PYVCsJsvq6Ct3fg2CJPOs0X1DHuxZKoIGIqcbeK4XEO5a0h5TAuJObKdfO0dKwfNSSbpu5sFrpRFwV2FTTYoqF4zI46N9-_hMIznlEpftRXhScEJuZ9HG8C8CHB1WRZ_J48PleqdhF4o7fB5J1wFqUXBtbtuGJ_A2Xe6AEhrlzCOw' iv = intarr2long([227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219]) aadp = b64_header + b'.' + b64_ejek gcm = AES_GCM(cek) ctxt, tag = gcm.encrypt(iv, msg, aadp) _va = to_intarr(ctxt) assert _va == [ 229, 236, 166, 241, 53, 191, 115, 196, 174, 43, 73, 109, 39, 122, 233, 96, 140, 206, 120, 52, 51, 237, 48, 11, 190, 219, 186, 80, 111, 104, 50, 142, 47, 167, 59, 61, 181, 127, 196, 21, 40, 82, 242, 32, 123, 143, 168, 226, 73, 216, 176, 144, 138, 247, 106, 60, 16, 205, 160, 109, 64, 63, 192 ] assert long2intarr(tag) == [ 130, 17, 32, 198, 120, 167, 144, 113, 0, 50, 158, 49, 102, 208, 118, 152 ] tag = long2hexseq(tag) iv = long2hexseq(iv) res = b".".join([b64_header, b64_ejek, b64e(iv), b64e(ctxt), b64e(tag)]) #print(res.split(b'.')) expected = b'.'.join([ b'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ', b'ApfOLCaDbqs_JXPYy2I937v_xmrzj-Iss1mG6NAHmeJViM6j2l0MHvfseIdHVyU2BIoGVu9ohvkkWiRq5DL2jYZTPA9TAdwq3FUIVyoH-Pedf6elHIVFi2KGDEspYMtQARMMSBcS7pslx6flh1Cfh3GBKysztVMEhZ_maFkm4PYVCsJsvq6Ct3fg2CJPOs0X1DHuxZKoIGIqcbeK4XEO5a0h5TAuJObKdfO0dKwfNSSbpu5sFrpRFwV2FTTYoqF4zI46N9-_hMIznlEpftRXhScEJuZ9HG8C8CHB1WRZ_J48PleqdhF4o7fB5J1wFqUXBtbtuGJ_A2Xe6AEhrlzCOw', b'48V1_ALb6US04U3b', b'5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A', b'ghEgxninkHEAMp4xZtB2mA' ]) assert res == expected
def test_jwe_09_a1(): # RSAES OAEP and AES GCM msg = "The true sign of intelligence is not knowledge but imagination." # A.1.1 header = '{"alg":"RSA-OAEP","enc":"A256GCM"}' b64_header = b64e(header) # A.1.2 assert b64_header == "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ" # A.1.3 cek = intarr2str([177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154, 212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122, 234, 64, 252]) # A.1.4 Key Encryption enc_key = [ 56, 163, 154, 192, 58, 53, 222, 4, 105, 218, 136, 218, 29, 94, 203, 22, 150, 92, 129, 94, 211, 232, 53, 89, 41, 60, 138, 56, 196, 216, 82, 98, 168, 76, 37, 73, 70, 7, 36, 8, 191, 100, 136, 196, 244, 220, 145, 158, 138, 155, 4, 117, 141, 230, 199, 247, 173, 45, 182, 214, 74, 177, 107, 211, 153, 11, 205, 196, 171, 226, 162, 128, 171, 182, 13, 237, 239, 99, 193, 4, 91, 219, 121, 223, 107, 167, 61, 119, 228, 173, 156, 137, 134, 200, 80, 219, 74, 253, 56, 185, 91, 177, 34, 158, 89, 154, 205, 96, 55, 18, 138, 43, 96, 218, 215, 128, 124, 75, 138, 243, 85, 25, 109, 117, 140, 26, 155, 249, 67, 167, 149, 231, 100, 6, 41, 65, 214, 251, 232, 87, 72, 40, 182, 149, 154, 168, 31, 193, 126, 215, 89, 28, 111, 219, 125, 182, 139, 235, 195, 197, 23, 234, 55, 58, 63, 180, 68, 202, 206, 149, 75, 205, 248, 176, 67, 39, 178, 60, 98, 193, 32, 238, 122, 96, 158, 222, 57, 183, 111, 210, 55, 188, 215, 206, 180, 166, 150, 166, 106, 250, 55, 229, 72, 40, 69, 214, 216, 104, 23, 40, 135, 212, 28, 127, 41, 80, 175, 174, 168, 115, 171, 197, 89, 116, 92, 103, 246, 83, 216, 182, 176, 84, 37, 147, 35, 45, 219, 172, 99, 226, 233, 73, 37, 124, 42, 72, 49, 242, 35, 127, 184, 134, 117, 114, 135, 206] b64_ejek = "ApfOLCaDbqs_JXPYy2I937v_xmrzj-Iss1mG6NAHmeJViM6j2l0MHvfseIdHVyU2BIoGVu9ohvkkWiRq5DL2jYZTPA9TAdwq3FUIVyoH-Pedf6elHIVFi2KGDEspYMtQARMMSBcS7pslx6flh1Cfh3GBKysztVMEhZ_maFkm4PYVCsJsvq6Ct3fg2CJPOs0X1DHuxZKoIGIqcbeK4XEO5a0h5TAuJObKdfO0dKwfNSSbpu5sFrpRFwV2FTTYoqF4zI46N9-_hMIznlEpftRXhScEJuZ9HG8C8CHB1WRZ_J48PleqdhF4o7fB5J1wFqUXBtbtuGJ_A2Xe6AEhrlzCOw" iv = intarr2str([227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219]) aadp = b64_header + b'.' + b64_ejek ctxt, tag = gcm_encrypt(cek, iv, msg, aadp) _va = [ord(c) for c in ctxt] assert _va == [229, 236, 166, 241, 53, 191, 115, 196, 174, 43, 73, 109, 39, 122, 233, 96, 140, 206, 120, 52, 51, 237, 48, 11, 190, 219, 186, 80, 111, 104, 50, 142, 47, 167, 59, 61, 181, 127, 196, 21, 40, 82, 242, 32, 123, 143, 168, 226, 73, 216, 176, 144, 138, 247, 106, 60, 16, 205, 160, 109, 64, 63, 192] assert [ord(c) for c in tag] == [130, 17, 32, 198, 120, 167, 144, 113, 0, 50, 158, 49, 102, 208, 118, 152] res = b".".join([b64_header, b64_ejek, b64e(iv), b64e(ctxt), b64e(tag)]) assert res == "".join([ "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.", "ApfOLCaDbqs_JXPYy2I937v_xmrzj-Iss1mG6NAHmeJViM6j2l0MHvfseIdHVyU2", "BIoGVu9ohvkkWiRq5DL2jYZTPA9TAdwq3FUIVyoH-Pedf6elHIVFi2KGDEspYMtQ", "ARMMSBcS7pslx6flh1Cfh3GBKysztVMEhZ_maFkm4PYVCsJsvq6Ct3fg2CJPOs0X", "1DHuxZKoIGIqcbeK4XEO5a0h5TAuJObKdfO0dKwfNSSbpu5sFrpRFwV2FTTYoqF4", "zI46N9-_hMIznlEpftRXhScEJuZ9HG8C8CHB1WRZ_J48PleqdhF4o7fB5J1wFqUX", "BtbtuGJ_A2Xe6AEhrlzCOw.", "48V1_ALb6US04U3b.", "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji", "SdiwkIr3ajwQzaBtQD_A.", "ghEgxninkHEAMp4xZtB2mA"])
def _encoded_header(self, extra=None): return b64e(json.dumps(self._header(extra), separators=(",", ":")))
def test_thumbprint(): keyl = KEYS() keyl.load_dict(JWKS) for key in keyl: txt = key.thumbprint('SHA-256') assert b64e(txt) in EXPECTED
def enc_setup(self, msg, auth_data, key=None, **kwargs): encrypted_key = "" self.msg = msg self.auth_data = auth_data # Generate the input parameters try: apu = b64d(kwargs["apu"]) except KeyError: apu = Random.get_random_bytes(16) try: apv = b64d(kwargs["apv"]) except KeyError: apv = Random.get_random_bytes(16) # Handle Local Key and Ephemeral Public Key if not key: raise Exception("EC Key Required for ECDH-ES JWE Encrpytion Setup") # Generate an ephemeral key pair if none is given curve = NISTEllipticCurve.by_name(key.crv) if "epk" in kwargs: epk = kwargs["epk"] if isinstance(kwargs["epk"], ECKey) else ECKey(kwargs["epk"]) else: raise Exception( "Ephemeral Public Key (EPK) Required for ECDH-ES JWE " "Encryption Setup") params = { "apu": b64e(apu), "apv": b64e(apv), "epk": epk.serialize(False) } cek = iv = None if 'cek' in kwargs and kwargs['cek']: cek = kwargs['cek'] if 'iv' in kwargs and kwargs['iv']: iv = kwargs['iv'] cek, iv = self._generate_key_and_iv(self.enc, cek=cek, iv=iv) if self.alg == "ECDH-ES": try: dk_len = KEYLEN[self.enc] except KeyError: raise Exception( "Unknown key length for algorithm %s" % self.enc) cek = ecdh_derive_key(curve, epk.d, (key.x, key.y), apu, apv, str(self.enc).encode(), dk_len) elif self.alg in ["ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"]: _pre, _post = self.alg.split("+") klen = int(_post[1:4]) kek = ecdh_derive_key(curve, epk.d, (key.x, key.y), apu, apv, str(_post).encode(), klen) encrypted_key = aes_wrap_key(kek, cek) else: raise Exception("Unsupported algorithm %s" % self.alg) return cek, encrypted_key, iv, params, epk
def test_a_1_1a(): header = b'{"typ":"JWT",\r\n "alg":"HS256"}' val = b64e(header) assert val == b"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"
def _encoded_payload(self): if isinstance(self.msg, basestring): return b64e(self.msg) else: return b64e(json.dumps(self.msg, separators=(",", ":")))