コード例 #1
0
    def __init__(
        self,
        spiffe_id: SpiffeId,
        cert_chain: List[Certificate],
        private_key: _PRIVATE_KEY_TYPES,
    ) -> None:
        """Creates a X509Svid instance.

        Args:
            spiffe_id: A SpiffeId instance.
            cert_chain: A list representing a chain of X.509 Certificate.
            private_key: A Private Key object.
        """

        if not spiffe_id:
            raise ArgumentError('spiffe_id cannot be None')

        if not cert_chain:
            raise ArgumentError('cert_chain cannot be empty')

        if not private_key:
            raise ArgumentError('private_key cannot be None')

        self._spiffe_id = spiffe_id
        self._cert_chain = cert_chain
        self._private_key = private_key
コード例 #2
0
    def validate_jwt_svid(self, token: str, audience: str) -> JwtSvid:
        """Validates the JWT-SVID token. The parsed and validated JWT-SVID is returned.

        Args:
            token: JWT to validate.
            audience: Audience to validate against.

        Returns:
            JwtSvid: If the token and audience could be validated.

        Raises:
            ArgumentError: In case token or audience is empty.
            ValidateJwtSvidError: In case an error occurs calling the Workload API or
                                in case the response from the Workload API cannot be processed.
        """
        if not token:
            raise ArgumentError('Token cannot be empty')
        if not audience:
            raise ArgumentError('Audience cannot be empty')

        self._spiffe_workload_api_stub.ValidateJWTSVID(
            request=workload_pb2.ValidateJWTSVIDRequest(
                audience=audience,
                svid=token,
            ))

        return JwtSvid.parse_insecure(token, [audience])
コード例 #3
0
    def validate_jwt_svid(self, token: str, audience: str) -> JwtSvid:
        """Validates the JWT-SVID token. The parsed and validated JWT-SVID is returned.

        Args:
            token: JWT to validate.
            audience: Audience to validate against.

        Returns:
            JwtSvid: If the token and audience could be validated.
            ArgumentError: In case token or audience is empty.
        """
        if not token:
            raise ArgumentError('Token cannot be empty')
        if not audience:
            raise ArgumentError('Audience cannot be empty')

        try:
            request = workload_pb2.ValidateJWTSVIDRequest(
                audience=audience,
                svid=token,
            )

            self._spiffe_workload_api_stub.ValidateJWTSVID(request)
        except Exception as e:
            raise ValidateJwtSvidError(str(e))

        return JwtSvid.parse_insecure(token, [audience])
コード例 #4
0
    def validate_header(self, parameters: Dict[str, str]) -> None:
        """Validates token headers by verifying if headers specifies supported algorithms and token type.

        Type is optional but in case it is present, it must be set to one of the supported values (JWT or JOSE).

        Args:
            parameters: Header parameters.

        Returns:
            None.

        Raises:
            ArgumentError: In case header is not specified.
            InvalidAlgorithmError: In case specified 'alg' is not supported as specified by the SPIFFE standard.
            InvalidTypeError: In case 'typ' is present in header but is not set to 'JWT' or 'JOSE'.
        """
        if not parameters:
            raise ArgumentError(
                INVALID_INPUT_ERROR.format('header cannot be empty'))

        alg = parameters.get('alg')
        if not alg:
            raise ArgumentError(
                INVALID_INPUT_ERROR.format('header alg cannot be empty'))

        if alg not in self._SUPPORTED_ALGORITHMS:
            raise InvalidAlgorithmError(alg)

        typ = parameters.get('typ')
        if typ and typ not in self._SUPPORTED_TYPES:
            raise InvalidTypeError(typ)
コード例 #5
0
 def validate_uri(uri: ParseResult) -> None:
     if uri.scheme != SPIFFE_SCHEME:
         raise ArgumentError(
             'Trust domain: invalid scheme: expected {}'.format(
                 SPIFFE_SCHEME))
     if not uri.hostname:
         raise ArgumentError(EMPTY_DOMAIN_ERROR)
     if uri.port:
         raise ArgumentError('Trust domain: port is not allowed')
