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
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()
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
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
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("")