6

How can I get the public key of msg.sender from inside the smart contract?

function getPublicKey() returns (string publicKey) {}

From Solidity docs, ecrecover returns an address:

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns
(address): recover the address associated with the public key from
elliptic curve signature or return zero on error

From ethereumjs-util library, ecrecover returns a public key:

/**
 * ECDSA public key recovery from signature
 * @param {Buffer} msgHash
 * @param {Number} v
 * @param {Buffer} r
 * @param {Buffer} s
 * @return {Buffer} publicKey
 */
exports.ecrecover = function (msgHash, v, r, s) {
  var signature = Buffer.concat([exports.setLength(r, 32), exports.setLength(s, 32)], 64)
  var recovery = v - 27
  if (recovery !== 0 && recovery !== 1) {
    throw new Error('Invalid signature v value')
  }
  var senderPubKey = secp256k1.recover(msgHash, signature, recovery)
  return secp256k1.publicKeyConvert(senderPubKey, false).slice(1)
}

I assume, that when the transaction is sent, there is all information necessary to derive a public key for the sender's address, but I'm struggling to find any clear documentation on how to do it.


My current approach is:

function getPublicKey(account) {
  return new Promise((resolve, reject) => {
    web3.eth.sendTransaction({
      from: account,
      to: account,
      value: 0,
    }, (err, txId) => {
      if (err) return reject(err)
      web3.eth.getTransaction(txId, (err, tx) => {
        if (err) return reject(err)
        return resolve(tx.publicKey)
      })
    })
  })
}
2
  • 2
    I don't believe that contracts can access the public key of the sender, since the raw transaction data is not available to it. Given the public key, however, it would be easy to verify that it matches the address Commented Apr 12, 2017 at 12:46
  • I've added my current implementation to get public key of the account, which is further saved in a smart contract. But I want to at least verify (on-chain) that it's indeed the right public key of msg.sender. @TjadenHess could you advice how this could be done? Commented Apr 13, 2017 at 10:02

1 Answer 1

6

Contracts cannot access the public key of the sender, since the raw transaction data is not visible to contracts. However, given the public key, you can verify that it does in fact match the address of the sender by re-deriving the address from the public key.

In Solidity it would look something like this:

function checkPubKey(bytes pubkey) constant returns (bool){
    return (uint(keccak256(pubkey)) & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) == uint(msg.sender);
}
4
  • 1
    Error: Operator & not compatible with types bytes32 and address\n return (keccak256(publicKey) & 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF) == uint(addr); Commented Apr 13, 2017 at 14:54
  • Error: Operator == not compatible with types bytes32 and uint256\n return (keccak256(publicKey) & 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF) == uint(addr);\n Commented Apr 13, 2017 at 14:55
  • 3
    Oh yeah, sorry just had to cast the type Commented Apr 13, 2017 at 14:55
  • 1
    Wow, that was a quick edit! Commented Apr 13, 2017 at 14:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.