Source code for pyseto.key

from typing import Any, Union

from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
    Ed25519PrivateKey,
    Ed25519PublicKey,
)
from cryptography.hazmat.primitives.serialization import (
    load_pem_private_key,
    load_pem_public_key,
)

from .key_interface import KeyInterface
from .versions.v1 import V1Local, V1Public
from .versions.v2 import V2Local, V2Public
from .versions.v3 import V3Local, V3Public
from .versions.v4 import V4Local, V4Public


[docs] class Key: """ Tha factory methods for PASETO keys. """ _PASERK_TYPE_SUPPORTED = [ "local", "public", "secret", "seal", "local-wrap", "secret-wrap", "local-pw", "secret-pw", ]
[docs] @classmethod def new(cls, version: int, purpose: str, key: Union[bytes, str] = b"") -> KeyInterface: """ Constructor of a PASETO key object which has :class:`KeyInterface <pyseto.key_interface.KeyInterface>`. Args: version(int): The version of the key. It will be ``1``, ``2``, ``3`` or ``4``. purpose (str): The purpose of the key. It will be ``public`` or ``local``. key (Union[bytes, str]): A key itself or keying material. Returns: KeyInterface: A PASETO key object. Raise: ValueError: Invalid arguments. """ bkey = key if isinstance(key, bytes) else key.encode("utf-8") if purpose == "local": return cls._create_private_key(version, bkey) elif purpose == "public": k: Any = None if bkey.startswith(b"-----BEGIN EC PRIVATE"): k = load_pem_private_key(bkey, password=None) elif bkey.startswith(b"-----BEGIN PRIVATE"): k = load_pem_private_key(bkey, password=None) elif bkey.startswith(b"-----BEGIN PUBLIC"): k = load_pem_public_key(bkey) elif bkey.startswith(b"-----BEGIN RSA PRIVATE"): k = load_pem_private_key(bkey, password=None) else: raise ValueError("Invalid or unsupported PEM format.") return cls._create_public_key(version, k) raise ValueError(f"Invalid purpose: {purpose}.")
[docs] @classmethod def from_paserk( cls, paserk: str, wrapping_key: Union[bytes, str] = b"", password: Union[bytes, str] = b"", unsealing_key: Union[bytes, str] = b"", ) -> KeyInterface: """ Generates a PASETO key object which has :class:`KeyInterface <pyseto.key_interface.KeyInterface>` from PASERK. Args: paserk (str): A PASERK string. wrapping_key (Union[bytes, str]): A wrapping key. If the `wrapping_key` is specified, `password` should not be specified. password (Union[bytes, str]): A password for key wrapping. If the `password` is specified, `wrapping_key` should not be specified. unsealing_key (Union[bytes, str]): A password for key wrapping. If the `password` is specified, `wrapping_key` should not be specified. Returns: KeyInterface: A PASETO key object. Raise: ValueError: Invalid arguments. """ frags = paserk.split(".") if frags[1] not in cls._PASERK_TYPE_SUPPORTED: raise ValueError(f"Invalid PASERK key type: {frags[1]}.") bwk = wrapping_key if isinstance(wrapping_key, bytes) else wrapping_key.encode("utf-8") bpw = password if isinstance(password, bytes) else password.encode("utf-8") bsk = unsealing_key if isinstance(unsealing_key, bytes) else unsealing_key.encode("utf-8") if frags[0] == "k1": return ( V1Local.from_paserk(paserk, bwk, bpw, bsk) if frags[1].startswith("local") or frags[1] == "seal" else V1Public.from_paserk(paserk, bwk, bpw) ) if frags[0] == "k2": return ( V2Local.from_paserk(paserk, bwk, bpw, bsk) if frags[1].startswith("local") or frags[1] == "seal" else V2Public.from_paserk(paserk, bwk, bpw) ) if frags[0] == "k3": return ( V3Local.from_paserk(paserk, bwk, bpw, bsk) if frags[1].startswith("local") or frags[1] == "seal" else V3Public.from_paserk(paserk, bwk, bpw) ) if frags[0] == "k4": return ( V4Local.from_paserk(paserk, bwk, bpw, bsk) if frags[1].startswith("local") or frags[1] == "seal" else V4Public.from_paserk(paserk, bwk, bpw) ) raise ValueError(f"Invalid PASERK version: {frags[0]}.")
# @classmethod # def from_public_bytes(cls, version: int, key: bytes) -> KeyInterface: # if version == 1: # raise ValueError(f"RSA key is not supported.") # if version == 2: # return V2Public.from_public_bytes(key) # if version == 3: # return V3Public.from_public_bytes(key) # if version == 4: # return V4Public.from_public_bytes(key) # raise ValueError(f"Invalid version: {version}.")
[docs] @staticmethod def from_asymmetric_key_params(version: int, x: bytes = b"", y: bytes = b"", d: bytes = b"") -> KeyInterface: """ Constructor of a PASETO key object which has :class:`KeyInterface <pyseto.key_interface.KeyInterface>` wth asymmetric key parameters (x-coordinate, y-coordinate, and/or private key). This is intended to be used to generate keys for PASETO from JWK and other sources. Args: version(int): The version of the key. It will be ``1``, ``2``, ``3`` or ``4``. x (bytes): The x coordinate of the key. y (bytes): The y coordinate of the key. d (bytes): The private key component of the key. Returns: KeyInterface: A PASETO key object. Raise: ValueError: Invalid arguments. """ k: Any = None if version == 1: raise ValueError("v1.public is not supported on from_key_parameters.") if version == 2: if x and d: raise ValueError("Only one of x or d should be set for v2.public.") if x: try: k = Ed25519PublicKey.from_public_bytes(x) except Exception as err: raise ValueError("Failed to load key.") from err return V2Public(k) if d: try: k = Ed25519PrivateKey.from_private_bytes(d) except Exception as err: raise ValueError("Failed to load key.") from err return V2Public(k) raise ValueError("x or d should be set for v2.public.") if version == 3: if not x or not y: raise ValueError("x and y (and d) should be set for v3.public.") try: pn = ec.EllipticCurvePublicNumbers( x=int.from_bytes(x, byteorder="big"), y=int.from_bytes(y, byteorder="big"), curve=ec.SECP384R1(), ) k = pn.public_key() except Exception as err: raise ValueError("Failed to load key.") from err if not d: return V3Public(k) try: k = ec.EllipticCurvePrivateNumbers(int.from_bytes(d, byteorder="big"), pn).private_key() except Exception as err: raise ValueError("Failed to load key.") from err return V3Public(k) if version == 4: if x and d: raise ValueError("Only one of x or d should be set for v4.public.") if x: try: k = Ed25519PublicKey.from_public_bytes(x) except Exception as err: raise ValueError("Failed to load key.") from err return V4Public(k) if d: try: k = Ed25519PrivateKey.from_private_bytes(d) except Exception as err: raise ValueError("Failed to load key.") from err return V4Public(k) raise ValueError("x or d should be set for v4.public.") raise ValueError(f"Invalid version: {version}.")
@staticmethod def _create_public_key(version: int, key: Any) -> KeyInterface: if version == 1: return V1Public(key) if version == 2: return V2Public(key) if version == 3: return V3Public(key) if version == 4: return V4Public(key) raise ValueError(f"Invalid version: {version}.") @staticmethod def _create_private_key(version: int, key: bytes) -> KeyInterface: if version == 1: return V1Local(key) if version == 2: return V2Local(key) if version == 3: return V3Local(key) if version == 4: return V4Local(key) raise ValueError(f"Invalid version: {version}.")