Пример #1
0
    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)))
Пример #2
0
    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
Пример #3
0
    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)),
        )
Пример #4
0
    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)),
        )
Пример #5
0
    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))
Пример #6
0
    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))
Пример #7
0
    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))
        }
Пример #8
0
    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))
Пример #9
0
def S256_challenge(challenge: str, verifier: str):
    hashed_verifier = to_string(
        base64url_encode(hashlib.sha256(to_bytes(verifier, "ascii")).digest()))
    return challenge == hashed_verifier
Пример #10
0
    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
Пример #11
0
    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
Пример #12
0
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)),
        )