Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
enforce eip-2 for impersonated account signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmurdoch committed Apr 20, 2022
1 parent e79ad48 commit 5bd1931
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 6 deletions.
5 changes: 2 additions & 3 deletions src/chains/ethereum/ethereum/src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
WEI
} from "@ganache/utils";
import { privateToAddress } from "ethereumjs-util";
import secp256k1 from "@ganache/secp256k1";
import secp256k1, { SECP256K1_MAX_PRIVATE_KEY } from "@ganache/secp256k1";
import { mnemonicToSeedSync } from "bip39";
import { alea } from "seedrandom";
import crypto from "crypto";
Expand All @@ -24,7 +24,6 @@ import { EthereumInternalOptions } from "@ganache/ethereum-options";
import { Address } from "@ganache/ethereum-address";

const TWELVE_255s = Buffer.allocUnsafe(12).fill(255);
const SECP256K1_MAX_PRIVATE_KEY = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;

//#region Constants
const SCRYPT_PARAMS = {
Expand Down Expand Up @@ -663,7 +662,7 @@ export default class Wallet {
// will. There are obviously many chances for a false positive, but the
// next condition in the `while` loop will catch those.
if (first12.compare(TWELVE_255s) === 0) {
while (BigInt(fakePrivateKey.toString()) >= SECP256K1_MAX_PRIVATE_KEY) {
while (BigInt(`0x${fakePrivateKey.toString("hex")}`) >= SECP256K1_MAX_PRIVATE_KEY) {
// keccak returns a 32 byte hash of the input data, which is the exact
// length we need for a private key.
// note: if keccak can return it's own input as it's output, then this
Expand Down
27 changes: 24 additions & 3 deletions src/chains/ethereum/transaction/src/transaction-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ import { decode } from "@ganache/rlp";
import { CodedError } from "@ganache/ethereum-utils";
import { TypedTransaction } from "./transaction-types";
import { EIP1559FeeMarketTransaction } from "./eip1559-fee-market-transaction";
import { SECP256K1_MAX_PRIVATE_KEY_DIV_2 } from "@ganache/secp256k1";

/**
* @param common
* @param tx
* @throws
*/
function assertValidTransactionSValue(common: Common, tx: LegacyTransaction | EIP2930AccessListTransaction | EIP1559FeeMarketTransaction) {
// Transaction signatures whose s-value is greater than secp256k1n/2 are
// invalid after the homestead hardfork. See: https://eips.ethereum.org/EIPS/eip-2
if (tx.s && tx.s.toBigInt() > SECP256K1_MAX_PRIVATE_KEY_DIV_2 && common.gteHardfork('homestead')) {
throw new Error("Invalid Signature: s-values greater than secp256k1n/2 are considered invalid")
}
}

const UNTYPED_TX_START_BYTE = 0xc0; // all txs with first byte >= 0xc0 are untyped

Expand Down Expand Up @@ -162,7 +176,9 @@ export class TransactionFactory {
) {
const txType = this.typeOfRPC(txData);

return this._fromData(txData, txType, common, extra);
const tx = this._fromData(txData, txType, common, extra);
assertValidTransactionSValue(common, tx);
return tx;
}
/**
* Create a transaction from a `txData` object
Expand Down Expand Up @@ -218,6 +234,7 @@ export class TransactionFactory {
let data = Data.from(txData).toBuffer();
const type = data[0];
const txType = this.typeOf(type);
let tx: LegacyTransaction | EIP2930AccessListTransaction | EIP1559FeeMarketTransaction;
if (common.isActivatedEIP(2718)) {
let raw: TypedDatabasePayload;
try {
Expand All @@ -227,16 +244,20 @@ export class TransactionFactory {
} catch (e: any) {
throw new Error("Could not decode transaction: " + e.message);
}
return this._fromData(raw, txType, common);
tx = this._fromData(raw, txType, common);
} else {
let raw: TypedDatabasePayload;
try {
raw = decode<LegacyDatabasePayload>(data);
} catch (e: any) {
throw new Error("Could not decode transaction: " + e.message);
}
return this._fromData(raw, TransactionType.Legacy, common);
tx = this._fromData(raw, TransactionType.Legacy, common);
}

assertValidTransactionSValue(common, tx);

return tx;
}

private static typeOf(type: number) {
Expand Down
3 changes: 3 additions & 0 deletions src/packages/secp256k1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

import { dirname } from "path";

export const SECP256K1_MAX_PRIVATE_KEY = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
export const SECP256K1_MAX_PRIVATE_KEY_DIV_2 = SECP256K1_MAX_PRIVATE_KEY / 2n;

let secp256k1: {
ecdsaRecover: (
output: Uint8Array,
Expand Down

0 comments on commit 5bd1931

Please sign in to comment.