Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

100 строки
3.2 KiB

  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright 2017 Gehirn Inc.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. import json
  17. from typing import (
  18. AbstractSet,
  19. Tuple,
  20. )
  21. from .exceptions import (
  22. JWSEncodeError,
  23. JWSDecodeError,
  24. )
  25. from .jwa import (
  26. supported_signing_algorithms,
  27. AbstractSigningAlgorithm,
  28. )
  29. from .jwk import AbstractJWKBase
  30. from .utils import (
  31. b64encode,
  32. b64decode,
  33. )
  34. __all__ = ['JWS']
  35. class JWS:
  36. def __init__(self) -> None:
  37. self._supported_algs = supported_signing_algorithms()
  38. def _retrieve_alg(self, alg: str) -> AbstractSigningAlgorithm:
  39. try:
  40. return self._supported_algs[alg]
  41. except KeyError as why:
  42. raise JWSDecodeError('Unsupported signing algorithm.')
  43. def encode(self, message: bytes, key: AbstractJWKBase = None, alg='HS256',
  44. optional_headers: dict = None) -> str:
  45. if alg not in self._supported_algs: # pragma: no cover
  46. raise JWSEncodeError('unsupported algorithm: {}'.format(alg))
  47. alg_impl = self._retrieve_alg(alg)
  48. header = optional_headers and optional_headers.copy() or {}
  49. header['alg'] = alg
  50. header_b64 = b64encode(
  51. json.dumps(header, separators=(',', ':')).encode('ascii'))
  52. message_b64 = b64encode(message)
  53. signing_message = header_b64 + '.' + message_b64
  54. signature = alg_impl.sign(signing_message.encode('ascii'), key)
  55. signature_b64 = b64encode(signature)
  56. return signing_message + '.' + signature_b64
  57. def _decode_segments(self, message: str) -> Tuple[dict, bytes, bytes, str]:
  58. try:
  59. signing_message, signature_b64 = message.rsplit('.', 1)
  60. header_b64, message_b64 = signing_message.split('.')
  61. except ValueError:
  62. raise JWSDecodeError('malformed JWS payload')
  63. header = json.loads(b64decode(header_b64).decode('ascii'))
  64. message_bin = b64decode(message_b64)
  65. signature = b64decode(signature_b64)
  66. return header, message_bin, signature, signing_message
  67. def decode(self, message: str, key: AbstractJWKBase = None,
  68. do_verify=True, algorithms: AbstractSet[str]=None) -> bytes:
  69. if algorithms is None:
  70. algorithms = set(supported_signing_algorithms().keys())
  71. header, message_bin, signature, signing_message = \
  72. self._decode_segments(message)
  73. alg_value = header['alg']
  74. if alg_value not in algorithms:
  75. raise JWSDecodeError('Unsupported signing algorithm.')
  76. alg_impl = self._retrieve_alg(alg_value)
  77. if do_verify and not alg_impl.verify(
  78. signing_message.encode('ascii'), key, signature):
  79. raise JWSDecodeError('JWS passed could not be validated')
  80. return message_bin