diff --git a/CHANGELOG.md b/CHANGELOG.md index 3828f89e72..60092f496c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Deprecated - `MerkleMap.computeRootAndKey()` deprecated in favor of `MerkleMap.computeRootAndKeyV2()` due to a potential issue of computing hash collisions in key indicies https://github.com/o1-labs/o1js/pull/1694 +- `createEcdsa`, `createForeignCurve`, `ForeignCurve` and `EcdsaSignature` deprecated in favor of `V2` versions due to a security vulnerability found in the current implementation https://github.com/o1-labs/o1js/pull/1703 ## [1.3.1](https://github.com/o1-labs/o1js/compare/1ad7333e9e...40c597775) - 2024-06-11 diff --git a/src/examples/circuit/ecdsa.ts b/src/examples/circuit/ecdsa.ts index 8dfe02627e..bb20414d88 100644 --- a/src/examples/circuit/ecdsa.ts +++ b/src/examples/circuit/ecdsa.ts @@ -3,16 +3,16 @@ import { circuitMain, public_, Crypto, - createEcdsa, - createForeignCurve, + createEcdsaV2, + createForeignCurveV2, Bytes, assert, } from 'o1js'; export { Secp256k1, Ecdsa, Bytes32, Reserves }; -class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) {} -class Ecdsa extends createEcdsa(Secp256k1) {} +class Secp256k1 extends createForeignCurveV2(Crypto.CurveParams.Secp256k1) {} +class Ecdsa extends createEcdsaV2(Secp256k1) {} class Bytes32 extends Bytes(32) {} class Reserves extends Circuit { diff --git a/src/examples/crypto/ecdsa/ecdsa.ts b/src/examples/crypto/ecdsa/ecdsa.ts index 986a0f2522..3d7c1a9d5c 100644 --- a/src/examples/crypto/ecdsa/ecdsa.ts +++ b/src/examples/crypto/ecdsa/ecdsa.ts @@ -2,14 +2,14 @@ import { ZkProgram, Crypto, createEcdsa, - createForeignCurve, + createForeignCurveV2, Bool, Bytes, } from 'o1js'; export { keccakAndEcdsa, ecdsa, Secp256k1, Ecdsa, Bytes32 }; -class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) {} +class Secp256k1 extends createForeignCurveV2(Crypto.CurveParams.Secp256k1) {} class Scalar extends Secp256k1.Scalar {} class Ecdsa extends createEcdsa(Secp256k1) {} class Bytes32 extends Bytes(32) {} diff --git a/src/index.ts b/src/index.ts index c23cac9260..3778236825 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,11 +9,15 @@ export { } from './lib/provable/foreign-field.js'; export { createForeignCurve, + createForeignCurveV2, ForeignCurve, + ForeignCurveV2, } from './lib/provable/crypto/foreign-curve.js'; export { createEcdsa, + createEcdsaV2, EcdsaSignature, + EcdsaSignatureV2, } from './lib/provable/crypto/foreign-ecdsa.js'; export { Poseidon, diff --git a/src/lib/provable/crypto/foreign-curve.ts b/src/lib/provable/crypto/foreign-curve.ts index 0bdfdb016b..323478b87a 100644 --- a/src/lib/provable/crypto/foreign-curve.ts +++ b/src/lib/provable/crypto/foreign-curve.ts @@ -11,9 +11,15 @@ import { Field3 } from '../gadgets/foreign-field.js'; import { assert } from '../gadgets/common.js'; import { Provable } from '../provable.js'; import { provableFromClass } from '../types/provable-derivers.js'; +import { multiRangeCheck } from '../gadgets/range-check.js'; // external API -export { createForeignCurve, ForeignCurve }; +export { + createForeignCurve, + createForeignCurveV2, + ForeignCurve, + ForeignCurveV2, +}; // internal API export { toPoint, FlexiblePoint }; @@ -26,6 +32,9 @@ function toPoint({ x, y }: ForeignCurve): Point { return { x: x.value, y: y.value }; } +/** + * @deprecated `ForeignCurve` is now deprecated and will be removed in a future release. Please use {@link ForeignCurveV2} instead. + */ class ForeignCurve { x: AlmostForeignField; y: AlmostForeignField; @@ -276,6 +285,48 @@ class ForeignCurve { } } +class ForeignCurveV2 extends ForeignCurve { + constructor(g: { + x: AlmostForeignField | Field3 | bigint | number; + y: AlmostForeignField | Field3 | bigint | number; + }) { + super(g); + } + + static check(g: ForeignCurveV2) { + multiRangeCheck(g.x.value); + multiRangeCheck(g.y.value); + // more efficient than the automatic check, which would do this for each field separately + this.Field.assertAlmostReduced(g.x, g.y); + this.assertOnCurve(g); + this.assertInSubgroup(g); + } +} + +/** + * @deprecated `createForeignCurve` is now deprecated and will be removed in a future release. Please use {@link createForeignCurveV2} instead. + */ +function createForeignCurve(params: CurveParams): typeof ForeignCurve { + const FieldUnreduced = createForeignField(params.modulus); + const ScalarUnreduced = createForeignField(params.order); + class Field extends FieldUnreduced.AlmostReduced {} + class Scalar extends ScalarUnreduced.AlmostReduced {} + + const BigintCurve = createCurveAffine(params); + + class Curve extends ForeignCurve { + static _Bigint = BigintCurve; + static _Field = Field; + static _Scalar = Scalar; + static _provable = provableFromClass(Curve, { + x: Field.provable, + y: Field.provable, + }); + } + + return Curve; +} + /** * Create a class representing an elliptic curve group, which is different from the native {@link Group}. * @@ -286,12 +337,12 @@ class ForeignCurve { * `createForeignCurve(params)` takes curve parameters {@link CurveParams} as input. * We support `modulus` and `order` to be prime numbers up to 259 bits. * - * The returned {@link ForeignCurve} class represents a _non-zero curve point_ and supports standard + * The returned {@link ForeignCurveV2} class represents a _non-zero curve point_ and supports standard * elliptic curve operations like point addition and scalar multiplication. * - * {@link ForeignCurve} also includes to associated foreign fields: `ForeignCurve.Field` and `ForeignCurve.Scalar`, see {@link createForeignField}. + * {@link ForeignCurveV2} also includes to associated foreign fields: `ForeignCurve.Field` and `ForeignCurve.Scalar`, see {@link createForeignFieldV2}. */ -function createForeignCurve(params: CurveParams): typeof ForeignCurve { +function createForeignCurveV2(params: CurveParams): typeof ForeignCurveV2 { const FieldUnreduced = createForeignField(params.modulus); const ScalarUnreduced = createForeignField(params.order); class Field extends FieldUnreduced.AlmostReduced {} @@ -299,7 +350,7 @@ function createForeignCurve(params: CurveParams): typeof ForeignCurve { const BigintCurve = createCurveAffine(params); - class Curve extends ForeignCurve { + class Curve extends ForeignCurveV2 { static _Bigint = BigintCurve; static _Field = Field; static _Scalar = Scalar; diff --git a/src/lib/provable/crypto/foreign-ecdsa.ts b/src/lib/provable/crypto/foreign-ecdsa.ts index 13d9aa04c0..d9840edfe2 100644 --- a/src/lib/provable/crypto/foreign-ecdsa.ts +++ b/src/lib/provable/crypto/foreign-ecdsa.ts @@ -4,20 +4,23 @@ import { ProvablePureExtended } from '../types/struct.js'; import { FlexiblePoint, ForeignCurve, + ForeignCurveV2, createForeignCurve, + createForeignCurveV2, toPoint, } from './foreign-curve.js'; import { AlmostForeignField } from '../foreign-field.js'; import { assert } from '../gadgets/common.js'; import { Field3 } from '../gadgets/foreign-field.js'; import { Ecdsa } from '../gadgets/elliptic-curve.js'; -import { l } from '../gadgets/range-check.js'; +import { l, multiRangeCheck } from '../gadgets/range-check.js'; import { Keccak } from './keccak.js'; import { Bytes } from '../wrapped-classes.js'; import { UInt8 } from '../int.js'; +import type { Bool } from '../bool.js'; // external API -export { createEcdsa, EcdsaSignature }; +export { createEcdsa, createEcdsaV2, EcdsaSignature, EcdsaSignatureV2 }; type FlexibleSignature = | EcdsaSignature @@ -26,6 +29,9 @@ type FlexibleSignature = s: AlmostForeignField | Field3 | bigint | number; }; +/** + * @deprecated `EcdsaSignature` is now deprecated and will be removed in a future release. Please use {@link EcdsaSignatureV2} instead. + */ class EcdsaSignature { r: AlmostForeignField; s: AlmostForeignField; @@ -69,7 +75,7 @@ class EcdsaSignature { /** * @deprecated There is a security vulnerability in this method. Use {@link verifyV2} instead. */ - verify(message: Bytes, publicKey: FlexiblePoint) { + verify(message: Bytes, publicKey: FlexiblePoint): Bool { let msgHashBytes = Keccak.ethereum(message); let msgHash = keccakOutputToScalar(msgHashBytes, this.Constructor.Curve); return this.verifySignedHash(msgHash, publicKey); @@ -109,7 +115,7 @@ class EcdsaSignature { * isValid.assertTrue('signature verifies'); * ``` */ - verifyV2(message: Bytes, publicKey: FlexiblePoint) { + verifyV2(message: Bytes, publicKey: FlexiblePoint): Bool { let msgHashBytes = Keccak.ethereum(message); let msgHash = keccakOutputToScalar(msgHashBytes, this.Constructor.Curve); return this.verifySignedHashV2(msgHash, publicKey); @@ -121,7 +127,7 @@ class EcdsaSignature { verifySignedHash( msgHash: AlmostForeignField | bigint, publicKey: FlexiblePoint - ) { + ): Bool { let msgHash_ = this.Constructor.Curve.Scalar.from(msgHash); let publicKey_ = this.Constructor.Curve.from(publicKey); return Ecdsa.verify( @@ -142,7 +148,7 @@ class EcdsaSignature { verifySignedHashV2( msgHash: AlmostForeignField | bigint, publicKey: FlexiblePoint - ) { + ): Bool { let msgHash_ = this.Constructor.Curve.Scalar.from(msgHash); let publicKey_ = this.Constructor.Curve.from(publicKey); return Ecdsa.verifyV2( @@ -210,8 +216,24 @@ class EcdsaSignature { } } +class EcdsaSignatureV2 extends EcdsaSignature { + constructor(signature: { + r: AlmostForeignField | Field3 | bigint | number; + s: AlmostForeignField | Field3 | bigint | number; + }) { + super(signature); + } + + static check(signature: EcdsaSignatureV2) { + multiRangeCheck(signature.r.value); + multiRangeCheck(signature.s.value); + // more efficient than the automatic check, which would do this for each scalar separately + this.Curve.Scalar.assertAlmostReduced(signature.r, signature.s); + } +} + /** - * Create a class {@link EcdsaSignature} for verifying ECDSA signatures on the given curve. + * @deprecated `EcdsaSignature` is now deprecated and will be removed in a future release. Please use {@link EcdsaSignatureV2} instead. */ function createEcdsa( curve: CurveParams | typeof ForeignCurve @@ -231,6 +253,27 @@ function createEcdsa( return Signature; } +/** + * Create a class {@link EcdsaSignatureV2} for verifying ECDSA signatures on the given curve. + */ +function createEcdsaV2( + curve: CurveParams | typeof ForeignCurveV2 +): typeof EcdsaSignatureV2 { + let Curve0: typeof ForeignCurveV2 = + 'b' in curve ? createForeignCurveV2(curve) : curve; + class Curve extends Curve0 {} + + class Signature extends EcdsaSignatureV2 { + static _Curve = Curve; + static _provable = provableFromClass(Signature, { + r: Curve.Scalar.provable, + s: Curve.Scalar.provable, + }); + } + + return Signature; +} + function toObject(signature: EcdsaSignature) { return { r: signature.r.value, s: signature.s.value }; } diff --git a/src/lib/provable/test/foreign-curve.unit-test.ts b/src/lib/provable/test/foreign-curve.unit-test.ts index 2861002421..24ce76d979 100644 --- a/src/lib/provable/test/foreign-curve.unit-test.ts +++ b/src/lib/provable/test/foreign-curve.unit-test.ts @@ -1,11 +1,11 @@ -import { createForeignCurve } from '../crypto/foreign-curve.js'; +import { createForeignCurveV2 } from '../crypto/foreign-curve.js'; import { Fq } from '../../../bindings/crypto/finite-field.js'; import { Vesta as V } from '../../../bindings/crypto/elliptic-curve.js'; import { Provable } from '../provable.js'; import { Field } from '../field.js'; import { Crypto } from '../crypto/crypto.js'; -class Vesta extends createForeignCurve(Crypto.CurveParams.Vesta) {} +class Vesta extends createForeignCurveV2(Crypto.CurveParams.Vesta) {} class Fp extends Vesta.Scalar {} let g = { x: Fq.negate(1n), y: 2n, infinity: false }; diff --git a/tests/vk-regression/diverse-zk-program.ts b/tests/vk-regression/diverse-zk-program.ts index 0261c72b94..69333903cf 100644 --- a/tests/vk-regression/diverse-zk-program.ts +++ b/tests/vk-regression/diverse-zk-program.ts @@ -1,8 +1,8 @@ import { ZkProgram, Crypto, - createEcdsa, - createForeignCurve, + createEcdsaV2, + createForeignCurveV2, Bytes, assert, Provable, @@ -18,9 +18,9 @@ import { export { diverse, Bytes128 }; -class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) {} +class Secp256k1 extends createForeignCurveV2(Crypto.CurveParams.Secp256k1) {} class Secp256k1Scalar extends Secp256k1.Scalar {} -class Secp256k1Signature extends createEcdsa(Secp256k1) {} +class Secp256k1Signature extends createEcdsaV2(Secp256k1) {} class Bytes128 extends Bytes(128) {} diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index db07d5bd82..c665480dfb 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -262,29 +262,29 @@ } }, "ecdsa-only": { - "digest": "3981c6b30a5d6eccbdd26351e5e059cbbee162f43a4cd6280b2335e66c8ae9ba", + "digest": "24900c7220649674063b13d759d14e5fa22783dd3d2b507be8d980b4cc62d6db", "methods": { "verifySignedHash": { - "rows": 31689, - "digest": "87cdb8324830be5385ab306e90c4bad2" + "rows": 31697, + "digest": "e75d4f621b2f027acf6bf60307832cc5" } }, "verificationKey": { - "data": "AAD0TiJIvE46IEuFjZed3VZt7S8Wp0kRCJXb3a2Ju7WUBunBgHb4SXvz1c3QjP2nd1qSYoUr66taz9IKVgu+5so8TiBnYTRcd7RsaAjbdbIbQJ9EuopFRFewZRx9qeQeEibNeMRcRMP4LdfS3AQRxhFZzN4HFa4MbtGs+Aja820cIw5azWYCxKf0pWJRDHbLUtJrjnQT0ATHD77rtbgLedcfFKfCQS5oAbF7hIhCbAsm7wMT+9+ZX8M5354UKZ03NiLUh5XNLAlGgVi7FfWR6p9P72AAymyD3lUdecJyZmCREiVgPrTdFppkp45TefJWNTySkV9c5YzpNxQoXedZDvYP/5s4KBkfIeK+zB2yJC9eZ1ZDYfM88shGDYxmBtur9AkQ49QGquR+kYUI0lpXtuNMG+ZRy0FRJ8ci/TE+PIPIFnSiGcSOA3YM2G171LYf89abU2QUoQRHSP3PmmAOy/8CoRLVro7Nl6z/Ou0oZzX7RjOEo//LBqcSWa2S9X8TQz0R3uivbovTdq0rrba56SbEnK6LWItmBc6CubYWL7UzDbD3RZM6iRz1hqTHDzDz7UIWOzHgLqW9rjnZllQCyfsSAHzKVEEoVCOPXn5qzQsEEa7rDqWgLi4KVq5R4NLLt5w/titLcOybRhYCMk4nB3OVJQuC3rQKXPhIyyao8UXiBjwgKzcNZhhPW5VfbcSYDpx5nVaU5pTEFl+2+RlcuhBpG1ksAWbD64AUKDjdyTWIC5Wn68AagPtG65V13eFS5LgkSfVtNXxGodg7SdP4AJmXpBgZfzMg4RW6Qje5ZFfrwRzoHPo0y7nO1hkaNLGV3Wvd3/pYiebXvyo+DdTZmaMbJpJaGSCysnovOrVUIpcn4h1hvA12jztQFQcbNHoVeZgslPxA54y9ynjhN7VZfT8lNXXIrRCpmPaxZW6Bw6Op/g6FGIs8TlruzZJRhz1lOLvl2FPvrUsFtz4yWTPjbT+VGsKuJFPvMuYybxq8pGyWVQN023uObDel47krlcQoH4MAm9ipKXzMO25PK8L3KtA4PPDP+En4qeIrshifHItQjCZDzgtE5oz0hXkEbNiWNzV7alOrR39PD1yuaLyXlx9DEvDR5DLlenSa0wQ3PXdv/C9LpDvkzJOLZs+/ZePd4YMI0+WuP2+6Xas4aNM+4JkNuHF5uMDcxgWID4TUy7Vdlzm3CVbhX15uBoKhuYWQgLr2rnVJ5SOZoDvlwJtcK2izLMYVAasejw4fvsehYGb88wvDbFxS6sM9gDSgTlavZRs95Qf+c1KpYf/jb8BxYNrwrqy8F++c1APDzfzQ/IbVLiaL28wkEy412qmXSjM+9hErKXFy8JIT/WBOIWMMg/7mMjvxngHnci+aYJZ6J+Lszh5zgo708vzO7fwaxC0wgd8anH3gFrbFnOg1hkmmoUEIgIwXh+ynuoZPOaoKNXNm1jOl8HpdFOG7vpQavC600YgzS2YGtY7K2WQ5GtN5ZTZBHPsUSir2yKSo9Le9CWXbDtn3SBDepWypwDa3YWKtNog+y10VmpL1N+RG3u1DXSuY7y9WZgkQ7tdvyx/Gjr91kjF0s3bt7vHIAZCtzNlRlWDBz3og0cSnEucCEuKR6dL2Mz+RuF1GmLoXZXapUjVG/82BjdAMAOxPlE67lEs+JWgnrVrA5NLJoL4DZ6+fhQKpNfk0uOrEfZIWR9Sau0IBwBxu6IYVm5/XAB19dt8MAuVcRdN/JGGzo0Hr3WVJuKzbAhuFwJZzcd1J1n4xO09ECT5NQdFSFXGsy8kIFjRNEOkLl+bAExePtGCt0w6cYqB0uCeX3lTI7ugIEgdStMtHFiWngJ218l8CuVrkwTJ7ZqHLtuJDiNqlLptkHWChDfw+IgDwz85dZrfBBzQrMRWranxQmisM+wx3vC+pLURRQHZJEasGCAElj0lTColrqQ/cXS7cBaqs1tBsQDGzKYMCMwsqL53fyxGCljVvljBa99+FpYfoUK+Fi0z6uEbem+luXRScr2yPB5I08lnBY23RmBb/pfSyBfbcmnmF5BkRlJTJKY7fQL/t9bFfywoquQe9e7OQvIjppA/FO7HmZS6hoOU+eS8+W94fEF2gvrowpTeqQHM6hLN9Qzl8niwZWUIyRCfyuzQnuSz/VP1K2sMFBKnZZNDcuBh1/xSFymOH6LfNKostvc6qHTIxrTjlH6952bo1bQl+mVvBUaJuRkYh12QbcyIyzcBFUYwaFazzkHXMof0O30oL3Q6wegTvJxTSZD5VCr5D26Myzoa0JBpqL0st9/MNGZe5a/+HW1qan/VtGA5nYkJcUzwKVqqlmZeuOZekFLGxlfp0lv9IQUQWtiU5uvd5HVoolEc/teUnx/IxYe01IDxX9cbmPMJnLYXJGSY=", - "hash": "5709505770243633710702038873416963172579507364104803648209582439539220801405" + "data": "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", + "hash": "1084248801390462271769951383316014811707920191311544091826735765051540268758" } }, "ecdsa": { - "digest": "1324f161aac5b24f90009d845c4f1273a52062fd54b96f69bfaf625ff06b5c8b", + "digest": "37010acaab02d5954aa948388c6bf6794c308008e432111528418dad3941840a", "methods": { "verifyEcdsa": { - "rows": 46187, - "digest": "f173d91e37a7d9041cef31c3f239a194" + "rows": 46195, + "digest": "b090e4f5803ff16334369eaf6db5fa11" } }, "verificationKey": { - "data": "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", - "hash": "10431289529378131910378558488657048256065054528583914701305345208193612782024" + "data": "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", + "hash": "22424774401338967221499466976597606662642952219226729808020331792226179683107" } }, "sha256": { @@ -301,11 +301,11 @@ } }, "diverse": { - "digest": "36308476aa4b0f23cef29b266a581832b549576aa604e4275670781feebd74d3", + "digest": "e00864ae3864d3fc7ae27c5b68305a371d54671bec3014c47d1ffb1d19863b7", "methods": { "ecdsa": { - "rows": 29105, - "digest": "9cb8cdc7da2003ebd5062766b6475848" + "rows": 29121, + "digest": "243c446e9f2936aa221506781051b39d" }, "generic": { "rows": 6, @@ -329,8 +329,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "25623831254983213172744462801407311873591580962193188412810615740754024675774" + "data": "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", + "hash": "12544024554190133573768877221117556210788739497688908501452657569872010600939" } } } \ No newline at end of file