Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gnark-crypto, eip-196: malformed input handling #188

Merged
merged 3 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ input,result,gas,notes
1ca0717a8dfb9c3940a731d7c52f1699f64fe05e76189a91dc622e8fafd99de62313a1df5b32b17c21e53e2d0a1ff3eeac6ab4359a9f86e51b1c236f414d87ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,1ca0717a8dfb9c3940a731d7c52f1699f64fe05e76189a91dc622e8fafd99de62313a1df5b32b17c21e53e2d0a1ff3eeac6ab4359a9f86e51b1c236f414d87ea,150,
1234,,150,invalid input parameters, Point 0 is not on curve
0174fc233104c2ad4f56a8396b8c1b7d9c6ad10bffc70761c5e8f5280862f137029733a9f20a4cdbb7ae9c5dd1adf6ccc7fe3439d7dc71093af0656ae0ca0f290964773f12e2292f332306374f957d10,00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,150,invalid input parameters, Point 1 is not on curve
0174fc233104c2ad4f56a8396b8c1b7d9c6ad10bffc70761c5e8f5280862f137029733a9f20a4cdbb7ae9c5dd1adf6ccc7fe3439d7dc71093af0656ae0ca0f290964773f12e2292f332306374f957d10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,150,invalid input parameters, Point 1 is not on curve
67376aad340c93eb0fc9bc8e040dc691bd00e426c6456b4d13079e7f1dbb3da847eb0fc271cd23da50c6ebc261928abb1af9bfcea998791e18af1817b06221e1fe708d2f4224275523fcd37460a310ce4b56f1694dfe36280410f0fb6efc5f47b85662e5b08d881242a72acbc2c8e2fa71ac593be977ad3e090c8158aace0247,,150,invalid input parameters, Failed to parse Fp element
,00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,150,
ff000000000000000000000000000000000000000000000000000000000000ff,,150,invalid input parameters, Failed to parse Fp element
1470b80a6d5de1470aeadfa0b2eb913d9286bd966aa8a99b09879f3b10c985d02432b8ccd7083d421127dac6ad90bd569e763810e32d37114a786c7645864647,1470b80a6d5de1470aeadfa0b2eb913d9286bd966aa8a99b09879f3b10c985d02432b8ccd7083d421127dac6ad90bd569e763810e32d37114a786c7645864647,150,
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ input,result,gas,notes
1c2cb2b4504b19c7e073679432e625f2706b7c4728cd9bd3ce36579f4de2f3902c8605f723ac2f73baa15eac674f62ab06c79809aa4a4be3391c4d41d5a6e62ca0a73a5b559cd0384ca11d444e34f40e04f2080483db9e54ae09e44ec19c26a1,1147da8fc5774b5ef94fda682437b04f9b6aea72987d1269496769baf7e67dc729fe144eab44c6da4c851123165cd6506ce3e10b7691b8bf11b757e2ae058fcb,6000,
0d6ad8e12b4f61e3e2a2252ce11428941f2a84b7f0a821cb8cc7699303bd4fec2247870562618fd8d6169072d9b33614d2acf800b3ba0ff68ef8d5fd4d6c250d3e70b3bed17894f958579644c83fa9d485121d580e2b061c697e68f950297768,0be6d75e2fe2887835d396dae11321ca7c53083abd6a0b270ee1c087593517aa2ffd1bad577de7cf2b19b82bfff0c66e2afbfb79a72cbe834290437f3caf2f21,6000,
d2acf800b3ba0ff68ef8d5fd4d6c250d3e70b3bed17894f958579644c83fa9d485121d580e2b061c697e68f9502977680d6ad8e12b4f61e3e2a2252ce11428941f2a84b7f0a821cb8cc7699303bd4fec2247870562618fd8d6169072d9b33614,,6000,invalid input parameters, Failed to parse Fp element
02acf800b3ba0ff68ef8d5fd4d6c250d3e70b3bed17894f958579644c83fa9d405121d580e2b061c697e68f9502977680d6ad8e12b4f61e3e2a2252ce11428941f2a84b7f0a821cb8cc7699303bd4fec2247870562618fd8d6169072d9b33614,,6000,invalid input parameters, Point is not on curve
,00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,6000,
1A87B0584CE92F4593D161480614F2989035225609F08058CCFA3D0F940FEBE31A2F3C951F6DADCC7EE9007DFF81504B0FCD6D7CF59996EFDC33D92BF7F9F8F60100000000000000000000000000000000,0x220cb1540fa85ba04d863dca86de9359a43ac9fc084aebb9f2a936d989abbb602ccdc6c020dd2cf78332132b3f1d1122391b515035623cd6f53d4aea24ea2466,6000,
1A87B0584CE92F4593D161480614F2989035225609F08058CCFA3D0F940FEBE31A2F3C951F6DADCC7EE9007DFF81504B0FCD6D7CF59996EFDC33D92BF7F9F8F60000000000000000000000000000000100000000000000000000000000000000,1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b,6000,
171 changes: 89 additions & 82 deletions gnark/gnark-jni/gnark-eip-196.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import (
"github.com/consensys/gnark-crypto/ecc/bn254/fp"
)

