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

            Line data    Source code
       1              : /**
       2              :  * @file test_srp_math_functional.c
       3              :  * @brief Real-crypto smoke test for the SRP math pieces used by
       4              :  *        auth_2fa_check_password (P3-03).
       5              :  *
       6              :  * We don't have access to server-generated reference vectors, so instead
       7              :  * we verify the *properties* that must hold independent of the exact
       8              :  * numbers:
       9              :  *   1. mod_mul + mod_sub + mod_exp agree on a known-small identity.
      10              :  *   2. mod_exp outputs are always reduced to the modulus length.
      11              :  *   3. The full PH1/PH2 chain is deterministic for the same password
      12              :  *      and salt pair.
      13              :  *
      14              :  * Known-answer for the full inputCheckPasswordSRP requires reproducing
      15              :  * the server's private ephemeral b, which we can't. Instead we land
      16              :  * just the SRP-adjacent primitive checks.
      17              :  */
      18              : 
      19              : #include "test_helpers.h"
      20              : #include "crypto.h"
      21              : 
      22              : #include <stdint.h>
      23              : #include <string.h>
      24              : 
      25              : static const uint8_t PRIME_8[1] = { 0x0B }; /* 11 decimal */
      26              : 
      27              : /* 2^5 mod 11 = 10 (=0x0a) */
      28            2 : static void test_mod_exp_small_known(void) {
      29            2 :     uint8_t base = 2, exp = 5;
      30            2 :     uint8_t out[1] = {0};
      31            2 :     size_t out_len = sizeof(out);
      32            2 :     CryptoBnCtx *ctx = crypto_bn_ctx_new();
      33            2 :     int rc = crypto_bn_mod_exp(out, &out_len, &base, 1, &exp, 1,
      34              :                                  PRIME_8, 1, ctx);
      35            2 :     crypto_bn_ctx_free(ctx);
      36            2 :     ASSERT(rc == 0, "mod_exp returns ok");
      37            2 :     ASSERT(out_len == 1, "output padded to modulus length");
      38            2 :     ASSERT(out[0] == 10, "2^5 mod 11 == 10");
      39              : }
      40              : 
      41              : /* 7 * 8 mod 11 == 1 */
      42            2 : static void test_mod_mul_small_known(void) {
      43            2 :     uint8_t a = 7, b = 8, out[1] = {0};
      44            2 :     size_t out_len = sizeof(out);
      45            2 :     CryptoBnCtx *ctx = crypto_bn_ctx_new();
      46            2 :     int rc = crypto_bn_mod_mul(out, &out_len, &a, 1, &b, 1,
      47              :                                  PRIME_8, 1, ctx);
      48            2 :     crypto_bn_ctx_free(ctx);
      49            2 :     ASSERT(rc == 0, "mod_mul ok");
      50            2 :     ASSERT(out[0] == 1, "7*8 mod 11 == 1");
      51              : }
      52              : 
      53              : /* (3 - 9) mod 11 = 5 (always non-negative) */
      54            2 : static void test_mod_sub_small_known(void) {
      55            2 :     uint8_t a = 3, b = 9, out[1] = {0};
      56            2 :     size_t out_len = sizeof(out);
      57            2 :     CryptoBnCtx *ctx = crypto_bn_ctx_new();
      58            2 :     int rc = crypto_bn_mod_sub(out, &out_len, &a, 1, &b, 1,
      59              :                                  PRIME_8, 1, ctx);
      60            2 :     crypto_bn_ctx_free(ctx);
      61            2 :     ASSERT(rc == 0, "mod_sub ok");
      62            2 :     ASSERT(out[0] == 5, "(3 - 9) mod 11 == 5");
      63              : }
      64              : 
      65              : /* Modular reduction fills to the modulus byte count even for small values. */
      66            2 : static void test_mod_exp_pads_output(void) {
      67            2 :     uint8_t base = 2, exp = 1;
      68            2 :     uint8_t prime[32] = {0};
      69            2 :     prime[31] = 0x0B;
      70            2 :     uint8_t out[32] = {0};
      71            2 :     size_t out_len = sizeof(out);
      72            2 :     CryptoBnCtx *ctx = crypto_bn_ctx_new();
      73            2 :     int rc = crypto_bn_mod_exp(out, &out_len, &base, 1, &exp, 1,
      74              :                                  prime, sizeof(prime), ctx);
      75            2 :     crypto_bn_ctx_free(ctx);
      76            2 :     ASSERT(rc == 0, "mod_exp padded ok");
      77            2 :     ASSERT(out_len == 32, "full modulus width returned");
      78           64 :     for (int i = 0; i < 31; i++) ASSERT(out[i] == 0, "leading zero pad");
      79            2 :     ASSERT(out[31] == 2, "last byte carries the small result");
      80              : }
      81              : 
      82              : /* ucmp semantics */
      83            2 : static void test_ucmp(void) {
      84            2 :     uint8_t a[2] = { 0x01, 0x00 };
      85            2 :     uint8_t b[2] = { 0x00, 0xFF };
      86            2 :     ASSERT(crypto_bn_ucmp(a, 2, b, 2) == 1, "a > b");
      87            2 :     ASSERT(crypto_bn_ucmp(b, 2, a, 2) == -1, "b < a");
      88            2 :     ASSERT(crypto_bn_ucmp(a, 2, a, 2) == 0, "eq");
      89              : }
      90              : 
      91              : /* PBKDF2 driven by the same salts / password must be deterministic. */
      92            2 : static void test_srp_x_deterministic(void) {
      93            2 :     const uint8_t salt1[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
      94            2 :     const uint8_t salt2[16] = {16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
      95              :     uint8_t a[32], b[32];
      96              : 
      97              :     /* Simulate PH2 core: pbkdf2(password, salt1, 100000, 64) folded
      98              :      * through an outer SHA-256 salted by salt2. Deterministic across
      99              :      * runs given the same inputs. */
     100              :     uint8_t pbkdf[64];
     101            2 :     int rc = crypto_pbkdf2_hmac_sha512((const uint8_t *)"hunter2", 7,
     102              :                                          salt1, sizeof(salt1),
     103              :                                          100, pbkdf, sizeof(pbkdf));
     104            2 :     ASSERT(rc == 0, "pbkdf ok");
     105              : 
     106              :     uint8_t buf[16 + 64 + 16];
     107            2 :     memcpy(buf, salt2, 16);
     108            2 :     memcpy(buf + 16, pbkdf, 64);
     109            2 :     memcpy(buf + 16 + 64, salt2, 16);
     110            2 :     crypto_sha256(buf, sizeof(buf), a);
     111              : 
     112              :     /* Call again — same inputs must yield same hash. */
     113            2 :     rc = crypto_pbkdf2_hmac_sha512((const uint8_t *)"hunter2", 7,
     114              :                                      salt1, sizeof(salt1),
     115              :                                      100, pbkdf, sizeof(pbkdf));
     116            2 :     ASSERT(rc == 0, "pbkdf ok (2nd)");
     117            2 :     memcpy(buf, salt2, 16);
     118            2 :     memcpy(buf + 16, pbkdf, 64);
     119            2 :     memcpy(buf + 16 + 64, salt2, 16);
     120            2 :     crypto_sha256(buf, sizeof(buf), b);
     121              : 
     122            2 :     ASSERT(memcmp(a, b, 32) == 0, "PH2-like chain is deterministic");
     123              : }
     124              : 
     125            2 : void run_srp_math_functional_tests(void) {
     126            2 :     RUN_TEST(test_mod_exp_small_known);
     127            2 :     RUN_TEST(test_mod_mul_small_known);
     128            2 :     RUN_TEST(test_mod_sub_small_known);
     129            2 :     RUN_TEST(test_mod_exp_pads_output);
     130            2 :     RUN_TEST(test_ucmp);
     131            2 :     RUN_TEST(test_srp_x_deterministic);
     132            2 : }
        

Generated by: LCOV version 2.0-1