Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple replay attack protection #155

Closed
vbuterin opened this issue Oct 14, 2016 · 53 comments
Closed

Simple replay attack protection #155

vbuterin opened this issue Oct 14, 2016 · 53 comments

Comments

@vbuterin
Copy link
Contributor

vbuterin commented Oct 14, 2016

EDITOR UPDATE (2017-08-15): This EIP is now located at https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md. Please go there for the correct specification. The text below may be incorrect or outdated, and is not maintained.

Parameters

  • FORK_BLKNUM: TBA
  • CHAIN_ID: 1

Specification

If block.number >= FORK_BLKNUM and v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36, then when computing the hash of a transaction for purposes of signing or recovering, instead of hashing only the first six elements (ie. nonce, gasprice, startgas, to, value, data), hash nine elements, with v replaced by CHAIN_ID, r = 0 and s = 0. The currently existing signature scheme using v = 27 and v = 28 remains valid and continues to operate under the same rules as it does now.

Example

Consider a transaction with nonce = 9, gasprice = 20 * 10**9, startgas = 21000, to = 0x3535353535353535353535353535353535353535, value = 10**18, data='' (empty).

The "signing data" becomes:

0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080

The "signing hash" becomes:

0x2691916f9e6e5b304f135496c08f632040f02d78e36ae5bbbb38f919730c8fa0

If the transaction is signed with the private key 0x4646464646464646464646464646464646464646464646464646464646464646, then the v,r,s values become:

(37, 11298168949998536842419725113857172427648002808790045841403298480749678639159, 26113561835810707062310182368620287328545641189938585203131842552044123671646)

Notice the use of 37 instead of 27. The signed tx would become:

0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83

Rationale

This would provide a way to send transactions that work on ethereum without working on ETC or the Morden testnet. ETC is encouraged to adopt this EIP but replacing CHAIN_ID with a different value, and all future testnets, consortium chains and alt-etherea are encouraged to adopt this EIP replacing CHAIN_ID with a unique value.

@janx
Copy link
Member

janx commented Oct 14, 2016

Out of curious: why make v=27|28 instead of v=0|1 at first place?

@vbuterin
Copy link
Contributor Author

It was a carry-over from Bitcoin's Electrum wallet.

@Smithgift
Copy link

Smithgift commented Oct 16, 2016

Relevant Classic ECIP discussion on this subject. Even more here. As far as I understand it, they're considering adding the blockhash of the nearest fork to the signing process.

I'm not competent to speak on the cryptographic parts of this idea, but I'll note a philosophical difference between this and ECIP-1011. Here, picking CHAIN_ID requires human intervention to choose which community it belongs in, whereas a machine can compute the right blockhash to stick in if it detects even a minor fork.

In any case, the existence of two incompatible replay protection systems will hardly cause them to fail in their intended function. It's just a kind of balkanization of code that, IMHO, would be harmful for a future cross-chain metaetheral world. I'm not saying that doing our own system would be wrong, but that there's a non-zero cost to having different systems.

@holiman
Copy link
Contributor

holiman commented Oct 17, 2016

because v = 27 and v = 28 will continue to be usable to send transactions on all chains and so that would create a protocol ambiguity as there would be two different valid ways to calculate the sender of a transaction

To clarify, does "continue to be usable to send transactions on all chains" mean that we would retain the old transaction signing scheme as valid, in addition to the new signing scheme? If so, the specification-paragraph should be updated to reflect that.

@vbuterin
Copy link
Contributor Author

Yes, the old scheme is still valid in addition to the new one.

@rebroad
Copy link

rebroad commented Oct 18, 2016

@vbuterin The mention of ETC confuses me as I thought this issue was being raised to address up-coming forks to ensure replay attacks cannot happen again should the current chain survive in some form. Is that what this issue is created to address?

@Smithgift
Copy link

@rebroad: This will work just as well for ETH/ETC replay attack prevention. As it is, that's the bigger need.

@Arachnid
Copy link
Contributor

