SetLog App Static Analysis Report: A Review of the Encryption Structure and Disclosure Consistency

Author: Cyber-Lenin Date: 2026-04-25 Subject: com.newchat.setlog (New Chat Inc.) Android app Document nature: Public research. Please cite the source when quoting.


0. Summary

SetLog is a closed video-sharing social app developed and distributed by New Chat Inc. The official privacy policy states that user content such as photos, videos, and messages is end-to-end encrypted (E2EE), so the company cannot see the contents. This report statically analyzes the APK binary of the Android beta build (v1.0) distributed on Google Play to examine whether this claim is actually implemented, the permission and infrastructure model, and consistency with app store disclosures.

Key findings:

  1. The encryption implementation is real. The app assembles and uses the hybrid public-key encryption envelope specified by the X25519-HKDF-SHA256+CHACHA20-POLY1305 identifier directly. The master key is protected in the Android Keystore.
  2. However, the key directory trust model is server-dependent. There is no UI, such as a safety number or QR code, that would let users verify the other party's public key. In other words, if the server delivers a forged or tampered public key, a MITM attack is theoretically possible. (See § 4-4 for details.)
  3. The app's control plane points to an AWS API Gateway and WebSocket in the United States, us-east-1 (Virginia). The privacy policy only states that the media S3 bucket is in Seoul, and does not disclose the region for message metadata or signaling traffic.
  4. The Apple App Store disclosure ("Data Not Collected") is not consistent with the privacy policy. The privacy policy states that the app collects names/usernames, IP addresses, device information, cookies, and the like. These items are not subject to E2EE, so there is no basis for excluding them from the data disclosure.
  5. No analytics or advertising SDKs were found in the app itself. There is no third-party data-transmission code other than Firebase Cloud Messaging.
  6. The permission model is conservative. Location, contacts, and external storage permissions are not declared at all.

0-1. What "MITM Risk" Means — A Plain-Language Explanation

Key finding #2 is the most important and also the hardest to understand. Let me unpack it intuitively.

Analogy: Think of the public key as a lock, and the private key as the key that opens that lock.

  • A lock (public key) can be taken by anyone, and copying it is harmless. That is because merely holding the lock does not let you open a locked box.
  • The key (private key) is held only by the owner and is confined to the security module in the owner's phone (Android Keystore), so it never leaves the device.

The server plays the role of a repository that gathers friends' locks. To be more precise about the actual media, it does not lock the video file with the public key directly; instead, it encrypts the video with a random content key and then sends a wrapped key, which wraps that content key with a per-recipient public key, along with it. When A wants to send a video to B:

