first commit
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user