-
Notifications
You must be signed in to change notification settings - Fork 771
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
Improve security of signature generation #3801
Comments
Viem is on board and implemented the changes today. |
Wow, this confuses me a lot. We had a rule in the past (Homestead fork) where we did not allow flipping certain values (namely we do not allow tx signatures where Test script (in import { bytesToHex } from "ethereum-cryptography/utils";
import { createLegacyTx } from "../src/index.js";
const d = createLegacyTx({})
const p = new Uint8Array(32).fill(0x20)
const s = d.sign(p)
console.log(bytesToHex(s.hash()), s.getSenderAddress().toString())
const s2 = d.sign(p)
console.log(bytesToHex(s2.hash()), s2.getSenderAddress().toString()) Without the extra entropy, change nothing (as of current master @ e86eace). The two signatures will yield the same hash and the same sender, also when started up a different time. Output:
To add entropy, change export function ecsign(
msgHash: Uint8Array,
privateKey: Uint8Array,
chainId?: bigint,
): ECDSASignature {
const sig = secp256k1.sign(msgHash, privateKey, { extraEntropy: true })
const buf = sig.toCompactRawBytes() Each time sign is run (also thus the first and second signature on the run) the hash will change but the sender will not. Sample output:
Forgive my novice-ness in cryptography here, but if we add the entropy, then this means that each time if someone uses our libraries and uses the same private key and the same data to sign (i.e. the same transaction), then it will yield a different |
Yes, this is exactly the point. It protects against "bad cryptography". "High-s" is not affected, it would still be low-s.
|
I see. Then my follow-up question would be, why is the |
It is not default because it would break tests, which assume deterministic outputs. It is also not clear which user applications rely on deterministic outputs. But I think it's safe to change in the upcoming major release while keeping a changelog NOTE. As long as there is an option to use no extraEntropy, users should be fine. Using extraEntropy would massively harden security of cryptography. Again, to prove my point, soon a vulnerability in popular library (not noble) would be disclosed. I can send you more info on it in direct messages. It looks bad and I want to prevent such things in advance. |
Besides By "not clear which user applications rely on deterministic outputs" I mean the following thing:
|
Problem
Transaction signatures use "nonce" / "k" during their construction. The nonce should never be equal between two different messages. Reusing them would allow attacker to recover private key.
Many years ago,
k
was generated using system randomness. On some systems with bad quality of randomness, that lead to breakages:k = random()
Today, the nonce is generated from private key and message hash using RFC 6979:
k = hash_6979(private_key, message)
However, if some issue would be found in serialization / parsing of those, and during generation of nonce, it would still be possible to recover private keys. The technique is described here: https://github.com/pcaversaccio/ecdsa-nonce-reuse-attack.
Impact
Private key leakage, hackers stealing money from users.
This is not some theoretical issue. This happened in the past. Soon there would be announcement of a new hack related to this.
Solution
Use RFC6979 3.6: additional k'
extraEntropy
to mix-in 32 byte of random data on every signature. It is standard way of doing this. It has been extensively used by Bitcoin for non-taproot transactions, to decrease signature size.k = hash_6979(private_key, message, extraEntropy)
ethereumjs-monorepo/packages/util/src/signature.ts
Lines 37 to 42 in e86eace
In this case it becomes
secp256k1.sign(msgHash, privateKey, { extraEntropy: true })
.extraEntropy
can also befalse
,true
(auto-fetch random), orUint8Array
which you specify.Disadvantages
random
extraData in tests.There is no risk for security. If passed-through random is bad, the signature security would be just like today, not worse
The text was updated successfully, but these errors were encountered: