[solved] How to verify that <cid> is signed by <peerid>?

As I have discovered, you may just to generate the CID for your PeerID string and then link the obtained CID to the graph with the CID of your data using ipfs object patch add-link <data CID> PID <CID generated for PeerID string>. This approach does not need to use additional software, it is truly IPFS-native.

I don’t get how that makes a third party (that does know the peer id) able verify that the one who added the link is the same one that added the original <cid>?

Also, what does PID have to do with this? PID is process identifier. I’m either missing something or it’s unrelated.

Could you give a complete example of your discovery, that might be more clear.

Could someone elaborate on the release note please? :

Previously go-ipfs generated 2048 bit RSA keys for new nodes, but it will now use ed25519 keys by default. This will not affect any existing keys, but newly created keys will be ed25519 by default. The main benefit of using ed25519 keys over RSA is that ed25519 keys have an inline public key. This means that someone only needs your PeerId to verify things you’ve signed , such as your Peer Records or in the future Signed Provider Records, which means we don’t have to worry about storing bulky RSA public keys.

As (i think) i really like to use what’s there but i’m beginning to have my doubts if that paragraph holds any truth. Please just forget what i’m trying to do (you can read it in the comments above). I now just simply hope to hear how to do exactly what’s written in the release notes.


Example of how things worked before and now:


IPFS tries to find /ipns/k2-ipns-key and receives a record from the DHT that looks essentially like

     value : "/ipfs/bafy-my-file",
     signature : []byte{someBytes},
     publicKey: []byte{publicKeyBytes},

It then verifies that SHA256(publicKeyBytes) == k2-ipns-key. If that’s true then it checks if the signature is verified by the public key extracted from publicKeyBytes. If so then it’s a good record and we can return /ipfs/bafy-myfile, if not then it’s a bad record so we cannot return the value.


IPFS tries to find /ipns/k51-ipns-key and receives a record from the DHT that looks essentially like

     value : "/ipfs/bafy-my-file",
     signature : []byte{someBytes},

Because the public key is so small it could be encoded in the name (i.e. less than 42 bytes per https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids) we can extract the public key from the peerID pubKey = peer.ExtractPublicKey(id). We then check if the signature is verified by pubKey. If so then it’s a good record and we can return /ipfs/bafy-myfile, if not then it’s a bad record so we cannot return the value.

TLDR: Less data

Not needing to embed an extra RSA public key in every single thing we sign is a really nice bandwidth + storage saver when aggregating over all the provider records, IPNS records, pubsub messages, libp2p signature handshakes, etc. that a node emits and processes. For example, DHT provider records are not currently signed which has some unfortunate ramifications (e.g. I cannot ask a third party to advertise in the DHT that I have some data and the inability to store metadata in provider records). Adding an RSA key into every single provider record we emit would be tremendously costly, but we can now avoid that when we switch to signed provider records.

That is really smart, @adin!
Hats of to the one that came up with that idea!

Thanks a lot for the clear explanation!

Note, the solution for this question too is now in [solved] How to use the IPFS self public and private key in node.js?. I made this topic when i didn’t know the route to get it working yet. The other topic was more to the point where i was in the right direction but missing some details. Your pointer to https://github.com/libp2p/go-libp2p-core/blob/a39b84ea2e340466d57fdb342c7d62f12957d972/peer/peer.go#L92 proved to be one of the missing pieces in this endeavor :slight_smile:

Experimenting with new version of IPFS daemon, I discovered that now the signing problem is successfully solved, but the deeper one is provenance that the given PeerID is owned by the person which operates the daemon. Embedding of the 3rd party digital signature into the DAG, which I planned to use at first, only partially solves this problem. This is why I did not publish my last discovery and proceeded with work on it

I don’t see a problem there.
The peer id is the users’ public key.
I trust “that peer id”.
The user creates whatever data and signs it with it’s private key. I can verify that sign with the public key (the peer id) as i’ve demonstrated in this post.

I can apply this concept now to IPNS keys (in the 0.7.0 scheme that is) and to any ipfs data where a public key and a sign is known to verify that i want that data. Now how to do that last part is up for debate as i’d need to make a format, say like:

  "pubkey" : "peer id or some other public key mechanism",
  "sign": "%$@#TGE#%YHH%#YHYT$R^",
  "contentHash": "Qm....."

I can then verify that the Qm..... is in fact signed by that public key (as you need the private key to sign it and only you should have that private key). Still, this only means that i trust whatever that given public key + sign tells me. In this scheme anyone can sign any Qm… hash. It’s just that i trust those that are signed with publickeys that i trust.

With that logic i’m able to confirm that <cid> is valid for me, provided that the user provided me that above json blob to be able to verify it. If the user were to send a IPNS in 0.7.0 format then the public key can be omitted as it’s part of the content hash.

Now it would be super awesome if the sign would be part of the content hash itself! Something like:
<cid><sign> = <newcid>
As it would then contain:

  1. public key
  2. sign of the content hash with the private key of that public key
  3. the content hash

It would solve everything i mentioned here.
It would allow verification with only <newcid>.

The downside is that this will explode the <newcid> in length to unusable long.
But i think this mechanism with the json blob above as “metadata” would be very workable already! Anything more that simplifies it further is just a big bonus :slight_smile:

With that, i’m making this thread as solved.