def dump(self, public: bool = True) -> dict: """ Returns a JSON-ready dictionary representation of the key's parameters. :param public: Dumps the public info of the key, defaults to True. :type public: bool, optional :return: Key in dict format. :rtype: dict """ numbers = self._public.public_numbers() data = { "kty": self.kty, "crv": self.CURVES_NAMES[numbers.curve.name], "x": to_string(int_to_b64(numbers.x)), "y": to_string(int_to_b64(numbers.y)), } if public: return data if not self._private: raise RuntimeError("This key is not a private key.") private_value = self._private.private_numbers().private_value return FullDict(data, d=to_string(int_to_b64(private_value)))
def parse( cls, path_or_secret: os.PathLike | bytes, password: bytes = None, format: str = "pem", ) -> OCTKey: """ Parses a raw secret into an OCTKey. :param path_or_secret: The path of the raw key or its bytes representation. :type path_or_secret: os.PathLike | bytes :param password: Password used to decrypt the raw key, defaults to None. :type password: bytes, optional :param format: The format of the raw key, defaults to `pem`. If `pem`, assumes it is Base64 Encoded. If `der`, assumes it is a regular sequence of bytes. :type format: str, optional :raises UnsupportedParsingMethod: Method not supported. :raises InvalidKey: The raw key type is different from the class. :return: Parsed key as OCTKey. :rtype: OCTKey """ raw = _parse_raw(path_or_secret) if format == "pem": invalid_strings = [ b"-----BEGIN CERTIFICATE-----", b"-----BEGIN PRIVATE KEY-----", b"-----BEGIN RSA PRIVATE KEY-----", b"-----BEGIN EC PRIVATE KEY-----", b"-----BEGIN PUBLIC KEY-----", b"-----BEGIN RSA PUBLIC KEY-----", b"-----BEGIN EC PUBLIC KEY-----", b"ssh-rsa", ] if any(string in raw for string in invalid_strings): raise InvalidKey( "The raw key is an asymmetric key or X.509 Certificate " "and CANNOT be used as a symmetric key.") data = to_string(base64url_encode(base64.b64decode(raw))) return cls(data) if format == "der": data = to_string(base64url_encode(raw)) return cls(data) raise UnsupportedParsingMethod
def dump(self, public: bool = True) -> dict: """ Returns a JSON-ready dictionary representation of the key's parameters. :param public: Dumps the public info of the key, defaults to True. :type public: bool, optional :return: Key in dict format. :rtype: dict """ numbers = self._public.public_numbers() data = { "kty": self.kty, "n": to_string(int_to_b64(numbers.n)), "e": to_string(int_to_b64(numbers.e)), } if public: return data if not self._private: raise RuntimeError("This key is not a private key.") private = self._private.private_numbers() return FullDict( data, d=to_string(int_to_b64(private.d)), p=to_string(int_to_b64(private.p)), q=to_string(int_to_b64(private.q)), dp=to_string(int_to_b64(private.dmp1)), dq=to_string(int_to_b64(private.dmq1)), qi=to_string(int_to_b64(private.iqmp)), )
def generate(cls, size: int = 2048) -> RSAKey: """ Generates a key on the fly based on the provided module size. :param size: Size of the modulus in bits, defaults to 2048. :type size: int, optional :raises InvalidKey: Invalid parameters for the key. :return: Generated key as RSAKey. :rtype: RSAKey """ if size < 512: raise InvalidKey("Size is too short. Must be AT LEAST 512 bits.") key = rsa.generate_private_key(65537, size, default_backend()) private = key.private_numbers() public = key.public_key().public_numbers() return cls( n=to_string(int_to_b64(public.n)), e=to_string(int_to_b64(public.e)), d=to_string(int_to_b64(private.d)), p=to_string(int_to_b64(private.p)), q=to_string(int_to_b64(private.q)), dp=to_string(int_to_b64(private.dmp1)), dq=to_string(int_to_b64(private.dmq1)), qi=to_string(int_to_b64(private.iqmp)), )
def form(self) -> dict: """ Parses the body as **application/x-www-form-encoded** and returns a dictionary of the parameters of the form. :return: Dictionary of the parsed body. :rtype: dict """ return urldecode(to_string(self._body))
def encode(self, key: JsonWebKey) -> str: """ Encodes the internal representation of the current JWT object, signs it with the provided key and returns the respective token. :param key: Key used to sign and encode the token. :type key: JsonWebKey :return: Encoded Json Web Token header, payload and signature. :rtype: bytes """ return to_string(self._jws.serialize(key))
def dump(self, public: bool = True) -> dict: """ Returns a JSON-ready dictionary representation of the key's parameters. :param public: Dumps the public info of the key, defaults to True. :type public: bool, optional :return: Key in dict format. :rtype: dict """ if public: warnings.warn("Secret keys fo not have public info.", RuntimeWarning) return { "kty": self.kty, "k": to_string(base64url_encode(self._secret)) }
def generate(cls, size: int = 32) -> OCTKey: """ Generates a secure random bytes sequence based on the provided size. :param size: Size of the secret in bytes, defaults to 32. :type size: int, optional :raises InvalidKey: Invalid parameters for the key. :return: Instance of an OCTKey. :rtype: OCTKey """ if size < 32: raise InvalidKey("Size is too short. MUST be AT LEAST 32 bytes.") secret = base64url_encode(secrets.token_bytes(size)) return cls(k=to_string(secret))
def S256_challenge(challenge: str, verifier: str): hashed_verifier = to_string( base64url_encode(hashlib.sha256(to_bytes(verifier, "ascii")).digest())) return challenge == hashed_verifier
def parse( cls, path_or_secret: os.PathLike | bytes, password: bytes = None, format: str = "pem", ) -> RSAKey: """ Parses a raw key into an RSAKey. :param path_or_secret: The path of the raw key or its bytes representation. :type path_or_secret: os.PathLike | bytes :param password: Password used to decrypt the raw key, defaults to None. :type password: bytes, optional :param format: The format of the raw key, defaults to `pem`. If `pem`, assumes it is PEM Encoded. If `der`, assumes it is a regular sequence of bytes. :type format: str, optional :raises UnsupportedParsingMethod: Method not supported. :raises InvalidKey: The raw key type is different from the class. :return: Parsed key as RSAKey. :rtype: RSAKey """ raw = _parse_raw(path_or_secret) if format == "pem": if b"PRIVATE" in raw: key: RSA_PRIVATE = serialization.load_pem_private_key( raw, password, default_backend()) if not isinstance(key, rsa.RSAPrivateKey): raise InvalidKey("The raw key is not an RSA Private Key.") private = key.private_numbers() public = key.public_key().public_numbers() return cls( n=to_string(int_to_b64(public.n)), e=to_string(int_to_b64(public.e)), d=to_string(int_to_b64(private.d)), p=to_string(int_to_b64(private.p)), q=to_string(int_to_b64(private.q)), dp=to_string(int_to_b64(private.dmp1)), dq=to_string(int_to_b64(private.dmq1)), qi=to_string(int_to_b64(private.iqmp)), ) if b"PUBLIC" in raw: key: RSA_PUBLIC = serialization.load_pem_public_key( raw, default_backend()) if not isinstance(key, rsa.RSAPublicKey): raise InvalidKey("The raw key is not an RSA Public Key.") public = key.public_numbers() return cls(n=to_string(int_to_b64(public.n)), e=to_string(int_to_b64(public.e))) raise InvalidKey("Unknown raw key format for RSA.") raise UnsupportedParsingMethod
def parse( cls, path_or_secret: os.PathLike | bytes, password: bytes = None, format: str = "pem", ) -> ECKey: """ Parses a raw key into an ECKey. :param path_or_secret: The path of the raw key or its bytes representation. :type path_or_secret: os.PathLike | bytes :param password: Password used to decrypt the raw key, defaults to None. :type password: bytes, optional :param format: The format of the raw key, defaults to `pem`. If `pem`, assumes it is PEM Encoded. If `der`, assumes it is a regular sequence of bytes. :type format: str, optional :raises UnsupportedParsingMethod: Method not supported. :raises InvalidKey: The raw key type is different from the class. :return: Parsed key as ECKey. :rtype: ECKey """ raw = _parse_raw(path_or_secret) if format == "pem": if b"PRIVATE" in raw: key: EC_PRIVATE = serialization.load_pem_private_key( raw, password, default_backend()) if not isinstance(key, ec.EllipticCurvePrivateKey): raise InvalidKey( "The raw key is not an Elliptic Curve Private Key.") private = key.private_numbers() public = key.public_key().public_numbers() return cls( crv=cls.CURVES_NAMES.get(public.curve.name), x=to_string(int_to_b64(public.x)), y=to_string(int_to_b64(public.y)), d=to_string(int_to_b64(private.private_value)), ) if b"PUBLIC" in raw: key: EC_PUBLIC = serialization.load_pem_public_key( raw, default_backend()) if not isinstance(key, ec.EllipticCurvePublicKey): raise InvalidKey( "The raw key is not an Elliptic Curve Public Key.") public = key.public_numbers() return cls( crv=cls.CURVES_NAMES.get(public.curve.name), x=to_string(int_to_b64(public.x)), y=to_string(int_to_b64(public.y)), ) raise InvalidKey("Unknown raw key format for Elliptic Curve.") raise UnsupportedParsingMethod
class ECKey(JWKAlgorithm): """ Implementation of the Elliptic Curve Asymmetric Key Algorithm. The standard curves are: "P-256", "P-384", "P-521". It is possible to add different curves, but they should be implemented by the application for a good support. :param crv: Name of the curve of the key. :type crv: str :param x: X coordinate of the Elliptic Curve. :type x: str :param y: Y coordinate of the Elliptic Curve. :type y: str :param d: Private value. **MANDATORY** if it is a private key. :type d: str, optional """ __allowed_attributes__ = frozenset(("crv", "x", "y", "d")) kty: str = "EC" CURVES: dict[str, ec.EllipticCurve] = { "P-256": ec.SECP256R1(), "P-384": ec.SECP384R1(), "P-521": ec.SECP521R1(), } CURVES_NAMES: dict[str, str] = { ec.SECP256R1.name: "P-256", ec.SECP384R1.name: "P-384", ec.SECP521R1.name: "P-521", } def __init__(self, crv: str, x: str, y: str, d: Optional[str] = None, **ignore) -> None: if crv not in self.CURVES.keys(): raise InvalidKey(f'Unknown curve: "{crv}".') self._private = None self._public = None curve = self.CURVES[crv] x_coord = b64_to_int(x) y_coord = b64_to_int(y) public = ec.EllipticCurvePublicNumbers(x_coord, y_coord, curve) self._public: EC_PUBLIC = public.public_key(default_backend()) if d: private_value = b64_to_int(d) private = ec.EllipticCurvePrivateNumbers(private_value, public) self._private: EC_PRIVATE = private.private_key(default_backend()) @classmethod def generate(cls, curve: str) -> ECKey: """ Generates a key on the fly based on the provided curve name. :param curve: Curve used to generate the key. :type curve: str :raises InvalidKey: Invalid parameters for the key. :return: Generated key as ECKey. :rtype: ECKey """ if not (crv := cls.CURVES.get(curve)): raise InvalidKey(f'Unknown curve "{curve}".') key: EC_PRIVATE = ec.generate_private_key(crv, default_backend()) private = key.private_numbers() public = key.public_key().public_numbers() return cls( crv=curve, x=to_string(int_to_b64(public.x)), y=to_string(int_to_b64(public.y)), d=to_string(int_to_b64(private.private_value)), )