コード例 #6
0
    def __set_name(self, name: str) -> None:
        if not name:
            raise ArgumentError(EMPTY_DOMAIN_ERROR)

        if len(name) > TRUST_DOMAIN_MAXIMUM_LENGTH:
            raise ArgumentError(
                'Trust domain cannot be longer than {} bytes'.format(
                    TRUST_DOMAIN_MAXIMUM_LENGTH))

        name = self.normalize(name)
        uri: ParseResult = urlparse(name)
        self.validate_uri(uri)
        self.__name = cast(str, uri.hostname).lower().strip()
コード例 #7
0
ファイル: spiffe_id.py プロジェクト: Andres-GC/py-spiffe-HPE
    def parse_and_validate_uri(spiffe_id: str) -> Dict:
        if len(spiffe_id) > SPIFFE_ID_MAXIMUM_LENGTH:
            raise ArgumentError('SPIFFE ID: maximum length is {} bytes'.format(
                SPIFFE_ID_MAXIMUM_LENGTH))
        try:
            uri = parse(spiffe_id.strip(), rule='URI')
        except ValueError as err:
            raise ArgumentError(str(err))

        scheme = uri.get('scheme')
        if scheme.lower() != SPIFFE_SCHEME:
            raise ArgumentError('SPIFFE ID: invalid scheme: expected spiffe')

        query = uri.get('query')
        if query:
            raise ArgumentError('SPIFFE ID: query is not allowed')

        fragment = uri.get('fragment')
        if fragment:
            raise ArgumentError('SPIFFE ID: fragment is not allowed')

        authority = uri.get('authority')
        if not authority:
            raise ArgumentError('SPIFFE ID: trust domain cannot be empty')

        # has user@info:
        if '@' in authority:
            raise ArgumentError('SPIFFE ID: userinfo is not allowed')

        # has domain:port
        if ':' in authority:
            raise ArgumentError('SPIFFE ID: port is not allowed')

        return uri
コード例 #8
0
ファイル: spiffe_id.py プロジェクト: HewlettPackard/py-spiffe
def validate_path(path):
    if not path:
        raise ArgumentError(EMPTY)

    segment_start = 0
    segment_end = 0

    while segment_end < len(path):
        c = path[segment_end]
        if c == '/':
            sub = path[segment_start:segment_end]
            if sub == '/':
                raise SpiffeIdError(EMPTY_SEGMENT)
            if sub == '/.' or sub == '/..':
                raise SpiffeIdError(DOT_SEGMENT)
            segment_start = segment_end
            segment_end += 1
            continue

        if not is_valid_path_segment_char(c):
            raise SpiffeIdError(BAD_PATH_SEGMENT_CHAR)

        segment_end += 1

    sub = path[segment_start:segment_end]
    if sub == '/':
        raise SpiffeIdError(TRAILING_SLASH)
    if sub == '/.' or sub == '/..':
        raise SpiffeIdError(DOT_SEGMENT)
コード例 #9
0
ファイル: spiffe_id.py プロジェクト: HewlettPackard/py-spiffe
    def parse(cls, id_or_name: str) -> 'TrustDomain':
        """Creates a new TrustDomain Object.

        Args:
            id_or_name: The name of a Trust Domain or a string representing a SPIFFE ID.

        Raises:
            ArgumentError: If the name of the trust domain is empty.
            SpiffeIdError: If the name contains an invalid char.

        Examples:
            >>> trust_domain = TrustDomain.parse('domain.test')
            >>> print(trust_domain)
            domain.test

            >>> print(trust_domain.as_str_id())
            spiffe://domain.test

        """

        if not id_or_name:
            raise ArgumentError(MISSING_TRUST_DOMAIN)

        # Something looks kinda like a scheme separator, let's try to parse as
        # an ID. We use :/ instead of :// since the diagnostics are better for
        # a bad input like spiffe:/trustdomain.
        if ':/' in id_or_name:
            spiffe_id = SpiffeId.parse(id_or_name)
            return spiffe_id.trust_domain()

        validate_trust_domain_name(id_or_name)

        result = TrustDomain()
        result._set_name(id_or_name)
        return result
