diff --git a/crypto/base64_test.go b/crypto/base64_test.go index 01be1be..307cd1d 100644 --- a/crypto/base64_test.go +++ b/crypto/base64_test.go @@ -1,10 +1,12 @@ package crypto import ( - "github.com/joeqian10/neo3-gogogo/helper" - "github.com/stretchr/testify/assert" "log" "testing" + + "github.com/stretchr/testify/assert" + + "github.com/joeqian10/neo3-gogogo/helper" ) func TestBase64Encode(t *testing.T) { @@ -22,7 +24,7 @@ func TestBase64Decode(t *testing.T) { } func TestBase64Decode2(t *testing.T) { - s := "A4uK9iEOz9y8qyJVLvjYz0HG+G+c+atT2GV0HP24M/Br" + s := "U2FsdGVkX1+iX5Ey7GqLND5UFUoV0b7rUJ2eEvHkYqA=" decoded, err := Base64Decode(s) assert.Nil(t, err) ss := helper.BytesToHex(decoded) @@ -36,3 +38,18 @@ func TestBase64Decode3(t *testing.T) { ss := string(decoded) assert.Equal(t, "Transaction has been executed", ss) } + +func TestBase64Decode4(t *testing.T) { + s := "FPHOAtHWr86lTMPb00238i9KzaWhFK17JowKjp28yE2ZYrJNkaYtDc1e05dZaMvZ1w0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9IQMEbQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfuhtBlhDF/iAa1Gfzmowxg8BULN+6G0GWEMX+IBrUZ/OajDGDwFQs37obQZYQxf4gGtRn85qMMYPAVCzfug=" + decoded, err := Base64Decode(s) + assert.Nil(t, err) + log.Println(helper.BytesToHex(decoded)) +} + +func TestBase64Decode5(t *testing.T) { + s := "TW/Nz+UkOwvhddhFmcl3fCXb3Uw=" + decoded, err := Base64Decode(s) + assert.Nil(t, err) + ss := helper.BytesToHex(decoded) + log.Println(ss) +} diff --git a/crypto/helper_test.go b/crypto/helper_test.go index 887d952..14a60ef 100644 --- a/crypto/helper_test.go +++ b/crypto/helper_test.go @@ -1,10 +1,12 @@ package crypto import ( - "github.com/joeqian10/neo3-gogogo/helper" - "github.com/stretchr/testify/assert" "log" "testing" + + "github.com/stretchr/testify/assert" + + "github.com/joeqian10/neo3-gogogo/helper" ) func TestAddressToScriptHash(t *testing.T) { @@ -26,7 +28,7 @@ func TestScriptHashToAddress(t *testing.T) { assert.Equal(t, "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf", a) } -func TestConvert(t *testing.T) { +func TestConvert(t *testing.T) { bs, err := Base64Decode("4rZTInKT6ZxPKQbVNVOrtKZy34Y=") assert.Nil(t, err) u := helper.UInt160FromBytes(bs) diff --git a/go.mod b/go.mod index 4cd5dc6..f64d7a3 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,18 @@ module github.com/joeqian10/neo3-gogogo -go 1.18 +go 1.21 -require ( - github.com/stretchr/testify v1.7.0 - golang.org/x/crypto v0.1.0 - golang.org/x/text v0.4.0 +toolchain go1.21.4 +require ( + github.com/stretchr/testify v1.9.0 + golang.org/x/crypto v0.25.0 + golang.org/x/text v0.16.0 ) require ( - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.1.0 // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f9b5aa9..e7e4876 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,80 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= +github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= +github.com/ethereum/go-ethereum v1.14.0 h1:xRWC5NlB6g1x7vNy4HDBLuqVNbtLrc7v8S6+Uxim1LU= +github.com/ethereum/go-ethereum v1.14.0/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/helper/util_test.go b/helper/util_test.go index fe52f5d..749aec5 100644 --- a/helper/util_test.go +++ b/helper/util_test.go @@ -3,11 +3,12 @@ package helper import ( "bytes" "fmt" - "github.com/stretchr/testify/assert" "log" "math/big" "strconv" "testing" + + "github.com/stretchr/testify/assert" ) func TestBytesToHex(t *testing.T) { @@ -138,7 +139,7 @@ func TestBigIntFromNeoBytes(t *testing.T) { assert.Equal(t, 0, big.NewInt(-200).Cmp(b)) } -func TestUp(t *testing.T) { +func TestUp(t *testing.T) { bi := ConvertFloat64StringToBigInt(strconv.FormatUint(5698643, 10), 36-8-2) fmt.Println(bi.String()) } diff --git a/keys/keyPair.go b/keys/keyPair.go index 29d0827..06709bd 100644 --- a/keys/keyPair.go +++ b/keys/keyPair.go @@ -7,9 +7,10 @@ import ( "crypto/rand" "crypto/sha256" "fmt" + "math/big" + "golang.org/x/crypto/scrypt" "golang.org/x/text/unicode/norm" - "math/big" "github.com/joeqian10/neo3-gogogo/crypto" "github.com/joeqian10/neo3-gogogo/helper" @@ -236,6 +237,7 @@ func VerifyMultiSig(message []byte, signatures [][]byte, pubKeys []crypto.ECPoin j++ if m-i > n-j { success = false + break } } return success diff --git a/keys/keyPair_test.go b/keys/keyPair_test.go index eced6b5..c5a7686 100644 --- a/keys/keyPair_test.go +++ b/keys/keyPair_test.go @@ -1,9 +1,11 @@ package keys import ( - "github.com/joeqian10/neo3-gogogo/helper" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" + + "github.com/joeqian10/neo3-gogogo/helper" ) func TestGenerateKeyPair(t *testing.T) { @@ -133,10 +135,3 @@ func TestVerifySignature2(t *testing.T) { actual := VerifySignature(sample, signedData, wrongPubKey) assert.Equal(t, false, actual) } - -func TestNewKeyPairFromNEP22(t *testing.T) { - nep2Key := "6PYURwh7xSUBA6yEbBsnYCGESd7UNHi3iWHsDweCxwzEH8xPBSphb1z7ss" - pwd := "poly" - _, err := NewKeyPairFromNEP2(nep2Key, pwd, helper.DefaultAddressVersion, N, R, P) - assert.Nil(t, err) -} diff --git a/keys/publicKey.go b/keys/publicKey.go index 6687f35..9d3b7c5 100644 --- a/keys/publicKey.go +++ b/keys/publicKey.go @@ -1,6 +1,10 @@ package keys import ( + "crypto/elliptic" + "fmt" + "math/big" + "github.com/joeqian10/neo3-gogogo/crypto" "github.com/joeqian10/neo3-gogogo/helper" "github.com/joeqian10/neo3-gogogo/sc" @@ -17,3 +21,147 @@ func PublicKeyToScriptHash(p *crypto.ECPoint) *helper.UInt160 { func PublicKeyToAddress(p *crypto.ECPoint, version byte) string { return crypto.ScriptHashToAddress(PublicKeyToScriptHash(p), version) } + +// RecoverPubKeyFromSigOnSecp256r1 recover a point on Secp256r1 from a signature +func RecoverPubKeyFromSigOnSecp256r1(message, signature []byte) ([]*crypto.ECPoint, error) { + if len(signature) != 64 { + return nil, fmt.Errorf("invalid neo signature length") + } + return RecoverPubKeyFromSig(crypto.P256, message, signature) +} + +// RecoverPubKeyFromSig recovers a point on a given curve from a signature +func RecoverPubKeyFromSig(curve elliptic.Curve, message, signature []byte) ([]*crypto.ECPoint, error) { + hash := crypto.Sha256(message) + //curve := crypto.P256 + bitLen := (curve.Params().BitSize + 7) / 8 + R := new(big.Int).SetBytes(signature[0:bitLen]) + S := new(big.Int).SetBytes(signature[bitLen:]) + sig := &Signature{ + R: R, + S: S, + } + return recoverKeyFromSignature(crypto.P256, sig, hash, true) +} + +// recoverKeyFromSignature recovers a public key from the signature "sig" on the +// given message hash "msg". Based on the algorithm found in section 5.1.5 of +// SEC 1 Ver 2.0, page 47-48 (53 and 54 in the pdf). This performs the details +// in the inner loop in Step 1. +func recoverKeyFromSignature(curve elliptic.Curve, sig *Signature, hash []byte, doChecks bool) ([]*crypto.ECPoint, error) { + + // calculate h = (Q + 1 + 2 * sqrt(Q)) / N + h := new(big.Int).Add(curve.Params().P, big.NewInt(1)) + h.Add(h, new(big.Int).Mul(big.NewInt(2), new(big.Int).Sqrt(curve.Params().P))) + h.Div(h, curve.Params().N) + + result := []*crypto.ECPoint{} + + for i := 0; uint64(i) <= h.Uint64(); i++ { + + // 1.1 x = (n * i) + r + Rx := new(big.Int).Mul(curve.Params().N, new(big.Int).SetInt64(int64(i))) + Rx.Add(Rx, sig.R) + if Rx.Cmp(curve.Params().P) != -1 { + continue + } + + // convert 02 to point R. (step 1.2 and 1.3). If we are on an odd + // iteration then 1.6 will be done with -R, so we calculate the other + // term when uncompressing the point. + //Ry, err := decodeCompressedY(curve, Rx, uint(i)) + for j := uint(0); j <= 1; j++ { + Ry, err := decodeCompressedY(curve, Rx, j) + if err != nil { + continue + } + + // 1.4 Check n*R is point at infinity + if doChecks { + nRx, nRy := curve.ScalarMult(Rx, Ry, curve.Params().N.Bytes()) + if nRx.Sign() != 0 || nRy.Sign() != 0 { + continue + } + } + + // 1.5 calculate e from message using the same algorithm as ecdsa + // signature calculation. + e := hashToInt(hash, curve) + + // Step 1.6.1: + // We calculate the two terms sR and eG separately multiplied by the + // inverse of r (from the signature). We then add them to calculate + // Q = r^-1(sR-eG) + invr := new(big.Int).ModInverse(sig.R, curve.Params().N) + + // first term. + invrS := new(big.Int).Mul(invr, sig.S) + invrS.Mod(invrS, curve.Params().N) + sRx, sRy := curve.ScalarMult(Rx, Ry, invrS.Bytes()) + + // second term. + e.Neg(e) + e.Mod(e, curve.Params().N) + e.Mul(e, invr) + e.Mod(e, curve.Params().N) + minuseGx, minuseGy := curve.ScalarBaseMult(e.Bytes()) + + // TODO: this would be faster if we did a mult and add in one + // step to prevent the jacobian conversion back and forth. + Qx, Qy := curve.Add(sRx, sRy, minuseGx, minuseGy) + Q, err := crypto.CreateECPoint(Qx, Qy, &curve) + if err != nil { + return nil, err + } + result = append(result, Q) + } + } + if len(result) == 0 { + return nil, fmt.Errorf("invalid signature") + } + return result, nil +} + +// hashToInt converts a hash value to an integer. There is some disagreement +// about how this is done. [NSA] suggests that this is done in the obvious +// manner, but [SECG] truncates the hash to the bit-length of the curve order +// first. We follow [SECG] because that's what OpenSSL does. Additionally, +// OpenSSL right shifts excess bits from the number if the hash is too large, and we mirror that too. +// This is borrowed from crypto/ecdsa. +func hashToInt(hash []byte, c elliptic.Curve) *big.Int { + orderBits := c.Params().N.BitLen() + orderBytes := (orderBits + 7) / 8 + if len(hash) > orderBytes { + hash = hash[:orderBytes] + } + + ret := new(big.Int).SetBytes(hash) + excess := len(hash)*8 - orderBits + if excess > 0 { + ret.Rsh(ret, uint(excess)) + } + return ret +} + +// decodeCompressedY performs decompression of Y coordinate for given X and Y's least significant bit +func decodeCompressedY(curve elliptic.Curve, x *big.Int, ylsb uint) (*big.Int, error) { + + cp := curve.Params() + three := big.NewInt(3) + + xCubed := new(big.Int).Exp(x, three, cp.P) + threeX := new(big.Int).Mul(x, three) + threeX.Mod(threeX, cp.P) + ySquared := new(big.Int).Sub(xCubed, threeX) + ySquared.Add(ySquared, cp.B) + ySquared.Mod(ySquared, cp.P) + y := new(big.Int).ModSqrt(ySquared, cp.P) + if y == nil { + return nil, fmt.Errorf("error computing Y for compressed point") + } + if y.Bit(0) != ylsb { + y.Neg(y) + y.Mod(y, cp.P) + } + return y, nil +} diff --git a/keys/publicKey_test.go b/keys/publicKey_test.go index 0c0888f..87795f7 100644 --- a/keys/publicKey_test.go +++ b/keys/publicKey_test.go @@ -1,10 +1,13 @@ package keys import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/joeqian10/neo3-gogogo/crypto" "github.com/joeqian10/neo3-gogogo/helper" - "github.com/stretchr/testify/assert" - "testing" ) func TestPublicKeyToScriptHash(t *testing.T) { @@ -25,3 +28,67 @@ func TestPublicKeyToAddress(t *testing.T) { assert.Equal(t, testCase.Address, address) } } + +func TestRecoverPubKeyFromSig(t *testing.T) { + // here is my message + msg := []byte("Hello World") + + // here is my private key + privateKey := []byte{ + 0xde, 0xed, 0xbe, 0xef, 0xde, 0xed, 0xbe, 0xef, + 0xde, 0xed, 0xbe, 0xef, 0xde, 0xed, 0xbe, 0xef, + 0xde, 0xed, 0xbe, 0xef, 0xde, 0xed, 0xbe, 0xef, + 0xde, 0xed, 0xbe, 0xef, 0xde, 0xed, 0xbe, 0xef, + } + + pair, err := NewKeyPair(privateKey) + assert.Nil(t, err) + fmt.Println(pair.PublicKey.String()) + fmt.Println() + + count := 0 + for i := 0; i < 100; i++ { + sig, _ := pair.Sign(msg) + pubKeys, _ := RecoverPubKeyFromSigOnSecp256r1(msg, sig) + match := false + for _, pubKey := range pubKeys { + if pair.PublicKey.String() == pubKey.String() { + match = true + break + } + } + if !match { + //fmt.Println(helper.BytesToHex(sig)) + //fmt.Println(pubKey.String()) + count++ + } + } + + fmt.Println(count) + //sig, err := pair.Sign(msg) + //assert.Nil(t, err) + // + //fmt.Println(helper.BytesToHex(sig)) + // + //pubKey, err := RecoverPubKeyFromSigOnSecp256r1(msg, sig) + //assert.Nil(t, err) + + //assert.Equal(t, pair.PublicKey.String(), pubKey.String()) +} + +func TestVerifySignature3(t *testing.T) { + msg := []byte("Hello World") + sig := helper.HexToBytes("4c2c6fd93dd5ae2feb4857747383b6ebb82bcf8db84cb02a62dc8cc5999958e34add7302209ee9905cf49b365d475967bfcc3ff8a220b5914b83275e76648676") + pubKey, err := crypto.NewECPointFromString("03f6838d75c7bdc8cbe3f79b2ffee75012abb79ef7c9f96dad713903cd420ebab8") + assert.Nil(t, err) + + b := VerifySignature(msg, sig, pubKey) + fmt.Println(b) +} + +//func TestRecoverPubKeyFromSigOnSecp256r1(t *testing.T) { +// msg := []byte("Hello World") +// sig := helper.HexToBytes("4c2c6fd93dd5ae2feb4857747383b6ebb82bcf8db84cb02a62dc8cc5999958e34add7302209ee9905cf49b365d475967bfcc3ff8a220b5914b83275e76648676") +// pubKey, _ := RecoverPubKeyFromSigOnSecp256r1(msg, sig) +// fmt.Println(pubKey.String()) +//} diff --git a/keys/signature.go b/keys/signature.go new file mode 100644 index 0000000..9cdff76 --- /dev/null +++ b/keys/signature.go @@ -0,0 +1,9 @@ +package keys + +import "math/big" + +// Signature is a type representing an ecdsa signature. +type Signature struct { + R *big.Int + S *big.Int +} diff --git a/mpt/stateroot_test.go b/mpt/stateroot_test.go index f4a98d1..3f6cd36 100644 --- a/mpt/stateroot_test.go +++ b/mpt/stateroot_test.go @@ -1,22 +1,23 @@ package mpt import ( - "github.com/joeqian10/neo3-gogogo/rpc/models" - "github.com/stretchr/testify/assert" "testing" + "github.com/stretchr/testify/assert" + "github.com/joeqian10/neo3-gogogo/helper" "github.com/joeqian10/neo3-gogogo/io" + "github.com/joeqian10/neo3-gogogo/rpc/models" ) func TestStateRoot_Serialize(t *testing.T) { sr := StateRoot{ - Version: 0, - Index: 1000, - RootHash: "0x53360e02b03f548c6fc2f74f760d82a6749df6a844fd117ad7b62504390c8f8c", + Version: 0, + Index: 1000, + RootHash: "0x53360e02b03f548c6fc2f74f760d82a6749df6a844fd117ad7b62504390c8f8c", Witnesses: []models.RpcWitness{ { - Invocation: "DEBnIRHFS8tG/6pw4cqZQbOQZri6rboaQPUJTCVS2ZD/HwOZG2m9IG3NJ8E/gTV++o7G1r35l+p5aQcAbqwoP1wTDECeyQcx2M1DP/irLP7sQy/tNRyina2rdK6ATV/QY+Ib4tJ3sYpXaiPx4iGo+AgqUeTRDmD8anfUNtYzjYgos6x9DEDS+medyKx59813WgtCusxLIK0tx50H36tbMGmTUQxR5nHzrpG8nzQ8HKNKRNMgQNBoT4U3pcHMpwJY9bXUge4R", + Invocation: "DEBnIRHFS8tG/6pw4cqZQbOQZri6rboaQPUJTCVS2ZD/HwOZG2m9IG3NJ8E/gTV++o7G1r35l+p5aQcAbqwoP1wTDECeyQcx2M1DP/irLP7sQy/tNRyina2rdK6ATV/QY+Ib4tJ3sYpXaiPx4iGo+AgqUeTRDmD8anfUNtYzjYgos6x9DEDS+medyKx59813WgtCusxLIK0tx50H36tbMGmTUQxR5nHzrpG8nzQ8HKNKRNMgQNBoT4U3pcHMpwJY9bXUge4R", Verification: "EwwhAnIujtkuXxpCUIyfti3TyTtoOhUd/wjLU4lwdHzBhsu6DCECkeyvwoMh29AA30IiQMankxndS3LESLsUGkLoTzfm/doMIQOyS9DtdzAVHs/Ne1yheExdO8NYTw1NkKyi1i4gd5tG1wwhA/sZ1ZOuaNWI9PnKa/3WYbE9xjnVVYWhVYYXFD38xLJWFEF7zmyl", }, }, diff --git a/wallet/nep6Wallet_test.go b/wallet/nep6Wallet_test.go index cd418d2..401a5a2 100644 --- a/wallet/nep6Wallet_test.go +++ b/wallet/nep6Wallet_test.go @@ -2,30 +2,31 @@ package wallet import ( "bytes" - "github.com/joeqian10/neo3-gogogo/crypto" - "github.com/joeqian10/neo3-gogogo/helper" - "github.com/joeqian10/neo3-gogogo/sc" "io/ioutil" "log" "testing" - "github.com/joeqian10/neo3-gogogo/keys" "github.com/stretchr/testify/assert" + + "github.com/joeqian10/neo3-gogogo/crypto" + "github.com/joeqian10/neo3-gogogo/helper" + "github.com/joeqian10/neo3-gogogo/keys" + "github.com/joeqian10/neo3-gogogo/sc" ) var hash = helper.UInt160FromBytes(crypto.Hash160([]byte{0x01})) var testAccount1 = NewNEP6Account(testWallet, hash, nil) -func resetTestWallet() { +func resetTestWallet() { testWallet = &NEP6Wallet{ protocolSettings: &helper.DefaultProtocolSettings, - password: nil, - Name: &dummy, - path: "", - Version: "3.0", - accounts: map[helper.UInt160]NEP6Account{}, - Scrypt: NewScryptParameters(2, 1, 1), - Extra: nil, + password: nil, + Name: &dummy, + path: "", + Version: "3.0", + accounts: map[helper.UInt160]NEP6Account{}, + Scrypt: NewScryptParameters(2, 1, 1), + Extra: nil, } } @@ -34,7 +35,7 @@ func TestNewNEP6Wallet1(t *testing.T) { path := "test.json" wallet, err := NewNEP6Wallet(path, &helper.DefaultProtocolSettings, nil, nil) assert.Nil(t, err) - assert.Equal(t, 1, len(wallet.Accounts)) + assert.Equal(t, 1, len(wallet.Accounts)) jsonBytes, err := wallet.JSON() assert.Nil(t, err) @@ -57,9 +58,9 @@ func TestNewNEP6Wallet(t *testing.T) { } func TestGetPrivateKeyFromNEP2(t *testing.T) { - pk, err := GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2", helper.DefaultAddressVersion,2, 1, 1) + pk, err := GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2", helper.DefaultAddressVersion, 2, 1, 1) assert.NotNil(t, err) - pk, err = GetPrivateKeyFromNEP2(nep2, password, helper.DefaultAddressVersion, 2, 1, 1) + pk, err = GetPrivateKeyFromNEP2(nep2, password, helper.DefaultAddressVersion, 2, 1, 1) assert.Equal(t, true, bytes.Equal(privateKey, pk)) } @@ -94,7 +95,7 @@ func TestNEP6Wallet_Contains(t *testing.T) { resetTestWallet() } -func TestAddAccount(t *testing.T) { +func TestAddAccount(t *testing.T) { _, err := testWallet.CreateAccountWithScriptHash(testScriptHash) assert.Nil(t, err) assert.Equal(t, true, testWallet.Contains(testScriptHash))