3

I am quite new to ethereum. I would like to know if its possible to write a contract in which, I can know the Owner's public key and 'Sender's public key ?

7
  • Out of curiosity, why do you need the public key in the contract, instead of the address? Jun 14, 2016 at 15:09
  • @NickJohnson Wanted to encrypt some data with the public key and send over to the sender.
    – jgm
    Jun 14, 2016 at 15:12
  • 1
    You're aware that the unencrypted data will be visible in the blockchain for anyone who cares to look? Jun 14, 2016 at 15:13
  • @NickJohnson Ya. So I wanted to send a transaction using the blockchain encrypted with the Public key so that others cant read it.
    – jgm
    Jun 14, 2016 at 15:16
  • That won't help, though - anyone can replay your transaction and see the data before it was encrypted. Jun 14, 2016 at 15:19

4 Answers 4

2

This is not possible directly: The public key is not directly available to the contract. The contract only has the address of the sender, which is derived from their public key.

However, it is possible for a contract to do the derivation the other way, so if you pass it a public key as a parameter, it can find out what address it corresponds to.

Although the contract can't see the public key, it is technically possible to retrieve it from the blockchain and/or the network using code running outside the contract, provided the address has actually been used at least once. This is possible because every time you send a transaction, checking the signature involves first recovering the public key from the signature, then calculating the address and checking it matches.

So one thing you (or somebody else) could do would be to make a directory contract that keeps a list of addresses to public keys, and run a bot to query it for missing addresses, recover the public key from the blockchain, and send it to the directory contract.

If you don't want to run the bot yourself, you could modify the directory contract to manage a bounty for each missing public key. So your contract would send a small amount of ETH to the directory contract whenever it wanted a public key, and some unknown person out on the internet would look up the public key, send it to the directory contract and claim the bounty.

2

I see this type of question popping up at a lot of different places and it is (unfortunately) often answered with "you don't need that" or "why would you want that".

When strictly speaking Blockchain, this type of answer is right, assuming that all "communication" is done by sending a signed transaction to the blockchain. Here, knowing the public key is usually useless.

Anyhow, if one sees Blockchain (read "Ethereum") as a distributed, public, verifiable ledger of records (and states of smart contracts for that matter) and not the basis for all data of an app/service the question suddenly makes a lot of sense.

A scenario could be that two communication partners are located on off-chain devices (servers, mobile phones, laptops, ...). Both have a keypair used for identification and encryption/decryption/signing. The ledger would then register both user's addresses (part of the hashes of the public keys), which are then used as the (provable) identifiers. Communication would then happen completely off-chain, i.e. directly between the devices of the two users.

If user A now received a signed message from user B, A can verify easily that the message has not been tempered with and that it actually comes from the owner of the private key associated with the address of A. (A would derive B's public key from the signature of the message, compute the address from it, and verify that it is B's actual address).

Anyhow, if A and B haven't been in contact yet and A wants to send an ENCRYPTED message to B, there is no way for A to learn the public key of B from simply knowing B's address.

So users need to somewhere publish a mapping of their addresses to their public keys. This is basically (part of) the idea behind SSI (DIDs and DID Documents).

Now, that being said, there are some options to achieve that:

  1. Somewhat require each user to create a digital signature that is publicly available and discoverable from only the user's address. As we're talking EC-crypto, one can derive the public key from that signature. One way to do this without having a specific registry somewhere would be to have each user to in some way make a transaction to Ethereum. From here, you can get transactions sent/signed from this address (see for a dumb random example https://etherscan.io/address/0x39ec1d8f3d431770a8c79b33ead5a2e9e3c02d25) and fetch signature and public key from there.

  2. Create a public registry with a mapping of address to public key. Can be either a smart contract having a mapping of address to string or a DID-Registry linking to (potentially off-chain) DID Documents. See uPort's ERC1056 for example.

To directly propose a MWE for option 2, one could do something like this:

contract registry {
  mapping (address => string) private keys;
  
  function getKey(address a) public view returns (string) {
    return keys[a];
  }

  function setKey(string key) public returns (address) {
    address a = address(keccak256(key));
    keys[a] = key;
    return a;
  }
}

EDIT: slapped together an actually working example:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

contract KeyRegistry
{
    mapping (address => bytes) private keys;
    
    function getKey(address a) public view returns (bytes)
    {
        if(keys[a] == 0x0)
            revert("Error: Unknown address");
            
        return keys[a];
    }
    
    function setKey(bytes key) public returns (address)
    {
        if(key.length == 0)
            revert("Error: Key cant be empty");
        
        address a = address(uint160(bytes20(key)));
        
        if(a != msg.sender)
            revert("Error: Only the owner of a key can register this key");
        
        if(keys[a] != 0)
            revert("Error: Key is already registered");
        
        keys[a] = key;
        
        return a;
    }
}
1

It seems like you are trying to make some sort of encrypted messaging dapp. Pretty cool.

Something you could do is create a user registry, and have the users "register" for the DAPP by providing their own public key as well as key-type. Then off chain in the JS layer retrieve that user metadata and do the expensive encryption computations then store the product of the encryption in a message mapping.

So you could do something like this

struct User{
  string pubKey;
  string keyType;
}

struct Message{
  string body;
}

mapping(address=>User) public users;
mapping(bytes32=>Message) public msgs;

Use some sort of unique identifier for the message mapping

0

For the Solidity contract language:
Whenever you send a transaction to a contract

  1. to call a function,
  2. to transfer ETH to the contract (here you use an anonymous fallback function function() {...}

you have access to a msg object that contains the senders public key: msg.sender.
This is true also in your constructor, where the sender is the owner/ creator of the contract.

2
  • kofier is it the public keyor the address of the User.
    – jgm
    Jun 14, 2016 at 10:09
  • I see you want the public key, since the address is a hash of it you are right it's not the same. No way to know the public key directly, AFAIK. Jun 14, 2016 at 11:12

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.