コード例 #10
0
    def fetch_jwt_svid(self,
                       audiences: List[str],
                       subject: Optional[SpiffeId] = None) -> JwtSvid:
        """Fetches a SPIFFE JWT-SVID.

        Args:
            audiences: List of audiences for the JWT SVID.
            subject: SPIFFE ID subject for the JWT.

        Returns:
            JwtSvid: Instance of JwtSvid object.
        Raises:
            ArgumentError: In case audience is empty.
            FetchJwtSvidError: In case there is an error in fetching the JWT-SVID from the Workload API.
        """
        if not audiences:
            raise ArgumentError('Parameter audiences cannot be empty')

        response = self._spiffe_workload_api_stub.FetchJWTSVID(
            request=workload_pb2.JWTSVIDRequest(
                audience=audiences,
                spiffe_id=str(subject),
            ))

        if len(response.svids) == 0:
            raise FetchJwtSvidError('JWT SVID response is empty')

        svid = response.svids[0].svid
        return JwtSvid.parse_insecure(svid, audiences)
コード例 #11
0
ファイル: spiffe_id.py プロジェクト: Andres-GC/py-spiffe-HPE
    def parse(cls, spiffe_id: str) -> 'SpiffeId':
        """Parses a SPIFFE ID from a string into a SpiffeId type instance.

        Args:
            spiffe_id: A string representing the SPIFFE ID.

        Returns:
            An instance of a compliant SPIFFE ID (SpiffeId type).

        Raises:
            ArgumentError: If the string spiffe_id doesn't comply the the SPIFFE standard.

        Examples:
            >>> spiffe_id = SpiffeId.parse('spiffe://domain.test/path/element')
            >>> print(spiffe_id.trust_domain())
            domain.test
            >>> print(spiffe_id.path())
            /path/element
        """

        if not spiffe_id:
            raise ArgumentError('SPIFFE ID cannot be empty.')

        uri = cls.parse_and_validate_uri(spiffe_id)

        result = SpiffeId()
        result.__set_path(uri['path'])
        result.__set_trust_domain(TrustDomain(uri['authority']))
        return result
コード例 #12
0
ファイル: config.py プロジェクト: Andres-GC/py-spiffe-HPE
    def _validate(self) -> None:
        endpoint_socket = self._raw_config[_SPIFFE_ENDPOINT_SOCKET]
        if not endpoint_socket:
            raise ArgumentError('SPIFFE endpoint socket: socket must be set')

        parsed_socket = urlparse(endpoint_socket)

        if not parsed_socket.scheme:
            raise ArgumentError('SPIFFE endpoint socket: scheme must be set')

        if parsed_socket.scheme == 'unix':
            self._validate_unix_socket(parsed_socket)
        elif parsed_socket.scheme == 'tcp':
            self._validate_tcp_socket(parsed_socket)
        else:
            raise ArgumentError('SPIFFE endpoint socket: unsupported scheme')
コード例 #13
0
    def save(
        cls,
        x509_bundle: 'X509Bundle',
        bundle_path: str,
        encoding: serialization.Encoding,
    ) -> None:
        """Saves an X.509 bundle to a file in disk.

        Args:
            x509_bundle: Instance of 'X509Bundle' to be saved to disk
            bundle_path: Path to the file containing a set of X.509 authorities
            encoding: Bundle encoding format, either serialization.Encoding.PEM or serialization.Encoding.DER

        Raises:
            ArgumentError: In case the encoding is not either PEM or DER (from serialization.Encoding)
            X509BundleError: In case the authorities in the bundle cannot be converted to bytes.
            SaveX509BundleError: In the case the file path in bundle_path cannot be open to write, or there is an error
                                writing the authorities bytes to the file.
        """

        if encoding not in [encoding.PEM, encoding.DER]:
            raise ArgumentError(
                'Encoding not supported: {}. Expected \'PEM\' or \'DER\''.
                format(encoding))
        _write_bundle_to_file(bundle_path, encoding, x509_bundle)
