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 · 52 comments

Comments

Projects
None yet
@vbuterin
Collaborator

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

This comment has been minimized.

Member

janx commented Oct 14, 2016

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

@vbuterin

This comment has been minimized.

Collaborator

vbuterin commented Oct 15, 2016

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

@Smithgift

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Collaborator

vbuterin commented Oct 17, 2016

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

@rebroad

This comment has been minimized.

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

This comment has been minimized.

Smithgift commented Oct 19, 2016

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

@Arachnid

This comment has been minimized.

Collaborator

Arachnid commented Oct 20, 2016

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.

@obscuren obscuren referenced this issue Oct 21, 2016

Merged

EIP158 & 160 Hardfork #3179

8 of 8 tasks complete

@chriseth chriseth referenced this issue Oct 28, 2016

Merged

EIP 158 + 160 protocol update #3381

2 of 2 tasks complete
@sebs

This comment has been minimized.

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

This comment has been minimized.

Contributor

holiman commented Oct 28, 2016

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

@gavofyork gavofyork referenced this issue Oct 30, 2016

Merged

EIPs 155, 160, 161 #2976

7 of 7 tasks complete

@gavofyork gavofyork closed this Oct 31, 2016

@gavofyork gavofyork reopened this Oct 31, 2016

@gavofyork

This comment has been minimized.

gavofyork commented Oct 31, 2016

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

Implement EIP155 specific tx test #417
This new testcase aims to reproduce
ethereum/EIPs#155
@konradkonrad

This comment has been minimized.

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

This comment has been minimized.

Contributor

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

Implement EIP155 specific tx test #417
This new testcase aims to reproduce
ethereum/EIPs#155
@vbuterin

This comment has been minimized.

Collaborator

vbuterin commented Nov 3, 2016

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

@Nashatyrev

This comment has been minimized.

Member

Nashatyrev commented Nov 17, 2016

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

@mohsenghajar

This comment has been minimized.

mohsenghajar commented Nov 21, 2016

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

This comment has been minimized.

Smithgift commented Nov 21, 2016

@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

This comment has been minimized.

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

This comment has been minimized.

Collaborator

vbuterin commented Nov 23, 2016

Updated, sorry about that!

@ashchan

This comment has been minimized.

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.

@aeidelman

This comment has been minimized.

aeidelman commented May 4, 2017

We are using 30 (Mainnet) and 31 (Testnet) in RSK

@jyap808

This comment has been minimized.

jyap808 commented May 7, 2017

Ubiq (ticker UBQ) is using ChainID 8 for Mainnet.

http://coinmarketcap.com/currencies/ubiq/

Thanks

@warren-bank

This comment has been minimized.

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

This comment has been minimized.

realcodywburns commented Aug 6, 2017

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

jhoenicke commented Jan 2, 2018

@talbp

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment