witnet / elliptic-curve-solidity

Elliptic Curve arithmetic operations written in Solidity
MIT License
168 stars 41 forks source link

Address from a private key #14

Closed rumkin closed 4 years ago

rumkin commented 4 years ago

Hi, I'm writing an EIP of non-transferable contracts. I'm wondering could you add an example of receiving an address from a private key. I would include references to the organization and the repository in my EIP.

mariocao commented 4 years ago

Hi @rumkin, thanks for considering our library.

The example/Secp256k1.sol contract was only included as an example as it is not recommended to use cryptographically sensitive information as input parameters. :)

However, generating the address is quite easy and can be computed as follows:

  1. derive public key from the example derivePubKey function
  2. compute hash using keccak256 precompiled function
  3. take only the last 20 bytes

Here you have an snippet of the code:

pragma solidity ^0.6.0;

import "elliptic-curve-solidity/contracts/EllipticCurve.sol";

/**
 * @title Secp256k1 Elliptic Curve
 * @notice Example of particularization of Elliptic Curve for secp256k1 curve
 * @author Witnet Foundation
 */
contract Secp256k1 {

  uint256 public constant GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
  uint256 public constant GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8;
  uint256 public constant AA = 0;
  uint256 public constant BB = 7;
  uint256 public constant PP = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;

  /// @dev Public Key derivation from private key
  /// @param privKey The private key
  /// @return (qx, qy) The Public Key
  function derivePubKey(uint256 privKey) external pure returns (uint256, uint256) {
    return EllipticCurve.ecMul(
      privKey,
      GX,
      GY,
      AA,
      PP
    );
  }

  function generateAddress(uint256 privKey) external pure returns (address) {
    (uint256 qx, uint256 qy) = EllipticCurve.ecMul(
      privKey,
      GX,
      GY,
      AA,
      PP
    );

    address addr;
    bytes32 hash = keccak256(encodePoint(qx,qy));
    assembly {
        mstore(0, hash)
        addr := mload(0)
    }

    return addr;
  }

  function encodePoint(uint256 _x, uint256 _y) internal pure returns (bytes memory) {
    uint8 prefix = uint8(2 + (_y % 2));

    return abi.encodePacked(prefix, _x);
  }
}
rumkin commented 4 years ago

@mariocao Mario, thanks a lot! To be honest to use sensitive information as input on public blockchain is what required for my proposal of proof of single person ownership. I need this to create a private key burning mechanism, so disclosure is required. I know the trade-offs of this and working on solving them. Thanks again.

mariocao commented 4 years ago

You are welcome. Let us know if you need anything else. :blush: