Files
sourcehub/.venv/Lib/site-packages/joserfc/_rfc7518/oct_key.py
T
2026-09-14 23:22:10 +09:00

108 lines
3.4 KiB
Python

import typing as t
from typing import Any
import secrets
import warnings
from ..errors import SecurityWarning
from ..util import (
to_bytes,
urlsafe_b64decode,
urlsafe_b64encode,
)
from ..registry import KeyParameter
from .._rfc7517.models import SymmetricKey, NativeKeyBinding
from .._rfc7517.types import KeyParameters, DictKey, AnyKey
POSSIBLE_UNSAFE_KEYS = (
b"-----BEGIN ",
b"---- BEGIN ",
b"ssh-rsa ",
b"ssh-dss ",
b"ssh-ed25519 ",
b"ecdsa-sha2-",
)
class OctBinding(NativeKeyBinding):
@classmethod
def convert_raw_key_to_dict(cls, raw_key: bytes, private: bool) -> DictKey:
k = urlsafe_b64encode(raw_key).decode("utf-8")
return {"k": k}
@classmethod
def import_from_dict(cls, value: DictKey) -> bytes:
return urlsafe_b64decode(to_bytes(value["k"]))
@classmethod
def import_from_bytes(cls, value: bytes, password: Any | None = None) -> bytes:
# security check
if value.startswith(POSSIBLE_UNSAFE_KEYS):
warnings.warn("This key should not be used as an oct key", SecurityWarning)
return value
class OctKey(SymmetricKey):
"""OctKey is a symmetric key, defined by RFC7518 Section 6.4."""
key_type = "oct"
binding = OctBinding
#: https://www.rfc-editor.org/rfc/rfc7518#section-6.4
value_registry = {"k": KeyParameter("Key Value", "str", True, True)}
@classmethod
def import_key(
cls: Any,
value: AnyKey,
parameters: KeyParameters | None = None,
password: Any = None,
) -> "OctKey":
key: OctKey = super(OctKey, cls).import_key(value, parameters, password)
if not key.raw_value:
# An empty oct key material produces a deterministic HMAC digest
# that any party can reproduce, allowing trivial signature forgery
# in JWS HS256/HS384/HS512 verification. Reject outright rather
# than emitting a SecurityWarning that callers commonly suppress.
raise ValueError("oct key material must not be empty")
if len(key.raw_value) < 14:
# https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final
warnings.warn("Key size should be >= 112 bits", SecurityWarning)
return key
@classmethod
def generate_key(
cls,
key_size: int | None = 256,
parameters: KeyParameters | None = None,
private: bool = True,
auto_kid: bool = False,
) -> "OctKey":
"""Generate a ``OctKey`` with the given bit size (not bytes).
:param key_size: size in bit
:param parameters: extra parameter in JWK
:param private: must be True
:param auto_kid: add ``kid`` automatically
"""
if not private:
raise ValueError("oct key can not be generated as public")
if key_size is None:
key_size = 256
if key_size % 8 != 0:
raise ValueError("Invalid bit size for oct key")
if key_size < 112:
# https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final
warnings.warn("Key size should be >= 112 bits", SecurityWarning)
raw_key = secrets.token_bytes(key_size // 8)
key: OctKey = cls(raw_key, raw_key, parameters)
if auto_kid:
key.ensure_kid()
return key
def as_dict(self, private: bool = False, **params: t.Any) -> DictKey:
return super().as_dict(private=True, **params)