Zhi Upstream API Documentation

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.

GET SigninSendCode

If you don't want to use email, you can obtain an anonymous account with BTC.

GET Signin

GET Auth

GET CreateChat

GET JoinChat

GET GetChat

GET LeaveChat

POST LeaveOtherChats

GET Time

POST LastPageMessages

GET PrevPageMessages

GET FirstLastPageMessages

GET ChatMembers

GET IAP

GET ws

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)
}

GET UpdatePushToken

As we know, as an instant messaging app, we need to implement push notifications.

  1. On the Apple platform: We must choose Apple APN (Apple Push Notification service).
  2. On the Android platform: We almost have no choice but to use Google FCM (Firebase Cloud Messaging).
  3. On the Web platform: Currently, we are only developing and doing compatibility testing on Chrome. To implement push notifications, we must use Google GCM (Google Cloud Messaging). Accessing: chrome://gcm-internals/

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.

GET room/ws

We use standard WebRTC technology to implement Meeting. WebRTC requires STUN stun.shiliew.com:3478 and TURN turn.shiliew.com:3478 server.

  1. We know that WebRTC is end-to-end encrypted, STUN and TURN will not break WebRTC's end-to-end encryption.
  2. And we enforce the use of TURN, so when you have a Meeting with other members, the other members are not able to know your IP.
  3. Because the key negotiation for WebRTC is achieved through signaling interaction. Therefore, it's essential to ensure the security of the signaling being transmitted.

Don't worry, all your signaling, 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:

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)
}

PUT upload.zhi.shiliew.com

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
}

GET zhi.shiliew.com

If you discover a security vulnerability, please send an e-mail to cloud@txthinking.com. Thank you very much.