Normal flow ✅
─────────────────────────────────────────────────────────
   [A's phone]           [Server = lock repository]          [B's phone]
        │                          │                              │
        │  "Give me B's lock" or
        │  "Give me info on the room B is in" ─▶│                              │
        │◀──── B's real public key/kid ─│                              │
        │                          │                              │
   Video is encrypted with content key     │                              │
   content key is wrapped as B's wrapped key                       │
        │                          │                              │
        │── encrypted video + B's wrapped key ─────▶│ ── relayed as-is ─▶│
        │                          │                          unwrap wrapped key with B's private key,
        │                          │                          watch video with content key
                              (server knows the public key and the ciphertext but cannot see them, lacking B's private key)

The problem is — what happens if the server lies?

MITM attack ❌
─────────────────────────────────────────────────────────
   [A's phone]           [Malicious server]                     [B's phone]
        │                          │                              │
        │  "Give me B's lock" or
        │  "Give me info on the room B is in" ─▶│                              │
        │                          │ (real B's lock kept separately)  │
        │◀── server's fake public key/kid ─│                              │
        │   ("This is B's")        │                              │
        │                          │                              │
   Video is encrypted with content key     │                              │
   content key is wrapped as server's wrapped key                  │
        │                          │                              │
        │── encrypted video + server's wrapped key ───▶│                  │
        │                       🔓 server obtains content key with its own private key
        │                          can decrypt video with content key │
        │                          │                              │
        │                       re-wraps same content key as B's wrapped key
        │                          │── encrypted video + B's wrapped key ─▶│
        │                          │                          B watches video normally

   👤 Both A and B think "the video went back and forth normally"
   👤 No trace anywhere on either device that "the server looked in the middle"

Why it cannot be prevented: A has no way to compare whether the lock it received is the real B's or a fake sent by the server. Signal and WhatsApp show a safety number and a QR code so that friends can meet in person and compare whether the locks have the same shape. SetLog has no such comparison UI. That is:

Threat SetLog's Defense
An external hacker intercepts the communication ✅ Blocked (both TLS and the E2EE envelope work)
The server is compromised and keys leak without the operator's knowledge ✅ Content is safe (the private key is not on the server)
The operator decides to swap the locks directly ❌ No means to prevent it
A law enforcement agency forces the operator to swap the locks ❌ No means to prevent it

→ The privacy policy statement that "the company cannot see the contents" rests on the assumption that the operator does not lie, on top of an E2EE that does work technically. The security industry calls this the absence of key transparency.


1. Method

Subject of Inspection

  • XAPK bundle (Google Play distribution)
  • package: com.newchat.setlog
  • versionName 1.0, versionCode 10
  • minSdk 32 (Android 12L), targetSdk 36 (Android 16)
  • XAPK SHA-256: 5aa92842a8b7f9e69c33d3f03e20fdff5c8e47bb31ad6e046dcff394ab0b26a4
  • base APK SHA-256: 14de20b4caf796f6a75d33bef64a07c57fb215afa25535f752fb823df98f9449
  • Bundled split APKs: arm64_v8a, en, fr, mdpi

Analysis Procedure

  1. Parse APK Signing Block v2 → extract signing certificate and verify with OpenSSL
  2. ZIP entry inventory → map DEX, native libraries, assets, META-INF
  3. classes.dex (4.5MB) → extract R8 decompilation output (Java pseudocode) and 23,592 ASCII strings
  4. Convert AndroidManifest.xml to text and audit permissions, intent filters, and exported components
  5. For encryption code paths and network endpoints, use keyword grep + direct reading of classes
  6. Directly check the official privacy policy (https://newchat.kr/privacy/) and app store disclosures (Apple App Store Korea page, Google Play Data Safety)

2. Verifying the Authenticity of the Distributed Build

2-1. Distribution Source

Evidence Value
META-INF/com.android.stamp.source metadata https://play.google.com/store
Pairip license component com.pairip.licensecheck.LicenseActivity registered
Play distribution stamp cert SHA-256 3257d599a49d2c961a471ca9843f59d341a405884583fc087df4237b733bbd6d (identical for base and all splits)

→ Identified as a genuine Google Play distribution. Since the stamps of all splits match, there are no traces of tampering in any individual split either.

2-2. Signer Certificate (APK Signing Scheme v2)

Item Value
Subject = Issuer C=US, ST=California, L=Mountain View, O=Google Inc., OU=Android, CN=Android
Signing algorithm RSA 4096-bit, SHA-256 with RSA
Validity period 2026-03-31 ~ 2056-03-31
Cert SHA-256 FD:5C:74:7C:34:C9:8F:81:02:6E:4A:22:CB:9E:8B:70:E9:5D:AB:12:2E:DB:1B:9F:6C:BE:70:FA:B8:9A:F7:62
Cert SHA-1 5E:52:53:B6:05:DE:7A:5C:9B:2B:03:A3:66:32:BA:33:16:28:8C:8A
Serial A95371D3B6835B54E0B576F587CD7AF93631E584

This is the result of re-signing with a key held by Google Play's App Signing program. It is a normal pattern in which the developer delegates its own upload key to Google, and it is typical for the Subject/Issuer to be "Google Inc. — Android".


3. Permission Model

The 11 permissions declared by AndroidManifest.xml and an assessment:

Permission Risk Level Notes
INTERNET / ACCESS_NETWORK_STATE / WAKE_LOCK Obvious network / keeping execution alive
POST_NOTIFICATIONS Low Android 13+ notifications
CAMERA / RECORD_AUDIO Requires user consent core feature (video recording)
USE_BIOMETRIC / USE_FINGERPRINT Low protect keys with biometric authentication
c2dm.RECEIVE Obvious FCM push
com.android.vending.CHECK_LICENSE Obvious Play licensing
…DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION Obvious protection of own components

Risky permissions not declared — and therefore absolutely impossible to collect in code:

  • Location: ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION
  • Contacts: READ_CONTACTS
  • External storage: READ_EXTERNAL_STORAGE, READ_MEDIA_IMAGES, READ_MEDIA_VIDEO
  • SMS, call logs, background location, calendar, background microphone access

This means that the "approximate location information" that the privacy policy states is automatically collected is not GPS but IP-based estimation.

Intent Filters

Deep links are restricted to two Korean domain hosts:

<data android:scheme="https" android:host="setlog.kr" android:pathPrefix="/invite"/>
<data android:scheme="https" android:host="newchat.kr" android:pathPrefix="/invite"/>

The exported components are only standard patterns such as MainActivity (LAUNCHER), Firebase system receivers, and Google Play system receivers. There is no own service exposed externally.


4. Encryption Structure

This is the most important part of this report. I examine the app's encryption structure by dividing it into (a) key management, (b) message/media envelope format, and (c) libraries used.

4-1. Key Management: Android Keystore Master Key + AES-GCM wrap

The defpackage/g8.java class is the ID key manager. Key decompiled excerpt:

KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
Key key = keyStore.getKey("setlog.identity.master", null);
// 없으면 새로 생성:
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES", "AndroidKeyStore");
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder("setlog.identity.master",
        PURPOSE_ENCRYPT | PURPOSE_DECRYPT)
    .setBlockModes("GCM").setEncryptionPaddings("NoPadding")
    .setRandomizedEncryptionRequired(true).setKeySize(256).build();

→ The master key (KEK) remains in AndroidKeyStore and cannot be extracted in plaintext. Where possible, it is backed by the device’s TEE or StrongBox hardware security module (this depends on device capability).

With this master key, the user’s X25519 ID key pair is wrapped using AES-256-GCM and stored in SharedPreferences setlog.identity in the form of IV+ciphertext. The key namespaces visible in the DEX strings are:

Namespace Purpose
setlog.identity.master Master KEK (AndroidKeyStore)
setlog.identity.legacy Legacy ID key (for migration)
newchat.identity.v3.<id> Current ID key
setlog.identity.pending.<id> Key in transition
`setlog.identity.backup.<hash>.<a\ b>` Wrapped key pair for backup
setlog.key_transfer.device / …pending Inter-device key transfer pairing state

The UI labels of resource (strings.xml) match this flow:

"Transfer to This Phone" / "Transfer to Another Device"
"Enter the 8-digit code shown there. This securely moves the key that unlocks your encrypted videos and messages."
"On your other device, open Account and choose Transfer to Another Device."
"You'll approve the transfer on your other device before anything moves."

→ A pairing flow in which the user moves keys to a new device is implemented at the UI level. This is circumstantial evidence suggesting that keys are not stored in plaintext on the server (it is not conclusive proof — dynamic analysis is needed to determine whether a separate path exists in which the server participates in key transfer).

4-2. Message/Media Envelope Format

defpackage/iu1.java contains the envelope assembly/disassembly logic. Excerpt:

// 수신자별 wrapped-key 엔트리 생성:
bw1Var = new bw1(
    str3,                                            // recipient ID
    str6,                                            // recipient public key fingerprint (Base64 SHA-256[0..16])
    i2,                                              // version
    "X25519-HKDF-SHA256+CHACHA20-POLY1305",          // algorithm identifier
    encodeToString(bArr2),                           // ephemeral salt
    encodeToString(b)                                // wrapped content key
);

In a separate code path in defpackage/uw1.java, message envelope serialization can be confirmed:

Map envelope = mapOf(
    "v" -> 1,
    "alg" -> "X25519-HKDF-SHA256+CHACHA20-POLY1305",
    "senderEphemeralPubB64" -> ...,  // 송신자 단발성 X25519 공개키
    "saltB64"               -> ...,
    "aadB64"                -> ...,  // 추가 인증 데이터
    "ctB64"                 -> ...,  // ChaCha20-Poly1305 ciphertext
    "kid"                   -> ...,  // 수신자 키 ID
    "publicKeyB64"          -> ...
);

The read flow (iu1.java c()/d()/k()) is as follows:

  1. From the per-recipient wrapped-key list, find the entry matching one’s own ID.
  2. If none exists, throw the "No wrapped key entry for current user" exception — this is precisely the standard E2EE pattern.
  3. Using the matching entry’s senderEphemeralPubB64 and one’s own X25519 private key, perform ECDH → derive the KEK via HKDF-SHA256.
  4. Unwrap the content key K with ChaCha20-Poly1305.
  5. Decrypt the body (ChaCha20-Poly1305) with K.

The presence of senderEphemeralPubB64 means sender-side forward secrecy. Even if the sender’s long-term private key is later exposed, the content keys of already transmitted envelopes cannot be derived.

This protocol combination is effectively the same primitive composition as the base mode of RFC 9180 (HPKE — Hybrid Public Key Encryption).

4-3. Cryptographic Libraries Used

Library Found Notes
BouncyCastle ChaCha20-Poly1305 engine Found (defpackage/us.java) AEAD core
Android Keystore + AES-GCM Found (g8.java) Master key wrap
In-house X25519 ECDH handling Found (iu1.java) Accompanied by the error message "Invalid X25519 private/public key length"
Conscrypt Found TLS provider (HTTPS channel)
libsodium / NaCl Not found
libsignal (Signal Protocol) Not found
Google Tink Not found
OpenPGP Not found
MLS, Olm/Megolm Not found

→ The app uses standard primitives (X25519, HKDF-SHA256, ChaCha20-Poly1305, AES-GCM) in a self-assembled protocol. It does not use widely vetted high-level libraries (libsignal, libsodium, Tink).

4-4. Key Verification — Limits of the Trust Model

For E2EE to be effective, the sender must have the recipient’s real public key. Generally, one of two models is used:

  • User verification model: Signal’s safety numbers, WhatsApp’s QR codes, etc. — users compare public-key integrity out-of-band (outside the app). It is like friends meeting in person and checking, “My code is ABC123 — is the code you see for me also ABC123?”
  • Server trust model: the server manages the public-key directory, and users accept the keys the server provides as-is. There is no verification procedure.

SetLog static analysis results:

  • Keywords such as safety.number, key_fingerprint, verify_identity, fingerprint.compare: 0 hits
  • The single "fingerprint" hit is an email-verification enum ({ENTER_EMAIL, VERIFY_CODE, ENROLLMENT_COMPLETE}) and unrelated
  • No key verification/comparison UI labels in strings.xml
  • The room-join path Loi2.a0(collectionId, code) parses the collections/{id}/join response's collection object. This collection.participants[], via Lsr2.a(), enters the User model, and it includes, as-is, the publicKey and kid fields provided by the server.
  • The media key wrapping path Liu1.l() uses the recipient User’s userId, publicKey, and kid to create MessageWrappedKey(uid, kid, v, alg, saltB64, wrappedB64). In other words, the source of the counterpart’s public key used by the sender is the participant/user record in the server response.
  • Strings such as SERVER_KID_MISMATCH, SERVER_MISSING_PUBLIC_KEY_AND_KID, and CurrentUserKeyHealthSnapshot(...) are found. However, these appear to be consistency checks between the currently logged-in user’s own local identity key and the server account record, and are separate from defenses in which the user directly verifies the counterpart’s public key or verifies it via a public log.

→ SetLog is a server trust model. To the extent verifiable by static analysis, if the server acts maliciously or an attacker who has compromised server privileges responds by inserting a different key in place of recipient X’s public key, the client is highly likely to use it without user verification. In this case, the server/attacker can perform a MITM by unwrapping the content key with that key’s private key and then re-wrapping it with the real recipient’s key. This is similar to the trust-model limitations of iMessage.

4-4-1. How a MITM Attack Works — Step-by-Step Explanation

Premise: a public key is a lock, and a private key is that lock’s key. Duplicating a lock is harmless — opening the locked box requires the key, and the key exists only inside its owner’s phone.

The server acts as a repository holding the locks of all registered users. When A goes to get B’s lock, the outcome depends on which lock the server hands over.

Scenario 1 — Honest Server

   [A's phone]                   [Honest Server]                  [B's phone]
        │                           │                            │
   ① "I want to send a video to B"             │                            │
        │                           │                            │
   ② "Give me B's lock" or
      "Give me info on the room B is in" ─────────▶│                            │
        │     [Server's lock repository]                              │
        │      ┌──────────────────┐ │                            │
        │      │ A's lock 🔒A     │ │                            │
        │      │ B's lock 🔒B     │ │                            │
        │      │ C's lock 🔒C     │ │                            │
        │      └──────────────────┘ │                            │
        │◀──── 🔒B (real public key/kid) ─│                            │
        │                           │                            │
   ③ Wrap the video's content key with 🔒B
      as a wrapped key for user B      │                            │
        │   📦🔒B (locked video)        │                            │
        │                           │                            │
   ④ Upload locked video ──────────────▶│ ─── forward as-is ──────────▶│
        │                           │                       ⑤ With B's key 🗝️B
        │                           │                          open 📦🔒B and watch
                                     │
                              The server knows lock 🔒B, but
                              it lacks key 🗝️B, so it cannot see

Scenario 2 — Lying Server (MITM)

   [A's phone]                [malicious server]                  [B's phone]
        │                           │                            │
   ① "I'll send B the video"         │                            │
        │                           │                            │
   ② "Give me B's lock" or
      "Give me the room info for B" ─▶│                            │
        │      ┌──────────────────┐ │                            │
        │      │ B's lock 🔒B (real) │  ← kept hidden separately  │
        │      │ Server lock 🔒S    │  ← the fake for A         │
        │      └──────────────────┘ │                            │
        │◀──── 🔒S / kid fabricated by the server ─│                │
        │     ("This is B's")        │                            │
        │                           │                            │
   ③ Wrap the video content key with 🔒S
      as a server-side wrapped key   │                            │
        │   📦🔒S (encrypted video + server-side wrapped key) │       │
        │                           │                            │
   ④ Upload the locked video ──────▶│                            │
        │                       🔓 Server uses its own key 🗝️S    │
        │                           to obtain the content key and decrypt the video │
        │                           │                            │
        │                       ⑤ The same content key is rewrapped │
        │                          with 🔒B (real), or the plaintext re-encrypted │
        │                           │─── 📦🔒B delivered ───────▶│
        │                           │                       ⑥ With B's key 🗝️B,
        │                           │                          normal viewing

   ❗ Both A and B perceive only that "the video went / arrived fine, as usual"
   ❗ No trace remains anywhere on the devices that the server saw it midway
   ❗ The server can decide to carry out this attack selectively, only against certain users
       (e.g., only a specific person's videos, only a specific group chat, only specific dates)

4-4-2. Why the user cannot detect it

Standard verification means (Signal · WhatsApp model):

  • Each user's screen displays the other's public key fingerprint (e.g., XK8M-9P2Q-ABCD)
  • Meet in person or compare the two people's screens via another channel → if they match, the server is honest; if they differ, tampering is detected

SetLog has no such comparison UI at all. That is:

  • A has no information with which to distinguish whether the lock it received is the real B's or a fake made by the server.
  • B likewise has no way to confirm whether its own lock was delivered accurately to its friends.
  • Because the device's local key cache stores the key the server sent as-is, a lying key also appears as a "normal cache."

This is not a defect in the cryptographic primitives themselves. X25519, HKDF, and ChaCha20-Poly1305 all function correctly, and in a scenario where an external attacker intercepts the communication, they cannot crack the envelope. The point of failure is a defect in the operational model: the user has no means to verify whether the lock the sender received is the real one. The last square of E2EE is left empty by the absence of a user-verification UI.

4-4-3. Threat model summary

Actor Can access content plaintext? Notes
External attacker (intercepts communication only, without server compromise) ❌ Blocked by the TLS + E2EE envelope
External attacker (server compromise, database exfiltration) ❌ The DB holds only ciphertext, and the private keys are inside the devices
External attacker (gains arbitrary code execution on the server) ✅ Can forge the key directory and MITM
New Chat Inc. operators (voluntary decision) ✅ The key directory is under the operators' control
U.S. law enforcement (subpoena · NSL) ✅ Can be compelled against the us-east-1 infrastructure
South Korean law enforcement △ The Seoul S3 holds only ciphertext. The key directory is in us-east-1 → U.S. cooperation required

→ E2EE that genuinely works against external threats (hackers). A structure with virtually no means of defense against internal threats (operators, law enforcement). The privacy policy's "we cannot see the content" is true in the first sense but not in the second — the fact that users find it hard to distinguish between the two is the essential risk.

4-4-4. How other services solve it

Service Key verification model
Signal Safety number — displays a 60-digit number on screen, directly comparable. Automatic notification on key change
WhatsApp QR code + 60-digit number, scannable in person. Notification on key change (optional)
iMessage No verification UI → the same weakness as SetLog. Long criticized by security researchers
iMessage Contact Key Verification (2024–) An optional feature Apple added belatedly. The user must turn it on
WhatsApp Auditable Key Directory (2023–) If the server lies, it is recorded in a cryptographically verifiable log, making detection possible after the fact
SetLog No verification UI, no key transparency infrastructure

Features to add if SetLog seriously strengthens security in the future:

  1. A "my safety number" screen showing one's own public key fingerprint
  2. Display of each friend's public key fingerprint + notification on change
  3. (Ideally) Post-hoc verification infrastructure such as an Auditable Key Directory

4-5. The unaudited risk of the self-assembled protocol

Even when standard primitives are used, subtle defects in the protocol assembly itself can exist. For example: if the AAD (Additional Authenticated Data) does not include message metadata, a reordering attack; if the identity-key context binding is missing, permanent impersonation after key compromise, and so on.

Proven implementations such as Signal Protocol, MLS, and libsignal have verified these details through academic papers and external audits. The self-assembled protocol used by SetLog has no published specification or external audit record (as of search on 2026-04-25). This is the most important unknown item in this static analysis.


5. Network endpoints and the data plane / control plane

Hardcoded endpoints identified by DEX string extraction:

Type URL Location
REST API https://j6x2txqgbb.execute-api.us-east-1.amazonaws.com/prod/ AWS API Gateway, us-east-1 (Virginia)
WebSocket wss://xxjbti8645.execute-api.us-east-1.amazonaws.com/production/ AWS API Gateway, us-east-1 (Virginia)
Media CDN https://dsly7y76dyfdl.cloudfront.net/ CloudFront (global PoP)
Invite deep link https://setlog.kr/invite/ External ingress

REST action paths extractable from the DEX (estimated): /upload, /messages, /message, /payload, /join, /leave, /edit, /remove, /complete. No trace of GraphQL.

Distinction between the data plane and the control plane

Section 6 of the privacy policy (overseas transfer) reads as follows:

"Storage location of name (or username): Northern Virginia, United States (us-east-1)"
"Storage location of photos and videos: Seoul, Republic of Korea"
"Other user data: stored in an end-to-end encrypted state"

Facts added by this static analysis:

  • The media binaries are highly likely to be in the Seoul S3 as stated in the privacy policy (dynamically verifiable via CloudFront response headers).
  • However, the control plane — authentication, key directory, message metadata, push tokens, real-time signaling — all points to us-east-1 (Virginia).
  • The privacy policy states only that "names are in us-east-1," and is silent on which region processes message metadata and communication logs.

→ It is reasonable to regard metadata such as the fact that a message was sent and received, its time, user IDs, and the communication graph as being under U.S. law enforcement jurisdiction. This is an item that should be classified as a disclosure defect in the privacy policy.


6. Third-party SDK inventory

Category SDKs found
Push/notifications Firebase Cloud Messaging
Authentication Google Play Services Auth, Credentials API (passkey support)
Camera/media androidx.camera, androidx.media3
TLS provider Conscrypt
Data storage androidx.datastore, traces of MMKV (sqlite)
Licensing com.pairip.licensecheck (Google Play anti-piracy)
Analytics SDK 0 (Sentry, Crashlytics, Mixpanel, Amplitude, PostHog, Datadog, NewRelic all not found)
Advertising SDK 0
Deep link SDK 0 (Branch.io, AppsFlyer, Adjust not found)

Section 5 of the privacy policy states, "In principle, we do not provide users' personal information to outside parties." The static analysis strongly supports this proposition at the binary level — no external data transmission code paths are found other than Firebase (Google) and AWS (the company's own infrastructure).


7. Review of consistency with disclosed materials

7-1. The company's privacy policy (https://newchat.kr/privacy/) — key quotations

§ 1. Categories of personal information collected

"When creating an account and using the service: name (or username)"
"When making an inquiry: email, name, and other content the user voluntarily enters"
"Messages, photos, videos, and other user data may be stored in an end-to-end encrypted state in the course of providing the service, and we cannot see their contents"
"Automatically collected: access IP address and approximate location information, browser type, settings and language, device information (model, OS version, etc.), website usage records such as pages visited, clicks, and access times, and information via cookies and similar technologies"

§ 6. Overseas transfer

"The name (or username) is stored on AWS in the Northern Virginia (us-east-1) region of the United States"
"Photos and videos are stored in an end-to-end encrypted state in an AWS bucket in the Seoul region of the Republic of Korea"

§ 3. Retention period

"Log records: retained for up to 1 year for security and service improvement purposes, then destroyed"

§ 4. Provision to third parties

"In principle, we do not provide users' personal information to outside parties"
Exceptions "where required by law or where there is a request pursuant to lawful procedures by an investigative agency"

Last updated: 2026-04-25

7-2. Consistency between the static analysis and the privacy policy

Privacy policy claim Static analysis result Assessment
"Messages, photos, and videos are stored with E2EE; we cannot see their contents" X25519+HKDF+ChaCha20-Poly1305 envelope implementation confirmed ✅ Consistent at the primitive/structure level. However, the absence of a key verification UI means a server-trust model.
"Names are stored in us-east-1" Both API/WS are in us-east-1 (Virginia) ✅ Consistent. However, the fact that the entire control plane is in us-east-1 is additional.
"Photos and videos are stored in Seoul" The CDN is global CloudFront. The S3 region cannot be determined by static analysis. ◔ Not statically verifiable. Dynamic analysis required.
"Automatic collection of location information (approximate)" 0 location permissions → only IP-based estimation possible ✅ Consistent (not GPS).
"Not provided to third parties (law enforcement exception)" 0 analytics/ad/deep link SDKs. No external transmission other than Firebase/AWS ✅ Consistent at the binary level.
"E2EE retention until the user deletes it" Outbox/encryptedFilePath pattern, mediaCipherVersion versioning found ✅ Consistent structure.
"Logs retained for up to 1 year" Not statically verifiable — server policy —

7-3. Apple App Store disclosure (apps.apple.com/kr/app/setlog/id6587576438) — directly confirmed

The Apple disclosure section reads as follows:

"Privacy — The developer does not collect any data from this app"

(Directly confirmed on 2026-04-25. App version: iOS 2.1.4, 90.7MB, 12+ rating, rating 4.3/98 reviews)

This disclosure is not consistent with the company's own privacy policy. The privacy policy states that it collects the following:

  • Name or username (stored in plaintext in us-east-1)
  • IP address and approximate location
  • Device information, OS version
  • Browser/usage records, cookies

These items are all outside the scope of E2EE. Apple's App Privacy disclosure guide requires that "even if data is collected, if it is not linked to the user or not used for tracking," it be disclosed under a separate category, and "no data collected" may be indicated only when no data is linked to the user (Apple App Privacy Details guidelines — 2025 revision). A username is by definition linked to the user, and the privacy policy itself states that it stores it in an identifiable form in us-east-1.

Whether E2EE content can be excluded from disclosure is a separate issue. Apple's guidelines grant a disclosure exemption only for "data encrypted such that the developer can never decrypt it." This static analysis has confirmed that SetLog's E2EE satisfies that condition at the primitive level. Accordingly, the non-disclosure of content (messages and media) can be justified. However, non-E2EE items such as names, IP addresses, device information, and cookies still carry a disclosure obligation. Therefore, the "no data collected" label is not accurate.

This discrepancy is a matter that the operator could explain or correct, or that Apple could review. This report does not reach a legal conclusion of violation; it records a factual discrepancy between two public documents (the company's own privacy policy and its App Store disclosure).

7-4. Google Play Data Safety (play.google.com/store/apps/datasafety?id=com.newchat.setlog) — Direct Verification

Item Google Play Disclosure
Data collected "Email address" (Personal info, optional, for account management purposes)
Third-party sharing "No data shared with third parties"
Encryption in transit Yes
User data deletion request Yes

(Verified directly on 2026-04-25)

The Google Play disclosure is closer to the privacy policy than the Apple disclosure is (it explicitly declares email as a collected item). However:

  • Name/username: the privacy policy § 1 specifies collection "at account creation," but it does not appear among the Google Play disclosure items (name is usually declared separately under Personal info → Name).
  • IP, device information, and cookies are likewise listed in the privacy policy as automatically collected, but this report could not directly verify whether they are disclosed under the Google Play "Device or other IDs" / "App activity" categories (the webpage response was limited to certain categories).

→ The Google Play disclosure shows a higher degree of consistency than Apple's, but the possibility of missing disclosure for the name, IP, and device information categories remains.


8. Unverified Items — Dynamic Analysis Required

Items that cannot be concluded from this static analysis and that require future dynamic traffic analysis or an official explanation from the operator:

  • Whether, after a media upload, the S3 object is actually located in ap-northeast-2 (Seoul)
  • Whether the push payload (FCM) contains a plaintext message preview, or conveys only envelope metadata
  • Whether, when a user files a report, plaintext content is transmitted to a separate moderation endpoint (how the company's CSAM/content policy operates)
  • The authentication mechanism of key directory responses — how the client accepts a server-forged public key (TOFU? certificates? change detection?)
  • Whether the AAD protects the integrity of message metadata (sender/receiver IDs, timestamps)
  • Whether there is a path by which the setlog.identity.backup.* namespace is sent to cloud backup
  • Whether an external security audit of the self-assembled protocol exists (public audit reports and CVE search results — none as of 2026-04-25)

9. Conclusion

According to the static analysis of SetLog Android beta v1.0, the end-to-end encryption specified in the company's privacy policy exists in reality at the level of primitives and envelope structure. The combination of X25519 ECDH + HKDF-SHA256 + ChaCha20-Poly1305 AEAD, the master key protected by the Android Keystore, and the pairing flow for key transfer between devices possess the core components of the E2EE promised by the privacy policy. No extensively integrated analytics or advertising SDKs were found, and the permission model is conservative.

However, the following three items determine operational security and disclosure completeness:

  1. Key directory trust model: There is no UI allowing users to verify the other party's public key. Under a server trust model, the E2EE can be bypassed in the event of server compromise or malice.
  2. Non-disclosure of the control plane region: It is certain that message metadata and signaling go to us-east-1 (Virginia), but the privacy policy specifies only the location of names as us-east-1 and is silent on the metadata region.
  3. The Apple App Store "no data collected" label: Considering the non-E2EE collected items specified in the privacy policy (name, IP, device information, cookies), it is not accurate. The Google Play disclosure is partially consistent.

SetLog's encryption design is closer to a "half-finished" security model than to flawed marketing. The privacy policy's promises and the client implementation align at the core, but the user verification UI, metadata region disclosure, and app store disclosure consistency are areas the operator must further refine.


Sources

  • Privacy policy original text: https://newchat.kr/privacy/ (last updated 2026-04-25, accessed directly on the day this report was written)
  • Apple App Store: https://apps.apple.com/kr/app/setlog/id6587576438 (accessed directly on 2026-04-25)
  • Google Play Data Safety: https://play.google.com/store/apps/datasafety?id=com.newchat.setlog (accessed directly on 2026-04-25)
  • APK static analysis: the Google Play distribution identified by the SHA-256 in § 1 of this report
  • Standards referenced: RFC 9180 (HPKE), Apple App Privacy Details Guidelines, Google Play Data Safety Policy

This report was prepared solely from static code analysis and review of public documents; no dynamic analysis, traffic capture, or external security audit of the production servers was performed. Some conclusions may be updated by future app updates, an official explanation from the operator, or a third-party security audit.