Peer Verification in SynapNet and InstaChat
I was wondering about the decentralised nature of the internet. How can I build a decentralised chat app that delivers messages to the right person without a central server?
I found the peer.js library, which is a JavaScript library that provides an easy way to create peer-to-peer connections between browsers. It uses WebRTC to establish direct connections between peers, enabling communication without a central server. When you connect to the broker server, you can provide an ID to identify yourself. Then you can connect to other peers by their ID. If not provided, the server will assign an ID to each peer. The broker server translates the ID to the peer's address so that you can connect to them directly.
But this raises a question: Who am I connecting to? How can I be sure that the peer I'm connecting to is who I want to talk to? Inspired by how wallet addresses work on the blockchain, I use the peer’s public key as their identity.
1 verifyPeer: async (connData) => {
2 const pubKeyId = getIDFromJwk(connData.pubKey)
3 const pubKeyVerified = pubKeyId === connData.id
4 if (!pubKeyVerified) {
5 return
6 }
7 const publicKey = await importPubKey(connData.pubKey).catch((e) => console.error(e))
8
9 const verify = await window.crypto.subtle.verify(
10 {
11 name: 'ECDSA',
12 hash: { name: 'SHA-384' },
13 },
14 publicKey,
15 base64ToBuff(connData.signature),
16 new TextEncoder().encode(JSON.stringify(connData.myInfo))
17 )
18 return verify
19 }
For example, the verifyPeer
function takes connection data from the peer, including the public key, ID, user information, and message signature. First, it checks if the public key matches the ID. Then, it creates a public key object from the public key string using the browser'sCrypto.subtle.importKey()
method. Finally, it verifies the signature using the public key and user information.
pubKey
is a base64-encoded string representing the public key. The ID removes invalid characters from the pubKey that are not allowed by the Peer.js library.In SynapNet, it will be a little different because the devices are not connected to the Internet. It uses Multipeer Connectivity, a set of technologies that connect devices locally via Bluetooth or Wi-Fi. The devices will be discovered by Bonjour, a protocol designed to discover services on a local network.
A peer will send an invitation containing the public key, MCPeerID
, and a message signature. The other peer will verify the invitation using the public key and signature. Then, they will accept the invitation to establish a connection with this peer.
1 var discoverInfo:[String:String] {
2 [
3 "id":self.id,
4 "displayName":myUserInfo.displayName,
5 "currentVersion": "\(MultipeerSession.protocalVer)",
6 "peerID": "\(myPeerID.hash)",
7 "date": Date.now.ISO8601Format()
8 ]
9}
10
11struct SignedJson:Codable {
12 let jsonData:Data
13 var signature:Data? = nil
14 func verify()->Bool{
15 guard let json = try? JSONDecoder().decode([String:String].self, from: jsonData) else {return false}
16 guard let publicKey = try? P256.Signing.PublicKey(rawRepresentation: Data(base64Encoded: json["id"] ?? "") ?? Data()) else {return false}
17 guard let signature = signature, let signature = try? P256.Signing.ECDSASignature(rawRepresentation: signature) else { return false }
18
19 return publicKey.isValidSignature(signature, for: jsonData)
20 }
21
22 mutating func sign(privateKey:P256.Signing.PrivateKey){
23 guard let signature = try? privateKey.signature(for: jsonData) else {return}
24 self.signature = signature.rawRepresentation
25 }
26}
I encapsulated the JSON data and its signature in a SignedJson
struct, which made the signing and verification process easier to manage. I used the encoded data instead of the dictionary because the order of the keys is not guaranteed in a dictionary, which could result in signature verification failures.
In summary, there are two identities involved:
1. The user identity, which is the public key;
2. The connection identity, which is the peer ID. This comes from the connection technology used (e.g., WebRTC or Multipeer Connectivity).
Each peer uses their public key to sign their connection and user information before sending it to the other peer. The other peer verifies the signature using the public key and establishes a connection if the verification is successful.
Last Update: Aug.2025