def two_layer_sym_asym_encrypt(message_bytes, public_key_bytes): nonce = generator.generate_nonce() symmetric_key = generate_symmetric_key() encrypted_message = encrypt_sym(message_bytes=SEP.join([message_bytes, nonce]), key_bytes=symmetric_key) encrypted_key = encrypt_asym(message_bytes=symmetric_key, puk_bytes=public_key_bytes) return SEP.join([encrypted_message, encrypted_key])
def create_ticket(): logger.log_access(request) message = messaging.get_request_data(request) encrypted_data = cryptography.strip_clear_signed_message(message) decrypted_data = cryptography.two_layer_sym_asym_decrypt( encrypted_data, data.private_key) client_identity, transactor_id, timestamp, symmetric_key = decrypted_data.split( SEP) if data.identifier != transactor_id: warn_message = 'transactor id does not match.' logger.log_warn(warn_message) return warn_message, 403 client_access_symmetric_key = cryptography.generate_symmetric_key() client_address = request.remote_addr.encode('utf-8') ticket_start_timestamp, ticket_end_timestamp = generator.get_ticket_life_span( ) ticket_unencrypted = SEP.join([ client_access_symmetric_key, client_identity, client_address, ticket_start_timestamp, ticket_end_timestamp ]) ticket = cryptography.encrypt_asym(ticket_unencrypted, data.public_key) response = cryptography.encrypt_sym( SEP.join([ticket, client_access_symmetric_key]), symmetric_key) logger.log_info( 'generated access ticket and key for client id `{}`'.format( client_identity)) return response, 200
def clear_sign(message_bytes, private_key_bytes): nonce = generator.generate_nonce() timestamp = generator.get_timestamp() final_message_bytes = SEP.join([message_bytes, nonce, timestamp]) signature = sign_message(final_message_bytes, private_key_bytes) return SEP.join([final_message_bytes, signature])
def create_psudonym(): logger.log_access(request) message = messaging.get_request_data(request) two_layer_enc_message, encrypted_key, nonce, timestamp, signature = message.split( SEP) plain_message = cryptography.two_layer_sym_asym_decrypt( SEP.join([two_layer_enc_message, encrypted_key]), data.private_key) true_identity, merchant_identifier, timestamp, offered_symmetric_key, type_ = plain_message.split( SEP) warn_message = None if true_identity not in data.public_keychain: warn_message = 'client unknown' elif merchant_identifier not in data.public_keychain: warn_message = 'merchant unknown' elif not cryptography.verify_clear_signature( message, data.public_keychain[true_identity]): warn_message = 'clear signature not verified.' elif type_ not in [ PsudonymTypes.PER_MERCHANT.value, PsudonymTypes.PER_SESSION.value ]: warn_message = 'invalid psudonym type.' elif type_ == PsudonymTypes.PER_SESSION: warn_message = 'psudonym type not implemented.' if warn_message is not None: logger.log_warn(warn_message) return warn_message, 403 psudonym_symmetric_key = cryptography.generate_symmetric_key() psudonym = generator.generate_random_identity() timestamp = generator.get_timestamp() psudonym_ticket = SEP.join( [psudonym, merchant_identifier, timestamp, psudonym_symmetric_key]) psudonym_ticket_two_layer_enc = \ cryptography.two_layer_sym_asym_encrypt(psudonym_ticket, data.public_keychain[merchant_identifier]) psudonym_ticket_two_layer_enc_clear_signed = cryptography.clear_sign( psudonym_ticket_two_layer_enc, data.private_key) psudonym_receipt = SEP.join( [true_identity, merchant_identifier, psudonym, timestamp]) psudonym_receipt_clear_signed = cryptography.clear_sign( psudonym_receipt, data.private_key) final_message_to_encrypt = SEP.join([ psudonym_symmetric_key, psudonym_ticket_two_layer_enc_clear_signed, psudonym_receipt_clear_signed ]) final_encrypted_message = cryptography.encrypt_sym( final_message_to_encrypt, offered_symmetric_key) return final_encrypted_message, 200
def verify_clear_signature(clear_signed_message_bytes, public_key_bytes): parts = clear_signed_message_bytes.split(SEP) message_bytes = SEP.join(parts[:-3]) nonce = parts[-3] timestamp = parts[-2] signature = parts[-1] verified = verify_signature(public_key_bytes, signature, SEP.join([message_bytes, nonce, timestamp])) if not verified: logger.log_warn('clear signature verification failed.') return message_bytes, verified
def register_merchant(): logger.log_access(request) message_enc = messaging.get_request_data(request) message = cryptography.two_layer_sym_asym_decrypt(message_enc, data.private_key) merchant_identifier, merchant_public_key, transactor_identifier = message.split( SEP) warn_message = None if transactor_identifier != data.identifier: warn_message = 'invalid transactor identifier.' elif merchant_identifier in data.public_keychain: warn_message = 'merchant exists. aborting.' if warn_message is not None: logger.log_warn(warn_message) return warn_message, 403 data.public_keychain[merchant_identifier] = merchant_public_key account_number = generator.generate_random_account_number() nonce = generator.generate_nonce() account_receipt = SEP.join([account_number, nonce]) account_receipt_enc = cryptography.two_layer_sym_asym_encrypt( account_receipt, merchant_public_key) data.credit_accounts[merchant_identifier] = (( account_number, nonce), config.INITIAL_MERCHANT_CREDIT) return account_receipt_enc, 200
def dsa_sign(message_bytes, private_key_bytes): private_key = __loads_prk(private_key_bytes) signature = private_key.sign( message_bytes, hashes.SHA256() ).hex().encode('utf-8') return SEP.join([message_bytes, signature])
def register_on_transactor(): transactor_id, transactor_public_key = data.get_transactor_contact() message = SEP.join([data.identifier, data.public_key, transactor_id]) message_encrypted = cryptography.two_layer_sym_asym_encrypt( message, transactor_public_key) messaging.transmit_message_and_get_response( config.ADDRESS_BOOK[config.TRANSACTOR_ID], static.REGISTER_GROUP, message_encrypted)
def register_on_transactor(): transactor_id, transactor_public_key = data.get_transactor_contact() message = SEP.join([data.identifier, data.public_key, transactor_id]) message_encrypted = cryptography.two_layer_sym_asym_encrypt( message, transactor_public_key) response = messaging.transmit_message_and_get_response( config.ADDRESS_BOOK[config.TRANSACTOR_ID], static.REGISTER_MERCHANT, message_encrypted) response_plain = cryptography.two_layer_sym_asym_decrypt( response, data.private_key) account_number, nonce = response_plain.split(SEP) data.account = (account_number, nonce)
def request_goods(): logger.log_access(request) message = messaging.get_request_data(request) ticket, goods_request_enc = message.split(SEP) sym_key, _, _, _, _ = cryptography.open_ticket(ticket, data.private_key) transaction_id = cryptography.decrypt_sym(goods_request_enc, sym_key) transaction_context = data.transaction_context[transaction_id] warn_message = None if transaction_id not in data.transaction_context: warn_message = 'invalid `transaction_id`' elif not should_proceed_transaction(transaction_context): warn_message = 'transaction not accepted by merchant' if warn_message is not None: logger.log_warn(warn_message) return warn_message, 403 goods_delivery_key = cryptography.generate_symmetric_key() product = __get_product(product_id=transaction_context['product_id']) product_enc = cryptography.encrypt_sym(product, goods_delivery_key) product_enc_checksum = cryptography.cryptographic_checksum(product_enc) epoid_serial_number = generator.generate_epoid_serial_number() data.epoid_serial_key_map[epoid_serial_number] = goods_delivery_key data.epoid_serial_transaction_id_map[epoid_serial_number] = transaction_id merchant_id = data.identifier receipt_plain = SEP.join( [product_enc_checksum, merchant_id, epoid_serial_number]) receipt_enc = cryptography.encrypt_sym(receipt_plain, sym_key) response = SEP.join([product_enc, receipt_enc]) return response, 200
def request_credentials(): logger.log_access(request) message = messaging.get_request_data(request) ticket, credentials_request_enc = message.split(SEP) sym_key, client_identity, _, _, _ = cryptography.open_ticket( ticket, data.private_key) credentials_request = cryptography.decrypt_sym(credentials_request_enc, sym_key) group_id, account_number = credentials_request.split(SEP) if group_id != data.identifier: warn_message = '`group_id` mismatch' logger.log_warn(warn_message) return warn_message, 403 detail = b'' nonce = generator.generate_nonce() account_number_nonce_checksum = cryptography.cryptographic_checksum( SEP.join([account_number, nonce])) timestamp = generator.get_timestamp() receipt = SEP.join([ group_id, detail, client_identity, account_number_nonce_checksum, timestamp ]) receipt_clear_signed = cryptography.clear_sign(receipt, data.private_key) response_plain = SEP.join([receipt_clear_signed, nonce]) response = cryptography.encrypt_sym(response_plain, sym_key) return response, 200
def dsa_verify(signed_message_bytes, public_key_bytes): public_key = __loads_puk(public_key_bytes) parts = signed_message_bytes.split(SEP) signature = bytes.fromhex(parts[-1].decode('utf-8')) message = SEP.join(parts[:-1]) try: public_key.verify( signature, message, hashes.SHA256() ) return message, True except InvalidSignature: return None, False
def __create_identity_ticket_request(merchant_id): if merchant_id not in data.public_keychain: logger.log_warn('merchant id', merchant_id, 'not found.') return identity = data.identifier timestamp = generator.get_timestamp() symmetric_key = cryptography.generate_symmetric_key() message_to_encrypt = SEP.join( [identity, merchant_id, timestamp, symmetric_key]) message_encrypted = cryptography.two_layer_sym_asym_encrypt( message_to_encrypt, data.public_keychain[merchant_id]) message_encrypted_signed = cryptography.clear_sign(message_encrypted, data.private_key) return symmetric_key, message_encrypted_signed
def submit_signed_epo(): logger.log_access(request) message = messaging.get_request_data(request) ticket, signed_epo_enc = message.split(SEP) client_sym_key, _, _, _, _ = cryptography.open_ticket( ticket, data.private_key) signed_epo = cryptography.decrypt_sym(signed_epo_enc, client_sym_key) epo = cryptography.strip_clear_signed_message(signed_epo) parts = epo.split(SEP) client_identity = parts[0] product_id = parts[1] price_to_pay = parts[2] merchant_id = parts[3] enc_product_checksum = parts[4] product_request_data_checksum = parts[5] account_data_checksum = parts[6] epoid_plain = SEP.join(parts[7:10]) client_transactor_ticket = parts[10] order_for_transactor = parts[11] epoid_merchant_id, epoid_timestamp, epoid_serial_number = epoid_plain.split( SEP) warn_message = None if epoid_merchant_id != data.identifier or merchant_id != data.identifier: warn_message = '`merchant_id` mismatch' elif epoid_serial_number not in data.epoid_serial_key_map: warn_message = 'invalid `epo_id_serial_number`' if warn_message is not None: logger.log_warn(warn_message) return warn_message, 403 product_key = data.epoid_serial_key_map[epoid_serial_number] transaction_id = data.epoid_serial_transaction_id_map[epoid_serial_number] transaction_context = data.transaction_context[transaction_id] warn_message = None if transaction_context['peer_id'] != client_identity: warn_message = '`client_id` mismatch' if cryptography.cryptographic_checksum( transaction_context['product_request_data'] ) != product_request_data_checksum: warn_message = 'corrupted `product_request_data`' if transaction_context['product_id'] != product_id: warn_message = '`product_id` mismatch' if warn_message is not None: logger.log_warn(warn_message) return warn_message, 403 transactor_ticket, transactor_sym_key = get_ticket_key_id( config.TRANSACTOR_ID) merchant_memo = b'' endorsed_signed_epo_plain = SEP.join( [signed_epo, data.account[0], merchant_memo, product_key]) endorsed_signed_epo_plain_signed = cryptography.clear_sign( endorsed_signed_epo_plain, data.private_key) endorsed_signed_epo_plain_signed_encrypted = cryptography.encrypt_sym( endorsed_signed_epo_plain_signed, transactor_sym_key) message_to_transactor = SEP.join( [transactor_ticket, endorsed_signed_epo_plain_signed_encrypted]) transactor_response = messaging.transmit_message_and_get_response( config.ADDRESS_BOOK[config.TRANSACTOR_ID], static.SUBMIT_ENDORSED_SIGNED_EPO, message_to_transactor) transactor_response_plain = cryptography.decrypt_sym( transactor_response, transactor_sym_key) parts = transactor_response_plain.split(SEP) client_receipt = parts[-1] transaction_receipt_dsa_signed = SEP.join(parts[:-1]) transaction_receipt, verified = cryptography.dsa_verify( transaction_receipt_dsa_signed, data.transactor_dsa_public_key) if not verified: warn_message = 'transaction receipt not verified' logger.log_warn(warn_message) return warn_message, 403 parts = transaction_receipt.split(SEP) receipt_result = parts[0] receipt_client_identity = parts[1] receipt_price = parts[2] receipt_product_id = parts[3] receipt_merchant_id = parts[4] receipt_product_key = parts[5] receipt_epoid = SEP.join(parts[6:]) warn_message = None if receipt_client_identity != client_identity: warn_message = '`client_id` mismatch' elif receipt_price != transaction_context['bid']: warn_message = 'paid `price` mismatch' elif receipt_product_id != product_id: warn_message = '`product_id` mismatch' elif receipt_merchant_id != merchant_id or receipt_merchant_id != data.identifier: warn_message = '`merchant_id` mismatch' elif receipt_product_key != product_key: warn_message = '`product_key` mismatch' elif receipt_epoid != epoid_plain: warn_message = '`epoid` mismatch' if warn_message is not None: logger.log_warn(warn_message) return warn_message, 403 return cryptography.encrypt_sym(transactor_response_plain, client_sym_key), 200
def get_contact_info(): logger.log_access(request) contact_info = SEP.join( [data.identifier, data.public_key, data.dsa_public_key]) return contact_info, 200
def submit_signed_endorsed_epo(): logger.log_access(request) message = messaging.get_request_data(request) ticket, signed_endorsed_epo_enc = message.split(SEP) merchant_sym_key, merchant_identity, _, _, _ = cryptography.open_ticket( ticket, data.private_key) signed_endorsed_epo_clear_signed = cryptography.decrypt_sym( signed_endorsed_epo_enc, merchant_sym_key) signed_endorsed_epo, verified = cryptography.verify_clear_signature( signed_endorsed_epo_clear_signed, data.public_keychain[merchant_identity]) if not verified: warn_message = 'client epo signature not verified' logger.log_warn(warn_message) return warn_message, 403 parts = signed_endorsed_epo.split(SEP) product_key = parts[-1] merchant_memo = parts[-2] merchant_account = parts[-3] signed_epo = SEP.join(parts[:-3]) epo = cryptography.strip_clear_signed_message(signed_epo) parts = epo.split(SEP) client_identity = parts[0] product_id = parts[1] price_to_pay = parts[2] epo_merchant_id = parts[3] enc_product_checksum = parts[4] product_request_data_checksum = parts[5] account_data_checksum = parts[6] epoid_plain = SEP.join(parts[7:10]) client_transactor_ticket = parts[10] order_for_transactor = parts[11] if client_identity not in data.public_keychain: warn_message = 'unknown client `{}`'.format(client_identity) logger.log_warn(warn_message) return warn_message, 403 _, verified = cryptography.verify_clear_signature( signed_epo, data.public_keychain[client_identity]) client_sym_key, ticket_client_identity, _, _, _ = cryptography.open_ticket( client_transactor_ticket, data.private_key) price_number = generator.get_price_number(price_to_pay) order_for_transactor_plain = cryptography.decrypt_sym( order_for_transactor, client_sym_key) client_authorization, client_account_number, client_account_nonce, client_memo = order_for_transactor_plain.split( SEP) warn_message = None if not verified: warn_message = 'client epo signature not verified' elif epo_merchant_id != merchant_identity: warn_message = '`merchant_id` mismatch' elif client_identity != ticket_client_identity: warn_message = '`client_id` mismatch' elif (client_account_number, client_account_nonce) != data.credit_accounts[client_identity][0]: warn_message = 'mismatch client account data' elif merchant_account != data.credit_accounts[merchant_identity][0][0]: warn_message = 'mismatch client account number' elif cryptography.cryptographic_checksum( SEP.join(data.credit_accounts[client_identity] [0])) != account_data_checksum: warn_message = 'corrupt client account checksum data' elif epoid_plain in data.past_epoids: warn_message = 'repetitive epo' elif data.credit_accounts[client_identity][1] < price_number: warn_message = 'insufficient funds' if warn_message is not None: logger.log_warn(warn_message) return warn_message, 403 data.past_epoids.add(epoid_plain) apply_credit_delta(client_identity, -price_number) apply_credit_delta(merchant_identity, +price_number) result = TransactionResultType.SUCCESS.value transaction_receipt_plain = SEP.join([ result, client_identity, price_to_pay, product_id, merchant_identity, product_key, epoid_plain ]) transaction_receipt_dsa_signed = cryptography.dsa_sign( transaction_receipt_plain, data.dsa_private_key) flags = b'' client_receipt_plain = SEP.join([ epoid_plain, client_account_number, generator.get_price_bytes(data.credit_accounts[client_identity][1]), flags ]) client_receipt = cryptography.encrypt_sym(client_receipt_plain, client_sym_key) response_plain = SEP.join([transaction_receipt_dsa_signed, client_receipt]) response = cryptography.encrypt_sym(response_plain, merchant_sym_key) return response, 200
def strip_clear_signed_message(clear_signed_message_bytes): parts = clear_signed_message_bytes.split(SEP) return SEP.join(parts[:-3])
def two_layer_sym_asym_decrypt(message_bytes, private_key_bytes): enc_message_bytes, enc_key_bytes = message_bytes.split(SEP) symmetric_key = decrypt_asym(enc_key_bytes, private_key_bytes) plain_bytes = decrypt_sym(enc_message_bytes, symmetric_key) plain_message = SEP.join(plain_bytes.split(SEP)[:-1]) return plain_message
def request_price(): logger.log_access(request) message = messaging.get_request_data(request) ticket, price_request_enc = message.split(SEP) sym_key, client_identity, _, _, _ = cryptography.open_ticket( ticket, data.private_key) price_request = cryptography.decrypt_sym(price_request_enc, sym_key) parts = price_request.split(SEP) transaction_id = parts[-1] bid = parts[-3] product_request_data = parts[-4] credentials = SEP.join(parts[:-4]) if credentials == b'': credentials = None if transaction_id == b'': transaction_id = generator.generate_random_transaction_id() if bid == b'': bid = None product_id = __get_product_id_from_product_request_data( product_request_data) if product_id not in config.PRODUCT_SHELF[data.identifier]: warn_message = 'product `{}` is not present in shelf for merchant `{}`'.format( product_id, data.identifier) logger.log_warn(warn_message) return warn_message, 403 if credentials is not None: credentials_message = cryptography.strip_clear_signed_message( credentials) group_id, detail, credentials_client_identity, account_checksum, timestamp = credentials_message.split( SEP) if group_id not in data.trusted_groups: warn_message = 'unknown group' logger.log_warn(warn_message) return warn_message, 403 _, verified = cryptography.verify_clear_signature( credentials, data.public_keychain[group_id]) if not verified: warn_message = 'group credentials not verified' logger.log_warn(warn_message) return warn_message, 403 price = __calculate_discounted_price(group_id, detail, credentials_client_identity, account_checksum, product_id) else: price = config.PRODUCT_SHELF[ data.identifier][product_id] + config.CURRENCY request_flags = b'' response_plain = SEP.join( [product_id, price, request_flags, transaction_id]) response = cryptography.encrypt_sym(response_plain, sym_key) data.update_transaction_context(transaction_id, product_request_data, product_id, bid, price, client_identity) return response, 200