Skip to content

Commit a8d3faa

Browse files
gabrocheleauScottyPoiholgerd77
authored
chore: upgrade noble curves (#4179)
* chore: upgrade noble curves * chore: upgrade noble hashes * chore: fix type issue for cjs * chore: attempt to fix import issues * fix: lint issue * evm: fix p256 import * evm: use updated p256 API * evm: use updated Point api * chore: attempt to fix tests * chore: attempt higher timeout * chore: attempt to fix * chore: add prehash to cspell * chore: attempt noble fix * Add lowS: false to p256.sign() and p256.verify() * Add prehash: false to p256.verify/sign methods * Attempted Noble bn254 mul fix (modulus is embedded in the create() method) --------- Co-authored-by: ScottyPoi <scott.simpson@ethereum.org> Co-authored-by: Holger Drewes <Holger.Drewes@gmail.com>
1 parent a5e725c commit a8d3faa

File tree

9 files changed

+99
-80
lines changed

9 files changed

+99
-80
lines changed

config/cspell-ts.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
}
2525
],
2626
"words": [
27+
"prehash",
2728
"viem",
2829
"immediates",
2930
"unerasable",
@@ -644,4 +645,4 @@
644645
"Damgård",
645646
"viem"
646647
]
647-
}
648+
}

package-lock.json

Lines changed: 28 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/evm/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ const pointY = bigIntToHex(pointPubKey.Y)
382382

383383
// Message (hash) / signature
384384
const msg = new TextEncoder().encode('Hello Fusaka!')
385-
const sig = p256.sign(msg, secretKey)
385+
const sig = p256.sign(msg, secretKey, { lowS: false, prehash: false })
386386
const msgHash = bytesToHex(sha256(msg))
387387
const sigR = bytesToHex(sig).substring(2, 64 + 2)
388388
const sigS = bytesToHex(sig).substring(64 + 2)

packages/evm/examples/precompiles/100-p256verify.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { Hardfork } from '@ethereumjs/common'
2-
import { type PrefixedHexString, bytesToHex, randomBytes, utf8ToBytes } from '@ethereumjs/util'
3-
import { p256 } from '@noble/curves/p256'
2+
import {
3+
type PrefixedHexString,
4+
bytesToHex,
5+
bytesToUnprefixedHex,
6+
randomBytes,
7+
utf8ToBytes,
8+
} from '@ethereumjs/util'
9+
import { p256 } from '@noble/curves/nist.js'
410
import { sha256 } from 'ethereum-cryptography/sha256.js'
511
import { runPrecompile } from './util.ts'
612