コード例 #14
0
    def __init__(self, spiffe_socket: str = None) -> None:
        """Creates a new Workload API Client.

        Args:
            spiffe_socket: Path to Workload API UDS. If not specified, the SPIFFE_ENDPOINT_SOCKET environment variable
                           must be set.

        Returns:
            DefaultWorkloadApiClient: New Workload API Client object.

        Raises:
            ArgumentError: If spiffe_socket_path is invalid or not provided and SPIFFE_ENDPOINT_SOCKET environment variable doesn't exist.
        """

        try:
            self._config = ConfigSetter(
                spiffe_endpoint_socket=spiffe_socket).get_config()
        except ArgumentError as e:
            raise ArgumentError(
                'Invalid DefaultWorkloadApiClient configuration: {}'.format(
                    str(e)))

        self._channel = self._get_spiffe_grpc_channel()
        self._spiffe_workload_api_stub = workload_pb2_grpc.SpiffeWorkloadAPIStub(
            self._channel)
コード例 #15
0
    def save(
        self,
        bundle_path: str,
        encoding: serialization.Encoding,
    ) -> None:
        """Saves the X.509 bundle to a file in disk.

        Args:
            bundle_path: Path to the file the set of X.509 authorities will be written to.
            encoding: Bundle encoding format, either serialization.Encoding.PEM or serialization.Encoding.DER

        Raises:
            ArgumentError: In case the encoding is not either PEM or DER (from serialization.Encoding)
            SaveX509BundleError: In the case the file path in bundle_path cannot be open to write, or there is an error
                                converting or writing the authorities bytes to the file.
        """

        if encoding not in [encoding.PEM, encoding.DER]:
            raise ArgumentError(
                'Encoding not supported: {}. Expected \'PEM\' or \'DER\''.format(
                    encoding
                )
            )
        try:
            write_certificates_to_file(bundle_path, encoding, self._x509_authorities)
        except Exception as err:
            raise SaveX509BundleError(
                'Error writing X.509 bundle to file: {}'.format(str(err))
            )
コード例 #16
0
    def load(
        cls,
        trust_domain: TrustDomain,
        bundle_path: str,
        encoding: serialization.Encoding,
    ) -> 'X509Bundle':
        """Loads an X.509 bundle from a file in disk containing DER or PEM encoded trusted authorities.

        Args:
            trust_domain: A trust domain to associate to the bundle.
            bundle_path: Path to the file containing a set of X.509 authorities.
            encoding: Bundle encoding format, either serialization.Encoding.PEM or serialization.Encoding.DER.

        Returns:
            An instance of 'X509Bundle' with the X.509 authorities associated to the given trust domain.

        Raises:
            X509BundleError: In case the trust_domain is empty.
            LoadX509BundleError: In case the set of x509_authorities cannot be parsed from the bundle_bytes.
        """

        try:
            bundle_bytes = load_certificates_bytes_from_file(bundle_path)
        except Exception as e:
            raise LoadX509BundleError(str(e))

        if encoding == serialization.Encoding.PEM:
            return cls.parse(trust_domain, bundle_bytes)

        if encoding == serialization.Encoding.DER:
            return cls.parse_raw(trust_domain, bundle_bytes)

        raise ArgumentError(
            'Encoding not supported: {}. Expected \'PEM\' or \'DER\''.format(encoding)
        )
コード例 #17
0
ファイル: config.py プロジェクト: Andres-GC/py-spiffe-HPE
    def _validate_tcp_socket(cls, socket: ParseResult) -> None:
        try:
            ipaddress.ip_address(socket.hostname)
        except ValueError:
            raise ArgumentError(
                'SPIFFE endpoint socket: host must be an IP address')

        cls._validate_forbidden_components(
            socket, cls._TCP_FORBIDDEN_SOCKET_COMPONENTS)
