Skip to content

Commit 2c3225d

Browse files
authored
Create sending.md
1 parent aedd63c commit 2c3225d

1 file changed

Lines changed: 187 additions & 0 deletions

File tree

docs/sending.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
Sending messages via the relay server
2+
=========================
3+
This document explains how to send messages via the webrelay to a recipient on the OpenBazaar network.
4+
5+
Before begining you will need to compile https://github.com/OpenBazaar/openbazaar-go/blob/master/pb/protos/message.proto to javascript.
6+
7+
The following are the steps needed to send a message:
8+
9+
1) Create the `pb.Envelope` protobuf object.
10+
```proto
11+
message Envelope {
12+
Message message = 1;
13+
bytes pubkey = 2;
14+
bytes signature = 3;
15+
}
16+
```
17+
`pubkey` is the serialized libp2p pubkey key that corresponds to your node's PeerID.
18+
19+
`signature` is a signature produced by signing the serialized `message` with the node's private key which corresponds to the PeerID.
20+
21+
`message` is a `pb.Message` object in this format:
22+
```proto
23+
message Message {
24+
MessageType messageType = 1;
25+
google.protobuf.Any payload = 2;
26+
}
27+
```
28+
`messageType` may be one of the following:
29+
```proto
30+
enum MessageType {
31+
PING = 0;
32+
CHAT = 1;
33+
FOLLOW = 2;
34+
UNFOLLOW = 3;
35+
ORDER = 4;
36+
ORDER_REJECT = 5;
37+
ORDER_CANCEL = 6;
38+
ORDER_CONFIRMATION = 7;
39+
ORDER_FULFILLMENT = 8;
40+
ORDER_COMPLETION = 9;
41+
DISPUTE_OPEN = 10;
42+
DISPUTE_UPDATE = 11;
43+
DISPUTE_CLOSE = 12;
44+
REFUND = 13;
45+
OFFLINE_ACK = 14;
46+
OFFLINE_RELAY = 15;
47+
MODERATOR_ADD = 16;
48+
MODERATOR_REMOVE = 17;
49+
STORE = 18;
50+
BLOCK = 19;
51+
VENDOR_FINALIZED_PAYMENT = 20;
52+
ERROR = 500;
53+
}
54+
```
55+
56+
Whereas the `payload` is a protobuf `Any` object which has the following format:
57+
```proto
58+
message Any {
59+
string type_url = 1;
60+
bytes value = 2;
61+
}
62+
```
63+
Where `type_url` must be set to "type.googleapis.com/{protobuf object name}" and `value` is a serialized protobuf object.
64+
65+
For example if sending a `Chat` message the `type_url` would be "type.googleapis.com/Chat" and the `value` would be the serialization of
66+
```proto
67+
message Chat {
68+
string messageId = 1;
69+
string subject = 2;
70+
string message = 3;
71+
google.protobuf.Timestamp timestamp = 4;
72+
Flag flag = 5;
73+
74+
enum Flag {
75+
MESSAGE = 0;
76+
TYPING = 1;
77+
READ = 2;
78+
}
79+
}
80+
```
81+
82+
The protobuf library will usually have a function to create an `Any` object.
83+
84+
The following is an example of creating a a chat message in Go:
85+
86+
```go
87+
// First we create the `pb.Chat` object
88+
chatMessage := "hey you bastard"
89+
subject := ""
90+
91+
// The chat message contains a timestamp. This must be in the protobuf `Timestamp` format.
92+
timestamp, _ := ptypes.TimestampProto(time.Now())
93+
94+
// The messageID is derived from the message data. In this case it's the hash of the message,
95+
// subject, and timestamp which is then multihash encoded.
96+
idBytes := sha256.Sum256([]byte(chatMessage + subject + ptypes.TimestampString(timestamp)))
97+
encoded, _ := mh.Encode(idBytes[:], mh.SHA2_256)
98+
msgId, _ := mh.Cast(encoded)
99+
100+
chatPb := &pb.Chat{
101+
MessageId: msgId.B58String(),
102+
Subject: subject,
103+
Message: chatMessage,
104+
Timestamp: timestamp,
105+
Flag: pb.Chat_MESSAGE,
106+
}
107+
108+
109+
// Now we wrap it in a `pb.Message` object
110+
payload, _ := ptypes.MarshalAny(chatMessage)
111+
m := pb.Message{
112+
MessageType: pb.Message_CHAT,
113+
Payload: payload,
114+
}
115+
116+
117+
// Now we wrap it in the envelop object
118+
pubKeyBytes, _ := n.IpfsNode.PrivateKey.GetPublic().Bytes()
119+
120+
// Use the protobuf serialize function to convert the object to a serialized byte array
121+
serializedMessage, _ := proto.Marshal(m)
122+
123+
// Sign the serializedMessage with the private key
124+
signature, _ := n.IpfsNode.PrivateKey.Sign(serializedMessage)
125+
126+
// Create the envelope
127+
env := pb.Envelope{
128+
Message: m,
129+
Pubkey: pubKeyBytes,
130+
Signature: signature,
131+
}
132+
```
133+
134+
2. Encrypt the serialized envelope using the recipient's public key. For this you will need to use an `nacl` library. NOTE for
135+
this you will need the recipient's public key. We will have to create a server endpoint to get the pubkey. Technically I think the
136+
gateway already has one but we may need to improve it for this purpose. The public key is also found inside a listing so if you're
137+
looking at a listing you should already have it.
138+
139+
```go
140+
// Serialize the envelope
141+
serializedEnvelope, _ := proto.Marshal(&env)
142+
143+
// Get the public key
144+
recipientPublicKey := getPublicKeyFromGatewayOrListing()
145+
146+
// Generate an ephemeral key pair
147+
ephemPub, ephemPriv, _ := box.GenerateKey(rand.Reader)
148+
149+
// Convert recipient's key into curve25519
150+
pk, _ := recipientPublicKey.ToCurve25519()
151+
152+
// Encrypt with nacl
153+
154+
// Nonce must be a random 24 bytes
155+
var nonce [24]byte
156+
n := make([]byte, 24)
157+
rand.Read(n)
158+
for i := 0; i < 24; i++ {
159+
nonce[i] = n[i]
160+
}
161+
162+
// Encrypt
163+
ciphertext := box.Seal(ciphertext, serializedEnvelope, &nonce, pk, ephemPriv)
164+
165+
// Prepend the ephemeral public key to the ciphertext
166+
ciphertext = append(ephemPub[:], ciphertext...)
167+
168+
// Prepend nonce to the ephemPubkey+ciphertext
169+
ciphertext = append(nonce[:], ciphertext...)
170+
171+
// Base64 encode
172+
encodedCipherText := base64.StdEncoding.EncodeToString(ciphertext)
173+
```
174+
175+
3. Create a `EncryptedMessage` JSON object to send to the webrelay server
176+
```go
177+
type EncryptedMessage struct {
178+
Message string `json:"encryptedMessage"`
179+
Recipient string `json:"recipient"`
180+
}
181+
```
182+
`Message` is the base64 `encodedCipherText` from the example above
183+
`Recipient` is the PeerID of the recipient
184+
185+
186+
187+

0 commit comments

Comments
 (0)