var ErrMalformedPoint = errors.New("invalid input parameters, invalid point encoding")
var ErrPointNotInField = errors.New("invalid input parameters, point not in field")
var ErrPointOnCurveCheckFailed = errors.New("invalid point: point is not on curve")
var ErrMalformedPointEIP196 = errors.New("invalid point encoding")
var ErrInvalidInputPairingLengthEIP196 = errors.New("invalid input parameters, invalid input length for pairing")
var ErrPointNotInFieldEIP196 = errors.New("point not in field")
var ErrPointOnCurveCheckFailedEIP196 = errors.New("point is not on curve")

const (
EIP196PreallocateForResult = 128
Expand All @@ -40,65 +41,52 @@ var bn254Modulus = new(big.Int).SetBytes([]byte{
})

// Predefine a zero slice of length 16
var zeroSlice = make([]byte, 16)
var zeroEIP196Slice = make([]byte, 16)

//export eip196altbn128G1Add
func eip196altbn128G1Add(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cInputLen, cOutputLen, cErrorLen C.int) C.int {
func eip196altbn128G1Add(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cInputLen C.int, cOutputLen, cErrorLen *C.int) C.int {
inputLen := int(cInputLen)
errorLen := int(cErrorLen)

if (inputLen > 2*EIP196PreallocateForG1) {
// trunc if input too long
inputLen = 2*EIP196PreallocateForG1
}
errorLen := (*int)(unsafe.Pointer(cErrorLen))
outputLen := (*int)(unsafe.Pointer(cOutputLen))

// Convert error C pointers to Go slices
errorBuf := castErrorBuffer(javaErrorBuf, errorLen)
errorBuf := castErrorBufferEIP196(javaErrorBuf, errorLen)

// check we have input size sufficient for a G1Affine
if inputLen < EIP196PreallocateForG1 {

// if not, check the X point and return an error if it is in the field, edge case
if inputLen >= EIP196PreallocateForFp {
input := (*[EIP196PreallocateForFp]byte)(unsafe.Pointer(javaInputBuf))[:EIP196PreallocateForFp:EIP196PreallocateForFp]
if !checkInField(input[:EIP196PreallocateForFp]) {
copy(errorBuf, ErrPointNotInField.Error())
return 1
}
}
// otherwise if we do not have complete input, return 0
return 0
if (inputLen > 2*EIP196PreallocateForG1) {
// trunc if input too long
inputLen = 2*EIP196PreallocateForG1
}

// Convert input C pointers to Go slices
input := (*[2*EIP196PreallocateForG1]byte)(unsafe.Pointer(javaInputBuf))[:inputLen:inputLen]

if (inputLen == 0) {
*outputLen = 0
return 0
}

// generate p0 g1 affine
p0, err := safeUnmarshal(input[:64])
p0, err := safeUnmarshalEIP196(input, 0)

if err != nil {
copy(errorBuf, "invalid input parameters, " + err.Error())
dryError(err, errorBuf, outputLen, errorLen)
return 1
}

if inputLen < 2*EIP196PreallocateForG1 {
// if incomplete input is all zero, return p0
if isAllZero(input[64:inputLen]) {
if isAllZeroEIP196(input, 64) {
ret := p0.Marshal()
g1AffineEncode(ret, javaOutputBuf)
*outputLen = EIP196PreallocateForG1
return 0;
} else {
// else return an incomplete input error
copy(errorBuf, ErrMalformedPoint.Error())
return 1
}
}

// generate p1 g1 affine
p1, err := safeUnmarshal(input[64:])
p1, err := safeUnmarshalEIP196(input, 64)

if err != nil {
copy(errorBuf, "invalid input parameters, " + err.Error())
dryError(err, errorBuf, outputLen, errorLen)
return 1
}

Expand All @@ -108,30 +96,23 @@ func eip196altbn128G1Add(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cInp
// marshal the resulting point and encode directly to the output buffer
ret := result.Marshal()
g1AffineEncode(ret, javaOutputBuf)
*outputLen = EIP196PreallocateForG1
return 0

}

//export eip196altbn128G1Mul
func eip196altbn128G1Mul(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cInputLen, cOutputLen, cErrorLen C.int) C.int {
func eip196altbn128G1Mul(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cInputLen C.int, cOutputLen, cErrorLen *C.int) C.int {
inputLen := int(cInputLen)
errorLen := int(cErrorLen)
errorLen := (*int)(unsafe.Pointer(cErrorLen))
outputLen := (*int)(unsafe.Pointer(cOutputLen))

// Convert error C pointers to Go slices
errorBuf := castErrorBuffer(javaErrorBuf, errorLen)
errorBuf := castErrorBufferEIP196(javaErrorBuf, errorLen)

if inputLen < EIP196PreallocateForG1 {

// check the X point and return an error if it is in the field, edge case
if inputLen >= EIP196PreallocateForFp {
input := (*[EIP196PreallocateForFp]byte)(unsafe.Pointer(javaInputBuf))[:EIP196PreallocateForFp:EIP196PreallocateForFp]
if !checkInField(input[:EIP196PreallocateForFp]) {
copy(errorBuf, ErrPointNotInField.Error())
return 1
}
}

// otherwise if we do not have complete input, return 0
// if we do not have complete input, return 0
*outputLen = 0
return 0
}

Expand All @@ -144,11 +125,10 @@ func eip196altbn128G1Mul(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cInp
input := (*[EIP196PreallocateForG1 + EIP196PreallocateForScalar]byte)(unsafe.Pointer(javaInputBuf))[:inputLen:inputLen]

// generate p0 g1 affine
var p0 bn254.G1Affine
err := p0.Unmarshal(input[:64])
p0, err := safeUnmarshalEIP196(input, 0)

if err != nil {
copy(errorBuf, err.Error())
dryError(err, errorBuf, outputLen, errorLen)
return 1
}

Expand All @@ -164,38 +144,41 @@ func eip196altbn128G1Mul(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cInp
scalar.SetBytes(scalarBytes[:])

// multiply g1 point by scalar
result := p0.ScalarMultiplication(&p0, scalar)
result := p0.ScalarMultiplication(p0, scalar)

// marshal the resulting point and encode directly to the output buffer
ret := result.Marshal()
g1AffineEncode(ret, javaOutputBuf)
*outputLen = EIP196PreallocateForG1
return 0
}

//export eip196altbn128Pairing
func eip196altbn128Pairing(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cInputLen, cOutputLen, cErrorLen C.int) C.int {
func eip196altbn128Pairing(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cInputLen C.int, cOutputLen, cErrorLen *C.int) C.int {
inputLen := int(cInputLen)
outputLen := int(cOutputLen)
errorLen := int(cErrorLen)
errorLen := (*int)(unsafe.Pointer(cErrorLen))
outputLen := (*int)(unsafe.Pointer(cOutputLen))

// Convert error C pointers to Go slices
output := castBuffer(javaOutputBuf, outputLen)
output := castBufferEIP196(javaOutputBuf, outputLen)

// Convert error C pointers to Go slices
errorBuf := castErrorBuffer(javaErrorBuf, errorLen)
errorBuf := castErrorBufferEIP196(javaErrorBuf, errorLen)

*outputLen = 32

if inputLen == 0 {
output[31]=0x01
return 0
}

if inputLen % (EIP196PreallocateForG2 + EIP196PreallocateForG1) != 0 {
copy(errorBuf, "invalid input parameters, invalid input length for pairing\x00")
dryError(ErrInvalidInputPairingLengthEIP196, errorBuf, outputLen, errorLen)
return 1
}

// Convert input C pointers to Go slice
input := castBufferToSlice(unsafe.Pointer(javaInputBuf), inputLen)
input := castBufferToSliceEIP196(unsafe.Pointer(javaInputBuf), inputLen)

var pairCount = inputLen / (EIP196PreallocateForG2 + EIP196PreallocateForG1)
g1Points := make([]bn254.G1Affine, pairCount)
Expand All @@ -209,7 +192,7 @@ func eip196altbn128Pairing(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cI
err := g1.Unmarshal(input[i*192:i*192+64])

if err != nil {
copy(errorBuf, err.Error())
dryError(err, errorBuf, outputLen, errorLen)
return 1
}

Expand All @@ -218,7 +201,7 @@ func eip196altbn128Pairing(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cI
err = g2.Unmarshal(input[i*192+64:(i+1)*192])

if err != nil {
copy(errorBuf, err.Error())
dryError(err, errorBuf, outputLen, errorLen)
return 1
}

Expand All @@ -229,15 +212,14 @@ func eip196altbn128Pairing(javaInputBuf, javaOutputBuf, javaErrorBuf *C.char, cI

isOne, err := bn254.PairingCheck(g1Points, g2Points)
if err != nil {
copy(errorBuf, err.Error())
dryError(err, errorBuf, outputLen, errorLen)
return -1
}

if (isOne) {
// respond with 1 if pairing check was true, leave 0's intact otherwise
output[31]=0x01
}

return 0

}
Expand All @@ -257,25 +239,40 @@ func g1AffineEncode(g1Point []byte, output *C.char) (error) {
return nil
}

func safeUnmarshal(input []byte) (*bn254.G1Affine, error) {
func safeUnmarshalEIP196(input []byte, offset int) (*bn254.G1Affine, error) {
var g1 bn254.G1Affine
err := g1.X.SetBytesCanonical(input[:32])

if (len(input) - offset < 64) {
return nil, ErrMalformedPointEIP196
}

if !checkInFieldEIP196(input[offset: offset + 32]) {
return nil, ErrPointNotInFieldEIP196
}

err := g1.X.SetBytesCanonical(input[offset:offset + 32])

if (err == nil) {
err := g1.Y.SetBytesCanonical(input[32:64])

if !checkInFieldEIP196(input[offset + 32: offset + 64]) {
return nil, ErrPointNotInFieldEIP196
}
err := g1.Y.SetBytesCanonical(input[offset + 32:offset + 64])
if (err == nil) {
if (!g1.IsOnCurve()) {
return nil, ErrPointOnCurveCheckFailedEIP196
}
return &g1, nil
}
}
if (!g1.IsOnCurve()) {
return nil, ErrPointOnCurveCheckFailed
}


return nil, err
}

// checkInField checks that an element is in the field, not-in-field will normally
// be caught during unmarshal, but here in case of no-op calls of a single parameter
func checkInField(data []byte) bool {
func checkInFieldEIP196(data []byte) bool {

// Convert the byte slice to a big.Int
elem := new(big.Int).SetBytes(data)
Expand All @@ -285,16 +282,26 @@ func checkInField(data []byte) bool {
}

// isAllZero checks if all elements in the byte slice are zero
func isAllZero(data []byte) bool {
for _, b := range data {
if b != 0 {
return false
func isAllZeroEIP196(data []byte, offset int) bool {
if len(data) > 64 {
slice := data [offset:]
for _, b := range slice {
if b != 0 {
return false
}
}
}
return true
}

func castBufferToSlice(buf unsafe.Pointer, length int) []byte {
func dryError(err error, errorBuf []byte, outputLen, errorLen *int) {
errStr := "invalid input parameters, " + err.Error();
copy(errorBuf, errStr)
*outputLen = 0
*errorLen = len(errStr)
}

func castBufferToSliceEIP196(buf unsafe.Pointer, length int) []byte {
var slice []byte
// Obtain the slice header
header := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
Expand All @@ -305,17 +312,17 @@ func castBufferToSlice(buf unsafe.Pointer, length int) []byte {
return slice
}

func castBuffer(javaOutputBuf *C.char, length int) []byte {
bufSize := length
if bufSize < EIP196PreallocateForResult {
func castBufferEIP196(javaOutputBuf *C.char, length *int) []byte {
bufSize := *length
if bufSize != EIP196PreallocateForResult {
bufSize = EIP196PreallocateForResult
}
return (*[EIP196PreallocateForResult]byte)(unsafe.Pointer(javaOutputBuf))[:bufSize:bufSize]
}

func castErrorBuffer(javaOutputBuf *C.char, length int) []byte {
bufSize := length
if bufSize < EIP196PreallocateForError {
func castErrorBufferEIP196(javaOutputBuf *C.char, length *int) []byte {
bufSize := *length
if bufSize != EIP196PreallocateForError {
bufSize = EIP196PreallocateForError
}
return (*[EIP196PreallocateForError]byte)(unsafe.Pointer(javaOutputBuf))[:bufSize:bufSize]
Expand Down
Loading
Loading