Beispiel #1
0
    def decode(self, msg):
        hdr, body = msg[:8], msg[8:]

        try:
            return asn1_decode(body, asn1Spec=self.decode_spec)[0]
        except pyasn1.error.PyAsn1Error:
            logger.error('Unable to decode PDU. Skipping ...')
        except TypeError:
            logger.error('Unable to decode PDU due to type error ...')
        return None
Beispiel #2
0
def check_invoke_credentials(initiator_invoker_credentials, initiator_id,
                             initiator_password):
    decoded_credentials = asn1_decode(initiator_invoker_credentials.asOctets(),
                                      asn1Spec=Isp1Credentials())[0]
    days, ms, us = struct.unpack(
        '!HIH', bytearray(decoded_credentials['time'].asNumbers()))
    time_delta = dt.timedelta(days=days, milliseconds=ms, microseconds=us)
    cred_time = time_delta + dt.datetime(1958, 1, 1)
    random_number = int(decoded_credentials['randomNumber'])
    invoker_credentials = _generate_encoded_credentials(
        cred_time, random_number, initiator_id, initiator_password)
    return invoker_credentials == initiator_invoker_credentials.asOctets()
Beispiel #3
0
    def set_signature(self, signature: bytes):
        """
        Modify the signature of a CSR.

        :param bytes signature: the new signature for a CSR.
        :rtype: None
        """
        der_csr = self.public_bytes(serialization.Encoding.DER)
        asn1_csr, _ = asn1_decode(
            der_csr,
            asn1Spec=CertificationRequest(),
        )

        asn1_csr.setComponentByName("signature",
                                    BitString.fromOctetString(signature))

        self._x509_req = x509.load_der_x509_csr(
            asn1_encode(asn1_csr))._x509_req
Beispiel #4
0
    def __disassemble_content_block(self, content, key):
        """ Decrypt and decode content from a content block

        @developer: ddnomad

        :param content: instance of MPContentContainer class
        :param key:     string AES key to be used for decryption

        :return: list of the following values:
                    [0] datetime.datetime timestamp object
                    [1] string decrypted message

        """

        # log entry
        self.logger.debug(logstr.DISASSEMBLE_CONTENT_BLOCK_CALL)

        # decode MPContentContainer from DER
        # TODO: try-except in a case when decoding failed
        mp_content_container_asn1 = content
        # recover values that are necessary for decryption
        # TODO: verify encryptionAlgorithm OID
        iv = bytes(mp_content_container_asn1[0])
        enc_content = bytes(mp_content_container_asn1[2])

        # decrypt DER-encoded MPContent
        mp_content_pt_der = self.__decrypt_with_aes(enc_content, key, iv)

        # recover timestamp and message from DER-encoded MPContent
        mp_content_pt_asn1 = asn1_decode(mp_content_pt_der)
        timestamp = datetime.strptime(str(mp_content_pt_asn1[0][0]),
                                      const.TIMESTAMP_FORMAT)
        message = str(mp_content_pt_asn1[0][1])

        # return the resulting data
        return timestamp, message
