XRPLF / XRPL-Standards

XLS: XRP(L) community defined Suggestions, Proposals, RFCs / Standards / Drafts & discussions, to be added to the core protocol, used for platform & apps devemopment, etc.
MIT License
191 stars 53 forks source link

XLS-9d: Blinded Tags #10

Closed nbougalis closed 3 years ago

nbougalis commented 4 years ago
  XRP Ledger Proposed Standard #9

  Title:        Blinded Tags
  Author:       Nikolaos D. Bougalis 
  Affiliation:  Ripple
  Created:      2020-03-30

Abstract

The XRP Ledger supports source and destination tags which can allow a single account to hold funds for multiple users. These arrangements are commonly called hosted wallets. When sending from a hosted wallet, the account uses a source tag to tell which user prompted the transaction. When sending to a hosted wallet, the destination tag tells the account how to further distribute those funds.

A typical example is an exchange like Bitstamp. When you want to deposit funds, Bitstamp provides two pieces of information: an address identifying a Bitstamp wallet, and a destination tag. Several (if not all) Bitstamp customers use the same Bitstamp address, but each customer gets a separate, unique destination tag.

A destination tag is presently an unsigned 32-bit integer. This means that there are 4,294,967,296 possible destination tags. While that's certainly enough tags to allow an exchange to use a unique tag per customer today and perhaps even for the foreseeable future, using a fixed tag presents privacy challenges: it allows transactions to be correlated by treating the { address, tag } pair as a unique address corresponding to a single customer.

As a possible workaround, exchanges could allow users to generate new tags as necessary. In fact, several existing exchanges do just that. The problem with this scheme is that it complicates things for the exchange and for the customer. This also uses more destination tags, which are still finite.

This proposal attempts to alleviate this concern by specifying a standard that allows for tag blinding. A blinded tag is mutated in such a way that it is meaningful only to the sender and the recipient of a transaction, but appears random to everyone else.

The Proposal

The goal of this proposal is to ensure that if blinded tags are in use, an attacker capable of observing every payment transaction will be unable to isolate a pair of transactions that refer to the same unblinded tag. This proposal aims to be secure, minimal, and performant; ideally, it should be possible to implement tag blinding as a single function call that does not noticeably increase the time necessary to assemble a transaction. Similarly, using a blinded tag should not make it significantly harder for the intended recipient to process a transaction.

Protocol Changes

This proposal assumes several new flags and fields for supporting blinded destination tags. One amendment to the XRP Ledger would introduce all of the following:

Two new AccountRoot ledger-state flags:

Two corresponding AccountSet flags to be used for enabling and disabling these flags:

Interaction between lsfRequireDest and blinded tags:

If the existing lsfRequireDest account flag is set on an account, then all transactions for which a DestinationTag can be specified must include a destination tag. For accounts which support blinded tags, the BlindedDestinationTag may be specified instead. More specifically:

lsfRequireDest lsfSupportsBlindedTags lsfRequiresBlindedTags Description
:x: :x: :x: The DestinationTag may be used. The BlindedDestinationTag may not be used.
:x: :x: :heavy_check_mark: The BlindedDestinationTag may be used. The DestinationTag may not be used.
:x: :heavy_check_mark: :x: The DestinationTag or BlindedDestinationTag may be used.
:x: :heavy_check_mark: :heavy_check_mark: Invalid configuration.
:heavy_check_mark: :x: :x: The DestinationTag is required. The BlindedDestinationTag may not be used.
:heavy_check_mark: :x: :heavy_check_mark: The BlindedDestinationTag is required. The DestinationTag may not be used.
:heavy_check_mark: :heavy_check_mark: :x: Either the DestinationTag or BlindedDestinationTag is required.
:heavy_check_mark: :heavy_check_mark: :heavy_check_mark: Invalid configuration.

New optional Transaction fields:

