├── go.sum ├── go.mod ├── core_test.go ├── .travis.yml ├── tests ├── go.mod ├── p256_sha256_tai.json ├── vrf_benchmark_test.go ├── go.sum ├── vrf_test.go └── secp256_k1_sha256_tai.json ├── .gitignore ├── config.go ├── LICENSE ├── config_test.go ├── README.md ├── vrf.go └── core.go /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/vechain/go-ecvrf 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /core_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 vechain.org. 2 | // Licensed under the MIT license. 3 | 4 | package ecvrf 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.12.x 4 | - 1.13.x 5 | - 1.14.x 6 | os: 7 | - linux 8 | - osx 9 | - windows 10 | sudo: false 11 | script: 12 | - go test ./... 13 | - cd tests && go test ./... 14 | -------------------------------------------------------------------------------- /tests/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/vechain/go-ecvrf/tests 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/btcsuite/btcd v0.20.1-beta 7 | github.com/stretchr/testify v1.5.1 8 | github.com/vechain/go-ecvrf v0.0.0-20200305101714-4252ed3a3b96 9 | ) 10 | 11 | replace github.com/vechain/go-ecvrf => ../ 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | .vscode/ 18 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 vechain.org. 2 | // Licensed under the MIT license. 3 | 4 | package ecvrf 5 | 6 | import ( 7 | "crypto/elliptic" 8 | "hash" 9 | "math/big" 10 | ) 11 | 12 | // Config contains VRF parameters. 13 | type Config struct { 14 | // a single nonzero octet specifying the ECVRF ciphersuite. 15 | SuiteString byte 16 | // number of points on curve divided by group order. 17 | Cofactor byte 18 | // create cryptographic hash function. 19 | NewHasher func() hash.Hash 20 | // function to calculate y^2. 21 | Y2 func(c elliptic.Curve, x *big.Int) *big.Int 22 | // function to calculate square root. 23 | Sqrt func(c elliptic.Curve, s *big.Int) *big.Int 24 | } 25 | 26 | // DefaultSqrt is the default sqrt method. nil is returned if s is not a square. 27 | func DefaultSqrt(c elliptic.Curve, s *big.Int) *big.Int { 28 | var r big.Int 29 | if nil == r.ModSqrt(s, c.Params().P) { 30 | return nil // s is not a square 31 | } 32 | return &r 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 vechain.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 vechain.org. 2 | // Licensed under the MIT license. 3 | 4 | package ecvrf 5 | 6 | import ( 7 | "crypto/elliptic" 8 | "math/big" 9 | "testing" 10 | ) 11 | 12 | func TestDefaultSqrt(t *testing.T) { 13 | type args struct { 14 | c elliptic.Curve 15 | s string 16 | } 17 | tests := []struct { 18 | name string 19 | args args 20 | want string 21 | }{ 22 | { 23 | "p256", 24 | args{ 25 | elliptic.P256(), 26 | "23641628374218637252523134409825450466172496366265976434932954203032325458800", 27 | }, 28 | "108980937802188484198425629766080801309523465968363373048763527645240613153665", 29 | }, 30 | { 31 | "p256 invalid", 32 | args{ 33 | elliptic.P256(), 34 | "23641628374218637252523134409825450466172496366265976434932954203032325458801", 35 | }, 36 | "", 37 | }, 38 | } 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | s := new(big.Int) 42 | s.UnmarshalText([]byte(tt.args.s)) 43 | 44 | got := DefaultSqrt(tt.args.c, s) 45 | gotStr := "" 46 | if got != nil { 47 | gotStr = got.String() 48 | } 49 | 50 | if gotStr != tt.want { 51 | t.Errorf("DefaultSqrt() = %v, want %v", gotStr, tt.want) 52 | } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/p256_sha256_tai.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "sk": "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721", 4 | "pk": "0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6", 5 | "alpha": "73616d706c65", 6 | "pi": "029bdca4cc39e57d97e2f42f88bcf0ecb1120fb67eb408a856050dbfbcbf57c524347fc46ccd87843ec0a9fdc090a407c6fbae8ac1480e240c58854897eabbc3a7bb61b201059f89186e7175af796d65e7", 7 | "beta": "59ca3801ad3e981a88e36880a3aee1df38a0472d5be52d6e39663ea0314e594c" 8 | }, 9 | { 10 | "sk": "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721", 11 | "pk": "0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6", 12 | "alpha": "74657374", 13 | "pi": "03873a1cce2ca197e466cc116bca7b1156fff599be67ea40b17256c4f34ba2549c94ffd2b31588b5fe034fd92c87de5b520b12084da6c4ab63080a7c5467094a1ee84b80b59aca54bba2e2baa0d108191b", 14 | "beta": "dc85c20f95100626eddc90173ab58d5e4f837bb047fb2f72e9a408feae5bc6c1" 15 | }, 16 | { 17 | "sk": "2ca1411a41b17b24cc8c3b089cfd033f1920202a6c0de8abb97df1498d50d2c8", 18 | "pk": "03596375e6ce57e0f20294fc46bdfcfd19a39f8161b58695b3ec5b3d16427c274d", 19 | "alpha": "4578616d706c65206f66204543445341207769746820616e736970323536723120616e64205348412d323536", 20 | "pi": "02abe3ce3b3aa2ab3c6855a7e729517ebfab6901c2fd228f6fa066f15ebc9b9d415a680736f7c33f6c796e367f7b2f467026495907affb124be9711cf0e2d05722d3a33e11d0c5bf932b8f0c5ed1981b64", 21 | "beta": "e880bde34ac5263b2ce5c04626870be2cbff1edcdadabd7d4cb7cbc696467168" 22 | } 23 | ] -------------------------------------------------------------------------------- /tests/vrf_benchmark_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 vechain.org. 2 | // Licensed under the MIT license. 3 | 4 | package tests 5 | 6 | import ( 7 | "crypto/ecdsa" 8 | "crypto/elliptic" 9 | "crypto/rand" 10 | "testing" 11 | 12 | "github.com/btcsuite/btcd/btcec" 13 | "github.com/vechain/go-ecvrf" 14 | ) 15 | 16 | func BenchmarkVRF(b *testing.B) { 17 | b.Run("secp256k1sha256tai-proving", func(b *testing.B) { 18 | sk, _ := ecdsa.GenerateKey(btcec.S256(), rand.Reader) 19 | alpha := []byte("Hello VeChain") 20 | 21 | for i := 0; i < b.N; i++ { 22 | _, _, err := ecvrf.NewSecp256k1Sha256Tai().Prove(sk, alpha) 23 | if err != nil { 24 | b.Fatal(err) 25 | } 26 | } 27 | }) 28 | b.Run("secp256k1sha256tai-verifying", func(b *testing.B) { 29 | sk, _ := ecdsa.GenerateKey(btcec.S256(), rand.Reader) 30 | alpha := []byte("Hello VeChain") 31 | 32 | _, pi, _ := ecvrf.NewSecp256k1Sha256Tai().Prove(sk, alpha) 33 | for i := 0; i < b.N; i++ { 34 | _, err := ecvrf.NewSecp256k1Sha256Tai().Verify(&sk.PublicKey, alpha, pi) 35 | if err != nil { 36 | b.Fatal(err) 37 | } 38 | } 39 | }) 40 | b.Run("p256sha256tai-proving", func(b *testing.B) { 41 | sk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 42 | alpha := []byte("Hello VeChain") 43 | 44 | for i := 0; i < b.N; i++ { 45 | _, _, err := ecvrf.NewP256Sha256Tai().Prove(sk, alpha) 46 | if err != nil { 47 | b.Fatal(err) 48 | } 49 | } 50 | }) 51 | b.Run("p256sha256tai-verifying", func(b *testing.B) { 52 | sk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 53 | alpha := []byte("Hello VeChain") 54 | 55 | _, pi, _ := ecvrf.NewP256Sha256Tai().Prove(sk, alpha) 56 | for i := 0; i < b.N; i++ { 57 | _, err := ecvrf.NewP256Sha256Tai().Verify(&sk.PublicKey, alpha, pi) 58 | if err != nil { 59 | b.Fatal(err) 60 | } 61 | } 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /tests/go.sum: -------------------------------------------------------------------------------- 1 | github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 2 | github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= 3 | github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= 4 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 5 | github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 6 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= 7 | github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= 8 | github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 9 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= 10 | github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 11 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 13 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 15 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 16 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 17 | github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 18 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 19 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 20 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 21 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 22 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 23 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 24 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 25 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 26 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 27 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 28 | golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 29 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 30 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 31 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 32 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 33 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 34 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 35 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 36 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 37 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 38 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 39 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-ecvrf 2 | 3 | [![GoDoc Reference](https://godoc.org/github.com/vechain/go-ecvrf?status.svg)](https://pkg.go.dev/github.com/vechain/go-ecvrf) 4 | [![Travis](https://travis-ci.org/vechain/go-ecvrf.svg?branch=master)](https://travis-ci.org/vechain/go-ecvrf) 5 | [![License](https://img.shields.io/github/license/vechain/go-ecvrf)](https://github.com/vechain/go-ecvrf/blob/master/LICENSE) 6 | 7 | Zero-dependency Golang implementation of Elliptic Curve Verifiable Random Function (VRF) follows [draft-irtf-cfrg-vrf-06](https://tools.ietf.org/id/draft-irtf-cfrg-vrf-06.html) and [RFC 6979](https://tools.ietf.org/html/rfc6979). 8 | 9 | # What's VRF 10 | 11 | A Verifiable Random Function (VRF) is the public-key version of a keyed cryptographic hash. Only the holder of the private key can compute the hash, but anyone with public key can verify the correctness of the hash. 12 | 13 | A key application of the VRF is to provide privacy against offline enumeration (e.g. dictionary attacks) on data stored in a hash-based data structure. In this application, a Prover holds the VRF private key and uses the VRF hashing to construct a hash-based data structure on the input data. Due to the nature of the VRF, only the Prover can answer queries about whether or not some data is stored in the data structure. Anyone who knows the public VRF key can verify that the Prover has answered the queries correctly. However no offline inferences (i.e. inferences without querying the Prover) can be made about the data stored in the data strucuture. 14 | 15 | # Installation 16 | 17 | ``` 18 | go get -u github.com/vechain/go-ecvrf 19 | ``` 20 | 21 | # Examples 22 | 23 | Using SECP256K1_SHA256_TAI cipher suite: 24 | 25 | * VRF Proving 26 | 27 | ```golang 28 | // the private key 29 | var sk *ecdsa.PrivateKey 30 | // code to load sk 31 | // ... 32 | 33 | // the input to be hashed by the VRF 34 | alpha := "Hello VeChain" 35 | 36 | // `beta`: the VRF hash output 37 | // `pi`: the VRF proof 38 | beta, pi, err := ecvrf.NewSecp256k1Sha256Tai().Prove(sk, []byte(alpha)) 39 | if err != nil { 40 | // something wrong. 41 | // most likely sk is not properly loaded. 42 | return 43 | } 44 | ``` 45 | 46 | * VRF Verifying 47 | 48 | ```golang 49 | // the public key 50 | var pk *ecdsa.PublicKey 51 | // code to load pk 52 | // ... 53 | 54 | // the input to be hashed by the VRF 55 | alpha := "Hello VeChain" 56 | 57 | // `pi` is the VRF proof 58 | beta, err := ecvrf.NewSecp256k1Sha256Tai().Verify(pk, []byte(alpha), pi) 59 | if err != nil { 60 | // invalid proof 61 | return 62 | } 63 | 64 | // got correct beta 65 | ``` 66 | 67 | 68 | # Supported Cipher Suites 69 | 70 | * P256_SHA256_TAI 71 | * SECP256K1_SHA256_TAI 72 | 73 | It's easy to extends this library to use different Weierstrass curves and Hash algorithms, by providing cooked `Config` like: 74 | 75 | ```golang 76 | // the following codes build a new P256_SHA256_TAI VRF object. 77 | vrf := ecvrf.New(&ecvrf.Config{ 78 | SuiteString: 0x01, 79 | Cofactor: 0x01, 80 | NewHasher: sha256.New, 81 | Y2: func(c elliptic.Curve, x *big.Int) *big.Int { 82 | // y² = x³ - 3x + b 83 | x3 := new(big.Int).Mul(x, x) 84 | x3.Mul(x3, x) 85 | 86 | threeX := new(big.Int).Lsh(x, 1) 87 | threeX.Add(threeX, x) 88 | 89 | x3.Sub(x3, threeX) 90 | x3.Add(x3, c.Params().B) 91 | x3.Mod(x3, c.Params().P) 92 | return x3 93 | }, 94 | Sqrt: ecvrf.DefaultSqrt, 95 | }) 96 | ``` 97 | 98 | # Benchmark 99 | 100 | On quad-core i5 13" macbook pro 2018 101 | 102 | ``` 103 | goos: darwin 104 | goarch: amd64 105 | pkg: github.com/vechain/go-ecvrf/tests 106 | BenchmarkVRF 107 | BenchmarkVRF/secp256k1sha256tai-proving 108 | BenchmarkVRF/secp256k1sha256tai-proving-8 2198 598180 ns/op 16680 B/op 604 allocs/op 109 | BenchmarkVRF/secp256k1sha256tai-verifying 110 | BenchmarkVRF/secp256k1sha256tai-verifying-8 1587 739716 ns/op 14214 B/op 406 allocs/op 111 | BenchmarkVRF/p256sha256tai-proving 112 | BenchmarkVRF/p256sha256tai-proving-8 4887 263843 ns/op 9509 B/op 243 allocs/op 113 | BenchmarkVRF/p256sha256tai-verifying 114 | BenchmarkVRF/p256sha256tai-verifying-8 3172 507240 ns/op 17636 B/op 428 allocs/op 115 | PASS 116 | ok github.com/vechain/go-ecvrf/tests 5.668s 117 | Success: Benchmarks passed. 118 | ``` 119 | 120 | # References 121 | 122 | * [draft-irtf-cfrg-vrf-06](https://tools.ietf.org/id/draft-irtf-cfrg-vrf-06.html) 123 | * [RFC 6979](https://tools.ietf.org/html/rfc6979) 124 | * [witnet/vrf-rs](https://github.com/witnet/vrf-rs) 125 | * [google/keytransparency](https://github.com/google/keytransparency) 126 | 127 | # License 128 | 129 | Copyright (c) 2020 vechain.org. 130 | Licensed under the MIT license. 131 | 132 | 133 | -------------------------------------------------------------------------------- /vrf.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 vechain.org. 2 | // Licensed under the MIT license. 3 | 4 | // Package ecvrf is the Elliptic Curve Verifiable Random Function (VRF) library. 5 | package ecvrf 6 | 7 | import ( 8 | "crypto/ecdsa" 9 | "crypto/elliptic" 10 | "crypto/sha256" 11 | "errors" 12 | "math/big" 13 | ) 14 | 15 | // VRF is the interface that wraps VRF methods. 16 | type VRF interface { 17 | // Prove constructs a VRF proof `pi` for the given input `alpha`, 18 | // using the private key `sk`. The hash output is returned as `beta`. 19 | Prove(sk *ecdsa.PrivateKey, alpha []byte) (beta, pi []byte, err error) 20 | 21 | // Verify checks the proof `pi` of the message `alpha` against the given 22 | // public key `pk`. The hash output is returned as `beta`. 23 | Verify(pk *ecdsa.PublicKey, alpha, pi []byte) (beta []byte, err error) 24 | } 25 | 26 | // New creates and initializes a VRF object using customized config. 27 | func New(cfg *Config) VRF { 28 | return &vrf{func(c elliptic.Curve) *core { 29 | return &core{Config: cfg, curve: c} 30 | }} 31 | } 32 | 33 | // NewSecp256k1Sha256Tai creates the VRF object configured with secp256k1/SHA256 and hash_to_curve_try_and_increment algorithm. 34 | func NewSecp256k1Sha256Tai() VRF { 35 | return New(&Config{ 36 | SuiteString: 0xfe, 37 | Cofactor: 0x01, 38 | NewHasher: sha256.New, 39 | Y2: func(c elliptic.Curve, x *big.Int) *big.Int { 40 | // y² = x³ + b 41 | x3 := new(big.Int).Mul(x, x) 42 | x3.Mul(x3, x) 43 | 44 | x3.Add(x3, c.Params().B) 45 | x3.Mod(x3, c.Params().P) 46 | return x3 47 | }, 48 | Sqrt: DefaultSqrt, 49 | }) 50 | } 51 | 52 | // NewP256Sha256Tai creates the VRF object configured with P256/SHA256 and hash_to_curve_try_and_increment algorithm. 53 | func NewP256Sha256Tai() VRF { 54 | return New(&Config{ 55 | SuiteString: 0x01, 56 | Cofactor: 0x01, 57 | NewHasher: sha256.New, 58 | Y2: func(c elliptic.Curve, x *big.Int) *big.Int { 59 | // y² = x³ - 3x + b 60 | x3 := new(big.Int).Mul(x, x) 61 | x3.Mul(x3, x) 62 | 63 | threeX := new(big.Int).Lsh(x, 1) 64 | threeX.Add(threeX, x) 65 | 66 | x3.Sub(x3, threeX) 67 | x3.Add(x3, c.Params().B) 68 | x3.Mod(x3, c.Params().P) 69 | return x3 70 | }, 71 | Sqrt: DefaultSqrt, 72 | }) 73 | } 74 | 75 | type vrf struct { 76 | newCore func(c elliptic.Curve) *core 77 | } 78 | 79 | // Prove constructs VRF proof following [draft-irtf-cfrg-vrf-06 section 5.1](https://tools.ietf.org/id/draft-irtf-cfrg-vrf-06.html#rfc.section.5.1). 80 | func (v *vrf) Prove(sk *ecdsa.PrivateKey, alpha []byte) (beta, pi []byte, err error) { 81 | var ( 82 | core = v.newCore(sk.Curve) 83 | q = core.Q() 84 | ) 85 | // step 1 is done by the caller. 86 | 87 | // step 2: H = ECVRF_hash_to_curve(suite_string, Y, alpha_string) 88 | // currently, try_and_increment algorithm is supported 89 | H, err := core.HashToCurveTryAndIncrement(&point{sk.X, sk.Y}, alpha) 90 | if err != nil { 91 | return 92 | } 93 | 94 | // step 3: h_string = point_to_string(H) 95 | hbytes := core.Marshal(H) 96 | 97 | // step 4: Gamma = x * H 98 | gamma := core.ScalarMult(H, sk.D.Bytes()) 99 | 100 | // step 5: k = ECVRF_nonce_generation(SK, h_string) 101 | // it follows RFC6979 102 | k := rfc6979nonce(sk.D, hbytes, core.Q(), core.NewHasher) 103 | kbytes := k.Bytes() 104 | 105 | // step 6: c = ECVRF_hash_points(H, Gamma, k*B, k*H) 106 | kB := core.ScalarBaseMult(kbytes) 107 | kH := core.ScalarMult(H, kbytes) 108 | c := core.HashPoints( 109 | H, 110 | gamma, 111 | kB, 112 | kH) 113 | 114 | // step 7: s = (k + c*x) mod q 115 | s := new(big.Int).Mul(c, sk.D) 116 | s.Add(s, k) 117 | s.Mod(s, q) 118 | 119 | // step 8: encode (gamma, c, s) as pi_string = point_to_string(Gamma) || int_to_string(c, n) || int_to_string(s, qLen) 120 | pi = core.EncodeProof(gamma, c, s) 121 | 122 | // step 9: Output pi_string 123 | // here also returns beta 124 | beta = core.GammaToHash(gamma) 125 | return 126 | } 127 | 128 | // Verify checks the correctness of proof following [draft-irtf-cfrg-vrf-06 section 5.3](https://tools.ietf.org/id/draft-irtf-cfrg-vrf-06.html#rfc.section.5.3). 129 | func (v *vrf) Verify(pk *ecdsa.PublicKey, alpha, pi []byte) (beta []byte, err error) { 130 | core := v.newCore(pk.Curve) 131 | 132 | // step 1: D = ECVRF_decode_proof(pi_string) 133 | gamma, c, s, err := core.DecodeProof(pi) 134 | 135 | // step 2: If D is "INVALID", output "INVALID" and stop 136 | if err != nil { 137 | return 138 | } 139 | // step 3: (Gamma, c, s) = D 140 | 141 | // step 4: H = ECVRF_hash_to_curve(suite_string, Y, alpha_string) 142 | H, err := core.HashToCurveTryAndIncrement(&point{pk.X, pk.Y}, alpha) 143 | if err != nil { 144 | return 145 | } 146 | 147 | // step 5: U = s*B - c*Y 148 | sB := core.ScalarBaseMult(s.Bytes()) 149 | cY := core.ScalarMult(&point{pk.X, pk.Y}, c.Bytes()) 150 | U := core.Sub(sB, cY) 151 | 152 | // step 6: V = s*H - c*Gamma 153 | sH := core.ScalarMult(H, s.Bytes()) 154 | cGamma := core.ScalarMult(gamma, c.Bytes()) 155 | V := core.Sub(sH, cGamma) 156 | 157 | // step 7: c' = ECVRF_hash_points(H, Gamma, U, V) 158 | derivedC := core.HashPoints(H, gamma, U, V) 159 | 160 | // step 8: If c and c' are equal, output ("VALID", ECVRF_proof_to_hash(pi_string)); else output "INVALID" 161 | if derivedC.Cmp(c) != 0 { 162 | err = errors.New("invalid proof") 163 | return 164 | } 165 | 166 | beta = core.GammaToHash(gamma) 167 | return 168 | } 169 | -------------------------------------------------------------------------------- /core.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 vechain.org. 2 | // Licensed under the MIT license. 3 | 4 | package ecvrf 5 | 6 | import ( 7 | "bytes" 8 | "crypto/elliptic" 9 | "crypto/hmac" 10 | "errors" 11 | "hash" 12 | "math/big" 13 | ) 14 | 15 | type point struct { 16 | X, Y *big.Int 17 | } 18 | 19 | type core struct { 20 | *Config 21 | curve elliptic.Curve 22 | cachedHasher hash.Hash 23 | } 24 | 25 | // Q returns prime order of large prime order subgroup. 26 | func (c *core) Q() *big.Int { 27 | return c.curve.Params().N 28 | } 29 | 30 | // N return half of length, in octets, of a field element in F, rounded up to the nearest even integer 31 | func (c *core) N() int { 32 | return ((c.curve.Params().P.BitLen()+1)/2 + 7) / 8 33 | } 34 | 35 | func (c *core) getCachedHasher() hash.Hash { 36 | if c.cachedHasher != nil { 37 | return c.cachedHasher 38 | } 39 | c.cachedHasher = c.NewHasher() 40 | return c.cachedHasher 41 | } 42 | 43 | // Marshal marshals a point into compressed form specified in section 4.3.6 of ANSI X9.62. 44 | // It's the alias of `point_to_string` specified in [draft-irtf-cfrg-vrf-06 section 5.5](https://tools.ietf.org/id/draft-irtf-cfrg-vrf-06.html#rfc.section.5.5). 45 | func (c *core) Marshal(pt *point) []byte { 46 | byteLen := (c.curve.Params().BitSize + 7) / 8 47 | out := make([]byte, byteLen+1) 48 | 49 | // compress format, 3 for odd y 50 | out[0] = 2 + byte(pt.Y.Bit(0)) 51 | 52 | bytes := pt.X.Bytes() 53 | 54 | if n := len(bytes); byteLen > n { 55 | copy(out[1+byteLen-n:], bytes) 56 | } else { 57 | copy(out[1:], bytes) 58 | } 59 | return out 60 | } 61 | 62 | // Unmarshal unmarshals a compressed point in the form specified in section 4.3.6 of ANSI X9.62. 63 | // It's the alias of `string_to_point` specified in [draft-irtf-cfrg-vrf-06 section 5.5](https://tools.ietf.org/id/draft-irtf-cfrg-vrf-06.html#rfc.section.5.5). 64 | // This is borrowed from the project https://github.com/google/keytransparency. 65 | func (c *core) Unmarshal(in []byte) (*point, error) { 66 | byteLen := (c.curve.Params().BitSize + 7) / 8 67 | if (in[0] &^ 1) != 2 { 68 | return nil, errors.New("unrecognized point encoding") 69 | } 70 | if len(in) != 1+byteLen { 71 | return nil, errors.New("invalid point data length") 72 | } 73 | // Based on Routine 2.2.4 in NIST Mathematical routines paper 74 | p := c.curve.Params().P 75 | x := new(big.Int).SetBytes(in[1 : 1+byteLen]) 76 | y2 := c.Y2(c.curve, x) 77 | 78 | y := c.Sqrt(c.curve, y2) 79 | if y == nil { 80 | return nil, errors.New("invalid point: y^2 is not a squire") 81 | } 82 | 83 | var y2c big.Int 84 | y2c.Mul(y, y).Mod(&y2c, p) 85 | if y2c.Cmp(y2) != 0 { 86 | return nil, errors.New("invalid point: sqrt(y2)^2 != y2") 87 | } 88 | 89 | if y.Bit(0) != uint(in[0]&1) { 90 | y.Sub(p, y) 91 | } 92 | 93 | // valid point: return it 94 | return &point{x, y}, nil 95 | } 96 | 97 | func (c *core) ScalarMult(pt *point, k []byte) *point { 98 | x, y := c.curve.ScalarMult(pt.X, pt.Y, k) 99 | return &point{x, y} 100 | } 101 | 102 | func (c *core) ScalarBaseMult(k []byte) *point { 103 | x, y := c.curve.ScalarBaseMult(k) 104 | return &point{x, y} 105 | } 106 | 107 | func (c *core) Add(pt1, pt2 *point) *point { 108 | x, y := c.curve.Add(pt1.X, pt1.Y, pt2.X, pt2.Y) 109 | return &point{x, y} 110 | } 111 | 112 | func (c *core) Sub(pt1, pt2 *point) *point { 113 | // pt1 - pt2 = pt1 + invert(pt2), 114 | // where invert(pt2) = (x2, P - y2) 115 | x, y := c.curve.Add( 116 | pt1.X, pt1.Y, 117 | pt2.X, new(big.Int).Sub(c.curve.Params().P, pt2.Y)) 118 | return &point{x, y} 119 | } 120 | 121 | // HashToCurveTryAndIncrement takes in the VRF input `alpha` and converts it to H, using the try_and_increment algorithm. 122 | // See: [draft-irtf-cfrg-vrf-06 section 5.4.1.1](https://tools.ietf.org/id/draft-irtf-cfrg-vrf-06.html#rfc.section.5.4.1.1). 123 | func (c *core) HashToCurveTryAndIncrement(pk *point, alpha []byte) (H *point, err error) { 124 | hasher := c.getCachedHasher() 125 | hash := make([]byte, 1+hasher.Size()) 126 | hash[0] = 2 // compress format 127 | 128 | // step 1: ctr = 0 129 | ctr := 0 130 | 131 | // step 2: PK_string = point_to_string(Y) 132 | pkBytes := c.Marshal(pk) 133 | 134 | // step 3 ~ 6 135 | prefix := []byte{c.SuiteString, 0x01} 136 | suffix := []byte{0} 137 | for ; ctr < 256; ctr++ { 138 | // hash_string = Hash(suite_string || one_string || PK_string || alpha_string || ctr_string) 139 | suffix[0] = byte(ctr) 140 | hasher.Reset() 141 | hasher.Write(prefix) 142 | hasher.Write(pkBytes) 143 | hasher.Write(alpha) 144 | hasher.Write(suffix) 145 | // apppend right after compress format 146 | hasher.Sum(hash[1:1]) 147 | 148 | // H = arbitrary_string_to_point(hash_string) 149 | if H, err = c.Unmarshal(hash); err == nil { 150 | if c.Cofactor > 1 { 151 | // If H is not "INVALID" and cofactor > 1, set H = cofactor * H 152 | H = c.ScalarMult(H, []byte{c.Cofactor}) 153 | } 154 | return H, nil 155 | } 156 | } 157 | return nil, errors.New("no valid point found") 158 | } 159 | 160 | // See: [draft-irtf-cfrg-vrf-06 section 5.4.3](https://tools.ietf.org/id/draft-irtf-cfrg-vrf-06.html#rfc.section.5.4.3) 161 | func (c *core) HashPoints(points ...*point) *big.Int { 162 | hasher := c.getCachedHasher() 163 | hasher.Reset() 164 | 165 | hasher.Write([]byte{c.SuiteString, 0x2}) 166 | for _, pt := range points { 167 | hasher.Write(c.Marshal(pt)) 168 | } 169 | return bits2int(hasher.Sum(nil), c.N()*8) 170 | } 171 | 172 | func (c *core) GammaToHash(gamma *point) []byte { 173 | gammaCof := c.ScalarMult(gamma, []byte{c.Cofactor}) 174 | hasher := c.getCachedHasher() 175 | hasher.Reset() 176 | hasher.Write([]byte{c.SuiteString, 0x03}) 177 | hasher.Write(c.Marshal(gammaCof)) 178 | return hasher.Sum(nil) 179 | } 180 | 181 | func (c *core) EncodeProof(gamma *point, C, S *big.Int) []byte { 182 | gammaBytes := c.Marshal(gamma) 183 | 184 | cbytes := int2octets(C, c.N()) 185 | sbytes := int2octets(S, (c.Q().BitLen()+7)/8) 186 | 187 | return append(append(gammaBytes, cbytes...), sbytes...) 188 | } 189 | 190 | // See: [draft-irtf-cfrg-vrf-06 section 5.4.4](https://tools.ietf.org/id/draft-irtf-cfrg-vrf-06.html#rfc.section.5.4.4) 191 | func (c *core) DecodeProof(pi []byte) (gamma *point, C, S *big.Int, err error) { 192 | var ( 193 | ptlen = (c.curve.Params().BitSize+7)/8 + 1 194 | clen = c.N() 195 | slen = (c.Q().BitLen() + 7) / 8 196 | ) 197 | if len(pi) != ptlen+clen+slen { 198 | err = errors.New("invalid proof length") 199 | return 200 | } 201 | 202 | if gamma, err = c.Unmarshal(pi[:ptlen]); err != nil { 203 | return 204 | } 205 | 206 | C = new(big.Int).SetBytes(pi[ptlen : ptlen+clen]) 207 | S = new(big.Int).SetBytes(pi[ptlen+clen:]) 208 | return 209 | } 210 | 211 | // https://tools.ietf.org/html/rfc6979#section-2.3.2 212 | func bits2int(in []byte, qlen int) *big.Int { 213 | out := new(big.Int).SetBytes(in) 214 | if inlen := len(in) * 8; inlen > qlen { 215 | return out.Rsh(out, uint(inlen-qlen)) 216 | } 217 | return out 218 | } 219 | 220 | // https://tools.ietf.org/html/rfc6979#section-2.3.3 221 | func int2octets(v *big.Int, rolen int) []byte { 222 | var ( 223 | out = v.Bytes() 224 | outlen = len(out) 225 | ) 226 | 227 | // left pad with zeros if it's too short 228 | if rolen > outlen { 229 | out2 := make([]byte, rolen) 230 | copy(out2[rolen-outlen:], out) 231 | return out2 232 | } 233 | 234 | // drop most significant bytes if it's too long 235 | return out[outlen-rolen:] 236 | } 237 | 238 | // https://tools.ietf.org/html/rfc6979#section-2.3.4 239 | func bits2octets(in []byte, q *big.Int, rolen int) []byte { 240 | z1 := bits2int(in, q.BitLen()) 241 | z2 := new(big.Int).Sub(z1, q) 242 | if z2.Sign() < 0 { 243 | return int2octets(z1, rolen) 244 | } 245 | return int2octets(z2, rolen) 246 | } 247 | 248 | // rfc6979nonce generates nonce according to [RFC6979](https://tools.ietf.org/html/rfc6979). 249 | func rfc6979nonce( 250 | sk *big.Int, 251 | m []byte, 252 | q *big.Int, 253 | newHasher func() hash.Hash, 254 | ) *big.Int { 255 | var ( 256 | qlen = q.BitLen() 257 | rolen = (qlen + 7) / 8 258 | hasher = newHasher() 259 | ) 260 | 261 | // Step A 262 | // Process m through the hash function H, yielding: 263 | // h1 = H(m) 264 | // (h1 is a sequence of hlen bits). 265 | hasher.Write(m) 266 | h1 := hasher.Sum(nil) 267 | hlen := len(h1) 268 | 269 | bx := int2octets(sk, rolen) 270 | bh := bits2octets(h1, q, rolen) 271 | 272 | // Step B 273 | // Set: 274 | // V = 0x01 0x01 0x01 ... 0x01 275 | v := bytes.Repeat([]byte{1}, hlen) 276 | 277 | // Step C 278 | // Set: 279 | // K = 0x00 0x00 0x00 ... 0x00 280 | k := make([]byte, hlen) 281 | 282 | // Step D ~ G 283 | for i := 0; i < 2; i++ { 284 | // Set: 285 | // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1)) 286 | mac := hmac.New(newHasher, k) 287 | mac.Write(v) 288 | mac.Write([]byte{byte(i)}) // internal octet 289 | mac.Write(bx) 290 | mac.Write(bh) 291 | mac.Sum(k[:0]) 292 | 293 | // Set: 294 | // V = HMAC_K(V) 295 | mac = hmac.New(newHasher, k) 296 | mac.Write(v) 297 | mac.Sum(v[:0]) 298 | } 299 | 300 | // Step H 301 | for { 302 | // Step H1 303 | var t []byte 304 | 305 | // Step H2 306 | mac := hmac.New(newHasher, k) 307 | for len(t)*8 < qlen { 308 | mac.Write(v) 309 | mac.Sum(v[:0]) 310 | mac.Reset() 311 | 312 | t = append(t, v...) 313 | } 314 | 315 | // Step H3 316 | secret := bits2int(t, qlen) 317 | if secret.Sign() > 0 && secret.Cmp(q) < 0 { 318 | return secret 319 | } 320 | mac.Write(v) 321 | mac.Write([]byte{0x00}) 322 | mac.Sum(k[:0]) 323 | 324 | mac = hmac.New(newHasher, k) 325 | mac.Write(v) 326 | mac.Sum(v[:0]) 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /tests/vrf_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 vechain.org. 2 | // Licensed under the MIT license. 3 | 4 | package tests 5 | 6 | import ( 7 | "bytes" 8 | "crypto/ecdsa" 9 | "crypto/elliptic" 10 | "encoding/hex" 11 | "encoding/json" 12 | "io/ioutil" 13 | "math/big" 14 | "math/rand" 15 | "os" 16 | "reflect" 17 | "testing" 18 | "testing/quick" 19 | 20 | "github.com/btcsuite/btcd/btcec" 21 | "github.com/vechain/go-ecvrf" 22 | ) 23 | 24 | // Case Testing cases structure. 25 | type Case struct { 26 | Sk string `json:"sk"` 27 | Pk string `json:"pk"` 28 | Alpha string `json:"alpha"` 29 | Pi string `json:"pi"` 30 | Beta string `json:"beta"` 31 | } 32 | 33 | func readCases(fileName string) ([]Case, error) { 34 | jsonFile, err := os.Open(fileName) 35 | if err != nil { 36 | return nil, err 37 | } 38 | defer jsonFile.Close() 39 | 40 | byteValue, err2 := ioutil.ReadAll(jsonFile) 41 | if err2 != nil { 42 | return nil, err2 43 | } 44 | 45 | var cases = make([]Case, 0) 46 | err3 := json.Unmarshal(byteValue, &cases) 47 | if err3 != nil { 48 | return cases, err3 49 | } 50 | 51 | return cases, nil 52 | } 53 | 54 | func Test_Secp256K1Sha256Tai_vrf_Prove(t *testing.T) { 55 | // Know Correct cases. 56 | var cases, _ = readCases("./secp256_k1_sha256_tai.json") 57 | 58 | type Test struct { 59 | name string 60 | sk *ecdsa.PrivateKey 61 | alpha []byte 62 | wantBeta []byte 63 | wantPi []byte 64 | wantErr bool 65 | } 66 | 67 | tests := []Test{} 68 | for _, c := range cases { 69 | skBytes, _ := hex.DecodeString(c.Sk) 70 | sk, _ := btcec.PrivKeyFromBytes(btcec.S256(), skBytes) 71 | 72 | alpha, _ := hex.DecodeString(c.Alpha) 73 | wantBeta, _ := hex.DecodeString(c.Beta) 74 | wantPi, _ := hex.DecodeString(c.Pi) 75 | 76 | tests = append(tests, Test{ 77 | c.Sk, 78 | sk.ToECDSA(), 79 | alpha, 80 | wantBeta, 81 | wantPi, 82 | false, 83 | }) 84 | } 85 | 86 | vrf := ecvrf.NewSecp256k1Sha256Tai() 87 | 88 | for _, tt := range tests { 89 | t.Run(tt.name, func(t *testing.T) { 90 | v := vrf 91 | gotBeta, gotPi, err := v.Prove(tt.sk, tt.alpha) 92 | if (err != nil) != tt.wantErr { 93 | t.Errorf("vrf.Prove() error = %v, wantErr %v", err, tt.wantErr) 94 | return 95 | } 96 | if !reflect.DeepEqual(gotBeta, tt.wantBeta) { 97 | t.Errorf("vrf.Prove() gotBeta = %v, want %v", hex.EncodeToString(gotBeta), hex.EncodeToString(tt.wantBeta)) 98 | } 99 | if !reflect.DeepEqual(gotPi, tt.wantPi) { 100 | t.Errorf("vrf.Prove() gotPi = %v, want %v", hex.EncodeToString(gotPi), hex.EncodeToString(tt.wantPi)) 101 | } 102 | }) 103 | } 104 | } 105 | 106 | func Test_Secp256K1Sha256Tai_vrf_Verify(t *testing.T) { 107 | // Know Correct cases. 108 | var cases, _ = readCases("./secp256_k1_sha256_tai.json") 109 | 110 | type Test struct { 111 | name string 112 | pk *ecdsa.PublicKey 113 | alpha []byte 114 | pi []byte 115 | wantBeta []byte 116 | wantErr bool 117 | } 118 | 119 | tests := []Test{} 120 | for _, c := range cases { 121 | skBytes, _ := hex.DecodeString(c.Sk) 122 | sk, _ := btcec.PrivKeyFromBytes(btcec.S256(), skBytes) 123 | 124 | pk := sk.PubKey().ToECDSA() 125 | 126 | alpha, _ := hex.DecodeString(c.Alpha) 127 | 128 | wantPi, _ := hex.DecodeString(c.Pi) 129 | 130 | wantBeta, _ := hex.DecodeString(c.Beta) 131 | 132 | tests = append(tests, Test{ 133 | c.Alpha, 134 | pk, 135 | alpha, 136 | wantPi, 137 | wantBeta, 138 | false, 139 | }) 140 | } 141 | 142 | vrf := ecvrf.NewSecp256k1Sha256Tai() 143 | 144 | for _, tt := range tests { 145 | t.Run(tt.name, func(t *testing.T) { 146 | v := vrf 147 | gotBeta, err := v.Verify(tt.pk, tt.alpha, tt.pi) 148 | if (err != nil) != tt.wantErr { 149 | t.Errorf("vrf.Verify() error = %v, wantErr %v", err, tt.wantErr) 150 | return 151 | } 152 | if !reflect.DeepEqual(gotBeta, tt.wantBeta) { 153 | t.Errorf("vrf.Verify() = %v, want %v", gotBeta, tt.wantBeta) 154 | } 155 | }) 156 | } 157 | } 158 | 159 | func Test_Secp256K1Sha256Tai_vrf_Verify_bad_message(t *testing.T) { 160 | type Test struct { 161 | name string 162 | pk *ecdsa.PublicKey 163 | alpha []byte 164 | pi []byte 165 | wantBeta []byte 166 | wantErr bool 167 | } 168 | 169 | // sk 170 | skBytes, _ := hex.DecodeString("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721") 171 | sk, _ := btcec.PrivKeyFromBytes(btcec.S256(), skBytes) 172 | 173 | // pk 174 | pk := sk.PubKey().ToECDSA() 175 | 176 | // correct alpha 177 | // alpha, _ := hex.DecodeString("73616d706c65") 178 | wrongAlpha := []byte("Hello VeChain") 179 | // pi 180 | wantPi, _ := hex.DecodeString("031f4dbca087a1972d04a07a779b7df1caa99e0f5db2aa21f3aecc4f9e10e85d08748c9fbe6b95d17359707bfb8e8ab0c93ba0c515333adcb8b64f372c535e115ccf66ebf5abe6fadb01b5efb37c0a0ec9") 181 | 182 | // beta 183 | wantBeta, _ := hex.DecodeString("612065e309e937ef46c2ef04d5886b9c6efd2991ac484ec64a9b014366fc5d81") 184 | 185 | // test case 186 | tt := Test{ 187 | "bad_message", 188 | pk, 189 | wrongAlpha, 190 | wantPi, 191 | wantBeta, 192 | true, 193 | } 194 | 195 | vrf := ecvrf.NewSecp256k1Sha256Tai() 196 | 197 | t.Run(tt.name, func(t *testing.T) { 198 | v := vrf 199 | _, err := v.Verify(tt.pk, tt.alpha, tt.pi) 200 | if (err != nil) != tt.wantErr { 201 | t.Errorf("vrf.Verify() error = %v, wantErr %v", err, tt.wantErr) 202 | return 203 | } 204 | }) 205 | 206 | } 207 | 208 | func Test_P256Sha256Tai_vrf_Prove(t *testing.T) { 209 | // Know Correct cases. 210 | var P256Sha256TaiCases, _ = readCases("./p256_sha256_tai.json") 211 | 212 | type Test struct { 213 | name string 214 | sk *ecdsa.PrivateKey 215 | alpha []byte 216 | wantBeta []byte 217 | wantPi []byte 218 | wantErr bool 219 | } 220 | 221 | tests := []Test{} 222 | for _, c := range P256Sha256TaiCases { 223 | skBytes, _ := hex.DecodeString(c.Sk) 224 | curve := elliptic.P256() 225 | pkX, pkY := curve.ScalarBaseMult(skBytes) 226 | sk := &ecdsa.PrivateKey{ 227 | PublicKey: ecdsa.PublicKey{ 228 | Curve: curve, 229 | X: pkX, 230 | Y: pkY, 231 | }, 232 | D: new(big.Int).SetBytes(skBytes), 233 | } 234 | alpha, _ := hex.DecodeString(c.Alpha) 235 | wantBeta, _ := hex.DecodeString(c.Beta) 236 | wantPi, _ := hex.DecodeString(c.Pi) 237 | 238 | tests = append(tests, Test{ 239 | c.Alpha, 240 | sk, 241 | alpha, 242 | wantBeta, 243 | wantPi, 244 | false, 245 | }) 246 | } 247 | 248 | vrf := ecvrf.NewP256Sha256Tai() 249 | 250 | for _, tt := range tests { 251 | t.Run(tt.name, func(t *testing.T) { 252 | v := vrf 253 | gotBeta, gotPi, err := v.Prove(tt.sk, tt.alpha) 254 | if (err != nil) != tt.wantErr { 255 | t.Errorf("vrf.Prove() error = %v, wantErr %v", err, tt.wantErr) 256 | return 257 | } 258 | if !reflect.DeepEqual(gotBeta, tt.wantBeta) { 259 | t.Errorf("vrf.Prove() gotBeta = %v, want %v", gotBeta, tt.wantBeta) 260 | } 261 | if !reflect.DeepEqual(gotPi, tt.wantPi) { 262 | t.Errorf("vrf.Prove() gotPi = %v, want %v", gotPi, tt.wantPi) 263 | } 264 | }) 265 | } 266 | } 267 | 268 | func Test_P256Sha256Tai_vrf_Verify(t *testing.T) { 269 | // Know Correct cases. 270 | var P256Sha256TaiCases, _ = readCases("./p256_sha256_tai.json") 271 | 272 | type Test struct { 273 | name string 274 | pk *ecdsa.PublicKey 275 | alpha []byte 276 | pi []byte 277 | wantBeta []byte 278 | wantErr bool 279 | } 280 | 281 | tests := []Test{} 282 | for _, c := range P256Sha256TaiCases { 283 | curve := elliptic.P256() 284 | skBytes, _ := hex.DecodeString(c.Sk) 285 | 286 | pkX, pkY := curve.ScalarBaseMult(skBytes) 287 | pk := ecdsa.PublicKey{ 288 | Curve: curve, 289 | X: pkX, 290 | Y: pkY, 291 | } 292 | 293 | alpha, _ := hex.DecodeString(c.Alpha) 294 | pi, _ := hex.DecodeString(c.Pi) 295 | wantBeta, _ := hex.DecodeString(c.Beta) 296 | 297 | tests = append(tests, Test{ 298 | c.Alpha, 299 | &pk, 300 | alpha, 301 | pi, 302 | wantBeta, 303 | false, 304 | }) 305 | } 306 | 307 | vrf := ecvrf.NewP256Sha256Tai() 308 | 309 | for _, tt := range tests { 310 | t.Run(tt.name, func(t *testing.T) { 311 | v := vrf 312 | gotBeta, err := v.Verify(tt.pk, tt.alpha, tt.pi) 313 | if (err != nil) != tt.wantErr { 314 | t.Errorf("vrf.Verify() error = %v, wantErr %v", err, tt.wantErr) 315 | return 316 | } 317 | if !reflect.DeepEqual(gotBeta, tt.wantBeta) { 318 | t.Errorf("vrf.Verify() = %v, want %v", gotBeta, tt.wantBeta) 319 | } 320 | }) 321 | } 322 | } 323 | 324 | type secp256k1gen struct { 325 | sk *ecdsa.PrivateKey 326 | alpha []byte 327 | } 328 | 329 | func (secp256k1gen) Generate(rand *rand.Rand, size int) reflect.Value { 330 | for { 331 | sk, err := ecdsa.GenerateKey(btcec.S256(), rand) 332 | if err != nil { 333 | continue 334 | } 335 | alpha := make([]byte, rand.Intn(256)) 336 | rand.Read(alpha) 337 | return reflect.ValueOf(secp256k1gen{sk, alpha}) 338 | } 339 | } 340 | 341 | type p256gen struct { 342 | sk *ecdsa.PrivateKey 343 | alpha []byte 344 | } 345 | 346 | func (p256gen) Generate(rand *rand.Rand, size int) reflect.Value { 347 | for { 348 | sk, err := ecdsa.GenerateKey(elliptic.P256(), rand) 349 | if err != nil { 350 | continue 351 | } 352 | alpha := make([]byte, rand.Intn(256)) 353 | rand.Read(alpha) 354 | return reflect.ValueOf(p256gen{sk, alpha}) 355 | } 356 | } 357 | 358 | func TestRandSkAndAlpha(t *testing.T) { 359 | t.Run("secp256k1", func(t *testing.T) { 360 | if err := quick.Check(func(gen secp256k1gen) bool { 361 | vrf := ecvrf.NewSecp256k1Sha256Tai() 362 | beta1, pi, err := vrf.Prove(gen.sk, gen.alpha) 363 | if err != nil { 364 | return false 365 | } 366 | beta2, err := vrf.Verify(&gen.sk.PublicKey, gen.alpha, pi) 367 | if err != nil { 368 | return false 369 | } 370 | return bytes.Compare(beta1, beta2) == 0 371 | }, nil); err != nil { 372 | t.Fatal(err) 373 | } 374 | }) 375 | 376 | t.Run("p256", func(t *testing.T) { 377 | if err := quick.Check(func(gen p256gen) bool { 378 | vrf := ecvrf.NewP256Sha256Tai() 379 | beta1, pi, err := vrf.Prove(gen.sk, gen.alpha) 380 | if err != nil { 381 | return false 382 | } 383 | beta2, err := vrf.Verify(&gen.sk.PublicKey, gen.alpha, pi) 384 | if err != nil { 385 | return false 386 | } 387 | return bytes.Compare(beta1, beta2) == 0 388 | }, nil); err != nil { 389 | t.Fatal(err) 390 | } 391 | }) 392 | } 393 | -------------------------------------------------------------------------------- /tests/secp256_k1_sha256_tai.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "beta": "612065e309e937ef46c2ef04d5886b9c6efd2991ac484ec64a9b014366fc5d81", 4 | "alpha": "73616d706c65", 5 | "pi": "031f4dbca087a1972d04a07a779b7df1caa99e0f5db2aa21f3aecc4f9e10e85d08748c9fbe6b95d17359707bfb8e8ab0c93ba0c515333adcb8b64f372c535e115ccf66ebf5abe6fadb01b5efb37c0a0ec9", 6 | "sk": "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721", 7 | "pk": "032c8c31fc9f990c6b55e3865a184a4ce50e09481f2eaeb3e60ec1cea13a6ae645" 8 | }, 9 | { 10 | "beta": "00acd42d48046e13552f54919286c2085aec6fb874854d036f66ad572c99e7ab", 11 | "alpha": "73616d706c65", 12 | "pi": "029a2df6ca1d5f734945fb6847669f839eb9ecf127fa8314e5a6a5c4695c3f4d159009b3741cdec6b0d7c70e3aae6b82aeb1aad555499bd6ce10b35fa230079e6fa752e8d4755ffd285aef5133dad7a64b", 13 | "sk": "01", 14 | "pk": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" 15 | }, 16 | { 17 | "beta": "c355718640883112731fce0b5dd97c34492d226280654dcf0ada1d6b32e3384b", 18 | "alpha": "73616d706c65", 19 | "pi": "0205c5a6ed80f7ffbf9f47583e873717e86c8405349266745a0504ea7ca68876ce6afe0e3cccfc7bba83a6d16771d80e26a46ad25be631869d6f60a34c12a19b868815182657288f57afb91166ceed3cc5", 20 | "sk": "02", 21 | "pk": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" 22 | }, 23 | { 24 | "beta": "09a93f6e8a6037db146d862eec33f56c8053bb4fda309f8ecbcbe04592aabd37", 25 | "alpha": "73616d706c65", 26 | "pi": "02a14b92076becc501b9ac761c18cacd792e0b30ad2b6907e1273dbe3762a9d29cf97fe3a904d2123ef98030a929ea91b40f1d5f78d9eb09c85ee2caa962b17de41abd4cf6be036e90c9826ae4e6e3673a", 27 | "sk": "03", 28 | "pk": "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9" 29 | }, 30 | { 31 | "beta": "5a0bce08f650d5ef80b0254ee814360e1f2c944a6c2b15e3171374f362cd92b4", 32 | "alpha": "73616d706c65", 33 | "pi": "03a3ca50b6f1873b8ca55348d0c62b0a2fd774aa9b96d1061c0917d6f9da5fe560dcddbda349d2db15751c965baf88ddb67075b5a441400c5ef85d9dffd6a16833993af6f05901c2421210d72d114c4ba0", 34 | "sk": "04", 35 | "pk": "02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13" 36 | }, 37 | { 38 | "beta": "e06dc093da223b40225633628c94a0bf909a3bb19a2f838932b4627f740354a1", 39 | "alpha": "73616d706c65", 40 | "pi": "030b4806f7f7398ba03951990740243d3f84a0815d85e2be439ee42bd8f249bd44e56800354296f9c05d30e06966009baf98c963028070217f28c0a298f9ecd56726bbc7c4faf1c62691972bfbc9fbd684", 41 | "sk": "05", 42 | "pk": "022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4" 43 | }, 44 | { 45 | "beta": "811a4fe231758d65b9b20d436a19256765776c812966032b16ce2b21e5c69be7", 46 | "alpha": "73616d706c65", 47 | "pi": "02f0fdc752a0c63cae4d33f65af4167a7a8b04711d840230df0d211ee3d6e2a2ad66625f4d9db7798ab664bb2da77736db6c9b7828cd8310a2e5f677224e1676afbb731cf92018a49c0452d1080aac5f3a", 48 | "sk": "06", 49 | "pk": "03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556" 50 | }, 51 | { 52 | "beta": "0b772c54b2125194bd26b7e3ce2c4606043f65d99d3c65aef6e3b61dffd20aae", 53 | "alpha": "73616d706c65", 54 | "pi": "036e041791df3880003115548bf1c491700133a23fb6c1edbef6a23f7dd67c05d5e227f496df589bdd68ffc28fb00246681abb9399a4207fbf42c2a64fa47615b4a64dc25cf0f6bc368353bfb25f63c95b", 55 | "sk": "07", 56 | "pk": "025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc" 57 | }, 58 | { 59 | "beta": "8f7729e4288e7630b358a6cef560479f0e441926ae0fa6a3a96f17b63b3d1fb8", 60 | "alpha": "73616d706c65", 61 | "pi": "02b5a04c6340c0d09b26da8f75c7713ed871e9f673bb87c00ee0e76e8045faad9ece28cfdbc41eab04205963c3252b7229d94793491e4a7f48e09c2d85b3ee2a1131a27b670b07c016dc00e4d8f3daa242", 62 | "sk": "08", 63 | "pk": "022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01" 64 | }, 65 | { 66 | "beta": "ce465b7e061bae415e10d8aafd32d92e2f499d959632fe5983cb0b2d4ee7b3b5", 67 | "alpha": "73616d706c65", 68 | "pi": "02cf1639991c8eb219993f1b9921a413e2ebc72d7b533ab952c92188b9b4ee2bd8cd5417a75c80d3022a3719297b2699b958d27e5e9fe03c8fc96766234fa497bee8287a76f6db22ff482cec9505e51a5f", 69 | "sk": "09", 70 | "pk": "03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe" 71 | }, 72 | { 73 | "beta": "941b5f76d95772045e824e30d85106a4a3b5920b901e178d3f62ec8e473ccf69", 74 | "alpha": "73616d706c65", 75 | "pi": "034e371e26303b9deb6f650f8faee60a2673b2bfef5b5a9af066aca5e7c12695becd27149218141ceb7f7e58da293679388c3b7b5109ec360a1f50decb1fa89c11b19476f5b2786ecba75f3642d942ba31", 76 | "sk": "0a", 77 | "pk": "03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7" 78 | }, 79 | { 80 | "beta": "79403a19944c3516d102cfa42cd5dd3f16c5c9bd457bf0659e2305374af3dccd", 81 | "alpha": "73616d706c65", 82 | "pi": "0384e3011d9e8235de77c211c0e57b0ed4bbffbe4b1d1a946278d13943be6c4280206842bfc58d564262a3214cfab3098d2887923eb659ff543aaa9c48d821acf1198bde5d824d27e9a9a6c2f84c6bdfdb", 83 | "sk": "0b", 84 | "pk": "03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb" 85 | }, 86 | { 87 | "beta": "746ca26b7c90df37d309ecce42757e6bf9423ad31211a02e8d9d86aea72f2f3a", 88 | "alpha": "73616d706c65", 89 | "pi": "03ad49eb72e2c7789d851c7bf2a3137cee17ab304ae7acb7b22739c5aae48eb339199381d85ae4654b2f5488499b4d3a13d6055ca740de70bb6d9b2c0cb962d873e13cd1d1d1c0c4eef9f9809f88e36fc0", 90 | "sk": "0c", 91 | "pk": "03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a" 92 | }, 93 | { 94 | "beta": "51096a3de69492e852477271a77a5b95de5a0cc2fc43128177b9a85e3184318d", 95 | "alpha": "73616d706c65", 96 | "pi": "038be9f8885740d6a336f0ec8249ec010f99fb4f0e42f09804c6f98f0be907327419f8249ebc3fb6fb372402893c55422987d9d36f6dfbdc7cfa88fb75dca13b095f412a1d82b358b1d17406991af576e4", 97 | "sk": "0d", 98 | "pk": "03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8" 99 | }, 100 | { 101 | "beta": "b3a048bc1682c3d5ba4d79a951b38f6d9729a328afb38fdd22c5f17247eeb642", 102 | "alpha": "73616d706c65", 103 | "pi": "0354a5641699f62565bec88e75ed465c052f655048a2de85ae39f32e968a80faeb6e21ac4919dc0710e1f1a76184988926a2b354c52cf1ca8529157aa5574c385b86def3a76a7cb9e0d0a11227bc651dac", 104 | "sk": "0e", 105 | "pk": "03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4" 106 | }, 107 | { 108 | "beta": "eb5c23bc8773fa4c83ef3bf88bf63aceffe6b042e220d8826db222dc55f915fe", 109 | "alpha": "73616d706c65", 110 | "pi": "0398f53f2ed4d687e6a65eaf7ce4d63e99e2db78a12302c782ffe6012737eb2d10257f7a86288479153dafa74c14092fd5acc60c953f225840ee6c92ec67106a979ead5f8c12142373b119190a012ac780", 111 | "sk": "0f", 112 | "pk": "02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e" 113 | }, 114 | { 115 | "beta": "e96f1bab42d66b32d2fb8ea1e4655808881cd9f208279a0757ef990575e7adc0", 116 | "alpha": "73616d706c65", 117 | "pi": "024ca2c0b270a6c632cade38ff097d66de362c54064e847fd96bd4067b71028db4ead3e112b4cc78b826165d6fde084924f58ae74f69c4542d68cdd5a33e03d5bd2e7fc5f5fda4c9e3d157fbb213fddd3d", 118 | "sk": "10", 119 | "pk": "03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a" 120 | }, 121 | { 122 | "beta": "dbfeac68bfc25d1fd3f9d0bfe093edfef9e90c1b4d2940ac961c1d08ba66deb4", 123 | "alpha": "73616d706c65", 124 | "pi": "028594a13f389fbb3618bb5db54059ea673f087d7193ed8513d1abc7f3a5228d0e99a976d08666bdb46b287ec41945de29cce12c2dac0ba226ef3ef383a0fba8e138b415816c5243af66ef5a51244d9616", 125 | "sk": "11", 126 | "pk": "03defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34" 127 | }, 128 | { 129 | "beta": "87a0ff6de5a282746082ed187abc29f534c1ee4f641e604c7d241fea5155b2a2", 130 | "alpha": "73616d706c65", 131 | "pi": "03e9593d98552a6e3c3f45a8a566b4d2de9bd5ff1837b4ca91655dc6e91d59fb5b335fd59c15668b371093ee81df275c876761ebcbd60fa8127c7e601f99ce340a3ae8c00864c0e38ebe749e566c151240", 132 | "sk": "12", 133 | "pk": "025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc" 134 | }, 135 | { 136 | "beta": "32306c22ad369a6e0fd638f503c0dbf674cd9d66b5da0a8c83c1d78157814f90", 137 | "alpha": "73616d706c65", 138 | "pi": "02833a4a802ec7f90b91dc59cfc8138669d2e6ec3f933c4fc7e44389a5e02c6032816f05729899c003ae77025e22c703809f8ee1b35d04d250800c7e90533dc4f29b3667065fc2cae0cc3cd356962ac62a", 139 | "sk": "13", 140 | "pk": "022b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c" 141 | }, 142 | { 143 | "beta": "4e853d2b79f1d2dd8e23be493efa8c685afc30198070aa5acbc53fddcace9aa9", 144 | "alpha": "73616d706c65", 145 | "pi": "022d2bb6961468e5a3aadae02157e584a4d45f58121185cafa11b93fae6eb4a4604d03e431646fb1ee06c04e562cbd0e7d8c4fc01bc8dc0ca0f7f5d4e138be2963f6ab25843a12dd6c5e34cfda1ecabf04", 146 | "sk": "14", 147 | "pk": "024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97" 148 | }, 149 | { 150 | "beta": "642a4819a8e8e5739cc0f4bd5221d641774f2d33b75007c4de61f4f2089df92c", 151 | "alpha": "73616d706c65", 152 | "pi": "02570056bade9dc5204721d0ecd39906a72468d027dfa1a896b8f6a88d50fc5526f7afb112bb95a2e13f326cbf1c8de77b7596195575b4dd712edbfd96acafdcbc0fa1dfae4041bb64d8e25b276426ea5a", 153 | "sk": "018ebbb95eed0e13", 154 | "pk": "02a90cc3d3f3e146daadfc74ca1372207cb4b725ae708cef713a98edd73d99ef29" 155 | }, 156 | { 157 | "beta": "b042e27b4d0e1f1a1c8ec7d4c2c0b2ae988d4cbc526d1ffb11b0087bea97a630", 158 | "alpha": "73616d706c65", 159 | "pi": "02b87402d5f26e06c14a3136a6c868316d2a2d413d5ef2ef91cae3625d57e87ea326b2bb5f67e4fe3f5c65df958d1ecd7a9bebea344b1facd08fbc7512701bcababbc39c410d9d1c0980e688feada47468", 160 | "sk": "159d893d4cdd747246cdca43590e13", 161 | "pk": "03e5a2636bcfd412ebf36ec45b19bfb68a1bc5f8632e678132b885f7df99c5e9b3" 162 | }, 163 | { 164 | "beta": "3a36fc2ff539e516897c53d61951209dcac171500ed79692434a28e0cb9c3272", 165 | "alpha": "73616d706c65", 166 | "pi": "02e111fc96dd022c02f45df180c3707d5a48a5eb669aad2f7b45345b5f95a38f68b82b8cfa5dee906023fe4c2a15723636e29c8fc8d65ade1620b0d6453331f0237f632e89f0352fd25662d5630b3a6034", 167 | "sk": "3fffffffffffffffffffffffffffffffaeabb739abd2280eeff497a3340d9050", 168 | "pk": "03a6b594b38fb3e77c6edf78161fade2041f4e09fd8497db776e546c41567feb3c" 169 | }, 170 | { 171 | "beta": "c6e3b662984301fc84c5eb2f5c0f435aee2975a731e6707bb9e50113e4bc2809", 172 | "alpha": "73616d706c65", 173 | "pi": "023a435fc5fab74b0b33eeb7c62447efc323e6e33a19657e7a0a473451b885fe841f28b91f43834ab659f26ea94d9dbca325192c45589afc1415e508b72247c64e385d3f9aa13c2e571d252335b8f63a3e", 174 | "sk": "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", 175 | "pk": "0300000000000000000000003b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63" 176 | }, 177 | { 178 | "beta": "a25353782f363555d90c822a347151272d364103aa49513105bcc247287ff6a9", 179 | "alpha": "73616d706c65", 180 | "pi": "02f769ca0cb1a96046265c19d5ef44deade0eb42a6aab094c7fde5bcdae09455db2ba3ffa7e72d01ead1fedc0162507c5d8ccbde61e09cd533b807404b75a4acb8573881f86abbf41bc0877acb3362604f", 181 | "sk": "bfffffffffffffffffffffffffffffff0c0325ad0376782ccfddc6e99c28b0f0", 182 | "pk": "02e24ce4beee294aa6350faa67512b99d388693ae4e7f53d19882a6ea169fc1ce1" 183 | }, 184 | { 185 | "beta": "9731f862d34587fb91521d785a30ff57188c57efd4b55239199ae4ed31bcebf8", 186 | "alpha": "73616d706c65", 187 | "pi": "02d196a5eb787d5d8a33247607d78d5a48164ac4d899dba33cb3a5f68032124ba1b2221b96ae997d450ddd8e5863a0cc7cf1b3f50431aa6031b6807396edfab5d1f5812c7e8d40f157028afb7aa51a41d5", 188 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036412d", 189 | "pk": "034ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97" 190 | }, 191 | { 192 | "beta": "54c8f3e98a8a8e5b77d327cdb7a71e5959996b5619972a8b472d7a3799a79387", 193 | "alpha": "73616d706c65", 194 | "pi": "0391aeaecb887e9d6f0c0f64d20f28acbe687bffabaaea0ff5237f236693eb5b56c88099425415cd9d242c70249f7abb8958a327ad341b4fca735f8ba61b57c9735252b72afab7e0cb6e49c9436366e800", 195 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036412e", 196 | "pk": "032b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c" 197 | }, 198 | { 199 | "beta": "f9fba571cb27776c07d2bc42d670952e1965357942eca3edb5f80e28bc9aaed0", 200 | "alpha": "73616d706c65", 201 | "pi": "03b9ffede3d97b9753f8f31cd5d56442c525a5bccc2de1fc547886ee08bca9b4f3c1d44da0826ddfd763801c42875d41deeae99422c0e9e6a97e07e2689f58289bffe499069785dcffa4ff93a5b1e15856", 202 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036412f", 203 | "pk": "035601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc" 204 | }, 205 | { 206 | "beta": "c66931ad96cb4e1a44202fcd7882089b1cf77b07c426d292e0e15deca5c1b027", 207 | "alpha": "73616d706c65", 208 | "pi": "03ca01e6d80f99bd12c5c00142a9eb0c0e029f999a1e945a70110c944d5d5981c9814fb051e88c36f0c14d9acdfc3040b37fcc77fa1bfc23466730a108849e0a08f5e9c79331b04803e568ea4b553ce3f1", 209 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364130", 210 | "pk": "02defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34" 211 | }, 212 | { 213 | "beta": "4b00d6d00864c7972105755b538d5f62a3585b6e8e7061fd107317fa1004efc0", 214 | "alpha": "73616d706c65", 215 | "pi": "03df963611501cf382e2730131618377ab38486f483db1eab7feb6ade0e1b0141bd3d291b7e45a1b94cabbafa5fca3fb7ba36b158bbcdb2292383689a6231e201a3f78f9f40757e99f80e8032adbc8d4ce", 216 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364131", 217 | "pk": "02e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a" 218 | }, 219 | { 220 | "beta": "e1e9b8491278b6faf79d433cee7d9b01256f18b3d63601f6231332ce3751411c", 221 | "alpha": "73616d706c65", 222 | "pi": "03ee58341c2222f7671318eff4bf2bd5588221d37d133a8aafcff5162d56af906581ce8cc5d45d546b8cf2c6d22026b934688e9a68555d4386d75f9b9e554b58b886ad91285872a2a6f137576b6bf9513f", 223 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364132", 224 | "pk": "03d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e" 225 | }, 226 | { 227 | "beta": "72c8e10530d3f0c6e452f8f20d911908eb01887c62bbae0b1eb35cb1f36b7985", 228 | "alpha": "73616d706c65", 229 | "pi": "03ae19c4bac9d64009b7dabf9095c3ee3c848249269d41d5ee492683cef4a0b8464fa567b84a2bfe1c7359696522d01e083defdf2c4fe5aaad7bf67c93aab74d23069d05419c59c5cd5daed14e63bdc26f", 230 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364133", 231 | "pk": "02499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4" 232 | }, 233 | { 234 | "beta": "11fee8e23d484d9aa8ed151f8452be11e70cfad8a44f707c00b04a11270c3d7a", 235 | "alpha": "73616d706c65", 236 | "pi": "0261cb37ca1f9c0ee11e41aadf4637fdddccb3f70f8ff1903727fbc2bd220720e737048df6aa7ddec95dc6a5f93f6808b5bbf2983c1733ce7b686dddc457001dbbae277d251d61f69e1af22d69b60c975e", 237 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364134", 238 | "pk": "02f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8" 239 | }, 240 | { 241 | "beta": "dfa3543db662a08aac90bfd7dd9b39b77dacf16dda462fe5eaa26b0f595fc0f5", 242 | "alpha": "73616d706c65", 243 | "pi": "0376661cbff92aae582298a7348f4d8f7834e2d8f6707c9706f52e65aacf968d80b24c972d16acca689cf66a1d100c26d2b141b1c8b9835e6710db5126284b9540f43cf31394f9b3e0a87449df7ef6aee6", 244 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364135", 245 | "pk": "02d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a" 246 | }, 247 | { 248 | "beta": "caae0b8dd19e20fd52f43b2fd416228b46ad625aa68ef6424ff388fb4727e0dc", 249 | "alpha": "73616d706c65", 250 | "pi": "0204fa576f63771c34e6cdb98f59997584528d109c7592ab867374d9b91051a4d1e875685ea35673d901ff06f18d7e89bc098be8762abf7688dba945d09d9b71348624b40c4b903de2bd3cc44abb2fafc7", 251 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364136", 252 | "pk": "02774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb" 253 | }, 254 | { 255 | "beta": "97e0785b78305d909af2a255e1b26d4faf5879d4e7640bceac71e56b3851bec0", 256 | "alpha": "73616d706c65", 257 | "pi": "024d4ff3ad7689b905b5c4be9de0bd8d7960e30f145903fe715af943852229f269122fadc5f835ee029d306ad7d90f5c6011ad67d24a327cccf3f39018e34df7a1544d6748755fa07ce2013729816e2330", 258 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364137", 259 | "pk": "02a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7" 260 | }, 261 | { 262 | "beta": "e0a3519f3dd1597039b5617d5b09c8ed5c723d1589010c1d6284dd8fb9d5ea7b", 263 | "alpha": "73616d706c65", 264 | "pi": "022eb72eccc7228307eaf7946a28feac02de8223534800cc71d7d1195fb0a7c88630d9be168212ecee12897644c456c22eaa9be58c9c8bb92a86a73e787f44f55a57203ddd9d80bf4e1ad4ac21c03a5f2c", 265 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364138", 266 | "pk": "02acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe" 267 | }, 268 | { 269 | "beta": "ba9235a6d8c3a2efa2b6cc2d8f23d3b9169ae0a0363db2192465aefbff07ed09", 270 | "alpha": "73616d706c65", 271 | "pi": "038fbe2d674ae973b17ee720413e94ee0387d1f794766ec9649d97ccbf6afaa22ce90783ffc7eb0c082db401cb81203ced6223bf24f66cd2c19351f4e18c9fd7a2884d82870ea357bd5bbb4ce9e1e4e840", 272 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139", 273 | "pk": "032f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01" 274 | }, 275 | { 276 | "beta": "9c9dc6b4b61b59950c15c35fb665cc94879b0297fac4edf2803de529b00f8c0c", 277 | "alpha": "73616d706c65", 278 | "pi": "0235731391a2ed6ff06cdb279b71ae0151d4f43041cfa8c27d958ab95d08b1bcae807694c56c2ec4ebc855253e3a66e798ea6701ae6861a2ad67a8c2a3ce14c3e8af2f3936fabd5a13dde062afd040e50a", 279 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413a", 280 | "pk": "035cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc" 281 | }, 282 | { 283 | "beta": "110f1fa93881cf624c22b72f1b79e6138a052a462ff10d7aa56f501835a8f6b8", 284 | "alpha": "73616d706c65", 285 | "pi": "03913bdfa315ab0963b03c34ebe265751e8b5904837bbda75629423b485924fee45b397e9d697239fdcfd435f1b21082d94f1917c792300c4ada68c0ac4e9da7cf2db99c5cbf2775ac4161ea2ed3c589a6", 286 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413b", 287 | "pk": "02fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556" 288 | }, 289 | { 290 | "beta": "a43ebaef2262310b95e140287c861c53edefc13c37696c3f89234d1a45eccf17", 291 | "alpha": "73616d706c65", 292 | "pi": "024dbc319514312b5544e6b587a978dfccbdc862d7fc33c5dda706efb569613df99fa8b04b237169cb92397564f92bd1e45e041817e7a3368fa1f47fcca1e9bd01a6915e645b2d411815b2b95c9efef590", 293 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413c", 294 | "pk": "032f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4" 295 | }, 296 | { 297 | "beta": "c8952a9439d26d3e761399de1fd734a2338c15893ece5a3efc72e25c9f007da8", 298 | "alpha": "73616d706c65", 299 | "pi": "03e30118c907034baf1456063bf7b423972e13e1743bf8dbb2e00fd8ba4a8c367a299bc3859123464d87fd4a508e5a1321f6bde3f00104b2c8af1769781dfb02e749946f4e17f6e429be0f4e4e3085e320", 300 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413d", 301 | "pk": "03e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13" 302 | }, 303 | { 304 | "beta": "25daded1cb7561c8e0013315a6f6d9dd1611d95c92caf5f920bc437ae0180a55", 305 | "alpha": "73616d706c65", 306 | "pi": "02ed1bb54a9092c8fd50ae8cea3322e127600a0e32840d9bc4664cfab08b1c6ba3a36ad7913367088f6e6cdbc91a061cfbd0fab0093344414aa16e43dcc5394c7a06cb46afb049eeffc2e99d16992bc228", 307 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", 308 | "pk": "03f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9" 309 | }, 310 | { 311 | "beta": "3bcee6576d82d011563480c8fcc5751ba6aea58313dbba4cb278d2f74eaee3ea", 312 | "alpha": "73616d706c65", 313 | "pi": "03359425334b14173856433b4e695f1d19c7c0cb4eb9b5c72b0b00afe170ce7fd738334a976a8be4582b05a480cdecf8a4f4dd9d0694ae1dcb384429a1c99082bdb2845e2c3010054071489f41fc4b65a5", 314 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f", 315 | "pk": "03c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" 316 | }, 317 | { 318 | "beta": "8efc7ce3fa0ee91c2e7d45ead94883776e37bdb3af67f386e7ec500e76e066dd", 319 | "alpha": "73616d706c65", 320 | "pi": "03cc27d840191d06dfca94d9346cc5b85830dcf9c9e7e4a41cc857d841bd48186c6c5d463591b9632297b3aab781d23263fd9cfc41fbb6affa02840bb903f51b494dc492087ba6fe04acf7c5b54ec0de24", 321 | "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", 322 | "pk": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" 323 | } 324 | ] --------------------------------------------------------------------------------