diff --git a/.gitmodules b/.gitmodules index dfdd2af..9e73484 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib/Solidity-RLP"] path = lib/Solidity-RLP url = https://github.com/hamdiallam/Solidity-RLP +[submodule "lib/elliptic-curve-solidity"] + path = lib/elliptic-curve-solidity + url = https://github.com/witnet/elliptic-curve-solidity diff --git a/README.md b/README.md index f27ce96..79efd27 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,22 @@ contract Example { } ``` +## crypto/Secp256k1.sol + +Helper library to interact with the `secp256k1` curve. + +```solidity +import "src/crypto/Secp256k1.sol"; + +contract Example { + function example() public { + // string memory privateKey = Suave.privateKeyGen(Suave.CryptoSignature.SECP256); + string memory privateKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"; + address found = Secp256k1.deriveAddress(privateKey); + } +} +``` + ## Forge integration In order to use `forge`, you need to have a running `Suave` node and the `suave` binary in your path. @@ -210,13 +226,14 @@ Use the `setConfidentialInputs` function to set the confidential inputs during t import "forge-std/Test.sol"; import "src/Test.sol"; import "src/suavelib/Suave.sol"; +import "src/Context.sol"; contract TestForge is Test, SuaveEnabled { function testConfidentialInputs() public { bytes memory input = hex"abcd"; ctx.setConfidentialInputs(input); - bytes memory found2 = Suave.confidentialInputs(); + bytes memory found2 = Context.confidentialInputs(); assertEq0(input, found2); } } diff --git a/lib/elliptic-curve-solidity b/lib/elliptic-curve-solidity new file mode 160000 index 0000000..3475478 --- /dev/null +++ b/lib/elliptic-curve-solidity @@ -0,0 +1 @@ +Subproject commit 347547890840fd501809dfe0b855206407136ec0 diff --git a/src/crypto/Secp256k1.sol b/src/crypto/Secp256k1.sol new file mode 100644 index 0000000..6b27593 --- /dev/null +++ b/src/crypto/Secp256k1.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import "elliptic-curve-solidity/contracts/EllipticCurve.sol"; +import "../utils/HexStrings.sol"; + +/// @notice Secp256k1 is a library with utilities to work with the secp256k1 curve. +library Secp256k1 { + uint256 internal constant GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798; + uint256 internal constant GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8; + uint256 internal constant AA = 0; + uint256 internal constant BB = 7; + uint256 internal constant PP = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; + uint256 internal constant NN = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + + function derivePubKey(uint256 privKey) internal pure returns (uint256 qx, uint256 qy) { + (qx, qy) = EllipticCurve.ecMul(privKey, GX, GY, AA, PP); + } + + function deriveAddress(uint256 privKey) internal pure returns (address) { + (uint256 qx, uint256 qy) = derivePubKey(privKey); + bytes memory ser = bytes.concat(bytes32(qx), bytes32(qy)); + return address(uint160(uint256(keccak256(ser)))); + } + + // @notice deriveAddress returns the address corresponding to the private key + // @param privKey is the private key + // @return address is the address derived from the private key + function deriveAddress(string memory privKey) internal pure returns (address) { + bytes memory privKeyBytes = HexStrings.fromHexString(privKey); + require(privKeyBytes.length == 32, "Invalid private key length"); + + return deriveAddress(uint256(bytes32(privKeyBytes))); + } +} diff --git a/test/crypto/Secp256k1.t.sol b/test/crypto/Secp256k1.t.sol new file mode 100644 index 0000000..17af2ad --- /dev/null +++ b/test/crypto/Secp256k1.t.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "src/Test.sol"; +import "src/suavelib/Suave.sol"; +import "src/Context.sol"; +import "src/crypto/Secp256k1.sol"; + +contract TestSecp256K1 is Test, SuaveEnabled { + function testRecoverAddress() public { + string memory signingKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"; + + address expected = 0x71562b71999873DB5b286dF957af199Ec94617F7; + address found = Secp256k1.deriveAddress(signingKey); + assert(expected == found); + } +} diff --git a/tools/stdchecker/remappings.txt b/tools/stdchecker/remappings.txt index 94c25ce..eeca152 100644 --- a/tools/stdchecker/remappings.txt +++ b/tools/stdchecker/remappings.txt @@ -3,3 +3,4 @@ Solidity-RLP/=../../lib/Solidity-RLP/contracts/ ds-test/=../../lib/forge-std/lib/ds-test/src/ forge-std/=../../lib/forge-std/src/ solady/=../../lib/solady/ +elliptic-curve-solidity/=../../lib/elliptic-curve-solidity \ No newline at end of file