Tag Type Description
BlindedSourceTag 64-bit UInt Blinded analog of SourceTag. If this tag is provided, the SourceTag field MUST NOT be provided.
BlindedDestinationTag 64-bit UInt Blinded analog of DestinationTag. If this tag is provided, the DestinationTag field MUST NOT be provided.
KeyIdentifier 32-bit UInt MUST BE provided if and only if the transaction also provides a BlindedSourceTag field, BlindedDestinationTag field, or both.
RandomValue 256-bit UInt MUST BE provided if and only if the transaction also provides a BlindedSourceTag field, BlindedDestinationTag field, or both.

Note: The new fields are supported on transaction which support the DestinationTag and SourceTag fields. Not all transaction types support them.

The rest of this specification describes the use and meaning of these new fields and flags.

Usage

In order to receive transactions with blinded tags, individuals accounts must opt-in to the feature set. This will be accomplished through the new and existing account settings, which can be enabled with an AccountSet transaction.

You do not need to configure any settings in the XRP Ledger to send transactions using blinded destination tags.

ECIES

This proposal uses ECIES to securely derive a shared key between the sender and the recipient, from which the blinding factors can be derived.

The ECIES process is different for the sender and the recipient.

Common Information required

Both sender and recipient need the following common information:

  1. The elliptic curve domain parameters for secp256k1 ( p , a , b , G , n , h ).
  2. The key derivation function KDF = RIPEMD-160.
  3. The sequence number (or ticket number) associated with the transaction, N.
Sender

The process always begins with the sender, who can choose whether to use blinded tags or not. Depending on the configuration of the recipient's account, blinded destination tags may or may not be supported and, if supported, may or may not be required. It is the responsibility of the sender to assemble the transaction appropriately.

In addition to the common information, the sender requires:

Steps:
  1. Generate a random number r, between 0 and 2128-1, and calculate R = rG.
  2. Calculate P = rKpub.
  3. Calculate the shared encryption key X = KDF(Px || N),...) where Px represents the x-coordinate of the point P which should not be the point at infinity, and ... denotes any additional parameters the function may require.
  4. Return {X, R}.
Recipient

In addition to the common information, the recipient requires:

Steps:
  1. Calculate P = KsecR.
  2. Calculate the shared encryption key X = KDF(Px || N),...) where Px represents the x-coordinate of the point P which should not be the point at infinity, and ... denotes any additional parameters the function may require.
  3. Return {X, R}.

Random Blinding Factor Generation

The sender and recipient must generate the same blinding factors in order for this proposal to work. This specification explicitly outlines the algorithm to use. To ensure interoperability, implementers must not deviate from this specification.

Using ECIES the sender and recipient can each derive the same 160-bit shared secret value X, then split this value into a 32-bit key identifier and two 64-bit blinding factors, as follows:

Both 64-bit tag blinding factors must remain secret. The 32-bit key identifier is made public.

What the sender of a transaction does

If the sender determines that the recipient of a transaction supports blinded tags, the sender does the following:

  1. Calculate the shared secret using ECIES.
  2. If a blinded destination tag is required:
    1. Start with an unblinded destination tag, Udst.
    2. Calculate the destination tag blinding factor Bdst as explained above.
    3. Calculate the blinded destination tag as Tdst = Udst ⊕ Bdst.
    4. Set the BlindedDestinationTag field in the transaction to Tdst.
  3. If a blinded source tag is required:
    1. Start with an unblinded source tag, Usrc.
    2. Calculate the source tag blinding factor Bsrc as explained above.
    3. Calculate Tsrc = Usrc ⊕ Bsrc.
    4. Set the BlindedSourceTag field in the transaction to Tsrc.
  4. Calculate the key identifier Z as explained above.
  5. Set the KeyIdentifier field in the transaction to Z.
  6. Set the RandomValue field in the transaction to R.

What the recipient of a transaction does:

