mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 22:26:42 +00:00
remove gmp_generic variant too -- reduce number of cgo calls
This commit is contained in:
parent
7fc1617ac1
commit
e33fcb3811
5 changed files with 16 additions and 468 deletions
|
|
@ -5,7 +5,6 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto/modexp/bigint"
|
"github.com/ethereum/go-ethereum/crypto/modexp/bigint"
|
||||||
gmpcwrapper "github.com/ethereum/go-ethereum/crypto/modexp/gmp/cwrapper"
|
gmpcwrapper "github.com/ethereum/go-ethereum/crypto/modexp/gmp/cwrapper"
|
||||||
gmpgeneric "github.com/ethereum/go-ethereum/crypto/modexp/gmp/generic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// generateWorstCase generates a byte array with all bits set to 1
|
// generateWorstCase generates a byte array with all bits set to 1
|
||||||
|
|
@ -44,7 +43,6 @@ func BenchmarkComprehensive(b *testing.B) {
|
||||||
fn func([]byte, []byte, []byte) ([]byte, error)
|
fn func([]byte, []byte, []byte) ([]byte, error)
|
||||||
}{
|
}{
|
||||||
{"BigInt", bigint.ModExp},
|
{"BigInt", bigint.ModExp},
|
||||||
{"GMPGeneric", gmpgeneric.ModExp},
|
|
||||||
{"GMPCWrapper", gmpcwrapper.ModExp},
|
{"GMPCWrapper", gmpcwrapper.ModExp},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +94,6 @@ func BenchmarkWorstCaseOnly(b *testing.B) {
|
||||||
fn func([]byte, []byte, []byte) ([]byte, error)
|
fn func([]byte, []byte, []byte) ([]byte, error)
|
||||||
}{
|
}{
|
||||||
{"BigInt", bigint.ModExp},
|
{"BigInt", bigint.ModExp},
|
||||||
{"GMPGeneric", gmpgeneric.ModExp},
|
|
||||||
{"GMPCWrapper", gmpcwrapper.ModExp},
|
{"GMPCWrapper", gmpcwrapper.ModExp},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,7 +139,6 @@ func BenchmarkMemoryProfile(b *testing.B) {
|
||||||
fn func([]byte, []byte, []byte) ([]byte, error)
|
fn func([]byte, []byte, []byte) ([]byte, error)
|
||||||
}{
|
}{
|
||||||
{"BigInt", bigint.ModExp},
|
{"BigInt", bigint.ModExp},
|
||||||
{"GMPGeneric", gmpgeneric.ModExp},
|
|
||||||
{"GMPCWrapper", gmpcwrapper.ModExp},
|
{"GMPCWrapper", gmpcwrapper.ModExp},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,12 +211,6 @@ func BenchmarkComparison(b *testing.B) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("GMPGeneric", func(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, _ = gmpgeneric.ModExp(data.base, data.exp, data.mod)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
b.Run("GMPCWrapper", func(b *testing.B) {
|
b.Run("GMPCWrapper", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = gmpcwrapper.ModExp(data.base, data.exp, data.mod)
|
_, _ = gmpcwrapper.ModExp(data.base, data.exp, data.mod)
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@ You need to have GMP development libraries installed on your system:
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
This package provides a GMP-backed implementation for modular exponentiation. The API can be expanded, however right now, the main usage is for the modexp precompile.
|
This package provides a GMP-backed implementation for modular exponentiation using direct C calls for maximum performance. The API can be expanded, however right now, the main usage is for the modexp precompile.
|
||||||
|
|
||||||
There are currently two implementations: generic (using Go wrapper types) and cwrapper (direct C calls).
|
|
||||||
|
|
||||||
### Byte Array Interface (Recommended)
|
### Byte Array Interface (Recommended)
|
||||||
|
|
||||||
|
|
@ -35,25 +33,6 @@ if err != nil {
|
||||||
// result = 24 (2^10 mod 1000)
|
// result = 24 (2^10 mod 1000)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Direct GMP Interface
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Create numbers
|
|
||||||
base := gmp.NewInt()
|
|
||||||
base.SetString("123456789", 10)
|
|
||||||
|
|
||||||
exp := gmp.NewInt()
|
|
||||||
exp.SetString("987654321", 10)
|
|
||||||
|
|
||||||
mod := gmp.NewInt()
|
|
||||||
mod.SetString("1000000007", 10)
|
|
||||||
|
|
||||||
// Compute base^exp mod mod
|
|
||||||
result := gmp.NewInt()
|
|
||||||
result.ExpMod(base, exp, mod)
|
|
||||||
|
|
||||||
fmt.Printf("Result: %s\n", result)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWrapperVsExisting compares the wrapper with existing GMP bindings
|
// TestWrapperVsBigInt compares the wrapper with Go's math/big
|
||||||
func TestWrapperVsExisting(t *testing.T) {
|
func TestWrapperVsBigInt(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
base string
|
base string
|
||||||
|
|
@ -60,22 +60,16 @@ func TestWrapperVsExisting(t *testing.T) {
|
||||||
t.Fatalf("Wrapper error: %v", err)
|
t.Fatalf("Wrapper error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with existing GMP bindings
|
// Test with math/big
|
||||||
gmpBase := NewInt()
|
bigResult := new(big.Int)
|
||||||
gmpBase.SetString(tt.base, 10)
|
bigResult.Exp(baseBig, expBig, modBig)
|
||||||
gmpExp := NewInt()
|
|
||||||
gmpExp.SetString(tt.exp, 10)
|
|
||||||
gmpMod := NewInt()
|
|
||||||
gmpMod.SetString(tt.mod, 10)
|
|
||||||
gmpResult := NewInt()
|
|
||||||
gmpResult.ExpMod(gmpBase, gmpExp, gmpMod)
|
|
||||||
|
|
||||||
existingResult := gmpResult.Bytes()
|
expectedResult := bigResult.Bytes()
|
||||||
|
|
||||||
// Compare results
|
// Compare results
|
||||||
if !bytes.Equal(wrapperResult, existingResult) {
|
if !bytes.Equal(wrapperResult, expectedResult) {
|
||||||
t.Errorf("Results differ:\nWrapper: %x\nExisting: %x",
|
t.Errorf("Results differ:\nWrapper: %x\nExpected: %x",
|
||||||
wrapperResult, existingResult)
|
wrapperResult, expectedResult)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +94,7 @@ func BenchmarkModExp(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkModExpExisting(b *testing.B) {
|
func BenchmarkModExpBigInt(b *testing.B) {
|
||||||
baseBytes := make([]byte, 60)
|
baseBytes := make([]byte, 60)
|
||||||
expBytes := make([]byte, 60)
|
expBytes := make([]byte, 60)
|
||||||
modBytes := make([]byte, 60)
|
modBytes := make([]byte, 60)
|
||||||
|
|
@ -112,17 +106,17 @@ func BenchmarkModExpExisting(b *testing.B) {
|
||||||
}
|
}
|
||||||
modBytes[59] |= 0x01
|
modBytes[59] |= 0x01
|
||||||
|
|
||||||
base := NewInt()
|
base := new(big.Int)
|
||||||
base.SetBytes(baseBytes)
|
base.SetBytes(baseBytes)
|
||||||
exp := NewInt()
|
exp := new(big.Int)
|
||||||
exp.SetBytes(expBytes)
|
exp.SetBytes(expBytes)
|
||||||
mod := NewInt()
|
mod := new(big.Int)
|
||||||
mod.SetBytes(modBytes)
|
mod.SetBytes(modBytes)
|
||||||
result := NewInt()
|
result := new(big.Int)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
result.ExpMod(base, exp, mod)
|
result.Exp(base, exp, mod)
|
||||||
_ = result.Bytes()
|
_ = result.Bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
package gmp
|
|
||||||
|
|
||||||
// Uses system-installed GMP library
|
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lgmp
|
|
||||||
// #include <gmp.h>
|
|
||||||
// #include <stdlib.h>
|
|
||||||
//
|
|
||||||
// static inline int mpz_sgn_wrapper(const mpz_t op) {
|
|
||||||
// return mpz_sgn(op);
|
|
||||||
// }
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Int represents a GMP integer
|
|
||||||
type Int struct {
|
|
||||||
mpz C.mpz_t
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInt creates a new GMP integer
|
|
||||||
func NewInt() *Int {
|
|
||||||
z := &Int{}
|
|
||||||
C.mpz_init(&z.mpz[0])
|
|
||||||
runtime.SetFinalizer(z, (*Int).destroy)
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
// destroy cleans up the GMP integer
|
|
||||||
func (z *Int) destroy() {
|
|
||||||
C.mpz_clear(&z.mpz[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetString sets the integer from a string in the given base
|
|
||||||
func (z *Int) SetString(s string, base int) (*Int, bool) {
|
|
||||||
cs := C.CString(s)
|
|
||||||
defer C.free(unsafe.Pointer(cs))
|
|
||||||
|
|
||||||
if C.mpz_set_str(&z.mpz[0], cs, C.int(base)) != 0 {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return z, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExpMod computes z = base^exp mod mod (modular exponentiation)
|
|
||||||
func (z *Int) ExpMod(base, exp, mod *Int) *Int {
|
|
||||||
C.mpz_powm(&z.mpz[0], &base.mpz[0], &exp.mpz[0], &mod.mpz[0])
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBytes sets z to the value of buf interpreted as a big-endian unsigned integer
|
|
||||||
func (z *Int) SetBytes(buf []byte) *Int {
|
|
||||||
if len(buf) == 0 {
|
|
||||||
C.mpz_set_ui(&z.mpz[0], 0)
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use GMP's import function for efficiency
|
|
||||||
C.mpz_import(&z.mpz[0], C.size_t(len(buf)), 1, 1, 0, 0, unsafe.Pointer(&buf[0]))
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the absolute value of z as a big-endian byte slice
|
|
||||||
func (z *Int) Bytes() []byte {
|
|
||||||
if z == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case: zero returns empty slice (matching big.Int)
|
|
||||||
if C.mpz_sgn_wrapper(&z.mpz[0]) == 0 {
|
|
||||||
return []byte{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the number of bytes needed
|
|
||||||
size := (C.mpz_sizeinbase(&z.mpz[0], 2) + 7) / 8
|
|
||||||
|
|
||||||
// Allocate buffer
|
|
||||||
buf := make([]byte, size)
|
|
||||||
|
|
||||||
// Export to bytes
|
|
||||||
var count C.size_t
|
|
||||||
C.mpz_export(unsafe.Pointer(&buf[0]), &count, 1, 1, 0, 0, &z.mpz[0])
|
|
||||||
|
|
||||||
// Trim if needed (shouldn't happen but just in case)
|
|
||||||
if int(count) < len(buf) {
|
|
||||||
buf = buf[:count]
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// BitLen returns the number of bits required to represent z
|
|
||||||
func (z *Int) BitLen() int {
|
|
||||||
if z == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(C.mpz_sizeinbase(&z.mpz[0], 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mod sets z to x mod y and returns z
|
|
||||||
func (z *Int) Mod(x, y *Int) *Int {
|
|
||||||
C.mpz_mod(&z.mpz[0], &x.mpz[0], &y.mpz[0])
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUint64 sets z to the value of x
|
|
||||||
func (z *Int) SetUint64(x uint64) *Int {
|
|
||||||
C.mpz_set_ui(&z.mpz[0], C.ulong(x))
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
// ModExp performs modular exponentiation on byte arrays using GMP
|
|
||||||
// result = base^exp mod mod
|
|
||||||
// This function matches the behavior of the EVM modexp precompile
|
|
||||||
func ModExp(base, exp, mod []byte) ([]byte, error) {
|
|
||||||
// Handle empty modulus - return empty result (EVM behavior)
|
|
||||||
if len(mod) == 0 {
|
|
||||||
return []byte{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for zero modulus
|
|
||||||
allZero := true
|
|
||||||
for _, b := range mod {
|
|
||||||
if b != 0 {
|
|
||||||
allZero = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allZero {
|
|
||||||
return []byte{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create GMP integers
|
|
||||||
baseInt := NewInt()
|
|
||||||
expInt := NewInt()
|
|
||||||
modInt := NewInt()
|
|
||||||
resultInt := NewInt()
|
|
||||||
|
|
||||||
// Set values
|
|
||||||
baseInt.SetBytes(base)
|
|
||||||
expInt.SetBytes(exp)
|
|
||||||
modInt.SetBytes(mod)
|
|
||||||
|
|
||||||
// Special case: base has bit length 1 (base == 1)
|
|
||||||
if baseInt.BitLen() == 1 {
|
|
||||||
// Just return base % mod
|
|
||||||
resultInt.Mod(baseInt, modInt)
|
|
||||||
} else {
|
|
||||||
// Normal case: perform modular exponentiation
|
|
||||||
resultInt.ExpMod(baseInt, expInt, modInt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get result bytes
|
|
||||||
return resultInt.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,257 +0,0 @@
|
||||||
package gmp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestModExpAgainstBigInt tests our GMP implementation against Go's math/big
|
|
||||||
func TestModExpAgainstBigInt(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
base string
|
|
||||||
exp string
|
|
||||||
mod string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "small_numbers",
|
|
||||||
base: "2",
|
|
||||||
exp: "10",
|
|
||||||
mod: "1000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "medium_numbers",
|
|
||||||
base: "123",
|
|
||||||
exp: "456",
|
|
||||||
mod: "789",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "large_base",
|
|
||||||
base: "123456789012345678901234567890",
|
|
||||||
exp: "2",
|
|
||||||
mod: "1000000007",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "large_exponent",
|
|
||||||
base: "2",
|
|
||||||
exp: "123456789012345678901234567890",
|
|
||||||
mod: "1000000007",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "all_large",
|
|
||||||
base: "123456789012345678901234567890",
|
|
||||||
exp: "987654321098765432109876543210",
|
|
||||||
mod: "111111111111111111111111111111",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "prime_modulus",
|
|
||||||
base: "12345",
|
|
||||||
exp: "67890",
|
|
||||||
mod: "2147483647", // 2^31 - 1 (Mersenne prime)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "fermat_little_theorem",
|
|
||||||
base: "3",
|
|
||||||
exp: "16",
|
|
||||||
mod: "17",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "carmichael_number",
|
|
||||||
base: "2",
|
|
||||||
exp: "560",
|
|
||||||
mod: "561", // First Carmichael number
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
// GMP calculation
|
|
||||||
gmpBase := NewInt()
|
|
||||||
gmpBase.SetString(tt.base, 10)
|
|
||||||
|
|
||||||
gmpExp := NewInt()
|
|
||||||
gmpExp.SetString(tt.exp, 10)
|
|
||||||
|
|
||||||
gmpMod := NewInt()
|
|
||||||
gmpMod.SetString(tt.mod, 10)
|
|
||||||
|
|
||||||
gmpResult := NewInt()
|
|
||||||
gmpResult.ExpMod(gmpBase, gmpExp, gmpMod)
|
|
||||||
|
|
||||||
// math/big calculation
|
|
||||||
bigBase := new(big.Int)
|
|
||||||
bigBase.SetString(tt.base, 10)
|
|
||||||
|
|
||||||
bigExp := new(big.Int)
|
|
||||||
bigExp.SetString(tt.exp, 10)
|
|
||||||
|
|
||||||
bigMod := new(big.Int)
|
|
||||||
bigMod.SetString(tt.mod, 10)
|
|
||||||
|
|
||||||
bigResult := new(big.Int)
|
|
||||||
bigResult.Exp(bigBase, bigExp, bigMod)
|
|
||||||
|
|
||||||
// Compare results
|
|
||||||
if gmpResult.String() != bigResult.String() {
|
|
||||||
t.Errorf("ModExp mismatch:\n GMP: %s\n big: %s",
|
|
||||||
gmpResult.String(), bigResult.String())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSetBytesAgainstBigInt tests byte conversion against math/big
|
|
||||||
func TestSetBytesAgainstBigInt(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
bytes []byte
|
|
||||||
}{
|
|
||||||
{"empty", []byte{}},
|
|
||||||
{"single_byte", []byte{0x42}},
|
|
||||||
{"two_bytes", []byte{0x12, 0x34}},
|
|
||||||
{"four_bytes", []byte{0xDE, 0xAD, 0xBE, 0xEF}},
|
|
||||||
{"eight_bytes", []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}},
|
|
||||||
{"all_zeros", []byte{0x00, 0x00, 0x00, 0x00}},
|
|
||||||
{"leading_zeros", []byte{0x00, 0x00, 0x12, 0x34}},
|
|
||||||
{"all_ones", []byte{0xFF, 0xFF, 0xFF, 0xFF}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
// GMP SetBytes
|
|
||||||
gmpInt := NewInt()
|
|
||||||
gmpInt.SetBytes(tt.bytes)
|
|
||||||
|
|
||||||
// math/big SetBytes
|
|
||||||
bigInt := new(big.Int)
|
|
||||||
bigInt.SetBytes(tt.bytes)
|
|
||||||
|
|
||||||
// Compare string representations
|
|
||||||
if gmpInt.String() != bigInt.String() {
|
|
||||||
t.Errorf("SetBytes mismatch for %x:\n GMP: %s\n big: %s",
|
|
||||||
tt.bytes, gmpInt.String(), bigInt.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test round trip
|
|
||||||
gmpBytes := gmpInt.Bytes()
|
|
||||||
bigBytes := bigInt.Bytes()
|
|
||||||
|
|
||||||
// Handle empty/zero cases
|
|
||||||
// Note: both empty input and all-zeros should produce empty output
|
|
||||||
if len(bigBytes) == 0 {
|
|
||||||
if len(gmpBytes) != 0 {
|
|
||||||
t.Errorf("Expected empty bytes, got %x", gmpBytes)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare bytes (handling leading zeros)
|
|
||||||
if !bytesEqual(gmpBytes, bigBytes) {
|
|
||||||
t.Errorf("Bytes() mismatch:\n GMP: %x\n big: %x",
|
|
||||||
gmpBytes, bigBytes)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestModExpByteArrays tests modular exponentiation with byte arrays
|
|
||||||
func TestModExpByteArrays(t *testing.T) {
|
|
||||||
// Test case: RSA-like encryption with byte arrays
|
|
||||||
msgBytes := []byte("Hello!")
|
|
||||||
|
|
||||||
// Convert to numbers
|
|
||||||
gmpMsg := NewInt()
|
|
||||||
gmpMsg.SetBytes(msgBytes)
|
|
||||||
|
|
||||||
bigMsg := new(big.Int)
|
|
||||||
bigMsg.SetBytes(msgBytes)
|
|
||||||
|
|
||||||
// Public exponent (common RSA value)
|
|
||||||
e := "65537"
|
|
||||||
|
|
||||||
// Small modulus for testing
|
|
||||||
n := "12345678901234567890"
|
|
||||||
|
|
||||||
// GMP calculation
|
|
||||||
gmpE := NewInt()
|
|
||||||
gmpE.SetString(e, 10)
|
|
||||||
|
|
||||||
gmpN := NewInt()
|
|
||||||
gmpN.SetString(n, 10)
|
|
||||||
|
|
||||||
gmpResult := NewInt()
|
|
||||||
gmpResult.ExpMod(gmpMsg, gmpE, gmpN)
|
|
||||||
|
|
||||||
// math/big calculation
|
|
||||||
bigE := new(big.Int)
|
|
||||||
bigE.SetString(e, 10)
|
|
||||||
|
|
||||||
bigN := new(big.Int)
|
|
||||||
bigN.SetString(n, 10)
|
|
||||||
|
|
||||||
bigResult := new(big.Int)
|
|
||||||
bigResult.Exp(bigMsg, bigE, bigN)
|
|
||||||
|
|
||||||
// Compare
|
|
||||||
if gmpResult.String() != bigResult.String() {
|
|
||||||
t.Errorf("Byte array ModExp mismatch:\n GMP: %s\n big: %s",
|
|
||||||
gmpResult.String(), bigResult.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify we can convert back to bytes
|
|
||||||
resultBytes := gmpResult.Bytes()
|
|
||||||
if len(resultBytes) == 0 {
|
|
||||||
t.Error("Result bytes should not be empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmark against math/big
|
|
||||||
func BenchmarkModExpGMP(b *testing.B) {
|
|
||||||
base := NewInt()
|
|
||||||
base.SetString("123456789012345678901234567890123456789012345678901234567890", 10)
|
|
||||||
|
|
||||||
exp := NewInt()
|
|
||||||
exp.SetString("987654321098765432109876543210987654321098765432109876543210", 10)
|
|
||||||
|
|
||||||
mod := NewInt()
|
|
||||||
mod.SetString("111111111111111111111111111111111111111111111111111111111111", 10)
|
|
||||||
|
|
||||||
result := NewInt()
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
result.ExpMod(base, exp, mod)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkModExpBigInt(b *testing.B) {
|
|
||||||
base := new(big.Int)
|
|
||||||
base.SetString("123456789012345678901234567890123456789012345678901234567890", 10)
|
|
||||||
|
|
||||||
exp := new(big.Int)
|
|
||||||
exp.SetString("987654321098765432109876543210987654321098765432109876543210", 10)
|
|
||||||
|
|
||||||
mod := new(big.Int)
|
|
||||||
mod.SetString("111111111111111111111111111111111111111111111111111111111111", 10)
|
|
||||||
|
|
||||||
result := new(big.Int)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
result.Exp(base, exp, mod)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function
|
|
||||||
func bytesEqual(a, b []byte) bool {
|
|
||||||
if len(a) != len(b) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := range a {
|
|
||||||
if a[i] != b[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue