LCOV - code coverage report
Current view: top level - tests/unit - test_auth_logout.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 145 145
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 11 11

            Line data    Source code
       1              : /**
       2              :  * @file test_auth_logout.c
       3              :  * @brief Unit tests for auth_logout.c (auth.logOut RPC).
       4              :  *
       5              :  * Tests:
       6              :  *   1. TL request byte layout for auth.logOut is correct.
       7              :  *   2. auth.loggedOut response without future_auth_token is accepted.
       8              :  *   3. auth.loggedOut response with future_auth_token flag set is accepted
       9              :  *      (token ignored).
      10              :  *   4. RPC error with error_code 401 (NOT_AUTHORIZED) is treated as success.
      11              :  *   5. Generic RPC error returns -1.
      12              :  *   6. Network failure (no response) returns -1.
      13              :  *   7. auth_logout() clears the session file even when the RPC fails.
      14              :  */
      15              : 
      16              : #include "test_helpers.h"
      17              : #include "auth_logout.h"
      18              : #include "mtproto_session.h"
      19              : #include "transport.h"
      20              : #include "tl_serial.h"
      21              : #include "tl_registry.h"
      22              : #include "mock_socket.h"
      23              : #include "mock_crypto.h"
      24              : 
      25              : #include <string.h>
      26              : #include <stdlib.h>
      27              : #include <stdint.h>
      28              : 
      29              : /* ---- Helpers shared with test_auth_session ---- */
      30              : 
      31            5 : static void build_fake_encrypted_response(const uint8_t *payload, size_t plen,
      32              :                                            uint8_t *out, size_t *out_len) {
      33              :     TlWriter w;
      34            5 :     tl_writer_init(&w);
      35              : 
      36            5 :     uint8_t zeros24[24] = {0};
      37            5 :     tl_write_raw(&w, zeros24, 24);
      38              : 
      39            5 :     uint8_t header[32] = {0};
      40            5 :     uint32_t plen32 = (uint32_t)plen;
      41            5 :     memcpy(header + 28, &plen32, 4);
      42            5 :     tl_write_raw(&w, header, 32);
      43            5 :     tl_write_raw(&w, payload, plen);
      44              : 
      45            5 :     size_t total = w.len;
      46            5 :     size_t payload_start = 24;
      47            5 :     size_t enc_part = total - payload_start;
      48            5 :     if (enc_part % 16 != 0) {
      49            4 :         size_t pad = 16 - (enc_part % 16);
      50            4 :         uint8_t zeros[16] = {0};
      51            4 :         tl_write_raw(&w, zeros, pad);
      52              :     }
      53              : 
      54            5 :     size_t wire_bytes = w.len;
      55            5 :     size_t wire_units = wire_bytes / 4;
      56              : 
      57            5 :     uint8_t *result = (uint8_t *)malloc(1 + wire_bytes);
      58            5 :     result[0] = (uint8_t)wire_units;
      59            5 :     memcpy(result + 1, w.data, wire_bytes);
      60            5 :     *out_len = 1 + wire_bytes;
      61            5 :     memcpy(out, result, *out_len);
      62            5 :     free(result);
      63            5 :     tl_writer_free(&w);
      64            5 : }
      65              : 
      66            6 : static void session_setup(MtProtoSession *s) {
      67            6 :     mtproto_session_init(s);
      68            6 :     s->session_id = 0; /* match the zero session_id in fake encrypted frames */
      69            6 :     uint8_t fake_key[256] = {0};
      70            6 :     mtproto_session_set_auth_key(s, fake_key);
      71            6 :     mtproto_session_set_salt(s, 0x1122334455667788ULL);
      72            6 : }
      73              : 
      74            6 : static void setup_session_and_transport(MtProtoSession *s, Transport *t,
      75              :                                         ApiConfig *cfg) {
      76            6 :     session_setup(s);
      77            6 :     transport_init(t);
      78            6 :     t->fd = 42; t->connected = 1; t->dc_id = 1;
      79            6 :     api_config_init(cfg);
      80            6 :     cfg->api_id = 12345; cfg->api_hash = "deadbeef";
      81            6 : }
      82              : 
      83              : /* ---- Test 1: TL request has correct CRC ---- */
      84            1 : static void test_logout_request_crc(void) {
      85            1 :     mock_socket_reset();
      86            1 :     mock_crypto_reset();
      87              : 
      88              :     /* Build a minimal loggedOut response so api_call succeeds */
      89              :     uint8_t payload[16];
      90              :     TlWriter w;
      91            1 :     tl_writer_init(&w);
      92            1 :     tl_write_uint32(&w, CRC_auth_loggedOut);
      93            1 :     tl_write_uint32(&w, 0); /* flags = 0 */
      94            1 :     memcpy(payload, w.data, w.len);
      95            1 :     size_t plen = w.len;
      96            1 :     tl_writer_free(&w);
      97              : 
      98            1 :     uint8_t resp_buf[512]; size_t resp_len = 0;
      99            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     100            1 :     mock_socket_set_response(resp_buf, resp_len);
     101              : 
     102              :     MtProtoSession s; Transport t; ApiConfig cfg;
     103            1 :     setup_session_and_transport(&s, &t, &cfg);
     104              : 
     105            1 :     int rc = auth_logout_rpc(&cfg, &s, &t);
     106            1 :     ASSERT(rc == 0, "logout_rpc: must succeed with loggedOut response");
     107              : 
     108              :     /* Verify the sent bytes contain the auth.logOut CRC (0x3e72ba19). */
     109            1 :     size_t sent_len = 0;
     110            1 :     const uint8_t *sent = mock_socket_get_sent(&sent_len);
     111            1 :     ASSERT(sent_len > 4, "logout_rpc: must have sent data");
     112              : 
     113              :     /* The CRC appears inside the encrypted+wrapped payload, but with mock
     114              :      * passthrough crypto we can search the raw bytes for the little-endian
     115              :      * CRC_auth_logOut = 0x3e72ba19 => bytes 19 ba 72 3e. */
     116            1 :     uint8_t expected[4] = {0x19, 0xba, 0x72, 0x3e};
     117            1 :     int found = 0;
     118          114 :     for (size_t i = 0; i + 4 <= sent_len; i++) {
     119          114 :         if (memcmp(sent + i, expected, 4) == 0) { found = 1; break; }
     120              :     }
     121            1 :     ASSERT(found, "logout_rpc: sent bytes must contain auth.logOut CRC");
     122              :     (void)sent;
     123              : }
     124              : 
     125              : /* ---- Test 2: auth.loggedOut with no token (flags=0) accepted ---- */
     126            1 : static void test_logged_out_no_token(void) {
     127            1 :     mock_socket_reset();
     128            1 :     mock_crypto_reset();
     129              : 
     130              :     uint8_t payload[16];
     131              :     TlWriter w;
     132            1 :     tl_writer_init(&w);
     133            1 :     tl_write_uint32(&w, CRC_auth_loggedOut);
     134            1 :     tl_write_uint32(&w, 0); /* flags = 0, no future_auth_token */
     135            1 :     memcpy(payload, w.data, w.len);
     136            1 :     size_t plen = w.len;
     137            1 :     tl_writer_free(&w);
     138              : 
     139            1 :     uint8_t resp_buf[512]; size_t resp_len = 0;
     140            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     141            1 :     mock_socket_set_response(resp_buf, resp_len);
     142              : 
     143              :     MtProtoSession s; Transport t; ApiConfig cfg;
     144            1 :     setup_session_and_transport(&s, &t, &cfg);
     145              : 
     146            1 :     int rc = auth_logout_rpc(&cfg, &s, &t);
     147            1 :     ASSERT(rc == 0, "logout_rpc: loggedOut no-token must succeed");
     148              : }
     149              : 
     150              : /* ---- Test 3: auth.loggedOut with future_auth_token flag set is accepted ---- */
     151            1 : static void test_logged_out_with_token(void) {
     152            1 :     mock_socket_reset();
     153            1 :     mock_crypto_reset();
     154              : 
     155              :     uint8_t payload[64];
     156              :     TlWriter w;
     157            1 :     tl_writer_init(&w);
     158            1 :     tl_write_uint32(&w, CRC_auth_loggedOut);
     159            1 :     tl_write_uint32(&w, 1u); /* flags.0 = future_auth_token present */
     160              :     /* future_auth_token: bytes (TL bytes type — length-prefixed) */
     161            1 :     uint8_t token[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44};
     162            1 :     tl_write_bytes(&w, token, sizeof(token));
     163            1 :     memcpy(payload, w.data, w.len);
     164            1 :     size_t plen = w.len;
     165            1 :     tl_writer_free(&w);
     166              : 
     167            1 :     uint8_t resp_buf[512]; size_t resp_len = 0;
     168            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     169            1 :     mock_socket_set_response(resp_buf, resp_len);
     170              : 
     171              :     MtProtoSession s; Transport t; ApiConfig cfg;
     172            1 :     setup_session_and_transport(&s, &t, &cfg);
     173              : 
     174            1 :     int rc = auth_logout_rpc(&cfg, &s, &t);
     175            1 :     ASSERT(rc == 0, "logout_rpc: loggedOut with future_auth_token must succeed");
     176              : }
     177              : 
     178              : /* ---- Test 4: RPC error 401 (NOT_AUTHORIZED) treated as success ---- */
     179            1 : static void test_logout_rpc_401_is_ok(void) {
     180            1 :     mock_socket_reset();
     181            1 :     mock_crypto_reset();
     182              : 
     183              :     uint8_t payload[128];
     184              :     TlWriter w;
     185            1 :     tl_writer_init(&w);
     186            1 :     tl_write_uint32(&w, TL_rpc_error);
     187            1 :     tl_write_int32(&w, 401);
     188            1 :     tl_write_string(&w, "AUTH_KEY_UNREGISTERED");
     189            1 :     memcpy(payload, w.data, w.len);
     190            1 :     size_t plen = w.len;
     191            1 :     tl_writer_free(&w);
     192              : 
     193            1 :     uint8_t resp_buf[512]; size_t resp_len = 0;
     194            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     195            1 :     mock_socket_set_response(resp_buf, resp_len);
     196              : 
     197              :     MtProtoSession s; Transport t; ApiConfig cfg;
     198            1 :     setup_session_and_transport(&s, &t, &cfg);
     199              : 
     200            1 :     int rc = auth_logout_rpc(&cfg, &s, &t);
     201            1 :     ASSERT(rc == 0, "logout_rpc: 401 NOT_AUTHORIZED must be treated as success");
     202              : }
     203              : 
     204              : /* ---- Test 5: Generic RPC error (non-401) returns -1 ---- */
     205            1 : static void test_logout_rpc_generic_error(void) {
     206            1 :     mock_socket_reset();
     207            1 :     mock_crypto_reset();
     208              : 
     209              :     uint8_t payload[128];
     210              :     TlWriter w;
     211            1 :     tl_writer_init(&w);
     212            1 :     tl_write_uint32(&w, TL_rpc_error);
     213            1 :     tl_write_int32(&w, 500);
     214            1 :     tl_write_string(&w, "INTERNAL");
     215            1 :     memcpy(payload, w.data, w.len);
     216            1 :     size_t plen = w.len;
     217            1 :     tl_writer_free(&w);
     218              : 
     219            1 :     uint8_t resp_buf[512]; size_t resp_len = 0;
     220            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     221            1 :     mock_socket_set_response(resp_buf, resp_len);
     222              : 
     223              :     MtProtoSession s; Transport t; ApiConfig cfg;
     224            1 :     setup_session_and_transport(&s, &t, &cfg);
     225              : 
     226            1 :     int rc = auth_logout_rpc(&cfg, &s, &t);
     227            1 :     ASSERT(rc != 0, "logout_rpc: non-401 RPC error must return -1");
     228              : }
     229              : 
     230              : /* ---- Test 6: Network failure returns -1 ---- */
     231            1 : static void test_logout_rpc_network_failure(void) {
     232            1 :     mock_socket_reset();
     233            1 :     mock_crypto_reset();
     234              :     /* No response queued — recv returns 0 — api_call fails. */
     235              : 
     236              :     MtProtoSession s; Transport t; ApiConfig cfg;
     237            1 :     setup_session_and_transport(&s, &t, &cfg);
     238              : 
     239            1 :     int rc = auth_logout_rpc(&cfg, &s, &t);
     240            1 :     ASSERT(rc != 0, "logout_rpc: network failure must return -1");
     241              : }
     242              : 
     243              : /* ---- Test 7: null args rejected ---- */
     244            1 : static void test_logout_null_args(void) {
     245            1 :     int rc = auth_logout_rpc(NULL, NULL, NULL);
     246            1 :     ASSERT(rc != 0, "logout_rpc: null args must return -1");
     247              : }
     248              : 
     249            1 : void run_auth_logout_tests(void) {
     250            1 :     RUN_TEST(test_logout_request_crc);
     251            1 :     RUN_TEST(test_logged_out_no_token);
     252            1 :     RUN_TEST(test_logged_out_with_token);
     253            1 :     RUN_TEST(test_logout_rpc_401_is_ok);
     254            1 :     RUN_TEST(test_logout_rpc_generic_error);
     255            1 :     RUN_TEST(test_logout_rpc_network_failure);
     256            1 :     RUN_TEST(test_logout_null_args);
     257            1 : }
        

Generated by: LCOV version 2.0-1