When the recipient receives a transaction, they examine if the transaction for the presence of the BlindedDestinationTag and BlindedSourceTag fields; if they are present, the recipient needs to preprocess the transaction to unblind the tags. The recipient unblinds the tags as follows:

  1. Extract the random value R from the RandomValue field.
  2. Calculate the shared secret using ECIES and R.
  3. If the KeyIdentifier field is present, do the following:
    1. Calculates the key identifier Z as explained above.
    2. Compares Z to the KeyIdentifier field in the transaction.
    3. If Z and the KeyIdentifier field are not the same, try to use an older keypair to determine the secret key to use. (Older keypairs are based on all MessageKey values the recipient has previously set.)
  4. If the BlindedDestinationTag field is present:
    1. Calculate the destination tag blinding factor Bdst as described above.
    2. Calculates the unblinded destination tag Udst = BlindedDestinationTag ⊕ Bdst.
  5. If the BlindedSourceTag field is present:
    1. Calculate the source tag blinding factor Bsrc as described above.
    2. Calculate the unblinded source tag Usrc = BlindedSourceTag ⊕ Bsrc.

Once the preprocessing stage is complete, the recipient now processes the incoming transaction normally, but uses the calculated unblinded source and destination tags (Usrc and Udst respectively) instead of the blinded tags present in the transaction.

Miscellaneous Details

Privacy Comment:

A different blinding factor is used for source and destination tags. This is necessary because of the nature of the exclusive OR operator. Consider:

X = A ⊕ K
Y = B ⊕ K

Now, consider:

X ⊕ Y = (A ⊕ K) ⊕ (B ⊕ K) = (A ⊕ B) ⊕ (K ⊕ K) = A ⊕ B

So, if both a source and a destination tag are present and the same blinding factor is used for both, then performing an exclusive OR operation "cancels out" the blinding factor and yields a stable token for a given pair Usrc and Udst. By using separate blinding factors for the two, this attack is mitigated.

Tag Address Space Extension

The existing SourceTag and DestinationTag fields are defined 32-bit values. As stated earlier, this restricts the number of tags available. Several questions about extending the fields to 64-bit have been raised. While a protocol extension could be implemented, redefining the existing fields as 64-bit values, such a change would be highly disruptive and would break existing code.

