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

            Line data    Source code
       1              : /**
       2              :  * @file test_auth_session.c
       3              :  * @brief Unit tests for auth_session.c (auth.sendCode + auth.signIn).
       4              :  *
       5              :  * Uses mock socket and mock crypto; verifies TL request structure and
       6              :  * response parsing.
       7              :  */
       8              : 
       9              : #include "test_helpers.h"
      10              : #include "auth_session.h"
      11              : #include "mtproto_session.h"
      12              : #include "transport.h"
      13              : #include "tl_serial.h"
      14              : #include "tl_registry.h"
      15              : #include "mock_socket.h"
      16              : #include "mock_crypto.h"
      17              : 
      18              : #include <string.h>
      19              : #include <stdlib.h>
      20              : #include <stdint.h>
      21              : 
      22              : /* ---- Helper: build a minimal fake encrypted response ----
      23              :  *
      24              :  * The RPC layer (rpc_recv_encrypted) expects:
      25              :  *   auth_key_id(8) + msg_key(16) + encrypted_payload
      26              :  * where encrypted_payload (after mock decrypt = identity) is:
      27              :  *   salt(8) + session_id(8) + msg_id(8) + seq_no(4) + data_len(4) + data
      28              :  * and data is the raw TL response we want to return.
      29              :  *
      30              :  * Since mock crypto uses passthrough (encrypt = copy), we build the
      31              :  * "encrypted" buffer as if it were already the decrypted payload.
      32              :  */
      33            9 : static void build_fake_encrypted_response(const uint8_t *payload, size_t plen,
      34              :                                            uint8_t *out, size_t *out_len) {
      35              :     TlWriter w;
      36            9 :     tl_writer_init(&w);
      37              : 
      38              :     /* auth_key_id (8) + msg_key (16) = 24 bytes header */
      39            9 :     uint8_t zeros24[24] = {0};
      40            9 :     tl_write_raw(&w, zeros24, 24);
      41              : 
      42              :     /* plaintext frame: salt(8)+session_id(8)+msg_id(8)+seq_no(4)+len(4)+data */
      43            9 :     uint8_t header[32] = {0};
      44            9 :     uint32_t plen32 = (uint32_t)plen;
      45            9 :     memcpy(header + 28, &plen32, 4); /* data_len */
      46            9 :     tl_write_raw(&w, header, 32);
      47            9 :     tl_write_raw(&w, payload, plen);
      48              : 
      49              :     /* Pad to 16-byte boundary */
      50            9 :     size_t total = w.len;
      51            9 :     size_t payload_start = 24; /* after auth_key_id + msg_key */
      52            9 :     size_t enc_part = total - payload_start;
      53            9 :     if (enc_part % 16 != 0) {
      54            8 :         size_t pad = 16 - (enc_part % 16);
      55            8 :         uint8_t zeros[16] = {0};
      56            8 :         tl_write_raw(&w, zeros, pad);
      57              :     }
      58              : 
      59              :     /* Wrap in abridged transport: 1-byte length prefix (in 4-byte units) */
      60            9 :     size_t wire_bytes = w.len;
      61            9 :     size_t wire_units = wire_bytes / 4;
      62              : 
      63            9 :     uint8_t *result = (uint8_t *)malloc(1 + wire_bytes);
      64            9 :     result[0] = (uint8_t)wire_units;
      65            9 :     memcpy(result + 1, w.data, wire_bytes);
      66            9 :     *out_len = 1 + wire_bytes;
      67            9 :     memcpy(out, result, *out_len);
      68            9 :     free(result);
      69            9 :     tl_writer_free(&w);
      70            9 : }
      71              : 
      72              : /* ---- Helper: make session ready for encrypted calls ---- */
      73           11 : static void session_setup(MtProtoSession *s) {
      74           11 :     mtproto_session_init(s);
      75           11 :     s->session_id = 0; /* match the zero session_id in fake encrypted frames */
      76           11 :     uint8_t fake_key[256] = {0};
      77           11 :     mtproto_session_set_auth_key(s, fake_key);
      78           11 :     mtproto_session_set_salt(s, 0x1122334455667788ULL);
      79           11 : }
      80              : 
      81              : /* ---- Helper: make fake sentCode TL payload ---- */
      82            1 : static size_t make_sent_code_payload(uint8_t *buf, size_t max) {
      83              :     TlWriter w;
      84            1 :     tl_writer_init(&w);
      85              : 
      86            1 :     tl_write_uint32(&w, CRC_auth_sentCode);
      87            1 :     tl_write_uint32(&w, 0);              /* flags = 0 */
      88              :     /* type: sentCodeTypeApp */
      89            1 :     tl_write_uint32(&w, CRC_auth_sentCodeTypeApp);
      90            1 :     tl_write_int32(&w, 5);              /* length = 5 digits */
      91              :     /* phone_code_hash */
      92            1 :     tl_write_string(&w, "abc123hash456xyz");
      93              : 
      94            1 :     size_t len = w.len < max ? w.len : max;
      95            1 :     memcpy(buf, w.data, len);
      96            1 :     tl_writer_free(&w);
      97            1 :     return len;
      98              : }
      99              : 
     100              : /* ---- Helper: make fake auth.authorization TL payload ---- */
     101            1 : static size_t make_authorization_payload(uint8_t *buf, size_t max,
     102              :                                           int64_t user_id) {
     103              :     TlWriter w;
     104            1 :     tl_writer_init(&w);
     105              : 
     106            1 :     tl_write_uint32(&w, CRC_auth_authorization);
     107            1 :     tl_write_uint32(&w, 0);             /* flags = 0 */
     108              :     /* user: user#3ff6ecb0 */
     109            1 :     tl_write_uint32(&w, TL_user);
     110            1 :     tl_write_uint32(&w, 0);            /* user flags */
     111            1 :     tl_write_int64(&w, user_id);       /* id */
     112              : 
     113            1 :     size_t len = w.len < max ? w.len : max;
     114            1 :     memcpy(buf, w.data, len);
     115            1 :     tl_writer_free(&w);
     116            1 :     return len;
     117              : }
     118              : 
     119              : /* ---- Test: auth_send_code sends correct TL request ---- */
     120            1 : static void test_send_code_request_structure(void) {
     121            1 :     mock_socket_reset();
     122            1 :     mock_crypto_reset();
     123              : 
     124              :     /* Prepare fake sentCode response */
     125              :     uint8_t payload[512];
     126            1 :     size_t plen = make_sent_code_payload(payload, sizeof(payload));
     127              : 
     128              :     uint8_t resp_buf[1024];
     129            1 :     size_t resp_len = 0;
     130            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     131            1 :     mock_socket_set_response(resp_buf, resp_len);
     132              : 
     133              :     MtProtoSession s;
     134            1 :     session_setup(&s);
     135              :     Transport t;
     136            1 :     transport_init(&t);
     137            1 :     t.fd = 42;
     138            1 :     t.connected = 1;
     139            1 :     t.dc_id = 1;
     140              : 
     141              :     ApiConfig cfg;
     142            1 :     api_config_init(&cfg);
     143            1 :     cfg.api_id   = 12345;
     144            1 :     cfg.api_hash = "deadbeef";
     145              : 
     146              :     AuthSentCode result;
     147            1 :     memset(&result, 0, sizeof(result));
     148            1 :     int rc = auth_send_code(&cfg, &s, &t, "+15551234567", &result, NULL);
     149              : 
     150            1 :     ASSERT(rc == 0, "send_code: must succeed with valid sentCode response");
     151            1 :     ASSERT(strcmp(result.phone_code_hash, "abc123hash456xyz") == 0,
     152              :            "send_code: phone_code_hash must be parsed correctly");
     153              : 
     154              :     /* Verify the sent bytes contain the phone number and api_id */
     155            1 :     size_t sent_len = 0;
     156            1 :     const uint8_t *sent = mock_socket_get_sent(&sent_len);
     157            1 :     ASSERT(sent_len > 10,  "send_code: must have sent data");
     158              :     (void)sent;
     159              : }
     160              : 
     161              : /* ---- Test: auth_send_code handles RPC error ---- */
     162            1 : static void test_send_code_rpc_error(void) {
     163            1 :     mock_socket_reset();
     164            1 :     mock_crypto_reset();
     165              : 
     166              :     /* Build rpc_error payload */
     167              :     uint8_t payload[256];
     168              :     TlWriter w;
     169            1 :     tl_writer_init(&w);
     170            1 :     tl_write_uint32(&w, TL_rpc_error);
     171            1 :     tl_write_int32(&w, 400);
     172            1 :     tl_write_string(&w, "PHONE_NUMBER_INVALID");
     173            1 :     memcpy(payload, w.data, w.len);
     174            1 :     size_t plen = w.len;
     175            1 :     tl_writer_free(&w);
     176              : 
     177              :     uint8_t resp_buf[1024];
     178            1 :     size_t resp_len = 0;
     179            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     180            1 :     mock_socket_set_response(resp_buf, resp_len);
     181              : 
     182              :     MtProtoSession s;
     183            1 :     session_setup(&s);
     184              :     Transport t;
     185            1 :     transport_init(&t);
     186            1 :     t.fd = 42; t.connected = 1; t.dc_id = 1;
     187              : 
     188              :     ApiConfig cfg;
     189            1 :     api_config_init(&cfg);
     190            1 :     cfg.api_id = 12345; cfg.api_hash = "deadbeef";
     191              : 
     192              :     AuthSentCode result;
     193            1 :     memset(&result, 0, sizeof(result));
     194            1 :     int rc = auth_send_code(&cfg, &s, &t, "+15551234567", &result, NULL);
     195              : 
     196            1 :     ASSERT(rc != 0, "send_code: must fail on RPC error");
     197              : }
     198              : 
     199              : /* ---- Test: auth_sign_in success path ---- */
     200            1 : static void test_sign_in_success(void) {
     201            1 :     mock_socket_reset();
     202            1 :     mock_crypto_reset();
     203              : 
     204              :     uint8_t payload[512];
     205            1 :     size_t plen = make_authorization_payload(payload, sizeof(payload), 987654321LL);
     206              : 
     207              :     uint8_t resp_buf[1024];
     208            1 :     size_t resp_len = 0;
     209            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     210            1 :     mock_socket_set_response(resp_buf, resp_len);
     211              : 
     212              :     MtProtoSession s;
     213            1 :     session_setup(&s);
     214              :     Transport t;
     215            1 :     transport_init(&t);
     216            1 :     t.fd = 42; t.connected = 1; t.dc_id = 1;
     217              : 
     218              :     ApiConfig cfg;
     219            1 :     api_config_init(&cfg);
     220            1 :     cfg.api_id = 12345; cfg.api_hash = "deadbeef";
     221              : 
     222            1 :     int64_t user_id = 0;
     223            1 :     int rc = auth_sign_in(&cfg, &s, &t,
     224              :                           "+15551234567", "abc123hash456xyz", "12345",
     225              :                           &user_id, NULL);
     226              : 
     227            1 :     ASSERT(rc == 0,             "sign_in: must succeed");
     228            1 :     ASSERT(user_id == 987654321LL, "sign_in: user_id must be parsed correctly");
     229              : }
     230              : 
     231              : /* ---- Test: auth_sign_in RPC error ---- */
     232            1 : static void test_sign_in_rpc_error(void) {
     233            1 :     mock_socket_reset();
     234            1 :     mock_crypto_reset();
     235              : 
     236              :     uint8_t payload[256];
     237              :     TlWriter w;
     238            1 :     tl_writer_init(&w);
     239            1 :     tl_write_uint32(&w, TL_rpc_error);
     240            1 :     tl_write_int32(&w, 400);
     241            1 :     tl_write_string(&w, "PHONE_CODE_INVALID");
     242            1 :     memcpy(payload, w.data, w.len);
     243            1 :     size_t plen = w.len;
     244            1 :     tl_writer_free(&w);
     245              : 
     246              :     uint8_t resp_buf[1024];
     247            1 :     size_t resp_len = 0;
     248            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     249            1 :     mock_socket_set_response(resp_buf, resp_len);
     250              : 
     251              :     MtProtoSession s;
     252            1 :     session_setup(&s);
     253              :     Transport t;
     254            1 :     transport_init(&t);
     255            1 :     t.fd = 42; t.connected = 1; t.dc_id = 1;
     256              : 
     257              :     ApiConfig cfg;
     258            1 :     api_config_init(&cfg);
     259            1 :     cfg.api_id = 12345; cfg.api_hash = "deadbeef";
     260              : 
     261            1 :     int64_t user_id = 0;
     262            1 :     int rc = auth_sign_in(&cfg, &s, &t,
     263              :                           "+15551234567", "abc123hash456xyz", "00000",
     264              :                           &user_id, NULL);
     265              : 
     266            1 :     ASSERT(rc != 0, "sign_in: must fail on RPC error");
     267              : }
     268              : 
     269              : /* ---- Test: null arguments are rejected ---- */
     270            1 : static void test_null_args_rejected(void) {
     271              :     AuthSentCode out;
     272            1 :     ASSERT(auth_send_code(NULL, NULL, NULL, NULL, &out, NULL) == -1,
     273              :            "send_code: null args must return -1");
     274            1 :     ASSERT(auth_sign_in(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == -1,
     275              :            "sign_in: null args must return -1");
     276              : }
     277              : 
     278              : /* ---- Helpers for additional error-path tests ---- */
     279            7 : static void setup_session_and_transport(MtProtoSession *s, Transport *t,
     280              :                                          ApiConfig *cfg) {
     281            7 :     session_setup(s);
     282            7 :     transport_init(t);
     283            7 :     t->fd = 42; t->connected = 1; t->dc_id = 1;
     284            7 :     api_config_init(cfg);
     285            7 :     cfg->api_id = 12345; cfg->api_hash = "deadbeef";
     286            7 : }
     287              : 
     288              : /* ---- Test: send_code with unexpected top-level constructor ---- */
     289            1 : static void test_send_code_unexpected_constructor(void) {
     290            1 :     mock_socket_reset();
     291            1 :     mock_crypto_reset();
     292              : 
     293              :     uint8_t payload[64];
     294              :     TlWriter w;
     295            1 :     tl_writer_init(&w);
     296            1 :     tl_write_uint32(&w, 0xDEADBEEF);         /* not auth.sentCode */
     297            1 :     tl_write_uint32(&w, 0);
     298            1 :     memcpy(payload, w.data, w.len);
     299            1 :     size_t plen = w.len;
     300            1 :     tl_writer_free(&w);
     301              : 
     302            1 :     uint8_t resp_buf[256]; size_t resp_len = 0;
     303            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     304            1 :     mock_socket_set_response(resp_buf, resp_len);
     305              : 
     306              :     MtProtoSession s; Transport t; ApiConfig cfg;
     307            1 :     setup_session_and_transport(&s, &t, &cfg);
     308              : 
     309            1 :     AuthSentCode result = {0};
     310            1 :     int rc = auth_send_code(&cfg, &s, &t, "+15551234567", &result, NULL);
     311            1 :     ASSERT(rc != 0, "send_code: unexpected constructor must fail");
     312              : }
     313              : 
     314              : /* ---- Test: send_code hitting sentCodeTypeFlashCall ---- */
     315            1 : static void test_send_code_flash_call_type(void) {
     316            1 :     mock_socket_reset();
     317            1 :     mock_crypto_reset();
     318              : 
     319              :     uint8_t payload[256];
     320              :     TlWriter w;
     321            1 :     tl_writer_init(&w);
     322            1 :     tl_write_uint32(&w, CRC_auth_sentCode);
     323            1 :     tl_write_uint32(&w, 1u << 2);            /* flags: timeout present */
     324            1 :     tl_write_uint32(&w, CRC_auth_sentCodeTypeFlashCall);
     325            1 :     tl_write_string(&w, "\\d{6}");           /* pattern */
     326            1 :     tl_write_string(&w, "flash_hash");
     327            1 :     tl_write_int32(&w, 120);                 /* timeout */
     328            1 :     memcpy(payload, w.data, w.len);
     329            1 :     size_t plen = w.len;
     330            1 :     tl_writer_free(&w);
     331              : 
     332            1 :     uint8_t resp_buf[512]; size_t resp_len = 0;
     333            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     334            1 :     mock_socket_set_response(resp_buf, resp_len);
     335              : 
     336              :     MtProtoSession s; Transport t; ApiConfig cfg;
     337            1 :     setup_session_and_transport(&s, &t, &cfg);
     338              : 
     339            1 :     AuthSentCode result = {0};
     340            1 :     int rc = auth_send_code(&cfg, &s, &t, "+15551234567", &result, NULL);
     341            1 :     ASSERT(rc == 0, "send_code: FlashCall path must succeed");
     342            1 :     ASSERT(result.timeout == 120, "timeout must be parsed from flags.2");
     343            1 :     ASSERT(strcmp(result.phone_code_hash, "flash_hash") == 0,
     344              :            "phone_code_hash must match");
     345              : }
     346              : 
     347              : /* ---- Test: send_code with unknown sentCodeType constructor ---- */
     348            1 : static void test_send_code_unknown_type(void) {
     349            1 :     mock_socket_reset();
     350            1 :     mock_crypto_reset();
     351              : 
     352              :     uint8_t payload[128];
     353              :     TlWriter w;
     354            1 :     tl_writer_init(&w);
     355            1 :     tl_write_uint32(&w, CRC_auth_sentCode);
     356            1 :     tl_write_uint32(&w, 0);
     357            1 :     tl_write_uint32(&w, 0xBADCAFEE);         /* unknown sentCodeType */
     358            1 :     memcpy(payload, w.data, w.len);
     359            1 :     size_t plen = w.len;
     360            1 :     tl_writer_free(&w);
     361              : 
     362            1 :     uint8_t resp_buf[256]; size_t resp_len = 0;
     363            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     364            1 :     mock_socket_set_response(resp_buf, resp_len);
     365              : 
     366              :     MtProtoSession s; Transport t; ApiConfig cfg;
     367            1 :     setup_session_and_transport(&s, &t, &cfg);
     368              : 
     369            1 :     AuthSentCode result = {0};
     370            1 :     int rc = auth_send_code(&cfg, &s, &t, "+15551234567", &result, NULL);
     371            1 :     ASSERT(rc != 0, "send_code: unknown sentCodeType must fail");
     372              : }
     373              : 
     374              : /* ---- Test: send_code API-call failure (no response) ---- */
     375            1 : static void test_send_code_api_call_fails(void) {
     376            1 :     mock_socket_reset();
     377            1 :     mock_crypto_reset();
     378              :     /* No response queued → recv returns 0 → api_call fails. */
     379              : 
     380              :     MtProtoSession s; Transport t; ApiConfig cfg;
     381            1 :     setup_session_and_transport(&s, &t, &cfg);
     382              : 
     383            1 :     AuthSentCode result = {0};
     384            1 :     int rc = auth_send_code(&cfg, &s, &t, "+15551234567", &result, NULL);
     385            1 :     ASSERT(rc != 0, "send_code: api_call failure must propagate");
     386              : }
     387              : 
     388              : /* ---- Test: sign_in with unexpected top-level constructor ---- */
     389            1 : static void test_sign_in_unexpected_constructor(void) {
     390            1 :     mock_socket_reset();
     391            1 :     mock_crypto_reset();
     392              : 
     393              :     uint8_t payload[64];
     394              :     TlWriter w;
     395            1 :     tl_writer_init(&w);
     396            1 :     tl_write_uint32(&w, 0xBADBADBA);
     397            1 :     tl_write_uint32(&w, 0);
     398            1 :     memcpy(payload, w.data, w.len);
     399            1 :     size_t plen = w.len;
     400            1 :     tl_writer_free(&w);
     401              : 
     402            1 :     uint8_t resp_buf[256]; size_t resp_len = 0;
     403            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     404            1 :     mock_socket_set_response(resp_buf, resp_len);
     405              : 
     406              :     MtProtoSession s; Transport t; ApiConfig cfg;
     407            1 :     setup_session_and_transport(&s, &t, &cfg);
     408              : 
     409            1 :     int64_t uid = 0;
     410            1 :     int rc = auth_sign_in(&cfg, &s, &t, "+1", "h", "12345", &uid, NULL);
     411            1 :     ASSERT(rc != 0, "sign_in: unexpected constructor must fail");
     412              : }
     413              : 
     414              : /* ---- Test: sign_in with unexpected user constructor — still OK, uid=0 ---- */
     415            1 : static void test_sign_in_unexpected_user_crc(void) {
     416            1 :     mock_socket_reset();
     417            1 :     mock_crypto_reset();
     418              : 
     419              :     uint8_t payload[128];
     420              :     TlWriter w;
     421            1 :     tl_writer_init(&w);
     422            1 :     tl_write_uint32(&w, CRC_auth_authorization);
     423            1 :     tl_write_uint32(&w, 0);          /* flags */
     424            1 :     tl_write_uint32(&w, 0xCAFEBABE); /* unexpected user constructor */
     425            1 :     memcpy(payload, w.data, w.len);
     426            1 :     size_t plen = w.len;
     427            1 :     tl_writer_free(&w);
     428              : 
     429            1 :     uint8_t resp_buf[256]; size_t resp_len = 0;
     430            1 :     build_fake_encrypted_response(payload, plen, resp_buf, &resp_len);
     431            1 :     mock_socket_set_response(resp_buf, resp_len);
     432              : 
     433              :     MtProtoSession s; Transport t; ApiConfig cfg;
     434            1 :     setup_session_and_transport(&s, &t, &cfg);
     435              : 
     436            1 :     int64_t uid = 77;
     437            1 :     int rc = auth_sign_in(&cfg, &s, &t, "+1", "h", "12345", &uid, NULL);
     438            1 :     ASSERT(rc == 0, "sign_in: unexpected user constructor still OK");
     439            1 :     ASSERT(uid == 0, "sign_in: uid must be reset to 0");
     440              : }
     441              : 
     442              : /* ---- Test: sign_in api_call fails ---- */
     443            1 : static void test_sign_in_api_call_fails(void) {
     444            1 :     mock_socket_reset();
     445            1 :     mock_crypto_reset();
     446              :     /* No response queued */
     447              : 
     448              :     MtProtoSession s; Transport t; ApiConfig cfg;
     449            1 :     setup_session_and_transport(&s, &t, &cfg);
     450              : 
     451            1 :     int64_t uid = 0;
     452            1 :     int rc = auth_sign_in(&cfg, &s, &t, "+1", "h", "12345", &uid, NULL);
     453            1 :     ASSERT(rc != 0, "sign_in: api_call failure must propagate");
     454              : }
     455              : 
     456            1 : void run_auth_session_tests(void) {
     457            1 :     RUN_TEST(test_send_code_request_structure);
     458            1 :     RUN_TEST(test_send_code_rpc_error);
     459            1 :     RUN_TEST(test_sign_in_success);
     460            1 :     RUN_TEST(test_sign_in_rpc_error);
     461            1 :     RUN_TEST(test_null_args_rejected);
     462            1 :     RUN_TEST(test_send_code_unexpected_constructor);
     463            1 :     RUN_TEST(test_send_code_flash_call_type);
     464            1 :     RUN_TEST(test_send_code_unknown_type);
     465            1 :     RUN_TEST(test_send_code_api_call_fails);
     466            1 :     RUN_TEST(test_sign_in_unexpected_constructor);
     467            1 :     RUN_TEST(test_sign_in_unexpected_user_crc);
     468            1 :     RUN_TEST(test_sign_in_api_call_fails);
     469            1 : }
        

Generated by: LCOV version 2.0-1