@@ -18,18 +24,19 @@ const main = async () => {
1824
const message = 'ethereumjs-evm-p256verify-example'
1925
const messageHash = sha256(utf8ToBytes(message))
2026

21-
const privateKey = randomBytes(32)
22-
const publicKey = p256.ProjectivePoint.fromPrivateKey(privateKey).toAffine()
27+
const privateKey = p256.utils.randomSecretKey()
28+
const publicKey = p256.getPublicKey(privateKey, false) // Get uncompressed public key
2329

24-
const signature = p256.sign(messageHash, privateKey)
30+
const signatureBytes = p256.sign(messageHash, privateKey, { lowS: false, prehash: false })
31+
const signature = p256.Signature.fromBytes(signatureBytes)
2532

2633
const padHex = (value: bigint) => value.toString(16).padStart(64, '0')
2734

2835
const msgHashHex = bytesToHex(messageHash)
2936
const rHex = padHex(signature.r)
3037
const sHex = padHex(signature.s)
31-
const qxHex = padHex(publicKey.x)
32-
const qyHex = padHex(publicKey.y)
38+
const qxHex = bytesToUnprefixedHex(publicKey.slice(1, 33)).padStart(64, '0')
39+
const qyHex = bytesToUnprefixedHex(publicKey.slice(33, 65)).padStart(64, '0')
3340

3441
const data: PrefixedHexString = `${msgHashHex}${rHex}${sHex}${qxHex}${qyHex}`
3542

packages/evm/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
"@ethereumjs/common": "^10.1.0",
5959
"@ethereumjs/statemanager": "^10.1.0",
6060
"@ethereumjs/util": "^10.1.0",
61-
"@noble/curves": "^1.9.0",
61+
"@noble/curves": "^2.0.1",
6262
"debug": "^4.4.0",
6363
"ethereum-cryptography": "^3.2.0",
6464
"eventemitter3": "^5.0.1"

packages/evm/src/precompiles/100-p256verify.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { bytesToBigInt, bytesToHex, setLengthLeft } from '@ethereumjs/util'
2-
import { p256 } from '@noble/curves/p256'
2+
import { p256 } from '@noble/curves/nist.js'
33

44
import { OOGResult } from '../evm.ts'
55

@@ -103,7 +103,7 @@ export function precompile100(opts: PrecompileInput): ExecResult {
103103

104104
try {
105105
// Create public key point
106-
const publicKey = p256.ProjectivePoint.fromAffine({
106+
const publicKey = p256.Point.fromAffine({
107107
x: qxBigInt,
108108
y: qyBigInt,
109109
})
@@ -115,10 +115,13 @@ export function precompile100(opts: PrecompileInput): ExecResult {
115115
signatureBytes.set(rBytes, 0)
116116
signatureBytes.set(sBytes, 32)
117117

118-
const signature = p256.Signature.fromCompact(signatureBytes)
118+
const signature = p256.Signature.fromBytes(signatureBytes).toBytes()
119119

120120
// Verify signature
121-
const isValid = p256.verify(signature, msgHash, publicKey.toRawBytes(false))
121+
const isValid = p256.verify(signature, msgHash, publicKey.toBytes(false), {
122+
lowS: false,
123+
prehash: false,
124+
})
122125

123126
if (isValid) {
124127
if (opts._debug !== undefined) {

packages/evm/src/precompiles/bls12_381/noble.ts

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
equalsBytes,
77
setLengthLeft,
88
} from '@ethereumjs/util'
9-
import { bls12_381 } from '@noble/curves/bls12-381'
9+
import { bls12_381 } from '@noble/curves/bls12-381.js'
1010

1111
import { EVMError } from '../../errors.ts'
1212

@@ -20,12 +20,14 @@ import {
2020
BLS_ZERO_BUFFER,
2121
} from './constants.ts'
2222

23-
import type { Fp2 } from '@noble/curves/abstract/tower'
24-
import type { AffinePoint } from '@noble/curves/abstract/weierstrass'
23+
import type { Fp2 } from '@noble/curves/abstract/tower.js'
24+
import type { AffinePoint } from '@noble/curves/abstract/weierstrass.js'
2525
import type { EVMBLSInterface } from '../../types.ts'
2626

27-
const G1_ZERO = bls12_381.G1.ProjectivePoint.ZERO
28-
const G2_ZERO = bls12_381.G2.ProjectivePoint.ZERO
27+
// @ts-ignore - @noble/curves v2 is ESM-only, TypeScript's moduleResolution: "node" doesn't properly resolve types for CJS build
28+
const G1_ZERO = bls12_381.G1.Point.ZERO
29+
// @ts-ignore - @noble/curves v2 is ESM-only, TypeScript's moduleResolution: "node" doesn't properly resolve types for CJS build
30+
const G2_ZERO = bls12_381.G2.Point.ZERO
2931

3032
function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array) {
3133
// check if the coordinates are in the field
@@ -56,7 +58,8 @@ function BLS12_381_ToG1Point(input: Uint8Array, verifyOrder = true) {
5658
const x = bytesToBigInt(input.subarray(16, BLS_G1_POINT_BYTE_LENGTH / 2))
5759
const y = bytesToBigInt(input.subarray(80, BLS_G1_POINT_BYTE_LENGTH))
5860

59-
const G1 = bls12_381.G1.ProjectivePoint.fromAffine({
61+
// @ts-ignore - @noble/curves v2 is ESM-only, TypeScript's moduleResolution: "node" doesn't properly resolve types for CJS build
62+
const G1 = bls12_381.G1.Point.fromAffine({
6063
x,
6164
y,
6265
})
@@ -99,7 +102,8 @@ function BLS12_381_ToG2Point(input: Uint8Array, verifyOrder = true) {
99102
const Fp2X = BLS12_381_ToFp2Point(p_x_1, p_x_2)
100103
const Fp2Y = BLS12_381_ToFp2Point(p_y_1, p_y_2)
101104

102-
const pG2 = bls12_381.G2.ProjectivePoint.fromAffine({
105+
// @ts-ignore - @noble/curves v2 is ESM-only, TypeScript's moduleResolution: "node" doesn't properly resolve types for CJS build
106+
const pG2 = bls12_381.G2.Point.fromAffine({
103107
x: Fp2X,
104108
y: Fp2Y,
105109
})
@@ -130,26 +134,9 @@ function BLS12_381_FromG2Point(input: AffinePoint<Fp2>): Uint8Array {
130134

131135
function BLS12_381_ToFrPoint(input: Uint8Array): bigint {
132136
const Fr = bls12_381.fields.Fr.fromBytes(input)
133-
134-
// TODO: This fixes the following two failing tests:
135-
// bls_g1mul_random*g1_unnormalized_scalar
136-
// bls_g1mul_random*p1_unnormalized_scalar
137-
// It should be nevertheless validated if this is (fully) correct,
138-
// especially if ">" or ">=" should be applied.
139-
//
140-
// Unfortunately the scalar in both test vectors is significantly
141-
// greater than the ORDER threshold, here are th values from both tests:
142-
//
143-
// Scalar / Order
144-
// 69732848789442042582239751384143889712113271203482973843852656394296700715236n
145-
// 52435875175126190479447740508185965837690552500527637822603658699938581184513n
146-
//
147-
// There should be 4 test cases added to the official test suite:
148-
// 1. bls_g1mul_random*g1_unnormalized_scalar within threshold (ORDER (?))
149-
// 2. bls_g1mul_random*g1_unnormalized_scalar outside threshold (ORDER + 1 (?))
150-
// 3. bls_g1mul_random*p1_unnormalized_scalar within threshold (ORDER (?))
151-
// 4. bls_g1mul_random*p1_unnormalized_scalar outside threshold (ORDER + 1 (?))
152-
//
137+
if (Fr >= bls12_381.fields.Fr.ORDER) {
138+
return bls12_381.fields.Fr.create(Fr % bls12_381.fields.Fr.ORDER)
139+
}
153140
return bls12_381.fields.Fr.create(Fr)
154141
}
155142

@@ -180,7 +167,7 @@ export class NobleBLS implements EVMBLSInterface {
180167
)
181168

182169
const p = p1.add(p2)
183-
const result = BLS12_381_FromG1Point(p)
170+
const result = BLS12_381_FromG1Point(p.toAffine())
184171

185172
return result
186173
}
@@ -194,7 +181,7 @@ export class NobleBLS implements EVMBLSInterface {
194181
return BLS_G1_INFINITY_POINT_BYTES
195182
}
196183
const result = p.multiplyUnsafe(scalar)
197-
return BLS12_381_FromG1Point(result)
184+
return BLS12_381_FromG1Point(result.toAffine())
198185
}
199186

200187
addG2(input: Uint8Array): Uint8Array {
@@ -204,7 +191,7 @@ export class NobleBLS implements EVMBLSInterface {
204191
false,
205192
)
206193
const p = p1.add(p2)
207-
const result = BLS12_381_FromG2Point(p)
194+
const result = BLS12_381_FromG2Point(p.toAffine())
208195

209196
return result
210197
}
@@ -218,20 +205,22 @@ export class NobleBLS implements EVMBLSInterface {
218205
return BLS_G2_INFINITY_POINT_BYTES
219206
}
220207
const result = p.multiplyUnsafe(scalar)
221-
return BLS12_381_FromG2Point(result)
208+
return BLS12_381_FromG2Point(result.toAffine())
222209
}
223210

224211
mapFPtoG1(input: Uint8Array): Uint8Array {
225212
// convert input to Fp1 point
226213
const FP = BLS12_381_ToFpPoint(input.subarray(0, 64))
227-
const result = bls12_381.G1.mapToCurve([FP]).toAffine()
214+
// @ts-ignore - @noble/curves v2 is ESM-only, TypeScript's moduleResolution: "node" doesn't properly resolve types for CJS build
215+
const result = bls12_381.G1.mapToCurve(FP).toAffine()
228216
const resultBytes = BLS12_381_FromG1Point(result)
229217
return resultBytes
230218
}
231219

232220
mapFP2toG2(input: Uint8Array): Uint8Array {
233221
// convert input to Fp2 point
234222
const Fp2Point = BLS12_381_ToFp2Point(input.subarray(0, 64), input.subarray(64, 128))
223+
// @ts-ignore - @noble/curves v2 is ESM-only, TypeScript's moduleResolution: "node" doesn't properly resolve types for CJS build
235224
const result = bls12_381.G2.mapToCurve([Fp2Point.c0, Fp2Point.c1]).toAffine()
236225
const resultBytes = BLS12_381_FromG2Point(result)
237226
return resultBytes
@@ -266,7 +255,7 @@ export class NobleBLS implements EVMBLSInterface {
266255
pRes = pRes.add(pMul)
267256
}
268257

269-
return BLS12_381_FromG1Point(pRes)
258+
return BLS12_381_FromG1Point(pRes.toAffine())
270259
}
271260

272261
msmG2(input: Uint8Array): Uint8Array {
@@ -298,7 +287,7 @@ export class NobleBLS implements EVMBLSInterface {
298287
pRes = pRes.add(pMul)
299288
}
300289

301-
return BLS12_381_FromG2Point(pRes)
290+
return BLS12_381_FromG2Point(pRes.toAffine())
302291
}
303292

304293
pairingCheck(input: Uint8Array): Uint8Array {
@@ -319,7 +308,7 @@ export class NobleBLS implements EVMBLSInterface {
319308

320309
// Filter out infinity pairs
321310
const filteredPairs = pairs.filter(
322-
(pair) => !pair.g1.equals(G1_ZERO) && !pair.g2.equals(G2_ZERO),
311+
(pair) => pair.g1.equals(G1_ZERO) === false && pair.g2.equals(G2_ZERO) === false,
323312
)
324313

325314
const FP12 = bls12_381.pairingBatch(filteredPairs, true)

0 commit comments

Comments
 (0)