Since the BlindedSourceTag and BlindedDestinationTag fields are new, no backward compatibility issues are present. Furthermore, since the new fields are opt-in (that is, both the source and the destination must explicitly opt in for the fields to be usable, existing code can continue operating normally.

Therefore, this proposal purposefully chooses to define BlindedSourceTag and BlindedDestinationTag as 64-bit fields, and to allow the underlying 'unblinded' tags to have a usable range of 0 through 264 inclusive.

Perfect Forward Secrecy

The scheme proposed does not afford perfect forward secrecy. The compromise of the recipient's secret key could allow an attacker to "unblind" the source and destination tags for all transactions where the compromised key or its corresponding public counterpart was used.

While implementing a scheme that affords perfect forward secrecy is possible, the additional complexity associated with doing so seems excessive, given the stated goal of this proposal: to improve the privacy of the existing system, where source and destination tags are transported in cleartext.

Integration with the X-address specification

The recently proposed and adopted X-address format allows the packed encoding of an address and a destination tag into a single address.

The blinding scheme is not conducive to pre-generation of blinded tags. Despite this, we recommend adding de minimis support, by allowing the use of 64-bit tags by setting that TAG_64 field in the X-address.

The tag specified in the X-address should be unblinded; applications (wallets) should accept such addresses and blind the tag, if appropriate, according to the sender's configuration.

Integration with tickets

No consideration is given at this time on how this scheme integrates with tickets.

movitto commented 4 years ago

@nbougalis I gave this a read through, seems like an interesting feature. One point of confusion for me is the need for the recipient to generate a separate keypairs for use in blinding factor generation & secret extraction. Could the sender / recipient simply not just use the recipients account public / private key to accomplish the same? Or is this so that the blinding factor keypair can be revoked if compromised?

Also requiring the MessageKey to be set per recipient means each recipient using blinded tags will now require two transactions to have the same effect, one to set the MessageKey and the 2nd to perform the actual transaction. Eg if I'm sending payments to two different exchanges using BlindedTags, I'll need to issue an AccountSet setting the MessageKey of the 1st, then send the payment, then issue another AccountSet setting the MessageKey for the 2nd, then send the 2nd payment. Not impossible, but burns more in fees.

Thanks again for the neat proposal

nbougalis commented 4 years ago

Hey @movitto!

Could the sender / recipient simply not just use the recipients account public / private key to accomplish the same?

Sure but only if the public key of the recipient's account is known. Maybe it is, maybe it's not. Remember that an account address is the hash of the public key and the public key itself isn't known unless it's been used to sign a transaction. This is why the MessageKey field actually contains a public key and not a hash of a public key (e.g. like the RegularKey field).

Also requiring the MessageKey to be set per recipient means each recipient using blinded tags will now require two transactions to have the same effect, one to set the MessageKey and the 2nd to perform the actual transaction.

I'm not sure I follow. An account that wants to receive transactions with blinded tags needs to complete a one-time setup procedure, first setting its MessageKey and then setting either the asfRequiresBlindedTags or the asfSupportsBlindedTags flag. Note: this only needs to be done once (unless it wants to rotate keys periodically).

In your example, where you send two payments to two different exchanges, you'll need precisely two payment transactions, regardless of whether you're using blinded or "classic" tags. In the case of blinded tags, exchanges will need to perform the one-time setup described above, if they haven't already.

Thanks for the feedback and your contributions!

mDuo13 commented 4 years ago

The interaction between the flags and the 64-bit tags is a bit confusing, but I'm not sure there's a simpler way that works with existing tag formats.

Also, a note, SourceTag is currently a common field to all transaction types, but as worded it sounds like all the new fields, including the blinded source tag field, would only be added to the transaction types that currently support destination tags?

movitto commented 4 years ago

Hey @movitto!

Could the sender / recipient simply not just use the recipients account public / private key to accomplish the same?

Sure but only if the public key of the recipient's account is known. Maybe it is, maybe it's not. Remember that an account address is the hash of the public key and the public key itself isn't known unless it's been used to sign a transaction. This is why the MessageKey field actually contains a public key and not a hash of a public key (e.g. like the RegularKey field).

Gotchya, so since the recipient has knowledge of their public key, they could simply using this as their MessageKey correct? Would there be any downside to doing so?


Also requiring the MessageKey to be set per recipient means each recipient using blinded tags will now require two transactions to have the same effect, one to set the MessageKey and the 2nd to perform the actual transaction.

I'm not sure I follow. An account that wants to receive transactions with blinded tags needs to complete a one-time setup procedure, first setting its MessageKey and then setting either the asfRequiresBlindedTags or the asfSupportsBlindedTags flag. Note: this only needs to be done once (unless it wants to rotate keys periodically).

In your example, where you send two payments to two different exchanges, you'll need precisely two payment transactions, regardless of whether you're using blinded or "classic" tags. In the case of blinded tags, exchanges will need to perform the one-time setup described above, if they haven't already.

OK, I was thrown off by this paragraph above:

The recipient must create a new secp256k1 keypair. The sender must set the public key of this keypair as the MessageKey of their account. Effectively, this means that the exchange will have associated an additional public key to their receiving wallet address.

"Sender" in this context should be "recipient" should it not?

Also with regard to the following:

If Z and the KeyIdentifier field are not the same, try to use an older keypair to determine the secret key to use. (Older keypairs are based on all MessageKey values the recipient has previously set.)

An interesting side effect of all this is that even if old keypairs are revoked / cycled out by the recipient, they will need to be kept around incase a sender uses one for a Blinded Tag transaction. Not ideal (especially if they have been compromised) but can't think of a way around this. At the very least I'd suggest prominently highlighting this in the user / developer docs pertaining to this feature.


Thanks for the feedback and your contributions!

:+1:

mDuo13 commented 4 years ago

Is there a cryptographic reason to use 64-bit fields for the blinded tags instead of reusing the existing fields? If one were willing to use 32-bit blinded tags, it seems like you could adapt this spec to accomplish this without amending the XRP Ledger protocol.

I don't really understand secp256k1 key derivation and ECIES, but it seems to me that you could simply truncate the blinding factors to 32 bits instead of 64 bits, then use a MemoType that indicates that the transaction uses blinded tags, with MemoData containing the KeyIdentifier and RandomValue fields.

As a rule, destinations need to be the ones to provide the unblinded tags to transaction senders, because it is the destination's responsibility to interpret the destination tag and take whatever action that tag is supposed to prompt. Therefore, the destination could simply look for a memo with a MemoType indicating the transaction is using blinded tags, and de-blind the tag if it finds one.

The only part of the spec I don't think this achieves—aside from increasing the tag bitspace—is rejecting transactions at a protocol level if they don't use blinded destination tags; however, I don't think that's a major loss since fools can always send invalid tags regardless.

Incidentally, it seems to me like a destination could also implement a "blinded tags helper" that generates a new blinded tag on-demand from a web interface: such a tool would provide the blinded tag and the necessary KeyIdentifier/RandomValue fields to potential senders when prompted. (Actually, anyone could implement such a tool, and the only non-public information it would need is the tag to blind. But having the destination do it keeps the tag to the fewest possible people.) Of course, it's great if the sender knows how to generate many new blinded tags on their own from a single destination tag that remains private, but it still seems like something that could be a handy helper tool for destinations that have to tell people their tags anyway.

lrvl commented 4 years ago

Small typo near "de minimis"? Shouldn't that be "the minimize" or is it my own ignorance on the subject matter.

Edit: Should be read as: "Despite this, we recommend adding at least support for the use of 64-bit tags by setting..".

intelliot commented 4 years ago

"de minimis" is a Latin expression meaning "minimal".

RareData commented 4 years ago

Is there a cryptographic reason to use 64-bit fields for the blinded tags instead of reusing the existing fields? If one were willing to use 32-bit blinded tags, it seems like you could adapt this spec to accomplish this without amending the XRP Ledger protocol.

I don't really understand secp256k1 key derivation and ECIES, but it seems to me that you could simply truncate the blinding factors to 32 bits instead of 64 bits, then use a MemoType that indicates that the transaction uses blinded tags, with MemoData containing the KeyIdentifier and RandomValue fields.

I'd really like to hear your (@nbougalis) input to this. What can we accomplish using memos and what requires an amendment?

RareData commented 4 years ago

Could we achieve the same outcome with less complexity and less new fields, by simply having the new account flags enforce a certain memo to be present?

WietseWind commented 4 years ago

@RareData That's on a transaction level, where it would make sense to keep it simple and put Memo's to good use, indeed. On an account level it would make sense to advertise (eg. exchange deposit account) the possibility to deposit using blinded tags.

RareData commented 4 years ago

Yes, so what I'd like to discuss is the possibility to just introduce the new Account flags and then use memos on a transaction level. Minimize rippled complexity and have the clients do the heavy lifting. One reserved MemoType to indicate "Blinded Tags Extension".

nbougalis commented 4 years ago

Thanks for the feedback, @mDuo13, @RareData, @WietseWind.

First of, there's no cryptographic reason to go with 64-bit tags; the blinding factor for each field can just be chopped down to 32 bits and it's fine. The key property we want for the blinding factor is that, assuming a size of N bits, it should be a uniformly distributed unsigned integer in the range [0, 2N-1], and whether we do 64- or 32-bit tags, we'd still have that property.

But I do believe that 64-bit tags are generally a better proposition and this was was a good opportunity to introduce them.

With all that said, let's talk about memos: I do agree that this (and more) could be achieved be using memos and would not have any objections is that's the route that gets more traction. I am supportive of finding uses for memos (and think that this proposal could make a good starting point for defining a "standard" to encrypt memo contents). As Wietse pointed out, what memos lose us is the ability to enforce flags like RequireDestinationTag.

Yes, so what I'd like to discuss is the possibility to just introduce the new Account flags and then use memos on a transaction level. Minimize rippled complexity and have the clients do the heavy lifting. One reserved MemoType to indicate "Blinded Tags Extension".

I don't think that having account flags that say "memo required" is a good idea. Using encrypted memos is fine and opens up more possibilities (including the ability to include a message for the recipient, which banks already allow with wire transfers, albeit not encrypted)

With that said, the existing spec has minimal complexity; the complexity basically is having new flags and new fields. Beyond that, it leaves all the heavy lifting to the clients which is, generally, a good idea.