コード例 #18
0
ファイル: config.py プロジェクト: Andres-GC/py-spiffe-HPE
 def _validate_forbidden_components(
         cls, socket: ParseResult,
         components: List[Tuple[str, Optional[str]]]) -> None:
     for component, description in components:
         has_component = component in dir(socket) and getattr(
             socket, component)
         if has_component:
             raise ArgumentError(
                 'SPIFFE endpoint socket: {} is not allowed'.format(
                     description or component))
コード例 #19
0
ファイル: spiffe_id.py プロジェクト: Andres-GC/py-spiffe-HPE
    def of(cls,
           trust_domain: TrustDomain,
           path_segments: Union[str, List[str]] = None) -> 'SpiffeId':
        """Creates SpiffeId type instance from a Trust Domain and zero or more paths.

        Args:
            trust_domain: The trust domain corresponds to the trust root of a system.
            path_segments: A single string or a list of path segments.

        Returns:
            An instance of a compliant SPIFFE ID (SpiffeId type).

        Raises:
            ArgumentError: If the trust_domain is None or is not instance of the class TrustDomain.

        Examples:
            >>> spiffe_id_1 = SpiffeId.of(TrustDomain('example.org'), 'path')
            >>> print(spiffe_id_1)
            spiffe://example.org/path

            an array of paths:
            >>> spiffe_id_2 = SpiffeId.of(TrustDomain('example.org'), ['path1', 'path2', 'element'])
            >>> print(spiffe_id_2)
            spiffe://example.org/path1/path2/element
        """

        if trust_domain is None:
            raise ArgumentError('SPIFFE ID: trust domain cannot be empty')

        if not isinstance(trust_domain, TrustDomain):
            raise ArgumentError(
                'SPIFFE ID: trust_domain argument must be a TrustDomain instance'
            )

        result = SpiffeId()
        result.__set_trust_domain(trust_domain)

        if path_segments is not None:
            result.__set_path(path_segments)

        cls.parse_and_validate_uri(str(result))
        return result
コード例 #20
0
    def parse(cls, trust_domain: TrustDomain,
              bundle_bytes: bytes) -> 'JwtBundle':
        """Parses a bundle from bytes. The data must be a standard RFC 7517 JWKS document.

        Args:
            trust_domain: A TrustDomain to associate to the bundle.
            bundle_bytes: An array of bytes that represents a set of JWKs.

        Returns:
            An instance of 'JWTBundle' with the JWT authorities associated to the given trust domain.

        Raises:
            ArgumentError: In case the trust_domain is empty or bundle_bytes is empty.
            ParseJWTBundleError: In case the set of jwt_authorities cannot be parsed from the bundle_bytes.
        """

        if not trust_domain:
            raise ArgumentError(MISSING_TRUST_DOMAIN)

        if not bundle_bytes:
            raise ArgumentError('Bundle bytes cannot be empty')

        try:
            jwks = PyJWKSet.from_json(bundle_bytes)
        except InvalidKeyError as ike:
            raise ParseJWTBundleError('Cannot parse jwks from bundle_bytes: ' +
                                      str(ike))
        except (JSONDecodeError, AttributeError):
            raise ParseJWTBundleError(
                'Cannot parse jwks. bundle_bytes does not represent a valid jwks'
            )

        jwt_authorities = {}
        for jwk in jwks.keys:
            if not jwk.key_id:
                raise ParseJWTBundleError(
                    'Error adding authority from JWKS: keyID cannot be empty')

            jwt_authorities[jwk.key_id] = jwk.key

        return JwtBundle(trust_domain, jwt_authorities)
