mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 22:26:42 +00:00
150 lines
No EOL
4.2 KiB
Go
150 lines
No EOL
4.2 KiB
Go
package gmp
|
|
|
|
// #cgo LDFLAGS: -lgmp
|
|
// #include <gmp.h>
|
|
// #include <stdlib.h>
|
|
// #include <string.h>
|
|
// #include <stdint.h>
|
|
//
|
|
// static int modexp_bytes(
|
|
// const uint8_t* base, size_t base_len,
|
|
// const uint8_t* exp, size_t exp_len,
|
|
// const uint8_t* mod, size_t mod_len,
|
|
// uint8_t* result, size_t* result_len)
|
|
// {
|
|
// mpz_t base_mpz, exp_mpz, mod_mpz, result_mpz;
|
|
//
|
|
// // Check for NULL pointers
|
|
// if (!base || !exp || !mod || !result || !result_len) {
|
|
// return -1;
|
|
// }
|
|
//
|
|
// // Initialize GMP integers
|
|
// mpz_init(base_mpz);
|
|
// mpz_init(exp_mpz);
|
|
// mpz_init(mod_mpz);
|
|
// mpz_init(result_mpz);
|
|
//
|
|
// // Import big-endian byte arrays into GMP integers
|
|
// // Handle empty arrays specially - GMP treats NULL with size 0 as 0
|
|
// if (base_len > 0) {
|
|
// mpz_import(base_mpz, base_len, 1, 1, 0, 0, base);
|
|
// }
|
|
// if (exp_len > 0) {
|
|
// mpz_import(exp_mpz, exp_len, 1, 1, 0, 0, exp);
|
|
// }
|
|
// if (mod_len > 0) {
|
|
// mpz_import(mod_mpz, mod_len, 1, 1, 0, 0, mod);
|
|
// }
|
|
//
|
|
// // Special case: modulus is zero - return empty result (EVM behavior)
|
|
// if (mpz_cmp_ui(mod_mpz, 0) == 0) {
|
|
// *result_len = 0;
|
|
// mpz_clear(base_mpz);
|
|
// mpz_clear(exp_mpz);
|
|
// mpz_clear(mod_mpz);
|
|
// mpz_clear(result_mpz);
|
|
// return 0;
|
|
// }
|
|
//
|
|
// // Special case: base has bit length 1 (base == 1)
|
|
// // Just return base % mod
|
|
// if (mpz_sizeinbase(base_mpz, 2) == 1) {
|
|
// mpz_mod(result_mpz, base_mpz, mod_mpz);
|
|
// } else {
|
|
// // Normal case: perform modular exponentiation
|
|
// mpz_powm(result_mpz, base_mpz, exp_mpz, mod_mpz);
|
|
// }
|
|
//
|
|
// // Get size needed for result
|
|
// size_t needed = (mpz_sizeinbase(result_mpz, 2) + 7) / 8;
|
|
// if (needed == 0) needed = 1; // For zero result
|
|
//
|
|
// // Check if result buffer is large enough
|
|
// if (*result_len < needed) {
|
|
// mpz_clear(base_mpz);
|
|
// mpz_clear(exp_mpz);
|
|
// mpz_clear(mod_mpz);
|
|
// mpz_clear(result_mpz);
|
|
// return -2;
|
|
// }
|
|
//
|
|
// // Export result to big-endian byte array
|
|
// size_t count;
|
|
// mpz_export(result, &count, 1, 1, 0, 0, result_mpz);
|
|
// *result_len = count;
|
|
//
|
|
// // Handle zero result specially
|
|
// if (count == 0) {
|
|
// result[0] = 0;
|
|
// *result_len = 1;
|
|
// }
|
|
//
|
|
// // Clean up
|
|
// mpz_clear(base_mpz);
|
|
// mpz_clear(exp_mpz);
|
|
// mpz_clear(mod_mpz);
|
|
// mpz_clear(result_mpz);
|
|
//
|
|
// return 0;
|
|
// }
|
|
import "C"
|
|
import (
|
|
"errors"
|
|
"unsafe"
|
|
)
|
|
|
|
|
|
// ModExp performs modular exponentiation using the C implementation directly
|
|
// This is a lower-level function that bypasses the Go wrapper types
|
|
//
|
|
// This is thread safe.
|
|
func ModExp(base, exp, mod []byte) ([]byte, error) {
|
|
// Handle empty modulus - return empty result (EVM behavior)
|
|
if len(mod) == 0 {
|
|
return []byte{}, nil
|
|
}
|
|
|
|
// Allocate result buffer (size of modulus is the max possible result)
|
|
result := make([]byte, len(mod))
|
|
resultLen := C.size_t(len(result))
|
|
|
|
// Handle empty slices - pass a dummy non-nil pointer with length 0
|
|
// This avoids UB when the length is zero
|
|
dummy := C.uint8_t(0)
|
|
var basePtr, expPtr, modPtr *C.uint8_t = &dummy, &dummy, &dummy
|
|
|
|
if len(base) > 0 {
|
|
basePtr = (*C.uint8_t)(unsafe.Pointer(&base[0]))
|
|
}
|
|
if len(exp) > 0 {
|
|
expPtr = (*C.uint8_t)(unsafe.Pointer(&exp[0]))
|
|
}
|
|
if len(mod) > 0 {
|
|
modPtr = (*C.uint8_t)(unsafe.Pointer(&mod[0]))
|
|
}
|
|
|
|
// Call C function
|
|
ret := C.modexp_bytes(
|
|
basePtr, C.size_t(len(base)),
|
|
expPtr, C.size_t(len(exp)),
|
|
modPtr, C.size_t(len(mod)),
|
|
(*C.uint8_t)(unsafe.Pointer(&result[0])), &resultLen,
|
|
)
|
|
|
|
// Check for errors
|
|
switch ret {
|
|
case 0:
|
|
// Success - trim result to actual size
|
|
if resultLen == 0 {
|
|
return []byte{}, nil
|
|
}
|
|
return result[:resultLen], nil
|
|
case -1:
|
|
return nil, errors.New("invalid parameter")
|
|
case -2:
|
|
return nil, errors.New("result buffer too small")
|
|
default:
|
|
return nil, errors.New("unknown error")
|
|
}
|
|
} |