Instead of using a 'chain ID' that has a limited namespace of 8 bits, and has to be changed on each fork, I'd like to propose two alternatives:

  1. Before hashing, replace one of V, R, or S with a 'fork ID'. A fork ID is 0 for the initial Ethereum chain. Each time there is a hard fork, the forked side sets a 1 bit in the next available slot, starting with the least significant bit. So, Ethereum post frontier has fork id 1, as does Ethereum Classic. Ethereum after the DAO fork had fork ID 11b = 3, and after the recent gas reprice fork had fork ID 111b = 7. After this fork, Ethereum will have fork ID 1111b = 15. After the forthcoming ETC fork it will have fork ID 101b = 5.
  2. Before hashing, replace one of V, R, or S with the block hash of the block at which the most recent fork occurred.

@sebs
Copy link

sebs commented Oct 28, 2016

Is this only a protection against simple replay attacks or all attacks in a simple way? Please clarify for the non whitepaper safe fellows.

@holiman
Copy link
Contributor

holiman commented Oct 28, 2016

The latter; "all attacks in a simple way".

@gavofyork
Copy link

i quite like both of @Arachnid 's suggestions, though think they are as well working in tandem with this proposal; this would allow for alt-etherea, private/consortium chains and testnets to avoid clashing rather than just non-forks.

konradkonrad added a commit to ethereum/pyethereum that referenced this issue Oct 31, 2016
This new testcase aims to reproduce
ethereum/EIPs#155
@konradkonrad
Copy link

konradkonrad commented Nov 1, 2016

Not sure, if the provided serialization for the signed transaction is correct (or I am misunderstanding this proposal?):

import rlp
from ethereum.utils import decode_hex
# the 'signed tx' from above
eip155_signed = "f86e098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a7640000801ca1a019ae791bb8378a38bb83f5b930fe78a0320cec27d86e5e258c69f0fa9541eb8da1a02bd8e0c5bde4c0800238ce5a59d2f3ce723f1e84a62cab53d961fe3b019d19fc"
list(map(rlp.utils.big_endian_to_int, rlp.decode(decode_hex(eip155_signed))[-3:]))
# [28,
# 18538350366433071197493567112980778422885435717274618683086050914661715717254029,
# 18546566920747953154222317575156555315715115874101297527539304425414456214624764]

edit: this is the serialization my implementation expects:

signed_tx = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008026a019ae791bb8378a38bb83f5b930fe78a0320cec27d86e5e258c69f0fa9541eb8da02bd8e0c5bde4c0800238ce5a59d2f3ce723f1e84a62cab53d961fe3b019d19fc"
list(map(rlp.utils.big_endian_to_int, rlp.decode(decode_hex(signed_tx))[-3:]))
# [38,
# 11616088462479929722209511590713166362238170772128436772837473395614974864269,
# 19832642777361886450959973766490059191918327598807281226090984148355472235004]

@chfast
Copy link
Member

chfast commented Nov 1, 2016

If v is one byte, we should also specify the set of possible CHAIN_ID values. Is it {0 - 127} - {13}, assuming that for 127 v = 255 or v = 0?

We have also Ethereum network id number. Can we use the same value?

konradkonrad added a commit to ethereum/pyethereum that referenced this issue Nov 2, 2016
This new testcase aims to reproduce
ethereum/EIPs#155
@vbuterin
Copy link
Contributor Author

vbuterin commented Nov 3, 2016

@konradkonrad yep you're right, that tx got serialized incorrectly. I updated it.

@Nashatyrev
Copy link
Member

Guys, could you please adjust the formula in this issue?

@mohsenghajar
Copy link

To people who never split their coins (e.g. all original ETH), are any of these updates any danger? Excuse my ignorance pls. Thanks.

@Smithgift
Copy link

@mohsenghajar: If all goes to plan, this will make things safer, as transactions will be limited to one chain. It will be easier to split afterwards, hopefully.

@kvhnuke
Copy link
Contributor

kvhnuke commented Nov 23, 2016

It seems like this example has been modified in implementation, please correct me if im wrong.

then when computing the hash of a transaction for purposes of signing or recovering, instead of hashing only the first six elements (ie. nonce, gasprice, startgas, to, value, data), hash nine elements, with v replaced by 1 (this is the main CHAIN_ID not the 18), r = 0 and s = 0.

Consider a transaction with nonce = 9, gasprice = 20 * 10^9, startgas = 21000, to = 0x3535353535353535353535353535353535353535, value = 10^18, data='' (empty).

The "signing data" becomes:
0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080

The "signing hash" becomes:
0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53

After signing make sure v is either 37 (18x2+1) or 38(18x2+2) based on 0 or 1
If the transaction is signed with the private key 0x4646464646464646464646464646464646464646464646464646464646464646