コード例 #21
0
ファイル: spiffe_id.py プロジェクト: HewlettPackard/py-spiffe
    def parse(cls, id: str) -> 'SpiffeId':
        """Parses a SPIFFE ID from a string into a SpiffeId type instance.

        Args:
            id: A string representing a SPIFFE ID.

        Returns:
            An instance of a compliant SPIFFE ID (SpiffeId type).

        Raises:
            ArgumentError: If the id is emtpy.
            SpiffeIdError: If the string spiffe_id doesn't comply the the SPIFFE standard.

        Examples:
            >>> spiffe_id = SpiffeId.parse('spiffe://domain.test/path/element')
            >>> print(spiffe_id.trust_domain())
            domain.test
            >>> print(spiffe_id.path())
            /path/element
        """

        if not id:
            raise ArgumentError(EMPTY)

        if SCHEME_PREFIX not in id:
            raise SpiffeIdError(WRONG_SCHEME)

        rest = id[len(SCHEME_PREFIX):]

        i = 0
        for c in rest:
            if c == '/':
                break
            if not is_valid_trustdomain_char(c):
                raise SpiffeIdError(BAD_TRUST_DOMAIN_CHAR)
            i += 1

        if i == 0:
            raise SpiffeIdError(MISSING_TRUST_DOMAIN)

        td = rest[:i]
        path = rest[i:]

        if path:
            validate_path(path)

        result = SpiffeId()
        trust_domain = TrustDomain()
        trust_domain._set_name(td)
        result._set_trust_domain(trust_domain)
        result._set_path(path)
        return result
コード例 #22
0
    def load(
        cls,
        certs_chain_path: str,
        private_key_path: str,
        encoding: serialization.Encoding,
    ) -> 'X509Svid':
        """Loads the X.509 SVID from PEM or DER encoded files on disk.

        The private key should be without encryption.

        Args:
            certs_chain_path: Path to the file containing one or more X.509 certificates as PEM blocks.
            private_key_path: Path the file containing a private key as PKCS#8 PEM block.
            encoding: The encoding used to serialize the certs and private key, can be
                                            serialization.Encoding.PEM or serialization.Encoding.DER.

        Returns:
            An instance of a 'X509Svid' containing the chain of certificates, the private key, and the SPIFFE ID of the
            leaf certificate in the chain.

        Raises:
            ArgumentError: In case the encoding is not either PEM or DER (from serialization.Encoding).
            X509SvidError: In case the file path in certs_chain_path or in private_key_path does not exists or cannot be open.
            ParseCertificateError: In case the chain of certificates cannot be parsed from the bytes read from certs_chain_path.
            ParsePrivateKeyError: In case the private key cannot be parsed from the bytes read from private_key_path.
            InvalidLeafCertificateError: In case the leaf certificate does not have a SPIFFE ID in the URI SAN,
                                         in case the leaf certificate is CA,
                                         in case the leaf certificate has 'keyCertSign' as key usage,
                                         in case the leaf certificate does not have 'digitalSignature' as key usage,
                                         in case the leaf certificate does not have 'cRLSign' as key usage.
            InvalidIntermediateCertificateError: In case one of the intermediate certificates is not CA,
                                                 in case one of the intermediate certificates does not have 'keyCertSign' as key usage.
        """

        try:
            chain_bytes = load_certificates_bytes_from_file(certs_chain_path)
        except Exception as e:
            raise LoadCertificateError(str(e))

        key_bytes = _load_private_key_bytes(private_key_path)

        if encoding == serialization.Encoding.PEM:
            return cls.parse(chain_bytes, key_bytes)

        if encoding == serialization.Encoding.DER:
            return cls.parse_raw(chain_bytes, key_bytes)

        raise ArgumentError(
            'Encoding not supported: {}. Expected \'PEM\' or \'DER\''.format(
                encoding))
コード例 #23
0
    def __init__(self, x509_svids: List[X509Svid],
                 x509_bundle_set: X509BundleSet) -> None:
        """Creates a new X509Context with a list of X509Svid object and a X509BundleSet.

        Args:
            x509_svids: A list of X509Svid objects.
            x509_bundle_set: An X509BundleSet object.
        """

        if not x509_svids:
            raise ArgumentError('X.509 SVID list cannot be empty')

        self._x509_svids = x509_svids.copy() if x509_svids else []
        self._x509_bundle_set = x509_bundle_set
