Exemple #1
0
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
Exemple #2
0
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
Exemple #3
0
def apply_credit_delta(identifier, delta):
    if identifier not in data.credit_accounts:
        logger.log_warn('identifier `{}` not found.'.format(identifier))
        return

    data.credit_accounts[identifier] = (data.credit_accounts[identifier][0],
                                        data.credit_accounts[identifier][1] +
                                        delta)
Exemple #4
0
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
Exemple #5
0
def get_account_balance_bytes(identifier):
    if identifier not in data.credit_accounts:
        logger.log_warn('identifier `{}` not found.'.format(identifier))
        return

    balance = data.credit_accounts[identifier][1]

    if balance == int(balance):
        return bytes(str(int(balance)), encoding='utf-8') + config.CURRENCY
    else:
        return bytes(str(balance), encoding='utf-8') + config.CURRENCY
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
Exemple #7
0
def should_proceed_transaction(transaction_context):
    if transaction_context['bid'] is None:
        logger.log_warn('there is no `bid` set for transaction')
        return False

    bid_value = float(transaction_context['bid'][:-len(config.CURRENCY)])
    price_value = float(transaction_context['price'][:-len(config.CURRENCY)])

    # custom, complex logic can be implemented here

    if bid_value / price_value >= BID_PRICE_RATIO_THRESHOLD:
        return True

    return False
Exemple #8
0
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
Exemple #9
0
def register_group():
    logger.log_access(request)
    message_enc = messaging.get_request_data(request)
    message = cryptography.two_layer_sym_asym_decrypt(message_enc,
                                                      data.private_key)

    group_identifier, group_public_key, transactor_identifier = message.split(
        SEP)

    warn_message = None
    if transactor_identifier != data.identifier:
        warn_message = 'invalid transactor identifier.'
    elif group_identifier in data.public_keychain:
        warn_message = 'group exists. aborting.'

    if warn_message is not None:
        logger.log_warn(warn_message)
        return warn_message, 403

    data.public_keychain[group_identifier] = group_public_key

    return 'success', 200
Exemple #10
0
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
Exemple #11
0
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
Exemple #12
0
def get_account_balance(identifier):
    if identifier not in data.credit_accounts:
        logger.log_warn('identifier `{}` not found.'.format(identifier))
        return

    return data.credit_accounts[identifier][1]
Exemple #13
0
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
Exemple #14
0
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
Exemple #15
0
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