From 2190f4ecb8328ca4bd6d8718f41be58ae73e3dd3 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 4 Jan 2021 17:13:01 +0100 Subject: [PATCH 01/12] adding support for short signature (eip-2098) in cryptography/ECDSA --- contracts/utils/cryptography/ECDSA.sol | 35 +++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/contracts/utils/cryptography/ECDSA.sol b/contracts/utils/cryptography/ECDSA.sol index b3ea4077403..98c79be29d4 100644 --- a/contracts/utils/cryptography/ECDSA.sol +++ b/contracts/utils/cryptography/ECDSA.sol @@ -24,23 +24,34 @@ library ECDSA { * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { - // Check the signature length - if (signature.length != 65) { - revert("ECDSA: invalid signature length"); - } - // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - // solhint-disable-next-line no-inline-assembly - assembly { - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) + // Check the signature length + // - case 65: r,s,v signature (standard) + // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) + if (signature.length == 65) { + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + // solhint-disable-next-line no-inline-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + } else if (signature.length == 64) { + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + // solhint-disable-next-line no-inline-assembly + assembly { + r := mload(add(signature, 0x20)) + s := and(mload(add(_sign, 0x40)), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + v := add(shr(7, byte(0, mload(add(_sign, 0x40)))), 27) + } + } else { + revert("ECDSA: invalid signature length"); } return recover(hash, v, r, s); From 3b5440c7a07e45bf061c78b7a92e068c23daa1bd Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 4 Jan 2021 21:48:07 +0100 Subject: [PATCH 02/12] fix typo in eip2098 implementation --- contracts/utils/cryptography/ECDSA.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/utils/cryptography/ECDSA.sol b/contracts/utils/cryptography/ECDSA.sol index 98c79be29d4..ca7a559a95b 100644 --- a/contracts/utils/cryptography/ECDSA.sol +++ b/contracts/utils/cryptography/ECDSA.sol @@ -47,8 +47,8 @@ library ECDSA { // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) - s := and(mload(add(_sign, 0x40)), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - v := add(shr(7, byte(0, mload(add(_sign, 0x40)))), 27) + s := and(mload(add(signature, 0x40)), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + v := add(shr(7, byte(0, mload(add(signature, 0x40)))), 27) } } else { revert("ECDSA: invalid signature length"); From 392e06fcb5036288fb69998e9a24f83f63c958b0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 12 Mar 2021 09:18:01 +0100 Subject: [PATCH 03/12] add tests for ERC2098 --- test/utils/cryptography/ECDSA.test.js | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index 9062b6913f9..f0e027a4c4a 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/test/utils/cryptography/ECDSA.test.js @@ -8,6 +8,22 @@ const ECDSAMock = artifacts.require('ECDSAMock'); const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin'); const WRONG_MESSAGE = web3.utils.sha3('Nope'); +function to2098Format (signature) { + const long = web3.utils.hexToBytes(signature); + expect(long.length).to.be.equal(65); + const short = long.slice(0, 64); + short[32] |= (long[64] % 27) << 7; + return web3.utils.bytesToHex(short); +} + +function from2098Format (signature) { + const short = web3.utils.hexToBytes(signature); + expect(short.length).to.be.equal(64); + short.push((short[32] >> 7) + 27); + short[32] &= (1 << 7) - 1; + return web3.utils.bytesToHex(short); +} + contract('ECDSA', function (accounts) { const [ other ] = accounts; @@ -61,6 +77,15 @@ contract('ECDSA', function (accounts) { await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); }); }); + + context('short 2098 format', function () { + it('works', async function () { + const version = '1b'; // 27 = 1b. + const signature = signatureWithoutVersion + version; + expect(await this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature))).to.equal(signer); + expect(await this.ecdsa.recover(TEST_MESSAGE, from2098Format(to2098Format(signature)))).to.equal(signer); + }); + }); }); context('with v1 signature', function () { @@ -93,6 +118,15 @@ contract('ECDSA', function (accounts) { await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); }); }); + + context('short 2098 format', function () { + it('works', async function () { + const version = '1c'; // 27 = 1b. + const signature = signatureWithoutVersion + version; + expect(await this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature))).to.equal(signer); + expect(await this.ecdsa.recover(TEST_MESSAGE, from2098Format(to2098Format(signature)))).to.equal(signer); + }); + }); }); context('with high-s value signature', function () { From f1f3d257da8f3ad419f9dda8244fe4d6c10056d2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 12 Mar 2021 09:19:35 +0100 Subject: [PATCH 04/12] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49eb3ebbcfe..92dfca8f7a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * `IERC20Metadata`: add a new extended interface that includes the optional `name()`, `symbol()` and `decimals()` functions. ([#2561](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2561)) * `ERC777`: make reception acquirement optional in `_mint`. ([#2552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2552)) * `ERC20Permit`: add a `_useNonce` to enable further usage of ERC712 signatures. ([#2565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2565)) + * `ECDSA`: add support for ERC2098 short-signatures. ([#2582](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2582)) ## 4.0.0 (2021-03-23) From f9b792dd5940d7e8c3f967235401f2322c5ad339 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 12 Mar 2021 14:49:20 +0100 Subject: [PATCH 05/12] minor gas optimization --- contracts/utils/cryptography/ECDSA.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/utils/cryptography/ECDSA.sol b/contracts/utils/cryptography/ECDSA.sol index ca7a559a95b..f10595c8b66 100644 --- a/contracts/utils/cryptography/ECDSA.sol +++ b/contracts/utils/cryptography/ECDSA.sol @@ -46,9 +46,10 @@ library ECDSA { // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { + let vs := mload(add(signature, 0x40)) r := mload(add(signature, 0x20)) - s := and(mload(add(signature, 0x40)), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - v := add(shr(7, byte(0, mload(add(signature, 0x40)))), 27) + s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + v := add(shr(7, byte(0, vs)), 27) } } else { revert("ECDSA: invalid signature length"); From 647f029f891e38cb063abdde532dad1ad1743f18 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 6 Apr 2021 09:45:29 +0200 Subject: [PATCH 06/12] Update test/utils/cryptography/ECDSA.test.js Co-authored-by: Francisco Giordano --- test/utils/cryptography/ECDSA.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index f0e027a4c4a..5e1e4273de1 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/test/utils/cryptography/ECDSA.test.js @@ -12,7 +12,7 @@ function to2098Format (signature) { const long = web3.utils.hexToBytes(signature); expect(long.length).to.be.equal(65); const short = long.slice(0, 64); - short[32] |= (long[64] % 27) << 7; + short[32] |= (long[64] % 27) << 7; // set the first bit of the 32nd byte to the v parity bit return web3.utils.bytesToHex(short); } From 5773f85915941f59a0542bc4a53607b2fa5ebea3 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 6 Apr 2021 09:45:34 +0200 Subject: [PATCH 07/12] Update test/utils/cryptography/ECDSA.test.js Co-authored-by: Francisco Giordano --- test/utils/cryptography/ECDSA.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index 5e1e4273de1..416befc2e0d 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/test/utils/cryptography/ECDSA.test.js @@ -20,7 +20,7 @@ function from2098Format (signature) { const short = web3.utils.hexToBytes(signature); expect(short.length).to.be.equal(64); short.push((short[32] >> 7) + 27); - short[32] &= (1 << 7) - 1; + short[32] &= (1 << 7) - 1; // zero out the first bit of 1 the 32nd byte return web3.utils.bytesToHex(short); } From 11e350391ffead1e7ffb53be25547e75573cdd06 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 6 Apr 2021 09:45:58 +0200 Subject: [PATCH 08/12] Update contracts/utils/cryptography/ECDSA.sol Co-authored-by: Francisco Giordano --- contracts/utils/cryptography/ECDSA.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/utils/cryptography/ECDSA.sol b/contracts/utils/cryptography/ECDSA.sol index f10595c8b66..2c75204398f 100644 --- a/contracts/utils/cryptography/ECDSA.sol +++ b/contracts/utils/cryptography/ECDSA.sol @@ -49,7 +49,7 @@ library ECDSA { let vs := mload(add(signature, 0x40)) r := mload(add(signature, 0x20)) s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - v := add(shr(7, byte(0, vs)), 27) + v := add(shr(vs, 255), 27) } } else { revert("ECDSA: invalid signature length"); From e04f8178fc4739a6cd1788f6f76257026baa5eb0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 6 Apr 2021 09:46:04 +0200 Subject: [PATCH 09/12] Update test/utils/cryptography/ECDSA.test.js Co-authored-by: Francisco Giordano --- test/utils/cryptography/ECDSA.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index 416befc2e0d..54d402f39e6 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/test/utils/cryptography/ECDSA.test.js @@ -78,7 +78,7 @@ contract('ECDSA', function (accounts) { }); }); - context('short 2098 format', function () { + context('short EIP2098 format', function () { it('works', async function () { const version = '1b'; // 27 = 1b. const signature = signatureWithoutVersion + version; From 157a74ac92d3912d7280aa2da126b64429a9b21e Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 6 Apr 2021 09:46:09 +0200 Subject: [PATCH 10/12] Update test/utils/cryptography/ECDSA.test.js Co-authored-by: Francisco Giordano --- test/utils/cryptography/ECDSA.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index 54d402f39e6..f1826d2563f 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/test/utils/cryptography/ECDSA.test.js @@ -119,7 +119,7 @@ contract('ECDSA', function (accounts) { }); }); - context('short 2098 format', function () { + context('short EIP2098 format', function () { it('works', async function () { const version = '1c'; // 27 = 1b. const signature = signatureWithoutVersion + version; From 9cb0fd5298c1d24855b1b6c5c1d779bb9d8cc43b Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 7 Apr 2021 17:57:00 +0200 Subject: [PATCH 11/12] fix assembly error --- contracts/utils/cryptography/ECDSA.sol | 2 +- test/utils/cryptography/ECDSA.test.js | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/contracts/utils/cryptography/ECDSA.sol b/contracts/utils/cryptography/ECDSA.sol index 2c75204398f..b58e26e9fa1 100644 --- a/contracts/utils/cryptography/ECDSA.sol +++ b/contracts/utils/cryptography/ECDSA.sol @@ -49,7 +49,7 @@ library ECDSA { let vs := mload(add(signature, 0x40)) r := mload(add(signature, 0x20)) s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - v := add(shr(vs, 255), 27) + v := add(shr(255, vs), 27) } } else { revert("ECDSA: invalid signature length"); diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index f1826d2563f..a9278ff02a3 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/test/utils/cryptography/ECDSA.test.js @@ -53,7 +53,7 @@ contract('ECDSA', function (accounts) { const signatureWithoutVersion = '0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892'; context('with 00 as version value', function () { - it('reverts', async function () { + it('reverts with 00 as version value', async function () { const version = '00'; const signature = signatureWithoutVersion + version; await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); @@ -61,7 +61,7 @@ contract('ECDSA', function (accounts) { }); context('with 27 as version value', function () { - it('works', async function () { + it('works with 27 as version value', async function () { const version = '1b'; // 27 = 1b. const signature = signatureWithoutVersion + version; expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.equal(signer); @@ -69,7 +69,7 @@ contract('ECDSA', function (accounts) { }); context('with wrong version', function () { - it('reverts', async function () { + it('reverts with wrong version', async function () { // The last two hex digits are the signature version. // The only valid values are 0, 1, 27 and 28. const version = '02'; @@ -79,7 +79,7 @@ contract('ECDSA', function (accounts) { }); context('short EIP2098 format', function () { - it('works', async function () { + it('works with short EIP2098 format', async function () { const version = '1b'; // 27 = 1b. const signature = signatureWithoutVersion + version; expect(await this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature))).to.equal(signer); @@ -94,7 +94,7 @@ contract('ECDSA', function (accounts) { const signatureWithoutVersion = '0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0'; context('with 01 as version value', function () { - it('reverts', async function () { + it('reverts with 01 as version value', async function () { const version = '01'; const signature = signatureWithoutVersion + version; await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); @@ -102,7 +102,7 @@ contract('ECDSA', function (accounts) { }); context('with 28 as version value', function () { - it('works', async function () { + it('works with 28 as version value', async function () { const version = '1c'; // 28 = 1c. const signature = signatureWithoutVersion + version; expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.equal(signer); @@ -110,7 +110,7 @@ contract('ECDSA', function (accounts) { }); context('with wrong version', function () { - it('reverts', async function () { + it('reverts with wrong version', async function () { // The last two hex digits are the signature version. // The only valid values are 0, 1, 27 and 28. const version = '02'; @@ -120,7 +120,7 @@ contract('ECDSA', function (accounts) { }); context('short EIP2098 format', function () { - it('works', async function () { + it('works with short EIP2098 format', async function () { const version = '1c'; // 27 = 1b. const signature = signatureWithoutVersion + version; expect(await this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature))).to.equal(signer); @@ -130,7 +130,7 @@ contract('ECDSA', function (accounts) { }); context('with high-s value signature', function () { - it('reverts', async function () { + it('reverts with high-s value signature', async function () { const message = '0xb94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'; // eslint-disable-next-line max-len const highSSignature = '0xe742ff452d41413616a5bf43fe15dd88294e983d3d36206c2712f39083d638bde0a0fc89be718fbc1033e1d30d78be1c68081562ed2e97af876f286f3453231d1b'; @@ -161,7 +161,7 @@ contract('ECDSA', function (accounts) { }); context('with invalid signature', function () { - it('reverts', async function () { + it('reverts with invalid signature', async function () { // eslint-disable-next-line max-len const signature = '0x332ce75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c'; await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature'); From 07f13007bb05e5ac1ec8bb99204cce3ce649e66d Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 9 Apr 2021 19:14:27 -0300 Subject: [PATCH 12/12] remove redundant context blocks --- test/utils/cryptography/ECDSA.test.js | 144 +++++++++++--------------- 1 file changed, 60 insertions(+), 84 deletions(-) diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index a9278ff02a3..468e36a1982 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/test/utils/cryptography/ECDSA.test.js @@ -52,39 +52,31 @@ contract('ECDSA', function (accounts) { // eslint-disable-next-line max-len const signatureWithoutVersion = '0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892'; - context('with 00 as version value', function () { - it('reverts with 00 as version value', async function () { - const version = '00'; - const signature = signatureWithoutVersion + version; - await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); - }); + it('reverts with 00 as version value', async function () { + const version = '00'; + const signature = signatureWithoutVersion + version; + await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); }); - context('with 27 as version value', function () { - it('works with 27 as version value', async function () { - const version = '1b'; // 27 = 1b. - const signature = signatureWithoutVersion + version; - expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.equal(signer); - }); + it('works with 27 as version value', async function () { + const version = '1b'; // 27 = 1b. + const signature = signatureWithoutVersion + version; + expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.equal(signer); }); - context('with wrong version', function () { - it('reverts with wrong version', async function () { - // The last two hex digits are the signature version. - // The only valid values are 0, 1, 27 and 28. - const version = '02'; - const signature = signatureWithoutVersion + version; - await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); - }); + it('reverts with wrong version', async function () { + // The last two hex digits are the signature version. + // The only valid values are 0, 1, 27 and 28. + const version = '02'; + const signature = signatureWithoutVersion + version; + await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); }); - context('short EIP2098 format', function () { - it('works with short EIP2098 format', async function () { - const version = '1b'; // 27 = 1b. - const signature = signatureWithoutVersion + version; - expect(await this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature))).to.equal(signer); - expect(await this.ecdsa.recover(TEST_MESSAGE, from2098Format(to2098Format(signature)))).to.equal(signer); - }); + it('works with short EIP2098 format', async function () { + const version = '1b'; // 27 = 1b. + const signature = signatureWithoutVersion + version; + expect(await this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature))).to.equal(signer); + expect(await this.ecdsa.recover(TEST_MESSAGE, from2098Format(to2098Format(signature)))).to.equal(signer); }); }); @@ -93,85 +85,69 @@ contract('ECDSA', function (accounts) { // eslint-disable-next-line max-len const signatureWithoutVersion = '0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0'; - context('with 01 as version value', function () { - it('reverts with 01 as version value', async function () { - const version = '01'; - const signature = signatureWithoutVersion + version; - await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); - }); + it('reverts with 01 as version value', async function () { + const version = '01'; + const signature = signatureWithoutVersion + version; + await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); }); - context('with 28 as version value', function () { - it('works with 28 as version value', async function () { - const version = '1c'; // 28 = 1c. - const signature = signatureWithoutVersion + version; - expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.equal(signer); - }); + it('works with 28 as version value', async function () { + const version = '1c'; // 28 = 1c. + const signature = signatureWithoutVersion + version; + expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.equal(signer); }); - context('with wrong version', function () { - it('reverts with wrong version', async function () { - // The last two hex digits are the signature version. - // The only valid values are 0, 1, 27 and 28. - const version = '02'; - const signature = signatureWithoutVersion + version; - await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); - }); + it('reverts with wrong version', async function () { + // The last two hex digits are the signature version. + // The only valid values are 0, 1, 27 and 28. + const version = '02'; + const signature = signatureWithoutVersion + version; + await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature \'v\' value'); }); - context('short EIP2098 format', function () { - it('works with short EIP2098 format', async function () { - const version = '1c'; // 27 = 1b. - const signature = signatureWithoutVersion + version; - expect(await this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature))).to.equal(signer); - expect(await this.ecdsa.recover(TEST_MESSAGE, from2098Format(to2098Format(signature)))).to.equal(signer); - }); + it('works with short EIP2098 format', async function () { + const version = '1c'; // 27 = 1b. + const signature = signatureWithoutVersion + version; + expect(await this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature))).to.equal(signer); + expect(await this.ecdsa.recover(TEST_MESSAGE, from2098Format(to2098Format(signature)))).to.equal(signer); }); }); - context('with high-s value signature', function () { - it('reverts with high-s value signature', async function () { - const message = '0xb94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'; - // eslint-disable-next-line max-len - const highSSignature = '0xe742ff452d41413616a5bf43fe15dd88294e983d3d36206c2712f39083d638bde0a0fc89be718fbc1033e1d30d78be1c68081562ed2e97af876f286f3453231d1b'; + it('reverts with high-s value signature', async function () { + const message = '0xb94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'; + // eslint-disable-next-line max-len + const highSSignature = '0xe742ff452d41413616a5bf43fe15dd88294e983d3d36206c2712f39083d638bde0a0fc89be718fbc1033e1d30d78be1c68081562ed2e97af876f286f3453231d1b'; - await expectRevert(this.ecdsa.recover(message, highSSignature), 'ECDSA: invalid signature \'s\' value'); - }); + await expectRevert(this.ecdsa.recover(message, highSSignature), 'ECDSA: invalid signature \'s\' value'); }); context('using web3.eth.sign', function () { - context('with correct signature', function () { - it('returns signer address', async function () { - // Create the signature - const signature = fixSignature(await web3.eth.sign(TEST_MESSAGE, other)); - - // Recover the signer address from the generated message and signature. - expect(await this.ecdsa.recover( - toEthSignedMessageHash(TEST_MESSAGE), - signature, - )).to.equal(other); - }); + it('returns signer address with correct signature', async function () { + // Create the signature + const signature = fixSignature(await web3.eth.sign(TEST_MESSAGE, other)); + + // Recover the signer address from the generated message and signature. + expect(await this.ecdsa.recover( + toEthSignedMessageHash(TEST_MESSAGE), + signature, + )).to.equal(other); }); - context('with wrong message', function () { - it('returns a different address', async function () { - const signature = fixSignature(await web3.eth.sign(TEST_MESSAGE, other)); - expect(await this.ecdsa.recover(WRONG_MESSAGE, signature)).to.not.equal(other); - }); + it('returns a different address', async function () { + const signature = fixSignature(await web3.eth.sign(TEST_MESSAGE, other)); + expect(await this.ecdsa.recover(WRONG_MESSAGE, signature)).to.not.equal(other); }); - context('with invalid signature', function () { - it('reverts with invalid signature', async function () { - // eslint-disable-next-line max-len - const signature = '0x332ce75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c'; - await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature'); - }); + it('reverts with invalid signature', async function () { + // eslint-disable-next-line max-len + const signature = '0x332ce75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c'; + await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature'); }); }); }); context('toEthSignedMessage', function () { - it('should prefix hashes correctly', async function () { + it('prefixes hashes correctly', async function () { expect(await this.ecdsa.toEthSignedMessageHash(TEST_MESSAGE)).to.equal(toEthSignedMessageHash(TEST_MESSAGE)); }); });