pbc icon indicating copy to clipboard operation
pbc copied to clipboard

Montgomery representation of integer modulo rings gives weird results when cross-compiled with mingw-w64

Open TransparentLC opened this issue 2 years ago • 0 comments

I use mingw-w64 on Ubuntu 22.04 to cross-compile pbc to get 64-bit Windows binaries since only 32-bit binaries are provided on the homepage.

Build script
set -e

PBC_BUILD_DIR=$(pwd)
GMP_VERSION=6.3.0
PBC_VERSION=0.5.14

apt install mingw-w64 m4 flex bison

wget https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.zst
tar --zstd -xvf gmp-${GMP_VERSION}.tar.zst
rm gmp-${GMP_VERSION}.tar.zst

cd gmp-${GMP_VERSION}
./configure \
    CC="x86_64-w64-mingw32-gcc" \
    CC_FOR_BUILD="x86_64-linux-gnu-gcc" \
    CPPFLAGS="-I${PBC_BUILD_DIR}/include -D__USE_MINGW_ANSI_STDIO=0" \
    LDFLAGS="-L${PBC_BUILD_DIR}/lib" \
    --host=x86_64-w64-mingw32 \
    --prefix=${PBC_BUILD_DIR} \
    --enable-static \
    --disable-shared
make -j$(nproc)
make check -j$(nproc)
make install
cd ..

wget https://crypto.stanford.edu/pbc/files/pbc-${PBC_VERSION}.tar.gz
tar zxvf pbc-${PBC_VERSION}.tar.gz
rm pbc-${PBC_VERSION}.tar.gz

cd pbc-${PBC_VERSION}
./configure \
    CC="x86_64-w64-mingw32-gcc" \
    CPPFLAGS="-I${PBC_BUILD_DIR}/include -D__USE_MINGW_ANSI_STDIO=0" \
    LDFLAGS="-L${PBC_BUILD_DIR}/lib" \
    LIBS="-lgmp" \
    --host=x86_64-w64-mingw32 \
    --prefix=${PBC_BUILD_DIR} \
    --enable-static \
    --disable-shared
make -j$(nproc)
make install
cd ..

# ├── build.sh
# ├── main.c
# ├── gmp-x.x.x
# │   └── ...
# ├── include
# │   ├── gmp.h
# │   └── pbc
# │       ├── pbc.h
# │       └── ...
# ├── lib
# │   ├── libgmp.a
# │   ├── libgmp.la
# │   ├── libpbc.a
# │   ├── libpbc.la
# │   └── pkgconfig
# │       └── gmp.pc
# ├── pbc-x.x.x
# │   └── ...
# └── share
#     └── ...
# x86_64-w64-mingw32-gcc -Wall -Iinclude -o main main.c -Llib -lpbc -lgmp

Then I found that these binaries always gives weird results. For example, for the following code:

#include "pbc/pbc.h"

// param/a.param
#define CURVE_PARAM ("" \
    "type a\n" \
    "q 8780710799663312522437781984754049815806883199414208211028653399266475630880222957078625179422662221423155858769582317459277713367317481324925129998224791\n" \
    "h 12016012264891146079388821366740534204802954401251311822919615131047207289359704531102844802183906537786776\n" \
    "r 730750818665451621361119245571504901405976559617\n" \
    "exp2 159\n" \
    "exp1 107\n" \
    "sign1 1\n" \
    "sign0 1\n" \
"")

int main(int argc, char const *argv[]) {
    pbc_random_set_deterministic(0x114514);

    pairing_t e;
    pairing_init_set_str(e, CURVE_PARAM);

    element_t x; element_init_Zr(x, e);
    element_set0(x);
    element_printf("x (set0) = %B\n", x);
    element_set1(x);
    element_printf("x (set1) = %B\n", x);
    element_add(x, x, x);
    element_printf("x (add(set1, set1)) = %B\n", x);
    element_set_si(x, 0x2);
    element_printf("x (set_si 0x2) = %B\n", x);
    element_random(x);
    element_printf("x (random seed=0x114514) = %B\n", x);
    element_from_hash(x, "Test data", 9);
    element_printf("x (from_hash \"Test data\") = %B\n", x);

    element_clear(x);
    pairing_clear(e);
    return 0;
}

The output when running on Ubuntu without cross-compiling:

x (set0) = 0
x (set1) = 1
x (add(set1, set1)) = 2
x (set_si 0x2) = 2
x (random seed=0x114514) = 627055856547974011506813785332492935521893207354
x (from_hash "Test data") = 481817657442947330395294957845829757162661110017

Running on Windows with cross-compiled binaries:

x (set0) = 0
x (set1) = 365375757781869456591985165927106086913303379970
x (add(set1, set1)) = 365375757781869456591985165927106086913303379971
x (set_si 0x2) = 365375757781869456591985165927106086913303379971
x (random seed=0x114514) = 704059715193577092360776882805714726779267438415
x (from_hash "Test data") = 269047460191534710553423228971153386492057830638

All of the assignment functions result in errors. The calculator and signature algorithms in the example also don't work. Someone on Stack Overflow seems to have encountered a similar problem.

This problem does not occur if the implementation of integer modulo ring used by pbc is changed to another implementation instead of the default Montgomery representation.

I don't know how to fix the implementation for now. A temporary fix is to add these lines to the code that uses pbc:

#ifdef __MINGW32__
#include "pbc/pbc_fp.h"
#endif

// ...

#ifdef __MINGW32__
pbc_tweak_use_fp("faster");
#endif

... or apply the patch before cross-compiling pbc:

--- arith/fp.c 2013-06-15 11:43:00.000000000 +0800
+++ arith/fp.c 2023-08-07 23:17:39.271450800 +0800
@@ -17,7 +17,11 @@
 // By default, use the montfp.c implementation of F_p. After
 // pbc_tweak_use_fp(), future field_init_fp calls will use the specified
 // implementation. This is useful for benchmarking and testing.
+#ifdef __MINGW32__
+static void (*option_fpinit) (field_ptr f, mpz_t prime) = field_init_faster_fp;
+#else
 static void (*option_fpinit) (field_ptr f, mpz_t prime) = field_init_mont_fp;

TransparentLC avatar Aug 07 '23 17:08 TransparentLC