コード例 #24
0
ファイル: spiffe_id.py プロジェクト: HewlettPackard/py-spiffe
    def from_segments(
            cls,
            trust_domain: TrustDomain,
            path_segments: Union[str, List[str]] = None) -> 'SpiffeId':
        """Creates SpiffeId type instance from a Trust Domain and one or more paths.

        Args:
            trust_domain: The trust domain corresponds to the trust root of a system.
            path_segments: A single string or a list of path segments.

        Returns:
            An instance of a compliant SPIFFE ID (SpiffeId type).

        Raises:
            ArgumentError: If the trust_domain is None.
            SpiffeIdError: If the path segments are not SPIFFE conformant.

        Examples:
            >>> spiffe_id_1 = SpiffeId.from_segments(TrustDomain.parse('example.org'), 'path')
            >>> print(spiffe_id_1)
            spiffe://example.org/path

            an array of paths:
            >>> spiffe_id_2 = SpiffeId.from_segments(TrustDomain.parse('example.org'), ['path1', 'path2', 'element'])
            >>> print(spiffe_id_2)
            spiffe://example.org/path1/path2/element
        """

        if not trust_domain:
            raise ArgumentError(MISSING_TRUST_DOMAIN)

        result = SpiffeId()
        result._set_trust_domain(trust_domain)

        if path_segments is not None:
            path = ''

            if isinstance(path_segments, List):
                for p in path_segments:
                    validate_path(p)
                    path += '/' + p
            else:
                validate_path(path_segments)
                path = '/' + path_segments

            result._set_path(path)

        return result
コード例 #25
0
    def get_jwt_svid(self,
                     audiences: Set[str],
                     subject: Optional[SpiffeId] = None) -> JwtSvid:
        """Returns an JWT-SVID from the source.

        Args:
            audiences: List of audiences for the JWT SVID.
            subject: SPIFFE ID subject for the JWT.

        Raises:
            ArgumentError: In case audiences is empty.
            FetchJwtSvidError: In case there is an error in fetching the JWT-SVID from the Workload API.
        """
        if not audiences:
            raise ArgumentError('Audience cannot be empty')

        jwt_svid = self._workload_api_client.fetch_jwt_svid(audiences, subject)
        return jwt_svid
コード例 #26
0
    def get_jwt_authority(self, key_id: str) -> Optional[_PUBLIC_KEY_TYPES]:
        """Returns the authority for the specified key_id.

        Args:
            key_id: Key id of the token to return the correspondent authority.

        Returns:
            The authority associated with the supplied key_id.
            None if the key_id is not found.

        Raises:
            ArgumentError: When key_id is not valid (empty or None).
        """
        if not key_id:
            raise ArgumentError('key_id cannot be empty')

        with self.lock:
            return self._jwt_authorities.get(key_id)
コード例 #27
0
    def _validate_aud(self, audience_claim: List[str],
                      expected_audience: List[str]) -> None:
        """Verifies if expected_audience is present in audience_claim. The aud claim MUST be present.

        Args:
            audience_claim: List of token's audience claim to be validated.
            expected_audience: Set of the claims expected to be present in the token's audience claim.

        Raises:
            InvalidClaimError: In expected_audience is not a subset of audience_claim or it is empty.
            ArgumentError: In case expected_audience is empty.
        """
        if not expected_audience:
            raise ArgumentError(
                INVALID_INPUT_ERROR.format(
                    'expected_audience cannot be empty'))

        if not audience_claim or all(aud == '' for aud in audience_claim):
            raise InvalidClaimError('audience_claim cannot be empty')

        if not all(aud in audience_claim for aud in expected_audience):
            raise InvalidClaimError(AUDIENCE_NOT_MATCH_ERROR)
