def test_signed_compact_create_split(self): payload = '{"iss":"joe"}' signature = _jwt_format.decode_signature( b'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk') unsigned_compact = _jwt_format.create_unsigned_compact( 'RS256', 'JWT', None, payload) signed_compact = _jwt_format.create_signed_compact(unsigned_compact, signature) un_comp, hdr, pay, sig = _jwt_format.split_signed_compact(signed_compact) self.assertEqual( unsigned_compact, b'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJqb2UifQ') self.assertEqual( signed_compact, 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.' 'eyJpc3MiOiJqb2UifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk') self.assertEqual(un_comp, unsigned_compact) self.assertEqual(sig, signature) self.assertEqual(hdr, '{"alg":"RS256","typ":"JWT"}') header = _jwt_format.json_loads(hdr) _jwt_format.validate_header(header, 'RS256') self.assertEqual(pay, payload) self.assertEqual(_jwt_format.get_type_header(header), 'JWT')
def test_signed_compact_create_split_with_kid(self): payload = '{"iss":"joe"}' signature = _jwt_format.decode_signature( b'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk') unsigned_compact = _jwt_format.create_unsigned_compact( 'RS256', None, 'AZxkm2U', payload) signed_compact = _jwt_format.create_signed_compact(unsigned_compact, signature) un_comp, hdr, pay, sig = _jwt_format.split_signed_compact(signed_compact) self.assertEqual( unsigned_compact, b'eyJraWQiOiJBWnhrbTJVIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJqb2UifQ') self.assertEqual( signed_compact, 'eyJraWQiOiJBWnhrbTJVIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJqb2UifQ' '.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk') self.assertEqual(un_comp, unsigned_compact) self.assertEqual(sig, signature) self.assertEqual(hdr, '{"kid":"AZxkm2U","alg":"RS256"}') header = _jwt_format.json_loads(hdr) _jwt_format.validate_header(header, 'RS256') self.assertEqual(pay, payload) self.assertIsNone(_jwt_format.get_type_header(header))
def compute_mac_and_encode_with_kid(self, raw_jwt: _raw_jwt.RawJwt, kid: Optional[str]) -> str: """Computes a MAC and encodes the token. Args: raw_jwt: The RawJwt token to be MACed and encoded. kid: Optional "kid" header value. It is set by the wrapper for keys with output prefix TINK, and it is None for output prefix RAW. Returns: The MACed token encoded in the JWS compact serialization format. Raises: tink.TinkError if the operation fails. """ if self._custom_kid is not None: if kid is not None: raise _jwt_error.JwtInvalidError( 'custom_kid must not be set for keys with output prefix type TINK' ) kid = self._custom_kid unsigned = _jwt_format.create_unsigned_compact(self._algorithm, kid, raw_jwt) return _jwt_format.create_signed_compact(unsigned, self._compute_mac(unsigned))
def compute_mac_and_encode(self, raw_jwt: _raw_jwt.RawJwt) -> Text: """Computes a MAC and encodes the token.""" unsigned = _jwt_format.create_unsigned_compact(self._algorithm, raw_jwt.json_payload()) return _jwt_format.create_signed_compact(unsigned, self._compute_mac(unsigned))
def gen_compact(json_header: str, json_payload: str, raw_sign) -> str: unsigned_compact = (_jwt_format.encode_header(json_header) + b'.' + _jwt_format.encode_payload(json_payload)) signature = raw_sign.sign(unsigned_compact) return _jwt_format.create_signed_compact(unsigned_compact, signature)
def test_weird_tokens_with_valid_signatures(self): handle = tink.new_keyset_handle(jwt.raw_jwt_es256_template()) sign = handle.primitive(jwt.JwtPublicKeySign) # Get the internal PublicKeySign primitive to create valid signatures. wrapped = cast(_jwt_signature_wrappers._WrappedJwtPublicKeySign, sign) raw_sign = cast( _jwt_signature_key_manager._JwtPublicKeySign, wrapped._primitive_set.primary().primitive)._public_key_sign verify = handle.public_keyset_handle().primitive( jwt.JwtPublicKeyVerify) validator = jwt.new_validator(expected_issuer='issuer', allow_missing_expiration=True) # Normal token. valid = gen_compact('{"alg":"ES256"}', '{"iss":"issuer"}', raw_sign) verified_jwt = verify.verify_and_decode(valid, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with unknown header is valid. unknown_header = gen_compact( '{"alg":"ES256","unknown_header":"abc"} \n ', '{"iss":"issuer" }', raw_sign) verified_jwt = verify.verify_and_decode(unknown_header, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with unknown kid is valid, since primitives with output prefix type # RAW ignore kid headers. unknown_header = gen_compact('{"alg":"ES256","kid":"unknown"} \n ', '{"iss":"issuer" }', raw_sign) verified_jwt = verify.verify_and_decode(unknown_header, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with invalid alg header alg_invalid = gen_compact('{"alg":"ES384"}', '{"iss":"issuer"}', raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode(alg_invalid, validator) # Token with empty header empty_header = gen_compact('{}', '{"iss":"issuer"}', raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode(empty_header, validator) # Token header is not valid JSON header_invalid = gen_compact('{"alg":"ES256"', '{"iss":"issuer"}', raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode(header_invalid, validator) # Token payload is not valid JSON payload_invalid = gen_compact('{"alg":"ES256"}', '{"iss":"issuer"', raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode(payload_invalid, validator) # Token with whitespace in header JSON string is valid. whitespace_in_header = gen_compact(' {"alg": \n "ES256"} \n ', '{"iss":"issuer" }', raw_sign) verified_jwt = verify.verify_and_decode(whitespace_in_header, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with whitespace in payload JSON string is valid. whitespace_in_payload = gen_compact('{"alg":"ES256"}', ' {"iss": \n"issuer" } \n', raw_sign) verified_jwt = verify.verify_and_decode(whitespace_in_payload, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with whitespace in base64-encoded header is invalid. with_whitespace = (_jwt_format.encode_header('{"alg":"ES256"}') + b' .' + _jwt_format.encode_payload('{"iss":"issuer"}')) token_with_whitespace = _jwt_format.create_signed_compact( with_whitespace, raw_sign.sign(with_whitespace)) with self.assertRaises(tink.TinkError): verify.verify_and_decode(token_with_whitespace, validator) # Token with invalid character is invalid. with_invalid_char = (_jwt_format.encode_header('{"alg":"ES256"}') + b'.?' + _jwt_format.encode_payload('{"iss":"issuer"}')) token_with_invalid_char = _jwt_format.create_signed_compact( with_invalid_char, raw_sign.sign(with_invalid_char)) with self.assertRaises(tink.TinkError): verify.verify_and_decode(token_with_invalid_char, validator) # Token with additional '.' is invalid. with_dot = (_jwt_format.encode_header('{"alg":"ES256"}') + b'.' + _jwt_format.encode_payload('{"iss":"issuer"}') + b'.') token_with_dot = _jwt_format.create_signed_compact( with_dot, raw_sign.sign(with_dot)) with self.assertRaises(tink.TinkError): verify.verify_and_decode(token_with_dot, validator) # num_recursions has been chosen such that parsing of this token fails # in all languages. We want to make sure that the algorithm does not # hang or crash in this case, but only returns a parsing error. num_recursions = 10000 rec_payload = ('{"a":' * num_recursions) + '""' + ('}' * num_recursions) rec_token = gen_compact('{"alg":"ES256"}', rec_payload, raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode( rec_token, validator=jwt.new_validator(allow_missing_expiration=True)) # test wrong types with self.assertRaises(tink.TinkError): verify.verify_and_decode(cast(str, None), validator) with self.assertRaises(tink.TinkError): verify.verify_and_decode(cast(str, 123), validator) with self.assertRaises(tink.TinkError): valid_bytes = valid.encode('utf8') verify.verify_and_decode(cast(str, valid_bytes), validator)
def test_weird_tokens_with_valid_macs(self): jwt_hmac = create_fixed_jwt_hmac() validator = jwt.new_validator( expected_issuer='joe', allow_missing_expiration=True) cc_mac = _cc_mac() # Normal token. valid_token = gen_token('{"alg":"HS256"}', '{"iss":"joe"}') verified = jwt_hmac.verify_mac_and_decode_with_kid( valid_token, validator, kid=None) self.assertEqual(verified.issuer(), 'joe') # Token with unknown header is valid. token_with_unknown_header = gen_token( '{"unknown_header":"123","alg":"HS256"}', '{"iss":"joe"}') verified2 = jwt_hmac.verify_mac_and_decode_with_kid( token_with_unknown_header, validator, kid=None) self.assertEqual(verified2.issuer(), 'joe') # Token with unknown kid is valid, since primitives with output prefix type # RAW ignore kid headers. token_with_unknown_kid = gen_token('{"kid":"unknown","alg":"HS256"}', '{"iss":"joe"}') verified2 = jwt_hmac.verify_mac_and_decode_with_kid( token_with_unknown_kid, validator, kid=None) self.assertEqual(verified2.issuer(), 'joe') # Token with invalid alg header alg_invalid = gen_token('{"alg":"HS384"}', '{"iss":"joe"}') with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid(alg_invalid, validator, kid=None) # Token with empty header empty_header = gen_token('{}', '{"iss":"joe"}') with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid(empty_header, validator, kid=None) # Token header is not valid JSON header_invalid = gen_token('{"alg":"HS256"', '{"iss":"joe"}') with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( header_invalid, validator, kid=None) # Token payload is not valid JSON payload_invalid = gen_token('{"alg":"HS256"}', '{"iss":"joe"') with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( payload_invalid, validator, kid=None) # Token with whitespace in header JSON string is valid. whitespace_in_header = gen_token(' {"alg": \n "HS256"} \n ', '{"iss":"joe" }') verified_jwt = jwt_hmac.verify_mac_and_decode_with_kid( whitespace_in_header, validator, kid=None) self.assertEqual(verified_jwt.issuer(), 'joe') # Token with whitespace in payload JSON string is valid. whitespace_in_payload = gen_token('{"alg":"HS256"}', ' {"iss": \n"joe" } \n') verified_jwt = jwt_hmac.verify_mac_and_decode_with_kid( whitespace_in_payload, validator, kid=None) self.assertEqual(verified_jwt.issuer(), 'joe') # Token with whitespace in base64-encoded header is invalid. with_whitespace_in_encoding = ( _jwt_format.encode_header('{"alg":"HS256"}') + b' .' + _jwt_format.encode_payload('{"iss":"joe"}')) token_with_whitespace_in_encoding = _jwt_format.create_signed_compact( with_whitespace_in_encoding, cc_mac.compute_mac(with_whitespace_in_encoding)) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( token_with_whitespace_in_encoding, validator, kid=None) # Token with invalid character is invalid. with_invalid_char = ( _jwt_format.encode_header('{"alg":"HS256"}') + b'.?' + _jwt_format.encode_payload('{"iss":"joe"}')) token_with_invalid_char = _jwt_format.create_signed_compact( with_invalid_char, cc_mac.compute_mac(with_invalid_char)) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( token_with_invalid_char, validator, kid=None) # Token with additional '.' is invalid. with_dot = ( _jwt_format.encode_header('{"alg":"HS256"}') + b'.' + _jwt_format.encode_payload('{"iss":"joe"}') + b'.') token_with_dot = _jwt_format.create_signed_compact( with_dot, cc_mac.compute_mac(with_dot)) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( token_with_dot, validator, kid=None) # num_recursions has been chosen such that parsing of this token fails # in all languages. We want to make sure that the algorithm does not # hang or crash in this case, but only returns a parsing error. num_recursions = 10000 rec_payload = ('{"a":' * num_recursions) + '""' + ('}' * num_recursions) rec_token = gen_token('{"alg":"HS256"}', rec_payload) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( rec_token, validator=jwt.new_validator(allow_missing_expiration=True), kid=None) # test wrong types with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( cast(str, None), validator, kid=None) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( cast(str, 123), validator, kid=None) with self.assertRaises(tink.TinkError): valid_token_bytes = valid_token.encode('utf8') jwt_hmac.verify_mac_and_decode_with_kid( cast(str, valid_token_bytes), validator, kid=None)