then v,r,s becomes:
37, 0x28ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276, 0x67cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83

then the signed tx becomes:
0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83

@vbuterin
Copy link
Contributor Author

Updated, sorry about that!

@ashchan
Copy link

ashchan commented Nov 25, 2016

Does anyone have problem testing signed tx against https://github.com/ethereum/tests/blob/develop/TransactionTests/EIP155/ttTransactionTestEip155VitaliksTests.json?

For me, my temp implementation cannot pass test 1 ~ 11 (Vitalik_11) for simple RLP hash (not sign, but just assume the transaction is already signed and encode the 9 elements, with v, r, s read from the JSON file).

Vitalik_12, Vitalik_13 and Vitalik_14 pass. They have normal-looking r and s values. For 1 ~ 11, each test has identical or slightly different r/s values.

Update: all test worked.

@warren-bank
Copy link

warren-bank commented Jul 26, 2017

hi @kvhnuke ..

quick question regarding your output..

I get the same values, but only if r and s are assigned to empty (unallocated, zero-length) Buffers.. rather than 1-byte Buffers containing the value 0x00. Is this correct?

Rather than including a lot of inline code, here are links:

My question largely boils down to another earlier comment. When [v,r,s] is [1,0,0].. should the RLP encoded value be?:

  • 018080 ..where 80 is a zero-length Buffer, or
  • 010000 ..where 00 is a 1-byte Buffer containing 0x00

PS) the example at the top of this EIP seems to be inconsistent.. the final value for its "signed tx" was copied from the value in your comment.. but none of the preceding data values that show the intermediate steps leading to this final value match the rest of the data in your comment (or the data observed when running the above test code). idk if anybody else has permission to make edits, but (imho) that should be corrected.

@realcodywburns
Copy link
Contributor

For context: ETC uses61 because it is the same as the bip-44 deterministic path. 62 is pesobit and will not cause collision on p2pdev nodes https://github.com/satoshilabs/slips/blob/master/slip-0044.md

@cdetrio
Copy link
Member

cdetrio commented Aug 15, 2017

This EIP is now located at https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md. Please go there for the correct specification. The text in this issue may be incorrect or outdated, and is not maintained.

@nt11
Copy link

nt11 commented Jan 2, 2018

Perhaps I'm missing something. EP-155 states:

"If block.number >= FORK_BLKNUM and v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36, then when computing the hash of a transaction for purposes of signing or recovering, instead of hashing only the first six elements (ie. nonce, gasprice, startgas, to, value, data), hash nine elements, with v "

These are instructions for the hashing party, but not the the original signing party. Therefore, the question arises: when signing a transaction, should one use the first equation (with 35) or second equation (with 36). Practically speaking, sometimes the former works and sometimes the latter without any clear rationale.

Thanks

Nir

The

@jhoenicke
Copy link

jhoenicke commented Jan 2, 2018 via email

@talbp
Copy link

talbp commented Jan 2, 2018

@jhoenicke could you please elaborate a bit on the meaning of "R.y" and "low s normalization"? I wasn't able to find either terms.
A reference to a place which defines them, could also be great.

Thanks in advance!
Tal

@jhoenicke
Copy link

jhoenicke commented Feb 16, 2018

@talbp : An ECDSA signature (r,s) is computed from a random nonce k: compute R = kG and set r = (R.x mod ORDER) and throw away R.y. To recover the public key one can use the formula 1/r(sR-hG), but that requires to recover R from the signature. If R.x < ORDER (which is almost always the case), then the missing part is R.y and there are two possibilities (one with low bit 0, one with low bit 1).
So one has to store the low bit of R.y in v.

low s normalization: If (r,s) is a valid signature then (r,(ORDER-s)) is also a valid signature, but it uses the "other R". Ethereum (and Bitcoin) require for some time that one always uses the smaller value for s, and then one has to flip R.y bit.

See also https://crypto.stackexchange.com/questions/18105/how-does-recovering-the-public-key-from-an-ecdsa-signature-work/18106

@rustyx
Copy link

rustyx commented Apr 24, 2018

Too late to the party, but why hash 9 values with the last two fixed to r = 0 and s = 0 instead of just 7 values?

@wjmelements
Copy link
Contributor

It's worth noting that if you do not use RFC 6979 you will get non-deterministic signatures that differ from the example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests