Payment account age witness - Bisq Wiki


Payment account age witness - Payment account age witness is a mechanism to protect against bank fraud when doing fiat transactions with strangers on the internet. Payment account aging worked well for about 2 years until it was significant...



Onion Details



Page Clicks: 1

First Seen: 03/11/2024

Last Indexed: 10/21/2024

Domain Index Total: 237



Onion Content



Payment account age witness From Bisq Wiki Jump to navigation Jump to search Payment account age witness is a mechanism to protect against bank fraud when doing fiat transactions with strangers on the internet. Payment account aging worked well for about 2 years until it was significantly improved in v1.5 with account signing . This article is adapted from a document originally hosted on the old documentation website ( https://docs.bisq.network ). It was written by Manfred Karrer and originally published on 14 September 2017. Contents 1 Problem 2 Overview 3 AccountAgeWitness 3.1 AccountAgeWitness data object 3.2 AccountAgeWitness propagation 3.3 Offer 3.4 Verification 4 Attempts to game the system 4.1 Broadcasting a back-dated AccountAgeWitness object 4.2 Hijacking a foreign AccountAgeWitness 4.3 Changing a foreign AccountAgeWitness 4.4 Using an older version of Bisq 5 User interface 5.1 Salt management 6 Update and migration process Problem Because bank transfers can be canceled and bitcoin transactions cannot be canceled, buying bitcoin with a stolen bank account is an attractive proposition for criminals. If a criminal does manage to buy bitcoin with Bisq using a stolen bank account, the rightful owner of the stolen bank account will probably discover the fraud and immediately cancel the payment (i.e., initiate a chargeback). Unfortunately, the bitcoin seller will have already unlocked their deposit and will lose the fiat payment they received. Payment account aging seeks to make it less practical for a criminal to cash out a stolen bank account in this way. The account aging protection mechanism described in this article assumes the following: a criminal will want to withdraw funds from a stolen account as quickly as possible a criminal would prefer to do a few large transactions instead of many small transactions (because with each transaction, risk of the fraud being discovered goes higher) With trade size limits in Bisq, there already is some protection against this fraud scheme, but it would be even better to increase security even more by adding a verification scheme for the age of the payment account within Bisq. To protect user privacy we use a hashing scheme. Only the other trading peer (who gets the other peer's payment account data anyway, so they can make the payment) can verify that the hash provided in the offer matches the peer's payment account hash. Overview The initial idea for this scheme was already discussed on the Bisq forum ( one , two ). This mechanism is only relevant for fiat payment accounts, because with altcoins, there is no chargeback risk. We posit that a 60-day-old payment account should be safe (it seems highly unlikely that a stolen bank account will not get discovered for such a long time). This is the core premise of account aging : from the time a user sets up a payment account in Bisq, until 60 days later, Bisq limits the size of trades handled by that payment account. Cheating this mechanism would be difficult because a scammer cannot know a stolen account's information before stealing the account. Consequently, we use following trade limits: Account age is 0-30 days: 25% of the payment method's maximum (e.g. 0.125 BTC if maximum is 0.5 BTC) Account age is 30-60 days: 50% of the payment method's maximum (e.g. 0.25 BTC if maximum is 0.5 BTC) Account age is 60+ days: 100% of the payment method's maximum (e.g. 0.50 BTC if maximum is 0.50 BTC) Please note that the numbers above are for illustration purposes only; trade size limits will fluctuate based on BTC market price. See trade limits for a listing of payment methods and their maximum allowed trade sizes. AccountAgeWitness When a user sets up a fiat payment account (e.g. SEPA, Zelle, etc​) in Bisq, he publishes an AccountAgeWitness data object to the P2P network. AccountAgeWitness data object The AccountAgeWitness object contains a hash and the date of publishing. // Ripemd160(Sha256(ageWitnessInputData, salt and pubKey)) - 20 bytes private final byte[] hash; // 8 bytes private final long date; The hash is created with Sha256 and wrapped into a Ripemd160 hash to get a 20 byte hash instead of 32 bytes as it would be with Sha256. Input for the hash is a concatenation of the ageWitnessInputData (e.g. IBAN), a 256 bit salt and the pubKey. The ageWitnessInputData is the smallest set of uniquely identifying payment account data (e.g. concatenated IBAN and BIC). We don’t use the complete payment account data because we don’t want to break an existing ageWitness by minor changes like changing the holder name (e.g. adding middle name). The public key is used in the verification process to check the signature which will get passed in the trade process. That will assure that the AccountAgeWitness data cannot be used by anyone else (see: Hijacking a foreign AccountAgeWitness ). The application wide 1024 bit DSA signing key is used. Signature algorithm is "SHA256withDSA". The salt value will be locally persisted with the payment account object. The date must not be older or newer than 1 day compared to a receiving peer’s local date. If the date is outside of that tolerance range the AccountAgeWitness object will get ignored and not further broadcasted. With that check we protect against back-dating attempts (see: Broadcasting a back-dated AccountAgeWitness object ). We allow a rather large tolerance because computer clocks might be out of sync and the relevant periods are rather long (30 or 60 days), so the max. gain from an abuse of that tolerance window of 1 day is negligible. This AccountAgeWitness data structure results in 28 bytes per item but as we use Protobuffer there is some overhead added which results in 33 bytes per data item. If we store 1 000 AccountAgeWitness objects we would have about 33 MB of data. The data are locally persisted and with every release we ship the latest state in a resource file. That helps that new users don’t need to retrieve all data from the P2P network. We also use a diff when requesting so we only request the missing data from the seed node at startup. If the data would become too large, we can consider a time-to-live (TTL) mechanism where AccountAgeWitness objects need to get triggered with a refresh message to stay active. That way outdated objects which have not received any TTL signal since a long period (e.g. 6 months) would get pruned. // class AccountAgeWitnessService public AccountAgeWitness getMyWitness(PaymentAccountPayload paymentAccountPayload) { byte[] accountInputDataWithSalt = Utilities.concatenateByteArrays(paymentAccountPayload.getAgeWitnessInputData(), paymentAccountPayload.getSalt()); byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(accountInputDataWithSalt, keyRing.getPubKeyRing().getSignaturePubKeyBytes())); long date = new Date().getTime(); return new AccountAgeWitness(hash, date); } // getAgeWitnessInputData at example class SepaAccount public byte[] getAgeWitnessInputData() { // We don't add holderName because we don't want to break age validation if the user recreates an account with // slight changes in holder name (e.g. add or remove middle name) return super.getAgeWitnessInputData(ArrayUtils.addAll(iban.getBytes(Charset.forName("UTF-8")), bic.getBytes(Charset.forName("UTF-8")))); } // CountryBasedPaymentAccountPayload super class @Override protected byte[] getAgeWitnessInputData(byte[] data) { return super.getAgeWitnessInputData(ArrayUtils.addAll(countryCode.getBytes(Charset.forName("UTF-8")), data)); } // PaymentAccountPayload base class protected byte[] getAgeWitnessInputData(byte[] data) { return ArrayUtils.addAll(paymentMethodId.getBytes(Charset.forName("UTF-8")), data); } // Getting salt from CryptoUtils, called from PaymentAccountPayload constructor with size 32 public static byte[] getRandomBytes(int size) { byte[] bytes = new byte[size]; new SecureRandom().nextBytes(bytes); return bytes; } AccountAgeWitness propagation The user will publish the AccountAgeWitness data when setting up the payment account and re-publish at each startup to ensure higher redundancy. Peers who have the data already will not broadcast it further. The AccountAgeWitness data will be distributed in the P2P network and stored locally at each user. At each new release we will ship the actual data set as resource file (e.g. PersistableNetworkPayload_BTC_MAINNET ) with the application binary to avoid that new users need to download the complete data set. When a node receives an AccountAgeWitness object it verifies that the tradeDate is not older or newer than 1 day compared with the local time of the node, otherwise it will reject the object. The date check is only done when receiving the data via the P2P network broadcasting, otherwise we could not fill up our initial map received form the seed node with the past distributed AccountAgeWitness objects. NOTE: There is no date check for the data we receive from seed nodes. This is in the current state not an issue because the seed nodes are bonded with BSQ against abuse but in future improvements we would like to distribute more functions from the seed node to ordinary nodes and then there is a security issue with that. There is no date check for the data we receive from seed nodes. This is currently not an issue because seed nodes are bonded with BSQ against abuse but in future improvements we would like to distribute more functions from the seed node to ordinary nodes and then there is a security issue with that. Offer The offer maker will add the hash used in the AccountAgeWitness object to his offer. With that hash all users can look up if they have an AccountAgeWitness matching the hash and if so they can evaluate the account age. The account age will be visually displayed in the offerbook. At that stage nobody can verify if the hash is matching the real payment account data. But this is not a problem because the verification will be done once someone takes the offer. A fraudulent offer would cause a failure in the take offer process. Verification When a trader takes an offer both users are exchanging in the trade process the signature of data defined by the other peer (for taker we use the offer ID, for maker we use the takers preparedDepositTx - we use that data like a nonce for the signature), the pubKey, the salt and the peer’s local date. With that data the other peer can verify that the other trader is the owner of the AccountAgeWitness data (as the pubKey is part of the hash and the signature gets verified with pubKey and predefined input data) and that the hash is matching the account data used for the trade. As the date of both users will differ at least slightly we exchange the peer’s local date and use that for calculating the age and trade limit. The date needs to be inside a 1 day tolerance otherwise the trade fails. That way we avoid problems with corner cases when the age just enters the next level for one peer but the verifying p