GithubHelp home page GithubHelp logo

Comments (2)

CedarMist avatar CedarMist commented on August 23, 2024

Converting compressed public key to ethereum address costs approx 5k gas with the following snippet, and a method to extract R &S params from DER encoded signature.

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

library EthereumUtils {
    uint256 constant k256_p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f;

    // (p+1)//4
    uint256 constant k256_p_plus_1_over_4 = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c;

    address constant PRECOMPILE_BIGMODEXP = address(0x5);

    function expmod(uint256 base, uint256 exponent, uint256 modulus)
        internal view
        returns (uint256 out)
    {
        (bool success, bytes memory result) = PRECOMPILE_BIGMODEXP.staticcall(abi.encodePacked(
            uint256(0x20),  // length of base
            uint256(0x20),  // length of exponent
            uint256(0x20),  // length of modulus
            base,
            exponent,
            modulus
        ));

        require( success );

        out = uint256(bytes32(result));
    }

    function k256_derive_y(uint8 _prefix, uint256 x)
        internal view
        returns (uint256 y)
    {
        require(_prefix == 0x02 || _prefix == 0x03);

        // x^3 + ax + b, where a=0, b=7
        y = addmod(
            mulmod(x, mulmod(x, x, k256_p), k256_p),
            7,
            k256_p
        );

        // find square root of quadratic residue
        y = expmod(y, k256_p_plus_1_over_4, k256_p);

        // negate y if indicated by sign bit
        if( (y + _prefix) % 2 != 0 )
        {
            y = k256_p - y;
        }
    }

    function k256_decompress(bytes memory pk)
        internal view
        returns (uint256 x, uint256 y)
    {
        require( pk.length == 33 );
        assembly {
            // skip 32 byte length prefix, plus one byte sign byte prefix
            x := mload(add(pk, 33))
        }
        y = k256_derive_y(uint8(pk[0]), x);
    }

    function ethereum_address(uint256 x, uint256 y)
        internal pure
        returns (address)
    {
        bytes32 digest = keccak256(abi.encodePacked(x, y));

        return address(uint160((uint256(digest)<<96) >> 96));
    }

    function k256_signature_der_split(bytes memory der)
        internal pure
        returns (bytes32 r, bytes32 s)
    {
        require( der.length == 70 );
        require( der[0] == 0x30 );
        require( der[1] == 0x44 );
        require( der[2] == 0x02 );
        require( der[3] == 0x20 );
        require( der[0x20+4] == 0x02 );
        require( der[0x20+5] == 0x20 );
        assembly {
            r := mload(add(der, 36))    // skip 32 bytes `der` length prefix
            s := mload(add(der, 70))
        }
        // Example of DER encoded signature
        // 0x3044022061676dce86847b7d79cc3740b71a651a3fef1edbb5b1d3b69c873ecca18fec31022074ca0050787a6980376e9d4f1bfc557bc3c67e6d565397cc862480d06c54ad63
        // 30
        // 44
        // 02
        // 20
        // 61676dce86847b7d79cc3740b71a651a3fef1edbb5b1d3b69c873ecca18fec31 = R
        // 02
        // 20
        // 74ca0050787a6980376e9d4f1bfc557bc3c67e6d565397cc862480d06c54ad63 = S
    }
}

from sapphire-paratime.

CedarMist avatar CedarMist commented on August 23, 2024

Turns out ASN.1 DER encoding is a little derpier than I expected, the variable length nature means field elements have their leading zeros removed and thus DER encoded signatures for k256 can be anywhere between 8 and 72 bytes. Sometimes it adds a leading zero too... presumably because the high bit is set to distinguish it from a signed value?

So, high probability of either R or S being 32-33 bytes, but also chances of them being 31 bytes or (with significantly decreasing probability) 30, 29 bytes etc. An example of a DER encoded signature of length 68 bytes:

0x3042022062fa7911022e734f7a40b6b545a2fc47923144ccffb9e2116cc8081d05c9ac56021e226163d65cde79b1e62c010f998890f25f1dc69db714b4a18a810ce32752

Which has 32 byte r and 30 byte s parameter

This means the k256_signature_der_split needs to handle variable lengths correctly otherwise it may fail intermittently with simpler checks, and so will be slightly more complex than the code above.

The good news is I've tested generate & verify signatures end to end through ecrecover with derived ethereum address from the public key and it works, so no blockers.

from sapphire-paratime.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.