LibTomMath Documentation

repository·develop·Indexed 21 days ago

https://github.com/libtom/libtommath

A portable, open-source multiple-precision integer (MPI) library written in C for number theoretic computations. It provides high-performance integer arithmetic, including basic operations, Montgomery reduction for modular exponentiation, primality testing, and binary export/import compatible with GMP.

Tokens
5.4K
Snippets
18
Records
27
Agent score
74%

What's inside LibTomMath

  1. Build LibTomMath using Make

    develop

    The project can be built using make. Standard targets include:

    • make: Build the library.
    • make clean: Remove build artifacts.
    • make install: Install the library to the system.

    Note: For detailed GNU Linux building instructions, refer to the "Building LibTomMath" section in the doc/bn.pdf documentation.

  2. Run LibTomMath tests

    develop

    Tests are located in the demo/ directory and can be run in two different modes:

    1. Standalone Tests: Run make test to create a standalone test binary that executes several internal test routines.
    2. Opponent Testing (mtest): This mode verifies LibTomMath against test vectors generated by an alternative MPI library.
      • Build mtest using make mtest.
      • Build the opponent binary using make mtest_opponent.
      • Execute the test pipeline using: ./mtest/mtest | ./mtest_opponent.
    # Standalone tests
    make test
    
    # mtest opponent testing
    make mtest
    make mtest_opponent
    ./mtest/mtest | ./mtest_opponent
  3. Build LibTomMath using CMake

    develop

    The project supports the CMake build system. To build the library, create a build directory, run cmake, and then make. By default, it builds static libraries. To build shared libraries instead, pass the -DBUILD_SHARED_LIBS=On flag to the cmake command.

    git clone https://github.com/libtom/libtommath.git
    mkdir -p libtommath/build
    cd libtommath/build
    cmake ..
    make -j$(nproc)
    
    # To build shared libraries:
    cmake -DBUILD_SHARED_LIBS=On ..
    make -j$(nproc)
  4. Generate performance graphs using ltmtest and gnuplot

    develop

    To generate visual performance graphs for libtommath, you must first build and run the ltmtest utility from the root directory of the package. This process generates timing data in .log files within the logs/ directory. Once the logs are generated, you can use gnuplot to convert them into PNG images and view them via index.html.

    # 1. Build and run the timing tests (takes ~10 minutes)
    make timing ; ltmtest
    
    # 2. Generate PNG graphs from the logs
    gnuplot graphs.dem
    
    # 3. Open logs/index.html in your browser to view results
  5. Access LibTomMath documentation

    develop

    Documentation is primarily available in two forms:

    • PDF: Detailed documentation is generated from the LaTeX file doc/bn.tex and is available as a PDF for each release.
    • Header Comments: Limited documentation is provided directly within tommath.h.
  6. Handle errors in LibTomMath

    develop

    Most functions return an mp_err type. To understand what went wrong, use mp_error_to_string(mp_err code) to get a human-readable ASCII message describing the error code.

    Common error codes mentioned in documentation include:

    • MP_OVF: Overflow (e.g., result too large for mp_int).
    • MP_VAL: Invalid value (e.g., negative exponent or invalid radix).
    • MP_MEM: Memory allocation failure.
    • MP_ERR: Error reading/writing to a file or stream.
    • MP_BUF: Buffer is too small.
  7. Example: Check if an integer is a perfect power

    develop

    This example demonstrates using a mix of Libtommath functions to determine if a number $n$ is a perfect power ($n = root^{exponent}$). It uses mp_prime_next_prime to iterate through possible prime exponents and mp_expt_n for exponentiation.

    static mp_err mp_is_perfect_power(const mp_int *n, bool *result,
                                        mp_int *rootout, mp_int *exponent)
    {
       mp_int root, power, prime, max;
       int highbit, lowbit, p;
       mp_err err = MP_OKAY;
    
       *result = false;
    
       if (mp_cmp_d(n, 4) == MP_LT) {
          err = MP_VAL;
          goto LTM_OUT;
       }
    
       highbit = mp_count_bits(n) - 1;
    
       if (MP_IS_POWER_OF_TWO(n)) {
          *result = true;
          if (exponent != NULL) {
             if ((err = mp_set_l(exponent, (long)highbit)) != MP_OKAY) return err;
          }
          if (rootout != NULL) {
             if ((err = mp_set_l(rootout, 2l)) != MP_OKAY) return err;
          }
          return err;
       }
    
       if ((err = mp_init_multi(&root, &power, &prime, &max, NULL)) != MP_OKAY) return err;
       if ((err = mp_set_l(&max, (long)highbit)) != MP_OKAY) goto LTM_ERR;
    
       mp_set(&prime, 2u);
    
       while (mp_cmp(&prime, &max) != MP_GT) {
          if ((err = mp_prime_next_prime(&prime, -1, false)) != MP_OKAY) goto LTM_ERR;
          p = (int)mp_get_l(&prime);
          if ((err = mp_root_n(n, p, &root)) != MP_OKAY) goto LTM_ERR;
          if ((err = mp_expt_n(&root, p, &power)) != MP_OKAY) goto LTM_ERR;
    
          if (mp_cmp(n, &power) == MP_EQ) {
             *result = true;
             if (rootout != NULL) mp_exch(&root, rootout);
             if (exponent != NULL) {
                if ((e = mp_set_l(exponent, (long)p)) != MP_OKAY) goto LTM_ERR;
             }
          }
       }
    
    LTM_OUT:
       if (rootout != NULL) mp_set(rootout, 0u);
       if (exponent != NULL) mp_set(exponent, 0u);
    LTM_ERR:
       mp_clear_multi(&root, &power, &prime, NULL);
       return err;
    }
  8. Use Montgomery reduction for fast modular exponentiation

    develop

    For high-performance modular arithmetic, use Montgomery reduction. This requires a pre-computation step.

    1. Setup: Use mp_montgomery_setup(const mp_int *a, mp_digit *mp) to compute the pre-computation value mp for an odd modulus a.
    2. Normalization: Use mp_montgomery_calc_normalization(&R, b) to compute $R = r^n$.
    3. Reduction: Use mp_montgomery_reduce(mp_int *a, const mp_int *m, mp_digit mp) to reduce a in place modulo m using the pre-computed mp.

    Example workflow for computing $a^3 ext{ mod } b$:

    /* ... initialization and setup ... */
    
    /* normalize 'a' so now a is equal to aR */
    mp_mulmod(&a, &R, &b, &a);
    
    /* square a to get c = a^2R^2 */
    mp_sqr(&a, &c);
    
    /* reduce 'c' back down to c = a^2R^2 * R^-1 == a^2R */
    mp_montgomery_reduce(&c, &b, mp);
    
    /* multiply a to get c = a^3R^2 */
    mp_mul(&a, &c, &c);
    
    /* reduce 'c' back down to c = a^3R^2 * R^-1 == a^3R */
    mp_montgomery_reduce(&c, &b, mp);
    
    /* reduce 'c' again to get c = a^3R * R^-1 == a^3 */
    mp_montgomery_reduce(&c, &b, mp);
    
    /* c now equals a^3 mod b */
    int main(void)
    {
       mp_int   a, b, c, R;
       mp_digit mp;
       mp_err      result;
    
    /* initialize a,b to desired values,
        * mp_init R, c and set c to 1....
        */
    
    /* get normalization */
       if ((result = mp_montgomery_calc_normalization(&R, b)) != MP_OKAY) {
          printf("Error getting norm.  %s",
                 mp_error_to_string(result));
          return EXIT_FAILURE;
       }
       /* get mp value */
       if ((result = mp_montgomery_setup(&c, &mp)) != MP_OKAY) {
          printf("Error setting up montgomery.  %s",
                 mp_error_to_string(result));
          return EXIT_FAILURE;
       }
    
    /* normalize `a' so now a is equal to aR */
       if ((result = mp_mulmod(&a, &R, &b, &a)) != MP_OKAY) {
          printf("Error computing aR.  %s",
                 mp_error_to_string(result));
          return EXIT_FAILURE;
       }
    
    /* square a to get c = a^2R^2 */
       if ((result = mp_sqr(&a, &c)) != MP_OKAY) {
          printf("Error squaring.  %s",
                 mp_error_to_string(result));
          return EXIT_FAILURE;
       }
       /* now reduce `c' back down to c = a^2R^2 * R^-1 == a^2R */
       if ((result = mp_montgomery_reduce(&c, &b, mp)) != MP_OKAY) {
          printf("Error reducing.  %s",
                 mp_error_to_string(result));
          return EXIT_FAILURE;
       }
    
    /* multiply a to get c = a^3R^2 */
       if ((result = mp_mul(&a, &c, &c)) != MP_OKAY) {
          printf("Error reducing.  %s",
                 mp_error_to_string(result));
          return EXIT_FAILURE;
       }
    
    /* now reduce `c' back down to c = a^3R^2 * R^-1 == a^3R */
       if ((result = mp_montgomery_reduce(&c, &b, mp)) != MP_OKAY) {
          printf("Error reducing.  %s",
                 mp_error_to_string(result));
          return EXIT_FAILURE;
       }
    
    /* now reduce (again) `c' back down to c = a^3R * R^-1 == a^3 */
       if ((result = mp_montgomery_reduce(&c, &b, mp)) != MP_OKAY) {
          printf("Error reducing.  %s",
                 mp_error_to_string(result));
          return EXIT_FAILURE;
       }
    
    /* c now equals a^3 mod b */
    
    return EXIT_SUCCESS;
    }
  9. Convert mp_int to standard integer types

    develop

    Use the mp_get_* family of functions to extract values from a big integer into standard C types. Note that these functions are truncating if the big integer is larger than the target type.

    • mp_get_i32(const mp_int *a): Returns a signed 32-bit integer.
    • mp_get_i64(const mp_int *a): Returns a signed 64-bit integer.
    • mp_get_l(const mp_int *a): Returns a signed long.
    • mp_get_u32(const mp_int *a): Returns an unsigned 32-bit integer.
    • mp_get_u64(const mp_int *a): Returns an unsigned 64-bit integer.
    • mp_get_mag_u32(const mp_int *a): Returns the absolute value (magnitude) as an unsigned 32-bit integer.
    • mp_get_double(const mp_int *a): Returns a double (binary64). This may overflow if the integer is too large.