Попытка обновления шифрования звонков и работа над UI
This commit is contained in:
390
app/src/main/cpp/rosetta_e2ee.cpp
Normal file
390
app/src/main/cpp/rosetta_e2ee.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
/**
|
||||
* JNI bridge for Rosetta E2EE.
|
||||
*
|
||||
* Provides:
|
||||
* 1. HSalsa20 — for nacl.box.before() compatible key derivation
|
||||
* 2. XChaCha20 FrameEncryptor / FrameDecryptor — inherits DIRECTLY from
|
||||
* webrtc::FrameEncryptorInterface / FrameDecryptorInterface so the
|
||||
* vtable is generated by the compiler, not guessed by us.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <android/log.h>
|
||||
|
||||
/* WebRTC M125 interface stubs (exact copies of the real headers) */
|
||||
#include "webrtc/api/crypto/frame_encryptor_interface.h"
|
||||
#include "webrtc/api/crypto/frame_decryptor_interface.h"
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#define TAG "RosettaE2EE"
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
|
||||
|
||||
/* ── Diagnostics file — written from native, visible in crash reports ── */
|
||||
|
||||
static char g_diag_path[512] = {0};
|
||||
static int g_diag_fd = -1;
|
||||
|
||||
static void diag_write(const char *fmt, ...) {
|
||||
if (g_diag_fd < 0) return;
|
||||
char buf[512];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int n = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
if (n > 0) write(g_diag_fd, buf, n);
|
||||
}
|
||||
|
||||
/* ── Native crash handler — writes to file before dying ──────── */
|
||||
|
||||
static char g_crash_path[512] = {0};
|
||||
static struct sigaction g_old_sigsegv = {};
|
||||
static struct sigaction g_old_sigabrt = {};
|
||||
|
||||
static void native_crash_handler(int sig, siginfo_t *info, void *ctx) {
|
||||
if (g_crash_path[0] != 0) {
|
||||
int fd = open(g_crash_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd >= 0) {
|
||||
const char *msg;
|
||||
if (sig == SIGSEGV)
|
||||
msg = "NATIVE CRASH: SIGSEGV (segmentation fault) in rosetta_e2ee\n"
|
||||
"Fault address: see logcat for details.\n";
|
||||
else if (sig == SIGABRT)
|
||||
msg = "NATIVE CRASH: SIGABRT (abort) in rosetta_e2ee\n";
|
||||
else
|
||||
msg = "NATIVE CRASH: unknown signal in rosetta_e2ee\n";
|
||||
write(fd, msg, strlen(msg));
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
LOGE("NATIVE CRASH sig=%d addr=%p", sig, info ? info->si_addr : nullptr);
|
||||
|
||||
struct sigaction *old = (sig == SIGSEGV) ? &g_old_sigsegv : &g_old_sigabrt;
|
||||
sigaction(sig, old, nullptr);
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* XChaCha20 Encryptor — inherits from the REAL interface
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
|
||||
class XChaCha20Encryptor final : public webrtc::FrameEncryptorInterface {
|
||||
public:
|
||||
explicit XChaCha20Encryptor(const uint8_t key[32]) {
|
||||
memcpy(key_, key, 32);
|
||||
}
|
||||
|
||||
/* ── RefCountInterface ─────────────────────────────────────── */
|
||||
void AddRef() const override {
|
||||
ref_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
rtc::RefCountReleaseStatus Release() const override {
|
||||
if (ref_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
delete this;
|
||||
return rtc::RefCountReleaseStatus::kDroppedLastRef;
|
||||
}
|
||||
return rtc::RefCountReleaseStatus::kOtherRefsRemained;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame format: [4-byte counter BE] + [xchacha20_xor(frame)]
|
||||
*
|
||||
* Nonce (24 bytes): [0,0,0,0, counter_BE_4bytes, 0,...,0]
|
||||
* This matches Desktop's layout where nonce[4..7] = timestamp.
|
||||
* The counter is embedded so the receiver can reconstruct the nonce
|
||||
* even if frames are dropped/reordered.
|
||||
*/
|
||||
int Encrypt(cricket::MediaType /*media_type*/,
|
||||
uint32_t /*ssrc*/,
|
||||
rtc::ArrayView<const uint8_t> /*additional_data*/,
|
||||
rtc::ArrayView<const uint8_t> frame,
|
||||
rtc::ArrayView<uint8_t> encrypted_frame,
|
||||
size_t* bytes_written) override {
|
||||
const size_t HEADER = 4; // counter prefix
|
||||
if (frame.size() == 0 || encrypted_frame.size() < frame.size() + HEADER) {
|
||||
*bytes_written = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t ctr = counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// Write 4-byte counter as big-endian prefix
|
||||
encrypted_frame.data()[0] = (uint8_t)(ctr >> 24);
|
||||
encrypted_frame.data()[1] = (uint8_t)(ctr >> 16);
|
||||
encrypted_frame.data()[2] = (uint8_t)(ctr >> 8);
|
||||
encrypted_frame.data()[3] = (uint8_t)(ctr);
|
||||
|
||||
// Build nonce from counter (same positions as Desktop's timestamp)
|
||||
uint8_t nonce[24] = {0};
|
||||
nonce[4] = encrypted_frame.data()[0];
|
||||
nonce[5] = encrypted_frame.data()[1];
|
||||
nonce[6] = encrypted_frame.data()[2];
|
||||
nonce[7] = encrypted_frame.data()[3];
|
||||
|
||||
rosetta_xchacha20_xor(encrypted_frame.data() + HEADER,
|
||||
frame.data(), frame.size(), nonce, key_);
|
||||
*bytes_written = frame.size() + HEADER;
|
||||
|
||||
// Diag: log first 3 frames
|
||||
int n = diag_count_.fetch_add(1, std::memory_order_relaxed);
|
||||
if (n < 3) {
|
||||
LOGI("ENC frame#%d: sz=%zu ctr=%u out=%zu",
|
||||
n, frame.size(), ctr, frame.size() + HEADER);
|
||||
diag_write("ENC frame#%d: sz=%zu ctr=%u nonce[4..7]=%02x%02x%02x%02x\n",
|
||||
n, frame.size(), ctr, nonce[4], nonce[5], nonce[6], nonce[7]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t GetMaxCiphertextByteSize(cricket::MediaType, size_t frame_size) override {
|
||||
return frame_size + 4; // +4 for counter prefix
|
||||
}
|
||||
|
||||
protected:
|
||||
~XChaCha20Encryptor() override { memset(key_, 0, 32); }
|
||||
|
||||
private:
|
||||
mutable std::atomic<int> ref_{0};
|
||||
mutable std::atomic<uint32_t> counter_{0};
|
||||
mutable std::atomic<int> diag_count_{0};
|
||||
uint8_t key_[32];
|
||||
};
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* XChaCha20 Decryptor — inherits from the REAL interface
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
|
||||
class XChaCha20Decryptor final : public webrtc::FrameDecryptorInterface {
|
||||
public:
|
||||
explicit XChaCha20Decryptor(const uint8_t key[32]) {
|
||||
memcpy(key_, key, 32);
|
||||
}
|
||||
|
||||
/* ── RefCountInterface ─────────────────────────────────────── */
|
||||
void AddRef() const override {
|
||||
ref_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
rtc::RefCountReleaseStatus Release() const override {
|
||||
if (ref_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
delete this;
|
||||
return rtc::RefCountReleaseStatus::kDroppedLastRef;
|
||||
}
|
||||
return rtc::RefCountReleaseStatus::kOtherRefsRemained;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt frame: read 4-byte counter prefix → derive nonce → decrypt.
|
||||
* If frame has no prefix (< 5 bytes or from Desktop), fallback to
|
||||
* nonce derived from additional_data (RTP header) or zeros.
|
||||
*/
|
||||
Result Decrypt(cricket::MediaType /*media_type*/,
|
||||
const std::vector<uint32_t>& /*csrcs*/,
|
||||
rtc::ArrayView<const uint8_t> additional_data,
|
||||
rtc::ArrayView<const uint8_t> encrypted_frame,
|
||||
rtc::ArrayView<uint8_t> frame) override {
|
||||
|
||||
const size_t HEADER = 4;
|
||||
uint8_t nonce[24] = {0};
|
||||
const uint8_t *payload;
|
||||
size_t payload_sz;
|
||||
|
||||
if (encrypted_frame.size() > HEADER) {
|
||||
// Android format: [4-byte counter] + [encrypted data]
|
||||
nonce[4] = encrypted_frame.data()[0];
|
||||
nonce[5] = encrypted_frame.data()[1];
|
||||
nonce[6] = encrypted_frame.data()[2];
|
||||
nonce[7] = encrypted_frame.data()[3];
|
||||
payload = encrypted_frame.data() + HEADER;
|
||||
payload_sz = encrypted_frame.size() - HEADER;
|
||||
} else {
|
||||
// Fallback: no counter prefix
|
||||
payload = encrypted_frame.data();
|
||||
payload_sz = encrypted_frame.size();
|
||||
}
|
||||
|
||||
if (payload_sz == 0 || frame.size() < payload_sz) {
|
||||
return {Result::Status::kFailedToDecrypt, 0};
|
||||
}
|
||||
|
||||
rosetta_xchacha20_xor(frame.data(), payload, payload_sz, nonce, key_);
|
||||
|
||||
// Diag: log first 3 frames
|
||||
int n = diag_count_.fetch_add(1, std::memory_order_relaxed);
|
||||
if (n < 3) {
|
||||
LOGI("DEC frame#%d: enc_sz=%zu payload=%zu nonce=%02x%02x%02x%02x",
|
||||
n, encrypted_frame.size(), payload_sz,
|
||||
nonce[4], nonce[5], nonce[6], nonce[7]);
|
||||
diag_write("DEC frame#%d: enc_sz=%zu payload=%zu nonce[4..7]=%02x%02x%02x%02x\n",
|
||||
n, encrypted_frame.size(), payload_sz,
|
||||
nonce[4], nonce[5], nonce[6], nonce[7]);
|
||||
}
|
||||
|
||||
return {Result::Status::kOk, payload_sz};
|
||||
}
|
||||
|
||||
size_t GetMaxPlaintextByteSize(cricket::MediaType, size_t encrypted_frame_size) override {
|
||||
return encrypted_frame_size; // >= actual (payload = enc - 4)
|
||||
}
|
||||
|
||||
protected:
|
||||
~XChaCha20Decryptor() override { memset(key_, 0, 32); }
|
||||
|
||||
private:
|
||||
mutable std::atomic<int> ref_{0};
|
||||
mutable std::atomic<int> diag_count_{0};
|
||||
uint8_t key_[32];
|
||||
};
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* JNI exports
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
|
||||
extern "C" {
|
||||
|
||||
/* ── HSalsa20 for nacl.box.before() ──────────────────────────── */
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_com_rosetta_messenger_network_XChaCha20E2EE_nativeHSalsa20(
|
||||
JNIEnv *env, jclass, jbyteArray jRawDh)
|
||||
{
|
||||
jsize len = env->GetArrayLength(jRawDh);
|
||||
if (len < 32) return nullptr;
|
||||
|
||||
auto *raw = (uint8_t *)env->GetByteArrayElements(jRawDh, nullptr);
|
||||
uint8_t out[32];
|
||||
uint8_t zeros[16] = {0};
|
||||
|
||||
rosetta_hsalsa20(out, zeros, raw);
|
||||
|
||||
env->ReleaseByteArrayElements(jRawDh, (jbyte *)raw, JNI_ABORT);
|
||||
|
||||
jbyteArray result = env->NewByteArray(32);
|
||||
env->SetByteArrayRegion(result, 0, 32, (jbyte *)out);
|
||||
memset(out, 0, 32);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ── Create / destroy encryptor ──────────────────────────────── */
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_rosetta_messenger_network_XChaCha20E2EE_nativeCreateEncryptor(
|
||||
JNIEnv *env, jclass, jbyteArray jKey)
|
||||
{
|
||||
jsize len = env->GetArrayLength(jKey);
|
||||
if (len < 32) return 0;
|
||||
|
||||
auto *key = (uint8_t *)env->GetByteArrayElements(jKey, nullptr);
|
||||
auto *enc = new XChaCha20Encryptor(key);
|
||||
env->ReleaseByteArrayElements(jKey, (jbyte *)key, JNI_ABORT);
|
||||
|
||||
// AddRef so the pointer we hand out has ref=1.
|
||||
// WebRTC's scoped_refptr will AddRef again when it takes ownership.
|
||||
enc->AddRef();
|
||||
|
||||
LOGI("Created XChaCha20 encryptor %p", enc);
|
||||
return reinterpret_cast<jlong>(enc);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_rosetta_messenger_network_XChaCha20E2EE_nativeReleaseEncryptor(
|
||||
JNIEnv *, jclass, jlong ptr)
|
||||
{
|
||||
if (ptr == 0) return;
|
||||
auto *enc = reinterpret_cast<XChaCha20Encryptor *>(ptr);
|
||||
enc->Release();
|
||||
}
|
||||
|
||||
/* ── Create / destroy decryptor ──────────────────────────────── */
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_rosetta_messenger_network_XChaCha20E2EE_nativeCreateDecryptor(
|
||||
JNIEnv *env, jclass, jbyteArray jKey)
|
||||
{
|
||||
jsize len = env->GetArrayLength(jKey);
|
||||
if (len < 32) return 0;
|
||||
|
||||
auto *key = (uint8_t *)env->GetByteArrayElements(jKey, nullptr);
|
||||
auto *dec = new XChaCha20Decryptor(key);
|
||||
env->ReleaseByteArrayElements(jKey, (jbyte *)key, JNI_ABORT);
|
||||
|
||||
dec->AddRef();
|
||||
|
||||
LOGI("Created XChaCha20 decryptor %p", dec);
|
||||
return reinterpret_cast<jlong>(dec);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_rosetta_messenger_network_XChaCha20E2EE_nativeReleaseDecryptor(
|
||||
JNIEnv *, jclass, jlong ptr)
|
||||
{
|
||||
if (ptr == 0) return;
|
||||
auto *dec = reinterpret_cast<XChaCha20Decryptor *>(ptr);
|
||||
dec->Release();
|
||||
}
|
||||
|
||||
/* ── Install native crash handler ─────────────────────────────── */
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_rosetta_messenger_network_XChaCha20E2EE_nativeInstallCrashHandler(
|
||||
JNIEnv *env, jclass, jstring jPath)
|
||||
{
|
||||
const char *path = env->GetStringUTFChars(jPath, nullptr);
|
||||
strncpy(g_crash_path, path, sizeof(g_crash_path) - 1);
|
||||
env->ReleaseStringUTFChars(jPath, path);
|
||||
|
||||
struct sigaction sa = {};
|
||||
sa.sa_sigaction = native_crash_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
sigaction(SIGSEGV, &sa, &g_old_sigsegv);
|
||||
sigaction(SIGABRT, &sa, &g_old_sigabrt);
|
||||
|
||||
LOGI("Native crash handler installed, path=%s", g_crash_path);
|
||||
}
|
||||
|
||||
/* ── Open diagnostics file for E2EE frame logging ────────────── */
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_rosetta_messenger_network_XChaCha20E2EE_nativeOpenDiagFile(
|
||||
JNIEnv *env, jclass, jstring jPath)
|
||||
{
|
||||
if (g_diag_fd >= 0) { close(g_diag_fd); g_diag_fd = -1; }
|
||||
|
||||
const char *path = env->GetStringUTFChars(jPath, nullptr);
|
||||
strncpy(g_diag_path, path, sizeof(g_diag_path) - 1);
|
||||
g_diag_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
env->ReleaseStringUTFChars(jPath, path);
|
||||
|
||||
if (g_diag_fd >= 0) {
|
||||
diag_write("=== E2EE DIAGNOSTICS ===\n");
|
||||
LOGI("Diag file opened: %s", g_diag_path);
|
||||
} else {
|
||||
LOGE("Failed to open diag file: %s", g_diag_path);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_rosetta_messenger_network_XChaCha20E2EE_nativeCloseDiagFile(
|
||||
JNIEnv *, jclass)
|
||||
{
|
||||
if (g_diag_fd >= 0) {
|
||||
diag_write("=== END ===\n");
|
||||
close(g_diag_fd);
|
||||
g_diag_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
||||
Reference in New Issue
Block a user