Updated at: 2024-11-29
You should read Are WhatsApp, Signal, Telegram really end-to-end encrypted? first.
This Documentation describes all data for upstream API requests.
If you don't want to use email, you can obtain an anonymous account with BTC.
auth token
Device UUID, because Zhi allow one account used on multiple devices, and Zhi will forward encrypted messages to all your devices.
Streamed encrypted message, the message is json format, like this:
{
"ChatUUID": "the Chat UUID",
"UserUUID": "your UUID in this Chat",
"MessageUUID": "the message UUID",
"Payload": "the encrypted message"
}
You can verify whether the Payload encrypted by your Chat Key and ChatUUID and your UserUUID, try to decrypt with this javascript function:
async function decrypt_payload(Key, ChatUUID, UserUUID, Payload) {
var b = Uint8Array.from(Payload.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
var k = await crypto.subtle.deriveKey(
{ name: 'HKDF', hash: 'SHA-256', salt: b.subarray(0, 12), info: new TextEncoder().encode(ChatUUID + UserUUID) },
await crypto.subtle.importKey('raw', new TextEncoder().encode(Key), 'HKDF', false, ['deriveKey']),
{ name: 'AES-GCM', length: 256 },
false,
['decrypt']
);
var ab = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: b.subarray(0, 12) },
k,
b.subarray(12)
);
return new TextDecoder().decode(ab)
}
As we know, as an instant messaging app, we need to implement push notifications.
Because Zhi server cannot decrypt messages, when there is a new message, we can only push a bland "New Message" string to the push service, which then sends it to the user's device. Therefore, neither Apple nor Google can decrypt your messages.
We use standard WebRTC technology to implement Meeting. WebRTC requires STUN stun.shiliew.com:3478
and TURN turn.shiliew.com:3478
server.
Don't worry, all your signaling, just like the messages above, is also encrypted with your Chat Key before transmission.
auth token
Chat UUID
UUID for current WebRTC session
Streamed encrypted signaling, the signaling is json format, like this:
{
"Kind": 0, // A numerical value representing the steps of WebRTC signaling.
"Member": "the Member UUID you want to send the signaling to",
"Payload": "the encrypted signaling"
}
You can verify whether the Payload encrypted by your Chat Key and ChatUUID and your UserUUID, try to decrypt with this javascript function:
async function decrypt_payload(Key, ChatUUID, UserUUID, Payload) {
var b = Uint8Array.from(Payload.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
var k = await crypto.subtle.deriveKey(
{ name: 'HKDF', hash: 'SHA-256', salt: b.subarray(0, 12), info: new TextEncoder().encode(ChatUUID + UserUUID) },
await crypto.subtle.importKey('raw', new TextEncoder().encode(Key), 'HKDF', false, ['deriveKey']),
{ name: 'AES-GCM', length: 256 },
false,
['decrypt']
);
var ab = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: b.subarray(0, 12) },
k,
b.subarray(12)
);
return new TextDecoder().decode(ab)
}
We use a file server to store encrypted avatars, images, videos, and files in order to reduce the size of the message body.
Don't worry, all your avatars, images, videos, and files, just like the messages above, is also encrypted with your Chat Key before transmission.
You can verify whether the Payload encrypted by your Chat Key and ChatUUID and your UserUUID, try to decrypt with this javascript function:
// Payload is the http body as Uint8Array, return the decrypted Uint8Array
async function decrypt_payload(Key, ChatUUID, UserUUID, Payload) {
var decrypt = async function(Key, ChatUUID, UserUUID, b) {
var k = await crypto.subtle.deriveKey(
{ name: 'HKDF', hash: 'SHA-256', salt: b.subarray(0, 12), info: new TextEncoder().encode(ChatUUID + UserUUID) },
await crypto.subtle.importKey('raw', new TextEncoder().encode(Key), 'HKDF', false, ['deriveKey']),
{ name: 'AES-GCM', length: 256 },
false,
['decrypt']
);
var ab = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: b.subarray(0, 12) },
k,
b.subarray(12)
);
return new Uint8Array(ab)
}
var b = Payload
var r = new Uint8Array()
for (; true;) {
if (b.length == 0) {
break
}
var n = 524316
if (b.length < 524316) {
n = b.length
}
var b1 = await decrypt(Key, ChatUUID, UserUUID, b.slice(0, n))
var r1 = new Uint8Array(r.length + b1.length);
r1.set(r)
r1.set(b1, r.length)
r = r1
b = b.subarray(n)
}
return r
}
If you discover a security vulnerability, please send an e-mail to cloud@txthinking.com. Thank you very much.