Beispiel #5
0
    def disassemble_message_packet(self, msg_packet, key_manager):
        """ Attempt to disassemble FLOD message packet that was received

        @developer: ddnomad

        Disassembly involves test decryption of header block with all available
        private keys of a user. If decryption was successful then the message
        was addressed to the user. This does not means though that the
        signature verification will succeed as well as HMAC integrity check.

        On successful decryption the method returns a recovered message
        together with a supplementary exit code which determines conditions
        that occur during disassembly process. The code can be one of the
        following integers:

            - 0: indicates that signature verification was successful with a
              known sender PGPKeyID
            - 1: indicates that signature verification was successful but the
              key used was not a PGP key (but it exists in a user key chain)
            - 2: indicates that decryption was successful but the message was
              not signed by a sender
            - 3: indicates that the signature authenticity cannot be
              established due to an absence of a corresponding public key

        :param msg_packet:          string DER-encoded ASN.1 structure of FLOD
                                    message packet to decrypt
        :param key_manager:         instance of mflod.crypto.key_manager.
                                    KeyManager that should implement two
                                    mandatory methods:
                                        - yield_keys() which return all user
                                          private keys (both PGP and plain
                                          ones) one by one (generator). The
                                          method has to return instances of
                                          cryptography.hazmat.primitives.
                                          asymmetric.rsa.RSAPrivateKey
                                        - get_pk_by_pgp_id(pgp_id) which
                                          attempts to find a matching to an
                                          string input ID PGP key. If the key
                                          was found - return an instance of
                                          cryptography.hazmat.primitives.
                                          asymmetric.rsa.RSAPublicKey. If there
                                          is not such key - return None. If the
                                          ID passed is all 0s - return a list
                                          of all user plain RSA public keys.

        :return: one of the following lists (see supplementary exit codes
                 paragraph for details):
                    - [timestamp, dec_msg, 0, pgp_key_id]
                    - [timestamp, dec_msg, 1, sign_pk]
                    - [timestamp, dec_msg, 2]
                    - [timestamp, dec_msg, 3]
                The values in lists are the following:
                    - timestamp:    instance of datetime.datetime time when
                                    the message was composed by a sender
                    - dec_msg:      string decryption of a message received
                    - pgp_key_id:   string PGPKeyID of a public key that
                                    verified a signature
                    - sign_pk:      an instance of cryptography.hazmat.
                                    primitives.rsa.RSAPublicKey that verified a
                                    signature

        :raise mflod.crypto.exceptions.NoMatchingRSAKeyForMessage,
               mflod.crypto.exceptions.SignatureVerificationFailed,
               mflod.crypto.exceptions.HMACVerificationFailed
        """

        # log entry
        self.logger.debug(logstr.DISASSEMBLE_MESSAGE_PACKET_CALL)

        # decode message packet from DER and get header block
        message_packet_asn1 = asn1_decode(msg_packet)
        header_block_asn1 = message_packet_asn1[0][1]

        # get encrypted from a header container
        mp_header_ct = bytes(header_block_asn1[1])

        # entering brute-force loop
        self.logger.debug(logstr.ATTEMPT_DECRYPT_HEADER)

        # try to decrypt a header with all available user keys
        for user_sk in key_manager.yield_keys():

            # determine a size of a current user secret key
            key_size = user_sk.key_size // 8

            # get decrypted first RSA block of MPHeader
            try:
                mp_header_pt_init_block = self.__decrypt_with_rsa(
                    mp_header_ct[:key_size], user_sk)
            except (InvalidKey, ValueError):

                # probably key size doesn't match
                self.logger.debug(logstr.INVALID_RSA_KEY)
                continue

            # calculate identification string offset
            offset = self.__calculate_der_id_string_offset(
                mp_header_pt_init_block)

            # check whether id string matches
            if not mp_header_pt_init_block[offset:offset + 4] == \
                    bytes(const.IS, 'utf-8'):

                # key doesn't fit
                self.logger.debug(logstr.WRONG_RSA_KEY)
                continue

            # found a matching key - message can be decrypted
            else:

                self.logger.info(logstr.MESSAGE_FOR_USER)

                # init exit code (optimistic)
                exit_code = 0
                signer_info = None

                # create a variable to hold the MPHeader plaintext
                mp_header_pt = mp_header_pt_init_block

                # decrypt the whole MPHeader DER
                for rsa_block in [
                        mp_header_ct[i:i + key_size]
                        for i in range(key_size, len(mp_header_ct), key_size)
                ]:

                    # append decrypted chunks
                    mp_header_pt += self.__decrypt_with_rsa(rsa_block, user_sk)

                # decode MPHeader from DER
                mp_header_pt_asn1 = asn1_decode(mp_header_pt)

                # determine whether the header was signed
                sign_oid = str(mp_header_pt_asn1[0][1][0])
                pgp_key_id = str(mp_header_pt_asn1[0][2])
                signature = bytes(mp_header_pt_asn1[0][3])
                hmac_key = bytes(mp_header_pt_asn1[0][4])
                aes_key = bytes(mp_header_pt_asn1[0][5])
                sign_content = hmac_key + aes_key

                # there is a signature
                if sign_oid != const.NO_SIGN_OID:

                    self.logger.info(logstr.MESSAGE_IS_SIGNED)

                    # get signer public key
                    signer_cands = key_manager.get_pk_by_pgp_id(pgp_key_id)

                    # there is a public key
                    if isinstance(signer_cands, RSAPublicKey):
                        if self.__verify_signature(signature, signer_cands,
                                                   sign_content):
                            signer_info = pgp_key_id
                        else:
                            # TODO: more verbose
                            raise exc.SignatureVerificationFailed("")

                    # nothing found for this PGP ID
                    elif signer_cands is None:
                        self.logger.warn(logstr.SIGN_CANNOT_VERIF)
                        exit_code = 3

                    # the PGP ID is zeros so signer used non-PGP key
                    # get_pk_byid_func returned a list of all user's
                    # non-PGP public keys (from an internal key chain
                    else:

                        # just in case
                        assert (isinstance(signer_cands, tuple))

                        self.logger.info(logstr.NON_PGP_KEY_SIGN)

                        # brute over all user non-PGP keys in attempt to verify
                        for cand_key in signer_cands:

                            # again just in case
                            assert (isinstance(cand_key, RSAPublicKey))

                            verif_ok = self.__verify_signature(
                                signature, cand_key, sign_content)

                            if verif_ok:
                                break

                        # update exit code state
                        if verif_ok:
                            exit_code = 1
                            signer_info = cand_key
                        else:
                            exit_code = 3

                # there is no signature in MPHeader
                else:

                    self.logger.info(logstr.NOT_SIGNED_MESSAGE)
                    exit_code = 2

                    # retrieve MPHMACContainer and MPContentContainer
                    mp_hmac_container = message_packet_asn1[0][2]
                    mp_content_container = message_packet_asn1[0][3]

                    # verify hmac
                    hmac_ver_res = self.__verify_hmac(
                        mp_hmac_container, hmac_key,
                        asn1_encode(mp_content_container))

                    if not hmac_ver_res:
                        # TODO: more verbose str
                        raise exc.HMACVerificationFailed("")

                    # all checks were successful - decrypt content
                    timestamp, message = self.__disassemble_content_block(
                        mp_content_container, aes_key)

                    self.logger.info(logstr.MSG_CONTENT_WAS_RECOVERED)

                    # return correct result
                    if signer_info:
                        return timestamp, message, exit_code, signer_info
                    return timestamp, message, exit_code

        # TODO: create more verbose exception
        self.logger.info(logstr.MESSAGE_NOT_FOR_USER)
        raise exc.NoMatchingRSAKeyForMessage("")