コード例 #28
0
    def save(
        cls,
        x509_svid: 'X509Svid',
        certs_chain_path: str,
        private_key_path: str,
        encoding: serialization.Encoding,
    ) -> None:
        """Saves the X.509 SVID certs chain and private key in PEM or DER encoded files on disk.

        The private key is stored without encryption, but the file is set with filemode = '0600' (only owner has read/write permission).

        Args:
            x509_svid: the 'X509Svid' that has the certs_chain and private_key to be saved on disk.
            certs_chain_path: Path to the file containing one or more X.509 certificates as PEM blocks.
                                    The certs_chain file is configured with a filemode = '0644'.
            private_key_path: Path the file containing a PKCS#8 PEM block.
                                    The private_key file is configured with a filemode = '0600'.
            encoding: The encoding used to serialize the certs and private key, can be
                                            serialization.Encoding.PEM or serialization.Encoding.DER.

        Raises:
            ArgumentError: In case the encoding is not either PEM or DER (from serialization.Encoding).
            X509SvidError: In case the certs chain or the private key in the X509Svid cannot be converted to bytes.
            StorePrivateKeyError: In the case there is an error storing the private key to the file.
            StoreCertificateError: In the case the file path in certs_chain_path cannot be open to write,
                                  or there is an error storing the certificates to the file.
        """

        if encoding not in [encoding.PEM, encoding.DER]:
            raise ArgumentError(
                'Encoding not supported: {}. Expected \'PEM\' or \'DER\''.
                format(encoding))

        _write_x509_svid_to_file(certs_chain_path, encoding, x509_svid)

        private_key_bytes = _extract_private_key_bytes(encoding,
                                                       x509_svid.private_key())
        _write_private_key_to_file(private_key_path, private_key_bytes)
コード例 #29
0
    def fetch_jwt_svid(self,
                       audiences: List[str],
                       subject: Optional[SpiffeId] = None) -> JwtSvid:
        """Fetches a SPIFFE JWT-SVID.

        Args:
            audiences: List of audiences for the JWT SVID.
            subject: SPIFFE ID subject for the JWT.

        Returns:
            JwtSvid: Instance of JwtSvid object.
        Raises:
            ArgumentError: In case audience is empty.
            FetchJwtSvidError: In case there is an error in fetching the JWTSVID from the Workload API.
        """
        if not audiences:
            raise ArgumentError('Parameter audiences cannot be empty')
        try:
            response = self._call_fetch_jwt_svids(audiences, str(subject))
            svid = response.svids[0].svid
            return JwtSvid.parse_insecure(svid, audiences)
        except JwtSvidError as e:
            raise FetchJwtSvidError(str(e))
コード例 #30
0
ファイル: jwt_svid.py プロジェクト: Andres-GC/py-spiffe-HPE
    def parse_insecure(cls, token: str,
                       expected_audience: List[str]) -> 'JwtSvid':
        """Parses and validates a JWT-SVID token and returns an instance of a JwtSvid with a SPIFFE ID parsed from the 'sub', audience from 'aud',
        and expiry from 'exp' claim. The JWT-SVID signature is not verified.

        Args:
            token: A token as a string that is parsed and validated.
            expected_audience: Audience as a list of strings used to validate the 'aud' claim.

        Returns:
            An instance of JwtSvid with a SPIFFE ID parsed from the 'sub', audience from 'aud', and expiry
            from 'exp' claim.

        Raises:
            ArgumentError: When the token is blank or cannot be parsed, or in case header is not specified or in case expected_audience is empty or
                if the SPIFFE ID in the 'sub' claim doesn't comply with the SPIFFE standard.
            InvalidAlgorithmError: In case specified 'alg' is not supported as specified by the SPIFFE standard.
            InvalidTypeError: If 'typ' is present in header but is not set to 'JWT' or 'JOSE'.
            InvalidClaimError: If a required claim ('exp', 'aud', 'sub') is not present in payload or expected_audience is not a subset of audience_claim.
            TokenExpiredError: If token is expired.
            InvalidTokenError: If token is malformed and fails to decode.
        """
        if not token:
            raise ArgumentError(
                INVALID_INPUT_ERROR.format('token cannot be empty'))
        try:
            header_params = jwt.get_unverified_header(token)
            validator = JwtSvidValidator()
            validator.validate_header(header_params)
            claims = jwt.decode(token, options={'verify_signature': False})
            validator.validate_claims(claims, expected_audience)
            spiffe_id = SpiffeId.parse(claims['sub'])
            return JwtSvid(spiffe_id, claims['aud'], claims['exp'], claims,
                           token)
        except PyJWTError as err:
            raise InvalidTokenError(str(err))