mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[NOD-882] Remove ecc and hdkeychain (#680)
* [NOD-882] Remove ecc and hdkeychain * [NOD-882] Remove HDCoinType from dagParams
This commit is contained in:
parent
1a38550fdd
commit
6aa5e0b5a8
@ -13,7 +13,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/kaspanet/kaspad/util/hdkeychain"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
"github.com/kaspanet/kaspad/wire"
|
||||
@ -177,13 +176,6 @@ type Params struct {
|
||||
|
||||
// Address encoding magics
|
||||
PrivateKeyID byte // First byte of a WIF private key
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDKeyIDPair hdkeychain.HDKeyIDPair
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType uint32
|
||||
}
|
||||
|
||||
// NormalizeRPCServerAddress returns addr with the current network default
|
||||
@ -238,13 +230,6 @@ var MainnetParams = Params{
|
||||
|
||||
// Address encoding magics
|
||||
PrivateKeyID: 0x80, // starts with 5 (uncompressed) or K (compressed)
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDKeyIDPair: hdkeychain.HDKeyPairMainnet,
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType: 0,
|
||||
}
|
||||
|
||||
// RegressionNetParams defines the network parameters for the regression test
|
||||
@ -295,13 +280,6 @@ var RegressionNetParams = Params{
|
||||
|
||||
// Address encoding magics
|
||||
PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed)
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDKeyIDPair: hdkeychain.HDKeyPairRegressionNet,
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType: 1,
|
||||
}
|
||||
|
||||
// TestnetParams defines the network parameters for the test Kaspa network.
|
||||
@ -350,13 +328,6 @@ var TestnetParams = Params{
|
||||
|
||||
// Address encoding magics
|
||||
PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed)
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDKeyIDPair: hdkeychain.HDKeyPairTestnet,
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType: 1,
|
||||
}
|
||||
|
||||
// SimnetParams defines the network parameters for the simulation test Kaspa
|
||||
@ -409,13 +380,6 @@ var SimnetParams = Params{
|
||||
PrivateKeyID: 0x64, // starts with 4 (uncompressed) or F (compressed)
|
||||
// Human-readable part for Bech32 encoded addresses
|
||||
Prefix: util.Bech32PrefixKaspaSim,
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDKeyIDPair: hdkeychain.HDKeyPairSimnet,
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType: 115, // ASCII for s
|
||||
}
|
||||
|
||||
// DevnetParams defines the network parameters for the development Kaspa network.
|
||||
@ -464,13 +428,6 @@ var DevnetParams = Params{
|
||||
|
||||
// Address encoding magics
|
||||
PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed)
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDKeyIDPair: hdkeychain.HDKeyPairDevnet,
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType: 1,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -1,9 +1,6 @@
|
||||
package dagconfig_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/kaspanet/kaspad/util/hdkeychain"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "github.com/kaspanet/kaspad/dagconfig"
|
||||
@ -15,10 +12,6 @@ import (
|
||||
var mockNetParams = Params{
|
||||
Name: "mocknet",
|
||||
Net: 1<<32 - 1,
|
||||
HDKeyIDPair: hdkeychain.HDKeyIDPair{
|
||||
PrivateKeyID: [4]byte{0x01, 0x02, 0x03, 0x04},
|
||||
PublicKeyID: [4]byte{0x05, 0x06, 0x07, 0x08},
|
||||
},
|
||||
}
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
@ -27,16 +20,10 @@ func TestRegister(t *testing.T) {
|
||||
params *Params
|
||||
err error
|
||||
}
|
||||
type hdTest struct {
|
||||
priv []byte
|
||||
want []byte
|
||||
err error
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
register []registerTest
|
||||
hdMagics []hdTest
|
||||
}{
|
||||
{
|
||||
name: "default networks",
|
||||
@ -62,40 +49,6 @@ func TestRegister(t *testing.T) {
|
||||
err: ErrDuplicateNet,
|
||||
},
|
||||
},
|
||||
hdMagics: []hdTest{
|
||||
{
|
||||
priv: MainnetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: MainnetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: TestnetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: TestnetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: RegressionNetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: RegressionNetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: SimnetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: SimnetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: mockNetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
err: hdkeychain.ErrUnknownHDKeyID,
|
||||
},
|
||||
{
|
||||
priv: []byte{0xff, 0xff, 0xff, 0xff},
|
||||
err: hdkeychain.ErrUnknownHDKeyID,
|
||||
},
|
||||
{
|
||||
priv: []byte{0xff},
|
||||
err: hdkeychain.ErrUnknownHDKeyID,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "register mocknet",
|
||||
@ -106,13 +59,6 @@ func TestRegister(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
hdMagics: []hdTest{
|
||||
{
|
||||
priv: mockNetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: mockNetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "more duplicates",
|
||||
@ -143,41 +89,6 @@ func TestRegister(t *testing.T) {
|
||||
err: ErrDuplicateNet,
|
||||
},
|
||||
},
|
||||
hdMagics: []hdTest{
|
||||
{
|
||||
priv: MainnetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: MainnetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: TestnetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: TestnetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: RegressionNetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: RegressionNetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: SimnetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: SimnetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: mockNetParams.HDKeyIDPair.PrivateKeyID[:],
|
||||
want: mockNetParams.HDKeyIDPair.PublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: []byte{0xff, 0xff, 0xff, 0xff},
|
||||
err: hdkeychain.ErrUnknownHDKeyID,
|
||||
},
|
||||
{
|
||||
priv: []byte{0xff},
|
||||
err: hdkeychain.ErrUnknownHDKeyID,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -185,25 +96,10 @@ func TestRegister(t *testing.T) {
|
||||
for _, regtest := range test.register {
|
||||
err := Register(regtest.params)
|
||||
|
||||
// HDKeyIDPairs must be registered separately
|
||||
hdkeychain.RegisterHDKeyIDPair(regtest.params.HDKeyIDPair)
|
||||
|
||||
if err != regtest.err {
|
||||
t.Errorf("%s:%s: Registered network with unexpected error: got %v expected %v",
|
||||
test.name, regtest.name, err, regtest.err)
|
||||
}
|
||||
}
|
||||
for i, magTest := range test.hdMagics {
|
||||
pubKey, err := hdkeychain.HDPrivateKeyToPublicKeyID(magTest.priv[:])
|
||||
if !reflect.DeepEqual(err, magTest.err) {
|
||||
t.Errorf("%s: HD magic %d mismatched error: got %v expected %v ",
|
||||
test.name, i, err, magTest.err)
|
||||
continue
|
||||
}
|
||||
if magTest.err == nil && !bytes.Equal(pubKey, magTest.want[:]) {
|
||||
t.Errorf("%s: HD magic %d private and public mismatch: got %v expected %v ",
|
||||
test.name, i, pubKey, magTest.want[:])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
ecc
|
||||
=====
|
||||
|
||||
[](https://choosealicense.com/licenses/isc/)
|
||||
[](http://godoc.org/github.com/kaspanet/kaspad/ecc)
|
||||
|
||||
Package ecc implements elliptic curve cryptography needed for working with
|
||||
Kaspa. It is designed so that it may be used with the standard crypto/ecdsa
|
||||
packages provided with go. A comprehensive suite of tests is provided to ensure
|
||||
proper functionality. Package ecc was originally based on work from ThePiachu
|
||||
which is licensed under the same terms as Go, but it has signficantly diverged
|
||||
since then. The kaspanet developers original is licensed under the liberal ISC
|
||||
license.
|
||||
|
||||
## Examples
|
||||
|
||||
* [Sign Message](http://godoc.org/github.com/kaspanet/kaspad/ecc#example-package--SignMessage)
|
||||
Demonstrates signing a message with a secp256k1 private key that is first
|
||||
parsed form raw bytes and serializing the generated signature.
|
||||
|
||||
* [Verify Signature](http://godoc.org/github.com/kaspanet/kaspad/ecc#example-package--VerifySignature)
|
||||
Demonstrates verifying a secp256k1 signature against a public key that is
|
||||
first parsed from raw bytes. The signature is also parsed from raw bytes.
|
||||
|
@ -1,123 +0,0 @@
|
||||
// Copyright 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import "testing"
|
||||
|
||||
// BenchmarkAddJacobian benchmarks the secp256k1 curve addJacobian function with
|
||||
// Z values of 1 so that the associated optimizations are used.
|
||||
func BenchmarkAddJacobian(b *testing.B) {
|
||||
b.StopTimer()
|
||||
x1 := new(fieldVal).SetHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6")
|
||||
y1 := new(fieldVal).SetHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232")
|
||||
z1 := new(fieldVal).SetHex("1")
|
||||
x2 := new(fieldVal).SetHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6")
|
||||
y2 := new(fieldVal).SetHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232")
|
||||
z2 := new(fieldVal).SetHex("1")
|
||||
x3, y3, z3 := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
curve := S256()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
curve.addJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkAddJacobianNotZOne benchmarks the secp256k1 curve addJacobian
|
||||
// function with Z values other than one so the optimizations associated with
|
||||
// Z=1 aren't used.
|
||||
func BenchmarkAddJacobianNotZOne(b *testing.B) {
|
||||
b.StopTimer()
|
||||
x1 := new(fieldVal).SetHex("d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718")
|
||||
y1 := new(fieldVal).SetHex("5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190")
|
||||
z1 := new(fieldVal).SetHex("2")
|
||||
x2 := new(fieldVal).SetHex("91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4")
|
||||
y2 := new(fieldVal).SetHex("03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1")
|
||||
z2 := new(fieldVal).SetHex("3")
|
||||
x3, y3, z3 := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
curve := S256()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
curve.addJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkScalarBaseMult benchmarks the secp256k1 curve ScalarBaseMult
|
||||
// function.
|
||||
func BenchmarkScalarBaseMult(b *testing.B) {
|
||||
k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575")
|
||||
curve := S256()
|
||||
for i := 0; i < b.N; i++ {
|
||||
curve.ScalarBaseMult(k.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkScalarBaseMultLarge benchmarks the secp256k1 curve ScalarBaseMult
|
||||
// function with abnormally large k values.
|
||||
func BenchmarkScalarBaseMultLarge(b *testing.B) {
|
||||
k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c005751111111011111110")
|
||||
curve := S256()
|
||||
for i := 0; i < b.N; i++ {
|
||||
curve.ScalarBaseMult(k.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkScalarMult benchmarks the secp256k1 curve ScalarMult function.
|
||||
func BenchmarkScalarMult(b *testing.B) {
|
||||
x := fromHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6")
|
||||
y := fromHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232")
|
||||
k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575")
|
||||
curve := S256()
|
||||
for i := 0; i < b.N; i++ {
|
||||
curve.ScalarMult(x, y, k.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkNAF benchmarks the NAF function.
|
||||
func BenchmarkNAF(b *testing.B) {
|
||||
k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575")
|
||||
for i := 0; i < b.N; i++ {
|
||||
NAF(k.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSigVerify benchmarks how long it takes the secp256k1 curve to
|
||||
// verify signatures.
|
||||
func BenchmarkSigVerify(b *testing.B) {
|
||||
b.StopTimer()
|
||||
// Randomly generated keypair.
|
||||
// Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
|
||||
pubKey := PublicKey{
|
||||
Curve: S256(),
|
||||
X: fromHex("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"),
|
||||
Y: fromHex("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"),
|
||||
}
|
||||
|
||||
// Double sha256 of []byte{0x01, 0x02, 0x03, 0x04}
|
||||
msgHash := fromHex("8de472e2399610baaa7f84840547cd409434e31f5d3bd71e4d947f283874f9c0")
|
||||
sig := Signature{
|
||||
R: fromHex("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"),
|
||||
S: fromHex("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"),
|
||||
}
|
||||
|
||||
if !sig.Verify(msgHash.Bytes(), &pubKey) {
|
||||
b.Errorf("Signature failed to verify")
|
||||
return
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
sig.Verify(msgHash.Bytes(), &pubKey)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkFieldNormalize benchmarks how long it takes the internal field
|
||||
// to perform normalization (which includes modular reduction).
|
||||
func BenchmarkFieldNormalize(b *testing.B) {
|
||||
// The normalize function is constant time so default value is fine.
|
||||
f := new(fieldVal)
|
||||
for i := 0; i < b.N; i++ {
|
||||
f.Normalize()
|
||||
}
|
||||
}
|
16
ecc/doc.go
16
ecc/doc.go
@ -1,16 +0,0 @@
|
||||
/*
|
||||
Package ecc implements support for the elliptic curves needed for kaspa.
|
||||
|
||||
Kaspa uses elliptic curve cryptography using koblitz curves
|
||||
(specifically secp256k1) for cryptographic functions. See
|
||||
http://www.secg.org/collateral/sec2_final.pdf for details on the
|
||||
standard.
|
||||
|
||||
This package provides the data structures and functions implementing the
|
||||
crypto/elliptic Curve interface in order to permit using these curves
|
||||
with the standard crypto/ecdsa package provided with go. Helper
|
||||
functionality is provided to parse signatures and public keys from
|
||||
standard formats. It was originally based on some initial work by
|
||||
ThePiachu, but has significantly diverged since then.
|
||||
*/
|
||||
package ecc
|
1083
ecc/ecc.go
1083
ecc/ecc.go
File diff suppressed because it is too large
Load Diff
887
ecc/ecc_test.go
887
ecc/ecc_test.go
@ -1,887 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Copyright 2011 ThePiachu. All rights reserved.
|
||||
// Copyright 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// isJacobianOnS256Curve returns boolean if the point (x,y,z) is on the
|
||||
// secp256k1 curve.
|
||||
func isJacobianOnS256Curve(x, y, z *fieldVal) bool {
|
||||
// Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7
|
||||
// In Jacobian coordinates, Y = y/z^3 and X = x/z^2
|
||||
// Thus:
|
||||
// (y/z^3)^2 = (x/z^2)^3 + 7
|
||||
// y^2/z^6 = x^3/z^6 + 7
|
||||
// y^2 = x^3 + 7*z^6
|
||||
var y2, z2, x3, result fieldVal
|
||||
y2.SquareVal(y).Normalize()
|
||||
z2.SquareVal(z)
|
||||
x3.SquareVal(x).Mul(x)
|
||||
result.SquareVal(&z2).Mul(&z2).MulInt(7).Add(&x3).Normalize()
|
||||
return y2.Equals(&result)
|
||||
}
|
||||
|
||||
// TestAddJacobian tests addition of points projected in Jacobian coordinates.
|
||||
func TestAddJacobian(t *testing.T) {
|
||||
tests := []struct {
|
||||
x1, y1, z1 string // Coordinates (in hex) of first point to add
|
||||
x2, y2, z2 string // Coordinates (in hex) of second point to add
|
||||
x3, y3, z3 string // Coordinates (in hex) of expected point
|
||||
}{
|
||||
// Addition with a point at infinity (left hand side).
|
||||
// ∞ + P = P
|
||||
{
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
"1",
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
"1",
|
||||
},
|
||||
// Addition with a point at infinity (right hand side).
|
||||
// P + ∞ = P
|
||||
{
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
"1",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
"1",
|
||||
},
|
||||
// Addition with z1=z2=1 different x values.
|
||||
{
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"1",
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
"1",
|
||||
"0cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a6",
|
||||
"e205f79361bbe0346b037b4010985dbf4f9e1e955e7d0d14aca876bfa79aad87",
|
||||
"44a5646b446e3877a648d6d381370d9ef55a83b666ebce9df1b1d7d65b817b2f",
|
||||
},
|
||||
// Addition with z1=z2=1 same x opposite y.
|
||||
// P(x, y, z) + P(x, -y, z) = infinity
|
||||
{
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"1",
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd",
|
||||
"1",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
},
|
||||
// Addition with z1=z2=1 same point.
|
||||
// P(x, y, z) + P(x, y, z) = 2P
|
||||
{
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"1",
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"1",
|
||||
"ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27",
|
||||
"b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a",
|
||||
"16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464",
|
||||
},
|
||||
|
||||
// Addition with z1=z2 (!=1) different x values.
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"5d2fe112c21891d440f65a98473cb626111f8a234d2cd82f22172e369f002147",
|
||||
"98e3386a0a622a35c4561ffb32308d8e1c6758e10ebb1b4ebd3d04b4eb0ecbe8",
|
||||
"2",
|
||||
"cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a60",
|
||||
"817de4d86ef80d1ac0ded00426176fd3e787a5579f43452b2a1db021e6ac3778",
|
||||
"129591ad11b8e1de99235b4e04dc367bd56a0ed99baf3a77c6c75f5a6e05f08d",
|
||||
},
|
||||
// Addition with z1=z2 (!=1) same x opposite y.
|
||||
// P(x, y, z) + P(x, -y, z) = infinity
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"a470ab21467813b6e0496d2c2b70c11446bab4fcbc9a52b7f225f30e869aea9f",
|
||||
"2",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
},
|
||||
// Addition with z1=z2 (!=1) same point.
|
||||
// P(x, y, z) + P(x, y, z) = 2P
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac",
|
||||
"2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988",
|
||||
"6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11",
|
||||
},
|
||||
|
||||
// Addition with z1!=z2 and z2=1 different x values.
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
"1",
|
||||
"3ef1f68795a6ccd1181e23eab80a1b9a2cebdcde755413bf097936eb5b91b4f3",
|
||||
"0bef26c377c068d606f6802130bb7e9f3c3d2abcfa1a295950ed81133561cb04",
|
||||
"252b235a2371c3bd3246b69c09b86cf7aad41db3375e74ef8d8ebeb4dc0be11a",
|
||||
},
|
||||
// Addition with z1!=z2 and z2=1 same x opposite y.
|
||||
// P(x, y, z) + P(x, -y, z) = infinity
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd",
|
||||
"1",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
},
|
||||
// Addition with z1!=z2 and z2=1 same point.
|
||||
// P(x, y, z) + P(x, y, z) = 2P
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"1",
|
||||
"9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac",
|
||||
"2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988",
|
||||
"6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11",
|
||||
},
|
||||
|
||||
// Addition with z1!=z2 and z2!=1 different x values.
|
||||
// P(x, y, z) + P(x, y, z) = 2P
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4",
|
||||
"03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1",
|
||||
"3",
|
||||
"3f07081927fd3f6dadd4476614c89a09eba7f57c1c6c3b01fa2d64eac1eef31e",
|
||||
"949166e04ebc7fd95a9d77e5dfd88d1492ecffd189792e3944eb2b765e09e031",
|
||||
"eb8cba81bcffa4f44d75427506737e1f045f21e6d6f65543ee0e1d163540c931",
|
||||
}, // Addition with z1!=z2 and z2!=1 same x opposite y.
|
||||
// P(x, y, z) + P(x, -y, z) = infinity
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7",
|
||||
"cafc41904dd5428934f7d075129c8ba46eb622d4fc88d72cd1401452664add18",
|
||||
"3",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
},
|
||||
// Addition with z1!=z2 and z2!=1 same point.
|
||||
// P(x, y, z) + P(x, y, z) = 2P
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7",
|
||||
"3503be6fb22abd76cb082f8aed63745b9149dd2b037728d32ebfebac99b51f17",
|
||||
"3",
|
||||
"9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac",
|
||||
"2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988",
|
||||
"6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Convert hex to field values.
|
||||
x1 := new(fieldVal).SetHex(test.x1)
|
||||
y1 := new(fieldVal).SetHex(test.y1)
|
||||
z1 := new(fieldVal).SetHex(test.z1)
|
||||
x2 := new(fieldVal).SetHex(test.x2)
|
||||
y2 := new(fieldVal).SetHex(test.y2)
|
||||
z2 := new(fieldVal).SetHex(test.z2)
|
||||
x3 := new(fieldVal).SetHex(test.x3)
|
||||
y3 := new(fieldVal).SetHex(test.y3)
|
||||
z3 := new(fieldVal).SetHex(test.z3)
|
||||
|
||||
// Ensure the test data is using points that are actually on
|
||||
// the curve (or the point at infinity).
|
||||
if !z1.IsZero() && !isJacobianOnS256Curve(x1, y1, z1) {
|
||||
t.Errorf("#%d first point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
if !z2.IsZero() && !isJacobianOnS256Curve(x2, y2, z2) {
|
||||
t.Errorf("#%d second point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
if !z3.IsZero() && !isJacobianOnS256Curve(x3, y3, z3) {
|
||||
t.Errorf("#%d expected point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the two points.
|
||||
rx, ry, rz := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
S256().addJacobian(x1, y1, z1, x2, y2, z2, rx, ry, rz)
|
||||
|
||||
// Ensure result matches expected.
|
||||
if !rx.Equals(x3) || !ry.Equals(y3) || !rz.Equals(z3) {
|
||||
t.Errorf("#%d wrong result\ngot: (%v, %v, %v)\n"+
|
||||
"want: (%v, %v, %v)", i, rx, ry, rz, x3, y3, z3)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddAffine tests addition of points in affine coordinates.
|
||||
func TestAddAffine(t *testing.T) {
|
||||
tests := []struct {
|
||||
x1, y1 string // Coordinates (in hex) of first point to add
|
||||
x2, y2 string // Coordinates (in hex) of second point to add
|
||||
x3, y3 string // Coordinates (in hex) of expected point
|
||||
}{
|
||||
// Addition with a point at infinity (left hand side).
|
||||
// ∞ + P = P
|
||||
{
|
||||
"0",
|
||||
"0",
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
},
|
||||
// Addition with a point at infinity (right hand side).
|
||||
// P + ∞ = P
|
||||
{
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
"0",
|
||||
"0",
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
},
|
||||
|
||||
// Addition with different x values.
|
||||
{
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
|
||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
|
||||
"fd5b88c21d3143518d522cd2796f3d726793c88b3e05636bc829448e053fed69",
|
||||
"21cf4f6a5be5ff6380234c50424a970b1f7e718f5eb58f68198c108d642a137f",
|
||||
},
|
||||
// Addition with same x opposite y.
|
||||
// P(x, y) + P(x, -y) = infinity
|
||||
{
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd",
|
||||
"0",
|
||||
"0",
|
||||
},
|
||||
// Addition with same point.
|
||||
// P(x, y) + P(x, y) = 2P
|
||||
{
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"59477d88ae64a104dbb8d31ec4ce2d91b2fe50fa628fb6a064e22582196b365b",
|
||||
"938dc8c0f13d1e75c987cb1a220501bd614b0d3dd9eb5c639847e1240216e3b6",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Convert hex to field values.
|
||||
x1, y1 := fromHex(test.x1), fromHex(test.y1)
|
||||
x2, y2 := fromHex(test.x2), fromHex(test.y2)
|
||||
x3, y3 := fromHex(test.x3), fromHex(test.y3)
|
||||
|
||||
// Ensure the test data is using points that are actually on
|
||||
// the curve (or the point at infinity).
|
||||
if !(x1.Sign() == 0 && y1.Sign() == 0) && !S256().IsOnCurve(x1, y1) {
|
||||
t.Errorf("#%d first point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
if !(x2.Sign() == 0 && y2.Sign() == 0) && !S256().IsOnCurve(x2, y2) {
|
||||
t.Errorf("#%d second point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
if !(x3.Sign() == 0 && y3.Sign() == 0) && !S256().IsOnCurve(x3, y3) {
|
||||
t.Errorf("#%d expected point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the two points.
|
||||
rx, ry := S256().Add(x1, y1, x2, y2)
|
||||
|
||||
// Ensure result matches expected.
|
||||
if rx.Cmp(x3) != 00 || ry.Cmp(y3) != 0 {
|
||||
t.Errorf("#%d wrong result\ngot: (%x, %x)\n"+
|
||||
"want: (%x, %x)", i, rx, ry, x3, y3)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDoubleJacobian tests doubling of points projected in Jacobian
|
||||
// coordinates.
|
||||
func TestDoubleJacobian(t *testing.T) {
|
||||
tests := []struct {
|
||||
x1, y1, z1 string // Coordinates (in hex) of point to double
|
||||
x3, y3, z3 string // Coordinates (in hex) of expected point
|
||||
}{
|
||||
// Doubling a point at infinity is still infinity.
|
||||
{
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
},
|
||||
// Doubling with z1=1.
|
||||
{
|
||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
|
||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
|
||||
"1",
|
||||
"ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27",
|
||||
"b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a",
|
||||
"16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464",
|
||||
},
|
||||
// Doubling with z1!=1.
|
||||
{
|
||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718",
|
||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190",
|
||||
"2",
|
||||
"9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac",
|
||||
"2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988",
|
||||
"6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11",
|
||||
},
|
||||
{
|
||||
"201e3f75715136d2f93c4f4598f91826f94ca01f4233a5bd35de9708859ca50d",
|
||||
"bdf18566445e7562c6ada68aef02d498d7301503de5b18c6aef6e2b1722412e1",
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"4a5e0559863ebb4e9ed85f5c4fa76003d05d9a7626616e614a1f738621e3c220",
|
||||
"00000000000000000000000000000000000000000000000000000001b1388778",
|
||||
"7be30acc88bceac58d5b4d15de05a931ae602a07bcb6318d5dedc563e4482993",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Convert hex to field values.
|
||||
x1 := new(fieldVal).SetHex(test.x1)
|
||||
y1 := new(fieldVal).SetHex(test.y1)
|
||||
z1 := new(fieldVal).SetHex(test.z1)
|
||||
x3 := new(fieldVal).SetHex(test.x3)
|
||||
y3 := new(fieldVal).SetHex(test.y3)
|
||||
z3 := new(fieldVal).SetHex(test.z3)
|
||||
|
||||
// Ensure the test data is using points that are actually on
|
||||
// the curve (or the point at infinity).
|
||||
if !z1.IsZero() && !isJacobianOnS256Curve(x1, y1, z1) {
|
||||
t.Errorf("#%d first point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
if !z3.IsZero() && !isJacobianOnS256Curve(x3, y3, z3) {
|
||||
t.Errorf("#%d expected point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// Double the point.
|
||||
rx, ry, rz := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
S256().doubleJacobian(x1, y1, z1, rx, ry, rz)
|
||||
|
||||
// Ensure result matches expected.
|
||||
if !rx.Equals(x3) || !ry.Equals(y3) || !rz.Equals(z3) {
|
||||
t.Errorf("#%d wrong result\ngot: (%v, %v, %v)\n"+
|
||||
"want: (%v, %v, %v)", i, rx, ry, rz, x3, y3, z3)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDoubleAffine tests doubling of points in affine coordinates.
|
||||
func TestDoubleAffine(t *testing.T) {
|
||||
tests := []struct {
|
||||
x1, y1 string // Coordinates (in hex) of point to double
|
||||
x3, y3 string // Coordinates (in hex) of expected point
|
||||
}{
|
||||
// Doubling a point at infinity is still infinity.
|
||||
// 2*∞ = ∞ (point at infinity)
|
||||
|
||||
{
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
},
|
||||
|
||||
// Random points.
|
||||
{
|
||||
"e41387ffd8baaeeb43c2faa44e141b19790e8ac1f7ff43d480dc132230536f86",
|
||||
"1b88191d430f559896149c86cbcb703193105e3cf3213c0c3556399836a2b899",
|
||||
"88da47a089d333371bd798c548ef7caae76e737c1980b452d367b3cfe3082c19",
|
||||
"3b6f659b09a362821dfcfefdbfbc2e59b935ba081b6c249eb147b3c2100b1bc1",
|
||||
},
|
||||
{
|
||||
"b3589b5d984f03ef7c80aeae444f919374799edf18d375cab10489a3009cff0c",
|
||||
"c26cf343875b3630e15bccc61202815b5d8f1fd11308934a584a5babe69db36a",
|
||||
"e193860172998751e527bb12563855602a227fc1f612523394da53b746bb2fb1",
|
||||
"2bfcf13d2f5ab8bb5c611fab5ebbed3dc2f057062b39a335224c22f090c04789",
|
||||
},
|
||||
{
|
||||
"2b31a40fbebe3440d43ac28dba23eee71c62762c3fe3dbd88b4ab82dc6a82340",
|
||||
"9ba7deb02f5c010e217607fd49d58db78ec273371ea828b49891ce2fd74959a1",
|
||||
"2c8d5ef0d343b1a1a48aa336078eadda8481cb048d9305dc4fdf7ee5f65973a2",
|
||||
"bb4914ac729e26d3cd8f8dc8f702f3f4bb7e0e9c5ae43335f6e94c2de6c3dc95",
|
||||
},
|
||||
{
|
||||
"61c64b760b51981fab54716d5078ab7dffc93730b1d1823477e27c51f6904c7a",
|
||||
"ef6eb16ea1a36af69d7f66524c75a3a5e84c13be8fbc2e811e0563c5405e49bd",
|
||||
"5f0dcdd2595f5ad83318a0f9da481039e36f135005420393e72dfca985b482f4",
|
||||
"a01c849b0837065c1cb481b0932c441f49d1cab1b4b9f355c35173d93f110ae0",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Convert hex to field values.
|
||||
x1, y1 := fromHex(test.x1), fromHex(test.y1)
|
||||
x3, y3 := fromHex(test.x3), fromHex(test.y3)
|
||||
|
||||
// Ensure the test data is using points that are actually on
|
||||
// the curve (or the point at infinity).
|
||||
if !(x1.Sign() == 0 && y1.Sign() == 0) && !S256().IsOnCurve(x1, y1) {
|
||||
t.Errorf("#%d first point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
if !(x3.Sign() == 0 && y3.Sign() == 0) && !S256().IsOnCurve(x3, y3) {
|
||||
t.Errorf("#%d expected point is not on the curve -- "+
|
||||
"invalid test data", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// Double the point.
|
||||
rx, ry := S256().Double(x1, y1)
|
||||
|
||||
// Ensure result matches expected.
|
||||
if rx.Cmp(x3) != 00 || ry.Cmp(y3) != 0 {
|
||||
t.Errorf("#%d wrong result\ngot: (%x, %x)\n"+
|
||||
"want: (%x, %x)", i, rx, ry, x3, y3)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnCurve(t *testing.T) {
|
||||
s256 := S256()
|
||||
if !s256.IsOnCurve(s256.Params().Gx, s256.Params().Gy) {
|
||||
t.Errorf("FAIL S256")
|
||||
}
|
||||
}
|
||||
|
||||
type baseMultTest struct {
|
||||
k string
|
||||
x, y string
|
||||
}
|
||||
|
||||
//TODO: add more test vectors
|
||||
var s256BaseMultTests = []baseMultTest{
|
||||
{
|
||||
"AA5E28D6A97A2479A65527F7290311A3624D4CC0FA1578598EE3C2613BF99522",
|
||||
"34F9460F0E4F08393D192B3C5133A6BA099AA0AD9FD54EBCCFACDFA239FF49C6",
|
||||
"B71EA9BD730FD8923F6D25A7A91E7DD7728A960686CB5A901BB419E0F2CA232",
|
||||
},
|
||||
{
|
||||
"7E2B897B8CEBC6361663AD410835639826D590F393D90A9538881735256DFAE3",
|
||||
"D74BF844B0862475103D96A611CF2D898447E288D34B360BC885CB8CE7C00575",
|
||||
"131C670D414C4546B88AC3FF664611B1C38CEB1C21D76369D7A7A0969D61D97D",
|
||||
},
|
||||
{
|
||||
"6461E6DF0FE7DFD05329F41BF771B86578143D4DD1F7866FB4CA7E97C5FA945D",
|
||||
"E8AECC370AEDD953483719A116711963CE201AC3EB21D3F3257BB48668C6A72F",
|
||||
"C25CAF2F0EBA1DDB2F0F3F47866299EF907867B7D27E95B3873BF98397B24EE1",
|
||||
},
|
||||
{
|
||||
"376A3A2CDCD12581EFFF13EE4AD44C4044B8A0524C42422A7E1E181E4DEECCEC",
|
||||
"14890E61FCD4B0BD92E5B36C81372CA6FED471EF3AA60A3E415EE4FE987DABA1",
|
||||
"297B858D9F752AB42D3BCA67EE0EB6DCD1C2B7B0DBE23397E66ADC272263F982",
|
||||
},
|
||||
{
|
||||
"1B22644A7BE026548810C378D0B2994EEFA6D2B9881803CB02CEFF865287D1B9",
|
||||
"F73C65EAD01C5126F28F442D087689BFA08E12763E0CEC1D35B01751FD735ED3",
|
||||
"F449A8376906482A84ED01479BD18882B919C140D638307F0C0934BA12590BDE",
|
||||
},
|
||||
}
|
||||
|
||||
//TODO: test different curves as well?
|
||||
func TestBaseMult(t *testing.T) {
|
||||
s256 := S256()
|
||||
for i, e := range s256BaseMultTests {
|
||||
k, ok := new(big.Int).SetString(e.k, 16)
|
||||
if !ok {
|
||||
t.Errorf("%d: bad value for k: %s", i, e.k)
|
||||
}
|
||||
x, y := s256.ScalarBaseMult(k.Bytes())
|
||||
if fmt.Sprintf("%X", x) != e.x || fmt.Sprintf("%X", y) != e.y {
|
||||
t.Errorf("%d: bad output for k=%s: got (%X, %X), want (%s, %s)", i, e.k, x, y, e.x, e.y)
|
||||
}
|
||||
if testing.Short() && i > 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaseMultVerify(t *testing.T) {
|
||||
s256 := S256()
|
||||
for bytes := 1; bytes < 40; bytes++ {
|
||||
for i := 0; i < 30; i++ {
|
||||
data := make([]byte, bytes)
|
||||
_, err := rand.Read(data)
|
||||
if err != nil {
|
||||
t.Errorf("failed to read random data for %d", i)
|
||||
continue
|
||||
}
|
||||
x, y := s256.ScalarBaseMult(data)
|
||||
xWant, yWant := s256.ScalarMult(s256.Gx, s256.Gy, data)
|
||||
if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 {
|
||||
t.Errorf("%d: bad output for %X: got (%X, %X), want (%X, %X)", i, data, x, y, xWant, yWant)
|
||||
}
|
||||
if testing.Short() && i > 2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMult(t *testing.T) {
|
||||
tests := []struct {
|
||||
x string
|
||||
y string
|
||||
k string
|
||||
rx string
|
||||
ry string
|
||||
}{
|
||||
// base mult, essentially.
|
||||
{
|
||||
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
"483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
|
||||
"18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725",
|
||||
"50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352",
|
||||
"2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6",
|
||||
},
|
||||
{
|
||||
"000000000000000000000000000000000000000000000000000000000000002c",
|
||||
"420e7a99bba18a9d3952597510fd2b6728cfeafc21a4e73951091d4d8ddbe94e",
|
||||
"a2e8ba2e8ba2e8ba2e8ba2e8ba2e8ba219b51835b55cc30ebfe2f6599bc56f58",
|
||||
"a2112dcdfbcd10ae1133a358de7b82db68e0a3eb4b492cc8268d1e7118c98788",
|
||||
"27fc7463b7bb3c5f98ecf2c84a6272bb1681ed553d92c69f2dfe25a9f9fd3836",
|
||||
},
|
||||
}
|
||||
|
||||
s256 := S256()
|
||||
for i, test := range tests {
|
||||
x, _ := new(big.Int).SetString(test.x, 16)
|
||||
y, _ := new(big.Int).SetString(test.y, 16)
|
||||
k, _ := new(big.Int).SetString(test.k, 16)
|
||||
xWant, _ := new(big.Int).SetString(test.rx, 16)
|
||||
yWant, _ := new(big.Int).SetString(test.ry, 16)
|
||||
xGot, yGot := s256.ScalarMult(x, y, k.Bytes())
|
||||
if xGot.Cmp(xWant) != 0 || yGot.Cmp(yWant) != 0 {
|
||||
t.Fatalf("%d: bad output: got (%X, %X), want (%X, %X)", i, xGot, yGot, xWant, yWant)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMultRand(t *testing.T) {
|
||||
// Strategy for this test:
|
||||
// Get a random exponent from the generator point at first
|
||||
// This creates a new point which is used in the next iteration
|
||||
// Use another random exponent on the new point.
|
||||
// We use BaseMult to verify by multiplying the previous exponent
|
||||
// and the new random exponent together (mod N)
|
||||
s256 := S256()
|
||||
x, y := s256.Gx, s256.Gy
|
||||
exponent := big.NewInt(1)
|
||||
for i := 0; i < 1024; i++ {
|
||||
data := make([]byte, 32)
|
||||
_, err := rand.Read(data)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read random data at %d", i)
|
||||
break
|
||||
}
|
||||
x, y = s256.ScalarMult(x, y, data)
|
||||
exponent.Mul(exponent, new(big.Int).SetBytes(data))
|
||||
xWant, yWant := s256.ScalarBaseMult(exponent.Bytes())
|
||||
if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 {
|
||||
t.Fatalf("%d: bad output for %X: got (%X, %X), want (%X, %X)", i, data, x, y, xWant, yWant)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitK(t *testing.T) {
|
||||
tests := []struct {
|
||||
k string
|
||||
k1, k2 string
|
||||
s1, s2 int
|
||||
}{
|
||||
{
|
||||
"6df2b5d30854069ccdec40ae022f5c948936324a4e9ebed8eb82cfd5a6b6d766",
|
||||
"00000000000000000000000000000000b776e53fb55f6b006a270d42d64ec2b1",
|
||||
"00000000000000000000000000000000d6cc32c857f1174b604eefc544f0c7f7",
|
||||
-1, -1,
|
||||
},
|
||||
{
|
||||
"6ca00a8f10632170accc1b3baf2a118fa5725f41473f8959f34b8f860c47d88d",
|
||||
"0000000000000000000000000000000007b21976c1795723c1bfbfa511e95b84",
|
||||
"00000000000000000000000000000000d8d2d5f9d20fc64fd2cf9bda09a5bf90",
|
||||
1, -1,
|
||||
},
|
||||
{
|
||||
"b2eda8ab31b259032d39cbc2a234af17fcee89c863a8917b2740b67568166289",
|
||||
"00000000000000000000000000000000507d930fecda7414fc4a523b95ef3c8c",
|
||||
"00000000000000000000000000000000f65ffb179df189675338c6185cb839be",
|
||||
-1, -1,
|
||||
},
|
||||
{
|
||||
"f6f00e44f179936f2befc7442721b0633f6bafdf7161c167ffc6f7751980e3a0",
|
||||
"0000000000000000000000000000000008d0264f10bcdcd97da3faa38f85308d",
|
||||
"0000000000000000000000000000000065fed1506eb6605a899a54e155665f79",
|
||||
-1, -1,
|
||||
},
|
||||
{
|
||||
"8679085ab081dc92cdd23091ce3ee998f6b320e419c3475fae6b5b7d3081996e",
|
||||
"0000000000000000000000000000000089fbf24fbaa5c3c137b4f1cedc51d975",
|
||||
"00000000000000000000000000000000d38aa615bd6754d6f4d51ccdaf529fea",
|
||||
-1, -1,
|
||||
},
|
||||
{
|
||||
"6b1247bb7931dfcae5b5603c8b5ae22ce94d670138c51872225beae6bba8cdb3",
|
||||
"000000000000000000000000000000008acc2a521b21b17cfb002c83be62f55d",
|
||||
"0000000000000000000000000000000035f0eff4d7430950ecb2d94193dedc79",
|
||||
-1, -1,
|
||||
},
|
||||
{
|
||||
"a2e8ba2e8ba2e8ba2e8ba2e8ba2e8ba219b51835b55cc30ebfe2f6599bc56f58",
|
||||
"0000000000000000000000000000000045c53aa1bb56fcd68c011e2dad6758e4",
|
||||
"00000000000000000000000000000000a2e79d200f27f2360fba57619936159b",
|
||||
-1, -1,
|
||||
},
|
||||
}
|
||||
|
||||
s256 := S256()
|
||||
for i, test := range tests {
|
||||
k, ok := new(big.Int).SetString(test.k, 16)
|
||||
if !ok {
|
||||
t.Errorf("%d: bad value for k: %s", i, test.k)
|
||||
}
|
||||
k1, k2, k1Sign, k2Sign := s256.splitK(k.Bytes())
|
||||
k1str := fmt.Sprintf("%064x", k1)
|
||||
if test.k1 != k1str {
|
||||
t.Errorf("%d: bad k1: got %v, want %v", i, k1str, test.k1)
|
||||
}
|
||||
k2str := fmt.Sprintf("%064x", k2)
|
||||
if test.k2 != k2str {
|
||||
t.Errorf("%d: bad k2: got %v, want %v", i, k2str, test.k2)
|
||||
}
|
||||
if test.s1 != k1Sign {
|
||||
t.Errorf("%d: bad k1 sign: got %d, want %d", i, k1Sign, test.s1)
|
||||
}
|
||||
if test.s2 != k2Sign {
|
||||
t.Errorf("%d: bad k2 sign: got %d, want %d", i, k2Sign, test.s2)
|
||||
}
|
||||
k1Int := new(big.Int).SetBytes(k1)
|
||||
k1SignInt := new(big.Int).SetInt64(int64(k1Sign))
|
||||
k1Int.Mul(k1Int, k1SignInt)
|
||||
k2Int := new(big.Int).SetBytes(k2)
|
||||
k2SignInt := new(big.Int).SetInt64(int64(k2Sign))
|
||||
k2Int.Mul(k2Int, k2SignInt)
|
||||
gotK := new(big.Int).Mul(k2Int, s256.lambda)
|
||||
gotK.Add(k1Int, gotK)
|
||||
gotK.Mod(gotK, s256.N)
|
||||
if k.Cmp(gotK) != 0 {
|
||||
t.Errorf("%d: bad k: got %X, want %X", i, gotK.Bytes(), k.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitKRand(t *testing.T) {
|
||||
s256 := S256()
|
||||
for i := 0; i < 1024; i++ {
|
||||
bytesK := make([]byte, 32)
|
||||
_, err := rand.Read(bytesK)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read random data at %d", i)
|
||||
break
|
||||
}
|
||||
k := new(big.Int).SetBytes(bytesK)
|
||||
k1, k2, k1Sign, k2Sign := s256.splitK(bytesK)
|
||||
k1Int := new(big.Int).SetBytes(k1)
|
||||
k1SignInt := new(big.Int).SetInt64(int64(k1Sign))
|
||||
k1Int.Mul(k1Int, k1SignInt)
|
||||
k2Int := new(big.Int).SetBytes(k2)
|
||||
k2SignInt := new(big.Int).SetInt64(int64(k2Sign))
|
||||
k2Int.Mul(k2Int, k2SignInt)
|
||||
gotK := new(big.Int).Mul(k2Int, s256.lambda)
|
||||
gotK.Add(k1Int, gotK)
|
||||
gotK.Mod(gotK, s256.N)
|
||||
if k.Cmp(gotK) != 0 {
|
||||
t.Errorf("%d: bad k: got %X, want %X", i, gotK.Bytes(), k.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test this curve's usage with the ecdsa package.
|
||||
|
||||
func testKeyGeneration(t *testing.T, c *KoblitzCurve, tag string) {
|
||||
priv, err := NewPrivateKey(c)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error: %s", tag, err)
|
||||
return
|
||||
}
|
||||
if !c.IsOnCurve(priv.PublicKey.X, priv.PublicKey.Y) {
|
||||
t.Errorf("%s: public key invalid: %s", tag, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyGeneration(t *testing.T) {
|
||||
testKeyGeneration(t, S256(), "S256")
|
||||
}
|
||||
|
||||
func testSignAndVerify(t *testing.T, c *KoblitzCurve, tag string) {
|
||||
priv, _ := NewPrivateKey(c)
|
||||
pub := priv.PubKey()
|
||||
|
||||
hashed := []byte("testing")
|
||||
sig, err := priv.Sign(hashed)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error signing: %s", tag, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !sig.Verify(hashed, pub) {
|
||||
t.Errorf("%s: Verify failed", tag)
|
||||
}
|
||||
|
||||
hashed[0] ^= 0xff
|
||||
if sig.Verify(hashed, pub) {
|
||||
t.Errorf("%s: Verify always works!", tag)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignAndVerify(t *testing.T) {
|
||||
testSignAndVerify(t, S256(), "S256")
|
||||
}
|
||||
|
||||
func TestNAF(t *testing.T) {
|
||||
tests := []string{
|
||||
"6df2b5d30854069ccdec40ae022f5c948936324a4e9ebed8eb82cfd5a6b6d766",
|
||||
"b776e53fb55f6b006a270d42d64ec2b1",
|
||||
"d6cc32c857f1174b604eefc544f0c7f7",
|
||||
"45c53aa1bb56fcd68c011e2dad6758e4",
|
||||
"a2e79d200f27f2360fba57619936159b",
|
||||
}
|
||||
negOne := big.NewInt(-1)
|
||||
one := big.NewInt(1)
|
||||
two := big.NewInt(2)
|
||||
for i, test := range tests {
|
||||
want, _ := new(big.Int).SetString(test, 16)
|
||||
nafPos, nafNeg := NAF(want.Bytes())
|
||||
got := big.NewInt(0)
|
||||
// Check that the NAF representation comes up with the right number
|
||||
for i := 0; i < len(nafPos); i++ {
|
||||
bytePos := nafPos[i]
|
||||
byteNeg := nafNeg[i]
|
||||
for j := 7; j >= 0; j-- {
|
||||
got.Mul(got, two)
|
||||
if bytePos&0x80 == 0x80 {
|
||||
got.Add(got, one)
|
||||
} else if byteNeg&0x80 == 0x80 {
|
||||
got.Add(got, negOne)
|
||||
}
|
||||
bytePos <<= 1
|
||||
byteNeg <<= 1
|
||||
}
|
||||
}
|
||||
if got.Cmp(want) != 0 {
|
||||
t.Errorf("%d: Failed NAF got %X want %X", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNAFRand(t *testing.T) {
|
||||
negOne := big.NewInt(-1)
|
||||
one := big.NewInt(1)
|
||||
two := big.NewInt(2)
|
||||
for i := 0; i < 1024; i++ {
|
||||
data := make([]byte, 32)
|
||||
_, err := rand.Read(data)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read random data at %d", i)
|
||||
break
|
||||
}
|
||||
nafPos, nafNeg := NAF(data)
|
||||
want := new(big.Int).SetBytes(data)
|
||||
got := big.NewInt(0)
|
||||
// Check that the NAF representation comes up with the right number
|
||||
for i := 0; i < len(nafPos); i++ {
|
||||
bytePos := nafPos[i]
|
||||
byteNeg := nafNeg[i]
|
||||
for j := 7; j >= 0; j-- {
|
||||
got.Mul(got, two)
|
||||
if bytePos&0x80 == 0x80 {
|
||||
got.Add(got, one)
|
||||
} else if byteNeg&0x80 == 0x80 {
|
||||
got.Add(got, negOne)
|
||||
}
|
||||
bytePos <<= 1
|
||||
byteNeg <<= 1
|
||||
}
|
||||
}
|
||||
if got.Cmp(want) != 0 {
|
||||
t.Errorf("%d: Failed NAF got %X want %X", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
152
ecc/ecmh.go
152
ecc/ecmh.go
@ -1,152 +0,0 @@
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
)
|
||||
|
||||
// Multiset tracks the state of a multiset as used to calculate the ECMH
|
||||
// (elliptic curve multiset hash) hash of an unordered set. The state is
|
||||
// a point on the curve. New elements are hashed onto a point on the curve
|
||||
// and then added to the current state. Hence elements can be added in any
|
||||
// order and we can also remove elements to return to a prior hash.
|
||||
type Multiset struct {
|
||||
curve *KoblitzCurve
|
||||
x *big.Int
|
||||
y *big.Int
|
||||
}
|
||||
|
||||
// NewMultiset returns an empty multiset. The hash of an empty set
|
||||
// is the 32 byte value of zero.
|
||||
func NewMultiset(curve *KoblitzCurve) *Multiset {
|
||||
return &Multiset{curve: curve, x: big.NewInt(0), y: big.NewInt(0)}
|
||||
}
|
||||
|
||||
// NewMultisetFromPoint initializes a new multiset with the given x, y
|
||||
// coordinate.
|
||||
func NewMultisetFromPoint(curve *KoblitzCurve, x, y *big.Int) *Multiset {
|
||||
var copyX, copyY big.Int
|
||||
if x != nil {
|
||||
copyX.Set(x)
|
||||
}
|
||||
if y != nil {
|
||||
copyY.Set(y)
|
||||
}
|
||||
return &Multiset{curve: curve, x: ©X, y: ©Y}
|
||||
}
|
||||
|
||||
// NewMultisetFromDataSlice gets a curve and a slice of byte
|
||||
// slices, creates an empty multiset, hashes each data and
|
||||
// add it to the multiset, and return the resulting multiset.
|
||||
func NewMultisetFromDataSlice(curve *KoblitzCurve, datas [][]byte) *Multiset {
|
||||
ms := NewMultiset(curve)
|
||||
for _, data := range datas {
|
||||
x, y := hashToPoint(curve, data)
|
||||
ms.addPoint(x, y)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
|
||||
// Clone returns a clone of this multiset.
|
||||
func (ms *Multiset) Clone() *Multiset {
|
||||
return NewMultisetFromPoint(ms.curve, ms.x, ms.y)
|
||||
}
|
||||
|
||||
// Add hashes the data onto the curve and returns
|
||||
// a multiset with the new resulting point.
|
||||
func (ms *Multiset) Add(data []byte) *Multiset {
|
||||
newMs := ms.Clone()
|
||||
x, y := hashToPoint(ms.curve, data)
|
||||
newMs.addPoint(x, y)
|
||||
return newMs
|
||||
}
|
||||
|
||||
func (ms *Multiset) addPoint(x, y *big.Int) {
|
||||
ms.x, ms.y = ms.curve.Add(ms.x, ms.y, x, y)
|
||||
}
|
||||
|
||||
// Remove hashes the data onto the curve, subtracts
|
||||
// the point from the existing multiset, and returns
|
||||
// a multiset with the new point. This function
|
||||
// will execute regardless of whether or not the passed
|
||||
// data was previously added to the set. Hence if you
|
||||
// remove an element that was never added and also remove
|
||||
// all the elements that were added, you will not get
|
||||
// back to the point at infinity (empty set).
|
||||
func (ms *Multiset) Remove(data []byte) *Multiset {
|
||||
newMs := ms.Clone()
|
||||
x, y := hashToPoint(ms.curve, data)
|
||||
newMs.removePoint(x, y)
|
||||
return newMs
|
||||
}
|
||||
|
||||
func (ms *Multiset) removePoint(x, y *big.Int) {
|
||||
y.Neg(y).Mod(y, ms.curve.P)
|
||||
ms.x, ms.y = ms.curve.Add(ms.x, ms.y, x, y)
|
||||
}
|
||||
|
||||
// Union will add the point of the passed multiset instance to the point
|
||||
// of this multiset and will return a multiset with the resulting point.
|
||||
func (ms *Multiset) Union(otherMultiset *Multiset) *Multiset {
|
||||
newMs := ms.Clone()
|
||||
otherMsCopy := otherMultiset.Clone()
|
||||
newMs.addPoint(otherMsCopy.x, otherMsCopy.y)
|
||||
return newMs
|
||||
}
|
||||
|
||||
// Subtract will remove the point of the passed multiset instance from the point
|
||||
// of this multiset and will return a multiset with the resulting point.
|
||||
func (ms *Multiset) Subtract(otherMultiset *Multiset) *Multiset {
|
||||
newMs := ms.Clone()
|
||||
otherMsCopy := otherMultiset.Clone()
|
||||
newMs.removePoint(otherMsCopy.x, otherMsCopy.y)
|
||||
return newMs
|
||||
}
|
||||
|
||||
// Hash serializes and returns the hash of the multiset. The hash of an empty
|
||||
// set is the 32 byte value of zero. The hash of a non-empty multiset is the
|
||||
// sha256 hash of the 32 byte x value concatenated with the 32 byte y value.
|
||||
func (ms *Multiset) Hash() *daghash.Hash {
|
||||
if ms.x.Sign() == 0 && ms.y.Sign() == 0 {
|
||||
return &daghash.Hash{}
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(append(ms.x.Bytes(), ms.y.Bytes()...))
|
||||
castHash := daghash.Hash(hash)
|
||||
return &castHash
|
||||
}
|
||||
|
||||
// Point returns a copy of the x and y coordinates of the current multiset state.
|
||||
func (ms *Multiset) Point() (x *big.Int, y *big.Int) {
|
||||
var copyX, copyY big.Int
|
||||
copyX.Set(ms.x)
|
||||
copyY.Set(ms.y)
|
||||
return ©X, ©Y
|
||||
}
|
||||
|
||||
// hashToPoint hashes the passed data into a point on the curve. The x value
|
||||
// is sha256(n, sha256(data)) where n starts at zero. If the resulting x value
|
||||
// is not in the field or x^3+7 is not quadratic residue then n is incremented
|
||||
// and we try again. There is a 50% chance of success for any given iteration.
|
||||
func hashToPoint(curve *KoblitzCurve, data []byte) (x *big.Int, y *big.Int) {
|
||||
i := uint64(0)
|
||||
var err error
|
||||
h := sha256.Sum256(data)
|
||||
n := make([]byte, 8)
|
||||
for {
|
||||
binary.LittleEndian.PutUint64(n, i)
|
||||
h2 := sha256.Sum256(append(n, h[:]...))
|
||||
|
||||
x = new(big.Int).SetBytes(h2[:])
|
||||
|
||||
y, err = decompressPoint(curve, x, false)
|
||||
if err == nil && x.Cmp(curve.N) < 0 {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
return x, y
|
||||
}
|
213
ecc/ecmh_test.go
213
ecc/ecmh_test.go
@ -1,213 +0,0 @@
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
)
|
||||
|
||||
var testVectors = []struct {
|
||||
dataElementHex string
|
||||
point [2]string
|
||||
ecmhHash string
|
||||
cumulativeHash string
|
||||
}{
|
||||
{
|
||||
"982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e00000000010000000100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac",
|
||||
[2]string{"4f9a5dce69067bf28603e73a7af4c3650b16539b95bad05eee95dfc94d1efe2c", "346d5b777881f2729e7f89b2de4e8e79c7f2f42d1a0b25a8f10becb66e2d0f98"},
|
||||
"9378d88aa60cfba3032cb19f27891886e26fc6de1afa340c1787a633591983f8",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9b00000000020000000100f2052a010000004341047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac",
|
||||
[2]string{"68cf91eb2388a0287c13d46011c73fb8efb6be89c0867a47feccb2d11c390d2d", "f42ba72b1079d3d941881836f88b5dcd7c207a6a4839f129272c77ebb7194d42"},
|
||||
"e2f3dc6f3aa867c50bd41b80aa3bdafcc9e1d13a6292ff8a5da95da123d185ef",
|
||||
"afaa1f7ba0bd8a789422fdd6968639a4b8575baf7d54342a987073d038fdbafa",
|
||||
},
|
||||
{
|
||||
"44f672226090d85db9a9f2fbfe5f0f9609b387af7be5b7fbb7a1767c831c9e9900000000030000000100f2052a0100000043410494b9d3e76c5b1629ecf97fff95d7a4bbdac87cc26099ada28066c6ff1eb9191223cd897194a08d0c2726c5747f1db49e8cf90e75dc3e3550ae9b30086f3cd5aaac",
|
||||
[2]string{"359c6f59859d1d5af8e7081905cb6bb734c010be8680c14b5a89ee315694fc2b", "fb6ba531d4bd83b14c970ad1bec332a8ae9a05706cd5df7fd91a2f2cc32482fe"},
|
||||
"ffed6804617a4a33b1037cdd26426e61fde0faa2c0cc045efffa17c00ff4adcf",
|
||||
"e236a694532be6a4926ab8d5b1ff9cbfe638178e0008b0a8c5e87c3da2cdbc1c",
|
||||
},
|
||||
}
|
||||
|
||||
func TestHashToPoint(t *testing.T) {
|
||||
for _, test := range testVectors {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
x, y := hashToPoint(S256(), data)
|
||||
if hex.EncodeToString(x.Bytes()) != test.point[0] || hex.EncodeToString(y.Bytes()) != test.point[1] {
|
||||
t.Fatal("hashToPoint return incorrect point")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiset_Hash(t *testing.T) {
|
||||
for _, test := range testVectors {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
x, y := hashToPoint(S256(), data)
|
||||
m := NewMultisetFromPoint(S256(), x, y)
|
||||
if m.Hash().String() != test.ecmhHash {
|
||||
t.Fatal("Multiset-Hash returned incorrect hash serialization")
|
||||
}
|
||||
}
|
||||
m := NewMultiset(S256())
|
||||
emptySet := m.Hash()
|
||||
zeroHash := daghash.Hash{}
|
||||
if !bytes.Equal(emptySet[:], zeroHash[:]) {
|
||||
t.Fatal("Empty set did not return zero hash")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiset_AddRemove(t *testing.T) {
|
||||
m := NewMultiset(S256())
|
||||
for i, test := range testVectors {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m = m.Add(data)
|
||||
if test.cumulativeHash != "" && m.Hash().String() != test.cumulativeHash {
|
||||
t.Fatalf("Test #%d: Multiset-Add returned incorrect hash. Expected %s but got %s", i, test.cumulativeHash, m.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(testVectors) - 1; i > 0; i-- {
|
||||
data, err := hex.DecodeString(testVectors[i].dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m = m.Remove(data)
|
||||
if testVectors[i-1].cumulativeHash != "" && m.Hash().String() != testVectors[i-1].cumulativeHash {
|
||||
t.Fatalf("Test #%d: Multiset-Remove returned incorrect hash. Expected %s but got %s", i, testVectors[i].cumulativeHash, m.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiset_UnionSubtract(t *testing.T) {
|
||||
m1 := NewMultiset(S256())
|
||||
zeroHash := m1.Hash().String()
|
||||
|
||||
for _, test := range testVectors {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m1 = m1.Add(data)
|
||||
}
|
||||
|
||||
m2 := NewMultiset(S256())
|
||||
for _, test := range testVectors {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m2 = m2.Remove(data)
|
||||
}
|
||||
m1m2Union := m1.Union(m2)
|
||||
if m1m2Union.Hash().String() != zeroHash {
|
||||
t.Fatalf("m1m2Union was expected to return to have zero hash, but was %s instead", m1m2Union.Hash())
|
||||
}
|
||||
|
||||
m1Inverse := NewMultiset(S256()).Subtract(m1)
|
||||
m1InverseM1Union := m1.Union(m1Inverse)
|
||||
if m1InverseM1Union.Hash().String() != zeroHash {
|
||||
t.Fatalf("m1InverseM1Union was expected to have zero hash, but got %s instead", m1InverseM1Union.Hash())
|
||||
}
|
||||
|
||||
m1SubtractM1 := m1.Subtract(m1)
|
||||
if m1SubtractM1.Hash().String() != zeroHash {
|
||||
t.Fatalf("m1SubtractM1 was expected to have zero hash, but got %s instead", m1SubtractM1.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiset_Commutativity(t *testing.T) {
|
||||
m := NewMultiset(S256())
|
||||
zeroHash := m.Hash().String()
|
||||
|
||||
// Check that if we subtract values from zero and then re-add them, we return to zero.
|
||||
for _, test := range testVectors {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m = m.Remove(data)
|
||||
}
|
||||
|
||||
for _, test := range testVectors {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m = m.Add(data)
|
||||
}
|
||||
if m.Hash().String() != zeroHash {
|
||||
t.Fatalf("m was expected to be zero hash, but was %s instead", m.Hash())
|
||||
}
|
||||
|
||||
// Here we first remove an element from an empty multiset, and then add some other
|
||||
// elements, and then we create a new empty multiset, then we add the same elements
|
||||
// we added to the previous multiset, and then we remove the same element we remove
|
||||
// the same element we removed from the previous multiset. According to commutativity
|
||||
// laws, the result should be the same.
|
||||
removeIndex := 0
|
||||
removeData, err := hex.DecodeString(testVectors[removeIndex].dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
m1 := NewMultiset(S256())
|
||||
m1 = m1.Remove(removeData)
|
||||
|
||||
for i, test := range testVectors {
|
||||
if i != removeIndex {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m1 = m1.Add(data)
|
||||
}
|
||||
}
|
||||
|
||||
m2 := NewMultiset(S256())
|
||||
for i, test := range testVectors {
|
||||
if i != removeIndex {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m2 = m2.Add(data)
|
||||
}
|
||||
}
|
||||
m2 = m2.Remove(removeData)
|
||||
|
||||
if m1.Hash().String() != m2.Hash().String() {
|
||||
t.Fatalf("m1 and m2 was exepcted to have the same hash, but got instead m1 %s and m2 %s", m1.Hash(), m2.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiset_NewMultisetFromDataSlice(t *testing.T) {
|
||||
m1 := NewMultiset(S256())
|
||||
datas := make([][]byte, 0, len(testVectors))
|
||||
for _, test := range testVectors {
|
||||
data, err := hex.DecodeString(test.dataElementHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
datas = append(datas, data)
|
||||
m1 = m1.Add(data)
|
||||
}
|
||||
|
||||
m2 := NewMultisetFromDataSlice(S256(), datas)
|
||||
if m1.Hash().String() != m2.Hash().String() {
|
||||
t.Fatalf("m1 and m2 was exepcted to have the same hash, but got instead m1 %s and m2 %s", m1.Hash(), m2.Hash())
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
|
||||
"github.com/kaspanet/kaspad/ecc"
|
||||
)
|
||||
|
||||
// This example demonstrates signing a message with a secp256k1 private key that
|
||||
// is first parsed form raw bytes and serializing the generated signature.
|
||||
func Example_signMessage() {
|
||||
// Decode a hex-encoded private key.
|
||||
pkBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2d4f87" +
|
||||
"20ee63e502ee2869afab7de234b80c")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
privKey, pubKey := ecc.PrivKeyFromBytes(ecc.S256(), pkBytes)
|
||||
|
||||
// Sign a message using the private key.
|
||||
message := "test message"
|
||||
messageHash := daghash.DoubleHashB([]byte(message))
|
||||
signature, err := privKey.Sign(messageHash)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize and display the signature.
|
||||
fmt.Printf("Serialized Signature: %x\n", signature.Serialize())
|
||||
|
||||
// Verify the signature for the message using the public key.
|
||||
verified := signature.Verify(messageHash, pubKey)
|
||||
fmt.Printf("Signature Verified? %v\n", verified)
|
||||
|
||||
// Output:
|
||||
// Serialized Signature: 275d1c73a01b023377633cd1eaa6460e8cd0542ba089ad50450a8197246b3ef6d6836ef5a18226132c305e2b2060a699529cadc816fc98d63bc7d05771acec4d
|
||||
// Signature Verified? true
|
||||
}
|
||||
|
||||
// This example demonstrates verifying a secp256k1 signature against a public
|
||||
// key that is first parsed from raw bytes. The signature is also parsed from
|
||||
// raw bytes.
|
||||
func Example_verifySignature() {
|
||||
// Decode hex-encoded serialized public key.
|
||||
pubKeyBytes, err := hex.DecodeString("02a673638cb9587cb68ea08dbef685c" +
|
||||
"6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
pubKey, err := ecc.ParsePubKey(pubKeyBytes, ecc.S256())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Decode hex-encoded serialized signature.
|
||||
sigBytes, err := hex.DecodeString("275d1c73a01b023377633cd1eaa6460e8cd0542ba" +
|
||||
"089ad50450a8197246b3ef6d6836ef5a18226132c305e2b2060a699529cadc816fc98d63bc7d05771acec4d")
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
signature, err := ecc.ParseSignature(sigBytes)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify the signature for the message using the public key.
|
||||
message := "test message"
|
||||
messageHash := daghash.DoubleHashB([]byte(message))
|
||||
verified := signature.Verify(messageHash, pubKey)
|
||||
fmt.Println("Signature Verified?", verified)
|
||||
|
||||
// Output:
|
||||
// Signature Verified? true
|
||||
}
|
1219
ecc/field.go
1219
ecc/field.go
File diff suppressed because it is too large
Load Diff
@ -1,822 +0,0 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Copyright (c) 2013-2016 Dave Collins
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestSetInt ensures that setting a field value to various native integers
|
||||
// works as expected.
|
||||
func TestSetInt(t *testing.T) {
|
||||
tests := []struct {
|
||||
in uint
|
||||
raw [10]uint32
|
||||
}{
|
||||
{5, [10]uint32{5, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
// 2^26
|
||||
{67108864, [10]uint32{67108864, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
// 2^26 + 1
|
||||
{67108865, [10]uint32{67108865, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
// 2^32 - 1
|
||||
{4294967295, [10]uint32{4294967295, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetInt(test.in)
|
||||
if !reflect.DeepEqual(f.n, test.raw) {
|
||||
t.Errorf("fieldVal.Set #%d wrong result\ngot: %v\n"+
|
||||
"want: %v", i, f.n, test.raw)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestZero ensures that zeroing a field value zero works as expected.
|
||||
func TestZero(t *testing.T) {
|
||||
f := new(fieldVal).SetInt(2)
|
||||
f.Zero()
|
||||
for idx, rawInt := range f.n {
|
||||
if rawInt != 0 {
|
||||
t.Errorf("internal field integer at index #%d is not "+
|
||||
"zero - got %d", idx, rawInt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsZero ensures that checking if a field IsZero works as expected.
|
||||
func TestIsZero(t *testing.T) {
|
||||
f := new(fieldVal)
|
||||
if !f.IsZero() {
|
||||
t.Errorf("new field value is not zero - got %v (rawints %x)", f,
|
||||
f.n)
|
||||
}
|
||||
|
||||
f.SetInt(1)
|
||||
if f.IsZero() {
|
||||
t.Errorf("field claims it's zero when it's not - got %v "+
|
||||
"(raw rawints %x)", f, f.n)
|
||||
}
|
||||
|
||||
f.Zero()
|
||||
if !f.IsZero() {
|
||||
t.Errorf("field claims it's not zero when it is - got %v "+
|
||||
"(raw rawints %x)", f, f.n)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStringer ensures the stringer returns the appropriate hex string.
|
||||
func TestStringer(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
expected string
|
||||
}{
|
||||
{"0", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"1", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"a", "000000000000000000000000000000000000000000000000000000000000000a"},
|
||||
{"b", "000000000000000000000000000000000000000000000000000000000000000b"},
|
||||
{"c", "000000000000000000000000000000000000000000000000000000000000000c"},
|
||||
{"d", "000000000000000000000000000000000000000000000000000000000000000d"},
|
||||
{"e", "000000000000000000000000000000000000000000000000000000000000000e"},
|
||||
{"f", "000000000000000000000000000000000000000000000000000000000000000f"},
|
||||
{"f0", "00000000000000000000000000000000000000000000000000000000000000f0"},
|
||||
// 2^26-1
|
||||
{
|
||||
"3ffffff",
|
||||
"0000000000000000000000000000000000000000000000000000000003ffffff",
|
||||
},
|
||||
// 2^32-1
|
||||
{
|
||||
"ffffffff",
|
||||
"00000000000000000000000000000000000000000000000000000000ffffffff",
|
||||
},
|
||||
// 2^64-1
|
||||
{
|
||||
"ffffffffffffffff",
|
||||
"000000000000000000000000000000000000000000000000ffffffffffffffff",
|
||||
},
|
||||
// 2^96-1
|
||||
{
|
||||
"ffffffffffffffffffffffff",
|
||||
"0000000000000000000000000000000000000000ffffffffffffffffffffffff",
|
||||
},
|
||||
// 2^128-1
|
||||
{
|
||||
"ffffffffffffffffffffffffffffffff",
|
||||
"00000000000000000000000000000000ffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
// 2^160-1
|
||||
{
|
||||
"ffffffffffffffffffffffffffffffffffffffff",
|
||||
"000000000000000000000000ffffffffffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
// 2^192-1
|
||||
{
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
// 2^224-1
|
||||
{
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
// 2^256-4294968273 (the ecc prime, so should result in 0)
|
||||
{
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
// 2^256-4294968274 (the secp256k1 prime+1, so should result in 1)
|
||||
{
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30",
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
},
|
||||
|
||||
// Invalid hex
|
||||
{"g", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"1h", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"i1", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in)
|
||||
result := f.String()
|
||||
if result != test.expected {
|
||||
t.Errorf("fieldVal.String #%d wrong result\ngot: %v\n"+
|
||||
"want: %v", i, result, test.expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNormalize ensures that normalizing the internal field words works as
|
||||
// expected.
|
||||
func TestNormalize(t *testing.T) {
|
||||
tests := []struct {
|
||||
raw [10]uint32 // Intentionally denormalized value
|
||||
normalized [10]uint32 // Normalized form of the raw value
|
||||
}{
|
||||
{
|
||||
[10]uint32{0x00000005, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x00000005, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^26
|
||||
{
|
||||
[10]uint32{0x04000000, 0x0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x00000000, 0x1, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^26 + 1
|
||||
{
|
||||
[10]uint32{0x04000001, 0x0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x00000001, 0x1, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^32 - 1
|
||||
{
|
||||
[10]uint32{0xffffffff, 0x00, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x03ffffff, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^32
|
||||
{
|
||||
[10]uint32{0x04000000, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x00000000, 0x40, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^32 + 1
|
||||
{
|
||||
[10]uint32{0x04000001, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x00000001, 0x40, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^64 - 1
|
||||
{
|
||||
[10]uint32{0xffffffff, 0xffffffc0, 0xfc0, 0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x03ffffff, 0x03ffffff, 0xfff, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^64
|
||||
{
|
||||
[10]uint32{0x04000000, 0x03ffffff, 0x0fff, 0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x00000000, 0x00000000, 0x1000, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^64 + 1
|
||||
{
|
||||
[10]uint32{0x04000001, 0x03ffffff, 0x0fff, 0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x00000001, 0x00000000, 0x1000, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^96 - 1
|
||||
{
|
||||
[10]uint32{0xffffffff, 0xffffffc0, 0xffffffc0, 0x3ffc0, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x03ffffff, 0x03ffffff, 0x03ffffff, 0x3ffff, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^96
|
||||
{
|
||||
[10]uint32{0x04000000, 0x03ffffff, 0x03ffffff, 0x3ffff, 0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x00000000, 0x00000000, 0x00000000, 0x40000, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^128 - 1
|
||||
{
|
||||
[10]uint32{0xffffffff, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffc0, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0xffffff, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^128
|
||||
{
|
||||
[10]uint32{0x04000000, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x0ffffff, 0, 0, 0, 0, 0},
|
||||
[10]uint32{0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1000000, 0, 0, 0, 0, 0},
|
||||
},
|
||||
// 2^256 - 4294968273 (secp256k1 prime)
|
||||
{
|
||||
[10]uint32{0xfffffc2f, 0xffffff80, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0x3fffc0},
|
||||
[10]uint32{0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000},
|
||||
},
|
||||
// Prime larger than P where both first and second words are larger
|
||||
// than P's first and second words
|
||||
{
|
||||
[10]uint32{0xfffffc30, 0xffffff86, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0x3fffc0},
|
||||
[10]uint32{0x00000001, 0x00000006, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000},
|
||||
},
|
||||
// Prime larger than P where only the second word is larger
|
||||
// than P's second words.
|
||||
{
|
||||
[10]uint32{0xfffffc2a, 0xffffff87, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0x3fffc0},
|
||||
[10]uint32{0x03fffffb, 0x00000006, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000},
|
||||
},
|
||||
// 2^256 - 1
|
||||
{
|
||||
[10]uint32{0xffffffff, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0x3fffc0},
|
||||
[10]uint32{0x000003d0, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000},
|
||||
},
|
||||
// Prime with field representation such that the initial
|
||||
// reduction does not result in a carry to bit 256.
|
||||
//
|
||||
// 2^256 - 4294968273 (secp256k1 prime)
|
||||
{
|
||||
[10]uint32{0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff},
|
||||
[10]uint32{0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
|
||||
},
|
||||
// Prime larger than P that reduces to a value which is still
|
||||
// larger than P when it has a magnitude of 1 due to its first
|
||||
// word and does not result in a carry to bit 256.
|
||||
//
|
||||
// 2^256 - 4294968272 (secp256k1 prime + 1)
|
||||
{
|
||||
[10]uint32{0x03fffc30, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff},
|
||||
[10]uint32{0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
|
||||
},
|
||||
// Prime larger than P that reduces to a value which is still
|
||||
// larger than P when it has a magnitude of 1 due to its second
|
||||
// word and does not result in a carry to bit 256.
|
||||
//
|
||||
// 2^256 - 4227859409 (secp256k1 prime + 0x4000000)
|
||||
{
|
||||
[10]uint32{0x03fffc2f, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff},
|
||||
[10]uint32{0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
|
||||
},
|
||||
// Prime larger than P that reduces to a value which is still
|
||||
// larger than P when it has a magnitude of 1 due to a carry to
|
||||
// bit 256, but would not be without the carry. These values
|
||||
// come from the fact that P is 2^256 - 4294968273 and 977 is
|
||||
// the low order word in the internal field representation.
|
||||
//
|
||||
// 2^256 * 5 - ((4294968273 - (977+1)) * 4)
|
||||
{
|
||||
[10]uint32{0x03ffffff, 0x03fffeff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x0013fffff},
|
||||
[10]uint32{0x00001314, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000000},
|
||||
},
|
||||
// Prime larger than P that reduces to a value which is still
|
||||
// larger than P when it has a magnitude of 1 due to both a
|
||||
// carry to bit 256 and the first word.
|
||||
{
|
||||
[10]uint32{0x03fffc30, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x07ffffff, 0x003fffff},
|
||||
[10]uint32{0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001},
|
||||
},
|
||||
// Prime larger than P that reduces to a value which is still
|
||||
// larger than P when it has a magnitude of 1 due to both a
|
||||
// carry to bit 256 and the second word.
|
||||
//
|
||||
{
|
||||
[10]uint32{0x03fffc2f, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x3ffffff, 0x07ffffff, 0x003fffff},
|
||||
[10]uint32{0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000, 0x00000000, 0x00000001},
|
||||
},
|
||||
// Prime larger than P that reduces to a value which is still
|
||||
// larger than P when it has a magnitude of 1 due to a carry to
|
||||
// bit 256 and the first and second words.
|
||||
//
|
||||
{
|
||||
[10]uint32{0x03fffc30, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x07ffffff, 0x003fffff},
|
||||
[10]uint32{0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001},
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal)
|
||||
f.n = test.raw
|
||||
f.Normalize()
|
||||
if !reflect.DeepEqual(f.n, test.normalized) {
|
||||
t.Errorf("fieldVal.Normalize #%d wrong result\n"+
|
||||
"got: %x\nwant: %x", i, f.n, test.normalized)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsOdd ensures that checking if a field value IsOdd works as expected.
|
||||
func TestIsOdd(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string // hex encoded value
|
||||
expected bool // expected oddness
|
||||
}{
|
||||
{"0", false},
|
||||
{"1", true},
|
||||
{"2", false},
|
||||
// 2^32 - 1
|
||||
{"ffffffff", true},
|
||||
// 2^64 - 2
|
||||
{"fffffffffffffffe", false},
|
||||
// secp256k1 prime
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", true},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in)
|
||||
result := f.IsOdd()
|
||||
if result != test.expected {
|
||||
t.Errorf("fieldVal.IsOdd #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, test.expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestEquals ensures that checking two field values for equality via Equals
|
||||
// works as expected.
|
||||
func TestEquals(t *testing.T) {
|
||||
tests := []struct {
|
||||
in1 string // hex encoded value
|
||||
in2 string // hex encoded value
|
||||
expected bool // expected equality
|
||||
}{
|
||||
{"0", "0", true},
|
||||
{"0", "1", false},
|
||||
{"1", "0", false},
|
||||
// 2^32 - 1 == 2^32 - 1?
|
||||
{"ffffffff", "ffffffff", true},
|
||||
// 2^64 - 1 == 2^64 - 2?
|
||||
{"ffffffffffffffff", "fffffffffffffffe", false},
|
||||
// 0 == prime (mod prime)?
|
||||
{"0", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", true},
|
||||
// 1 == prime+1 (mod prime)?
|
||||
{"1", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", true},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in1).Normalize()
|
||||
f2 := new(fieldVal).SetHex(test.in2).Normalize()
|
||||
result := f.Equals(f2)
|
||||
if result != test.expected {
|
||||
t.Errorf("fieldVal.Equals #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, test.expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNegate ensures that negating field values via Negate works as expected.
|
||||
func TestNegate(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string // hex encoded value
|
||||
expected string // expected hex encoded value
|
||||
}{
|
||||
// secp256k1 prime (aka 0)
|
||||
{"0", "0"},
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "0"},
|
||||
{"0", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"},
|
||||
// secp256k1 prime-1
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", "1"},
|
||||
{"1", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e"},
|
||||
// secp256k1 prime-2
|
||||
{"2", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d"},
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", "2"},
|
||||
// Random sampling
|
||||
{
|
||||
"b3d9aac9c5e43910b4385b53c7e78c21d4cd5f8e683c633aed04c233efc2e120",
|
||||
"4c2655363a1bc6ef4bc7a4ac381873de2b32a07197c39cc512fb3dcb103d1b0f",
|
||||
},
|
||||
{
|
||||
"f8a85984fee5a12a7c8dd08830d83423c937d77c379e4a958e447a25f407733f",
|
||||
"757a67b011a5ed583722f77cf27cbdc36c82883c861b56a71bb85d90bf888f0",
|
||||
},
|
||||
{
|
||||
"45ee6142a7fda884211e93352ed6cb2807800e419533be723a9548823ece8312",
|
||||
"ba119ebd5802577bdee16ccad12934d7f87ff1be6acc418dc56ab77cc131791d",
|
||||
},
|
||||
{
|
||||
"53c2a668f07e411a2e473e1c3b6dcb495dec1227af27673761d44afe5b43d22b",
|
||||
"ac3d59970f81bee5d1b8c1e3c49234b6a213edd850d898c89e2bb500a4bc2a04",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in).Normalize()
|
||||
expected := new(fieldVal).SetHex(test.expected).Normalize()
|
||||
result := f.Negate(1).Normalize()
|
||||
if !result.Equals(expected) {
|
||||
t.Errorf("fieldVal.Negate #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddInt ensures that adding an integer to field values via AddInt works as
|
||||
// expected.
|
||||
func TestAddInt(t *testing.T) {
|
||||
tests := []struct {
|
||||
in1 string // hex encoded value
|
||||
in2 uint // unsigned integer to add to the value above
|
||||
expected string // expected hex encoded value
|
||||
}{
|
||||
{"0", 1, "1"},
|
||||
{"1", 0, "1"},
|
||||
{"1", 1, "2"},
|
||||
// secp256k1 prime-1 + 1
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", 1, "0"},
|
||||
// secp256k1 prime + 1
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 1, "1"},
|
||||
// Random samples.
|
||||
{
|
||||
"ff95ad9315aff04ab4af0ce673620c7145dc85d03bab5ba4b09ca2c4dec2d6c1",
|
||||
0x10f,
|
||||
"ff95ad9315aff04ab4af0ce673620c7145dc85d03bab5ba4b09ca2c4dec2d7d0",
|
||||
},
|
||||
{
|
||||
"44bdae6b772e7987941f1ba314e6a5b7804a4c12c00961b57d20f41deea9cecf",
|
||||
0x2cf11d41,
|
||||
"44bdae6b772e7987941f1ba314e6a5b7804a4c12c00961b57d20f41e1b9aec10",
|
||||
},
|
||||
{
|
||||
"88c3ecae67b591935fb1f6a9499c35315ffad766adca665c50b55f7105122c9c",
|
||||
0x4829aa2d,
|
||||
"88c3ecae67b591935fb1f6a9499c35315ffad766adca665c50b55f714d3bd6c9",
|
||||
},
|
||||
{
|
||||
"8523e9edf360ca32a95aae4e57fcde5a542b471d08a974d94ea0ee09a015e2a6",
|
||||
0xa21265a5,
|
||||
"8523e9edf360ca32a95aae4e57fcde5a542b471d08a974d94ea0ee0a4228484b",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in1).Normalize()
|
||||
expected := new(fieldVal).SetHex(test.expected).Normalize()
|
||||
result := f.AddInt(test.in2).Normalize()
|
||||
if !result.Equals(expected) {
|
||||
t.Errorf("fieldVal.AddInt #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdd ensures that adding two field values together via Add works as
|
||||
// expected.
|
||||
func TestAdd(t *testing.T) {
|
||||
tests := []struct {
|
||||
in1 string // first hex encoded value
|
||||
in2 string // second hex encoded value to add
|
||||
expected string // expected hex encoded value
|
||||
}{
|
||||
{"0", "1", "1"},
|
||||
{"1", "0", "1"},
|
||||
{"1", "1", "2"},
|
||||
// secp256k1 prime-1 + 1
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", "1", "0"},
|
||||
// secp256k1 prime + 1
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "1", "1"},
|
||||
// Random samples.
|
||||
{
|
||||
"2b2012f975404e5065b4292fb8bed0a5d315eacf24c74d8b27e73bcc5430edcc",
|
||||
"2c3cefa4e4753e8aeec6ac4c12d99da4d78accefda3b7885d4c6bab46c86db92",
|
||||
"575d029e59b58cdb547ad57bcb986e4aaaa0b7beff02c610fcadf680c0b7c95e",
|
||||
},
|
||||
{
|
||||
"8131e8722fe59bb189692b96c9f38de92885730f1dd39ab025daffb94c97f79c",
|
||||
"ff5454b765f0aab5f0977dcc629becc84cabeb9def48e79c6aadb2622c490fa9",
|
||||
"80863d2995d646677a00a9632c8f7ab175315ead0d1c824c9088b21c78e10b16",
|
||||
},
|
||||
{
|
||||
"c7c95e93d0892b2b2cdd77e80eb646ea61be7a30ac7e097e9f843af73fad5c22",
|
||||
"3afe6f91a74dfc1c7f15c34907ee981656c37236d946767dd53ccad9190e437c",
|
||||
"02c7ce2577d72747abf33b3116a4df00b881ec6785c47ffc74c105d158bba36f",
|
||||
},
|
||||
{
|
||||
"fd1c26f6a23381e5d785ba889494ec059369b888ad8431cd67d8c934b580dbe1",
|
||||
"a475aa5a31dcca90ef5b53c097d9133d6b7117474b41e7877bb199590fc0489c",
|
||||
"a191d150d4104c76c6e10e492c6dff42fedacfcff8c61954e38a628ec541284e",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in1).Normalize()
|
||||
f2 := new(fieldVal).SetHex(test.in2).Normalize()
|
||||
expected := new(fieldVal).SetHex(test.expected).Normalize()
|
||||
result := f.Add(f2).Normalize()
|
||||
if !result.Equals(expected) {
|
||||
t.Errorf("fieldVal.Add #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdd2 ensures that adding two field values together via Add2 works as
|
||||
// expected.
|
||||
func TestAdd2(t *testing.T) {
|
||||
tests := []struct {
|
||||
in1 string // first hex encoded value
|
||||
in2 string // second hex encoded value to add
|
||||
expected string // expected hex encoded value
|
||||
}{
|
||||
{"0", "1", "1"},
|
||||
{"1", "0", "1"},
|
||||
{"1", "1", "2"},
|
||||
// secp256k1 prime-1 + 1
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", "1", "0"},
|
||||
// secp256k1 prime + 1
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "1", "1"},
|
||||
// close but over the secp256k1 prime
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000", "f1ffff000", "1ffff3d1"},
|
||||
// Random samples.
|
||||
{
|
||||
"ad82b8d1cc136e23e9fd77fe2c7db1fe5a2ecbfcbde59ab3529758334f862d28",
|
||||
"4d6a4e95d6d61f4f46b528bebe152d408fd741157a28f415639347a84f6f574b",
|
||||
"faed0767a2e98d7330b2a0bcea92df3eea060d12380e8ec8b62a9fdb9ef58473",
|
||||
},
|
||||
{
|
||||
"f3f43a2540054a86e1df98547ec1c0e157b193e5350fb4a3c3ea214b228ac5e7",
|
||||
"25706572592690ea3ddc951a1b48b504a4c83dc253756e1b96d56fdfb3199522",
|
||||
"19649f97992bdb711fbc2d6e9a0a75e5fc79d1a7888522bf5abf912bd5a45eda",
|
||||
},
|
||||
{
|
||||
"6915bb94eef13ff1bb9b2633d997e13b9b1157c713363cc0e891416d6734f5b8",
|
||||
"11f90d6ac6fe1c4e8900b1c85fb575c251ec31b9bc34b35ada0aea1c21eded22",
|
||||
"7b0ec8ffb5ef5c40449bd7fc394d56fdecfd8980cf6af01bc29c2b898922e2da",
|
||||
},
|
||||
{
|
||||
"48b0c9eae622eed9335b747968544eb3e75cb2dc8128388f948aa30f88cabde4",
|
||||
"0989882b52f85f9d524a3a3061a0e01f46d597839d2ba637320f4b9510c8d2d5",
|
||||
"523a5216391b4e7685a5aea9c9f52ed32e324a601e53dec6c699eea4999390b9",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in1).Normalize()
|
||||
f2 := new(fieldVal).SetHex(test.in2).Normalize()
|
||||
expected := new(fieldVal).SetHex(test.expected).Normalize()
|
||||
result := f.Add2(f, f2).Normalize()
|
||||
if !result.Equals(expected) {
|
||||
t.Errorf("fieldVal.Add2 #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMulInt ensures that adding an integer to field values via MulInt works as
|
||||
// expected.
|
||||
func TestMulInt(t *testing.T) {
|
||||
tests := []struct {
|
||||
in1 string // hex encoded value
|
||||
in2 uint // unsigned integer to multiply with value above
|
||||
expected string // expected hex encoded value
|
||||
}{
|
||||
{"0", 0, "0"},
|
||||
{"1", 0, "0"},
|
||||
{"0", 1, "0"},
|
||||
{"1", 1, "1"},
|
||||
// secp256k1 prime-1 * 2
|
||||
{
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e",
|
||||
2,
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d",
|
||||
},
|
||||
// secp256k1 prime * 3
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 3, "0"},
|
||||
// secp256k1 prime-1 * 8
|
||||
{
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e",
|
||||
8,
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc27",
|
||||
},
|
||||
// Random samples for first value. The second value is limited
|
||||
// to 8 since that is the maximum int used in the elliptic curve
|
||||
// calculations.
|
||||
{
|
||||
"b75674dc9180d306c692163ac5e089f7cef166af99645c0c23568ab6d967288a",
|
||||
6,
|
||||
"4c06bd2b6904f228a76c8560a3433bced9a8681d985a2848d407404d186b0280",
|
||||
},
|
||||
{
|
||||
"54873298ac2b5ba8591c125ae54931f5ea72040aee07b208d6135476fb5b9c0e",
|
||||
3,
|
||||
"fd9597ca048212f90b543710afdb95e1bf560c20ca17161a8239fd64f212d42a",
|
||||
},
|
||||
{
|
||||
"7c30fbd363a74c17e1198f56b090b59bbb6c8755a74927a6cba7a54843506401",
|
||||
5,
|
||||
"6cf4eb20f2447c77657fccb172d38c0aa91ea4ac446dc641fa463a6b5091fba7",
|
||||
},
|
||||
{
|
||||
"fb4529be3e027a3d1587d8a500b72f2d312e3577340ef5175f96d113be4c2ceb",
|
||||
8,
|
||||
"da294df1f013d1e8ac3ec52805b979698971abb9a077a8bafcb688a4f261820f",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in1).Normalize()
|
||||
expected := new(fieldVal).SetHex(test.expected).Normalize()
|
||||
result := f.MulInt(test.in2).Normalize()
|
||||
if !result.Equals(expected) {
|
||||
t.Errorf("fieldVal.MulInt #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMul ensures that multiplying two field valuess via Mul works as expected.
|
||||
func TestMul(t *testing.T) {
|
||||
tests := []struct {
|
||||
in1 string // first hex encoded value
|
||||
in2 string // second hex encoded value to multiply with
|
||||
expected string // expected hex encoded value
|
||||
}{
|
||||
{"0", "0", "0"},
|
||||
{"1", "0", "0"},
|
||||
{"0", "1", "0"},
|
||||
{"1", "1", "1"},
|
||||
// slightly over prime
|
||||
{
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff1ffff",
|
||||
"1000",
|
||||
"1ffff3d1",
|
||||
},
|
||||
// secp256k1 prime-1 * 2
|
||||
{
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e",
|
||||
"2",
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d",
|
||||
},
|
||||
// secp256k1 prime * 3
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "3", "0"},
|
||||
// secp256k1 prime-1 * 8
|
||||
{
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e",
|
||||
"8",
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc27",
|
||||
},
|
||||
// Random samples.
|
||||
{
|
||||
"cfb81753d5ef499a98ecc04c62cb7768c2e4f1740032946db1c12e405248137e",
|
||||
"58f355ad27b4d75fb7db0442452e732c436c1f7c5a7c4e214fa9cc031426a7d3",
|
||||
"1018cd2d7c2535235b71e18db9cd98027386328d2fa6a14b36ec663c4c87282b",
|
||||
},
|
||||
{
|
||||
"26e9d61d1cdf3920e9928e85fa3df3e7556ef9ab1d14ec56d8b4fc8ed37235bf",
|
||||
"2dfc4bbe537afee979c644f8c97b31e58be5296d6dbc460091eae630c98511cf",
|
||||
"da85f48da2dc371e223a1ae63bd30b7e7ee45ae9b189ac43ff357e9ef8cf107a",
|
||||
},
|
||||
{
|
||||
"5db64ed5afb71646c8b231585d5b2bf7e628590154e0854c4c29920b999ff351",
|
||||
"279cfae5eea5d09ade8e6a7409182f9de40981bc31c84c3d3dfe1d933f152e9a",
|
||||
"2c78fbae91792dd0b157abe3054920049b1879a7cc9d98cfda927d83be411b37",
|
||||
},
|
||||
{
|
||||
"b66dfc1f96820b07d2bdbd559c19319a3a73c97ceb7b3d662f4fe75ecb6819e6",
|
||||
"bf774aba43e3e49eb63a6e18037d1118152568f1a3ac4ec8b89aeb6ff8008ae1",
|
||||
"c4f016558ca8e950c21c3f7fc15f640293a979c7b01754ee7f8b3340d4902ebb",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in1).Normalize()
|
||||
f2 := new(fieldVal).SetHex(test.in2).Normalize()
|
||||
expected := new(fieldVal).SetHex(test.expected).Normalize()
|
||||
result := f.Mul(f2).Normalize()
|
||||
if !result.Equals(expected) {
|
||||
t.Errorf("fieldVal.Mul #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSquare ensures that squaring field values via Square works as expected.
|
||||
func TestSquare(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string // hex encoded value
|
||||
expected string // expected hex encoded value
|
||||
}{
|
||||
// secp256k1 prime (aka 0)
|
||||
{"0", "0"},
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "0"},
|
||||
{"0", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"},
|
||||
// secp256k1 prime-1
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", "1"},
|
||||
// secp256k1 prime-2
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", "4"},
|
||||
// Random sampling
|
||||
{
|
||||
"b0ba920360ea8436a216128047aab9766d8faf468895eb5090fc8241ec758896",
|
||||
"133896b0b69fda8ce9f648b9a3af38f345290c9eea3cbd35bafcadf7c34653d3",
|
||||
},
|
||||
{
|
||||
"c55d0d730b1d0285a1599995938b042a756e6e8857d390165ffab480af61cbd5",
|
||||
"cd81758b3f5877cbe7e5b0a10cebfa73bcbf0957ca6453e63ee8954ab7780bee",
|
||||
},
|
||||
{
|
||||
"e89c1f9a70d93651a1ba4bca5b78658f00de65a66014a25544d3365b0ab82324",
|
||||
"39ffc7a43e5dbef78fd5d0354fb82c6d34f5a08735e34df29da14665b43aa1f",
|
||||
},
|
||||
{
|
||||
"7dc26186079d22bcbe1614aa20ae627e62d72f9be7ad1e99cac0feb438956f05",
|
||||
"bf86bcfc4edb3d81f916853adfda80c07c57745b008b60f560b1912f95bce8ae",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in).Normalize()
|
||||
expected := new(fieldVal).SetHex(test.expected).Normalize()
|
||||
result := f.Square().Normalize()
|
||||
if !result.Equals(expected) {
|
||||
t.Errorf("fieldVal.Square #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestInverse ensures that finding the multiplicative inverse via Inverse works
|
||||
// as expected.
|
||||
func TestInverse(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string // hex encoded value
|
||||
expected string // expected hex encoded value
|
||||
}{
|
||||
// secp256k1 prime (aka 0)
|
||||
{"0", "0"},
|
||||
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "0"},
|
||||
{"0", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"},
|
||||
// secp256k1 prime-1
|
||||
{
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e",
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e",
|
||||
},
|
||||
// secp256k1 prime-2
|
||||
{
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d",
|
||||
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe17",
|
||||
},
|
||||
// Random sampling
|
||||
{
|
||||
"16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca",
|
||||
"987aeb257b063df0c6d1334051c47092b6d8766c4bf10c463786d93f5bc54354",
|
||||
},
|
||||
{
|
||||
"69d1323ce9f1f7b3bd3c7320b0d6311408e30281e273e39a0d8c7ee1c8257919",
|
||||
"49340981fa9b8d3dad72de470b34f547ed9179c3953797d0943af67806f4bb6",
|
||||
},
|
||||
{
|
||||
"e0debf988ae098ecda07d0b57713e97c6d213db19753e8c95aa12a2fc1cc5272",
|
||||
"64f58077b68af5b656b413ea366863f7b2819f8d27375d9c4d9804135ca220c2",
|
||||
},
|
||||
{
|
||||
"dcd394f91f74c2ba16aad74a22bb0ed47fe857774b8f2d6c09e28bfb14642878",
|
||||
"fb848ec64d0be572a63c38fe83df5e7f3d032f60bf8c969ef67d36bf4ada22a9",
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
f := new(fieldVal).SetHex(test.in).Normalize()
|
||||
expected := new(fieldVal).SetHex(test.expected).Normalize()
|
||||
result := f.Inverse().Normalize()
|
||||
if !result.Equals(expected) {
|
||||
t.Errorf("fieldVal.Inverse #%d wrong result\n"+
|
||||
"got: %v\nwant: %v", i, result, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
// Copyright 2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file is ignored during the regular build due to the following build tag.
|
||||
// It is called by go generate and used to automatically generate pre-computed
|
||||
// tables used to accelerate operations.
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/kaspanet/kaspad/ecc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fi, err := os.Create("secp256k1.go")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer fi.Close()
|
||||
|
||||
// Compress the serialized byte points.
|
||||
serialized := ecc.S256().SerializedBytePoints()
|
||||
var compressed bytes.Buffer
|
||||
w := zlib.NewWriter(&compressed)
|
||||
if _, err := w.Write(serialized); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
w.Close()
|
||||
|
||||
// Encode the compressed byte points with base64.
|
||||
encoded := make([]byte, base64.StdEncoding.EncodedLen(compressed.Len()))
|
||||
base64.StdEncoding.Encode(encoded, compressed.Bytes())
|
||||
|
||||
fmt.Fprintln(fi, "// Copyright (c) 2015 The btcsuite developers")
|
||||
fmt.Fprintln(fi, "// Use of this source code is governed by an ISC")
|
||||
fmt.Fprintln(fi, "// license that can be found in the LICENSE file.")
|
||||
fmt.Fprintln(fi)
|
||||
fmt.Fprintln(fi, "package ecc")
|
||||
fmt.Fprintln(fi)
|
||||
fmt.Fprintln(fi, "// Auto-generated file (see genprecomps.go)")
|
||||
fmt.Fprintln(fi, "// DO NOT EDIT")
|
||||
fmt.Fprintln(fi)
|
||||
fmt.Fprintf(fi, "var secp256k1BytePoints = %q\n", string(encoded))
|
||||
|
||||
a1, b1, a2, b2 := ecc.S256().EndomorphismVectors()
|
||||
fmt.Println("The following values are the computed linearly " +
|
||||
"independent vectors needed to make use of the secp256k1 " +
|
||||
"endomorphism:")
|
||||
fmt.Printf("a1: %x\n", a1)
|
||||
fmt.Printf("b1: %x\n", b1)
|
||||
fmt.Printf("a2: %x\n", a2)
|
||||
fmt.Printf("b2: %x\n", b2)
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
// Copyright (c) 2014-2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file is ignored during the regular build due to the following build tag.
|
||||
// This build tag is set during go generate.
|
||||
// +build gensecp256k1
|
||||
|
||||
package ecc
|
||||
|
||||
// References:
|
||||
// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone)
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// secp256k1BytePoints are dummy points used so the code which generates the
|
||||
// real values can compile.
|
||||
var secp256k1BytePoints = ""
|
||||
|
||||
// getDoublingPoints returns all the possible G^(2^i) for i in
|
||||
// 0..n-1 where n is the curve's bit size (256 in the case of secp256k1)
|
||||
// the coordinates are recorded as Jacobian coordinates.
|
||||
func (curve *KoblitzCurve) getDoublingPoints() [][3]fieldVal {
|
||||
doublingPoints := make([][3]fieldVal, curve.BitSize)
|
||||
|
||||
// initialize px, py, pz to the Jacobian coordinates for the base point
|
||||
px, py := curve.bigAffineToField(curve.Gx, curve.Gy)
|
||||
pz := new(fieldVal).SetInt(1)
|
||||
for i := 0; i < curve.BitSize; i++ {
|
||||
doublingPoints[i] = [3]fieldVal{*px, *py, *pz}
|
||||
// P = 2*P
|
||||
curve.doubleJacobian(px, py, pz, px, py, pz)
|
||||
}
|
||||
return doublingPoints
|
||||
}
|
||||
|
||||
// SerializedBytePoints returns a serialized byte slice which contains all of
|
||||
// the possible points per 8-bit window. This is used to when generating
|
||||
// secp256k1.go.
|
||||
func (curve *KoblitzCurve) SerializedBytePoints() []byte {
|
||||
doublingPoints := curve.getDoublingPoints()
|
||||
|
||||
// Segregate the bits into byte-sized windows
|
||||
serialized := make([]byte, curve.byteSize*256*3*10*4)
|
||||
offset := 0
|
||||
for byteNum := 0; byteNum < curve.byteSize; byteNum++ {
|
||||
// Grab the 8 bits that make up this byte from doublingPoints.
|
||||
startingBit := 8 * (curve.byteSize - byteNum - 1)
|
||||
computingPoints := doublingPoints[startingBit : startingBit+8]
|
||||
|
||||
// Compute all points in this window and serialize them.
|
||||
for i := 0; i < 256; i++ {
|
||||
px, py, pz := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
for j := 0; j < 8; j++ {
|
||||
if i>>uint(j)&1 == 1 {
|
||||
curve.addJacobian(px, py, pz, &computingPoints[j][0],
|
||||
&computingPoints[j][1], &computingPoints[j][2], px, py, pz)
|
||||
}
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
binary.LittleEndian.PutUint32(serialized[offset:], px.n[i])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
binary.LittleEndian.PutUint32(serialized[offset:], py.n[i])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
binary.LittleEndian.PutUint32(serialized[offset:], pz.n[i])
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// sqrt returns the square root of the provided big integer using Newton's
|
||||
// method. It's only compiled and used during generation of pre-computed
|
||||
// values, so speed is not a huge concern.
|
||||
func sqrt(n *big.Int) *big.Int {
|
||||
// Initial guess = 2^(log_2(n)/2)
|
||||
guess := big.NewInt(2)
|
||||
guess.Exp(guess, big.NewInt(int64(n.BitLen()/2)), nil)
|
||||
|
||||
// Now refine using Newton's method.
|
||||
big2 := big.NewInt(2)
|
||||
prevGuess := big.NewInt(0)
|
||||
for {
|
||||
prevGuess.Set(guess)
|
||||
guess.Add(guess, new(big.Int).Div(n, guess))
|
||||
guess.Div(guess, big2)
|
||||
if guess.Cmp(prevGuess) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return guess
|
||||
}
|
||||
|
||||
// EndomorphismVectors runs the first 3 steps of algorithm 3.74 from [GECC] to
|
||||
// generate the linearly independent vectors needed to generate a balanced
|
||||
// length-two representation of a multiplier such that k = k1 + k2λ (mod N) and
|
||||
// returns them. Since the values will always be the same given the fact that N
|
||||
// and λ are fixed, the final results can be accelerated by storing the
|
||||
// precomputed values with the curve.
|
||||
func (curve *KoblitzCurve) EndomorphismVectors() (a1, b1, a2, b2 *big.Int) {
|
||||
bigMinus1 := big.NewInt(-1)
|
||||
|
||||
// This section uses an extended Euclidean algorithm to generate a
|
||||
// sequence of equations:
|
||||
// s[i] * N + t[i] * λ = r[i]
|
||||
|
||||
nSqrt := sqrt(curve.N)
|
||||
u, v := new(big.Int).Set(curve.N), new(big.Int).Set(curve.lambda)
|
||||
x1, y1 := big.NewInt(1), big.NewInt(0)
|
||||
x2, y2 := big.NewInt(0), big.NewInt(1)
|
||||
q, r := new(big.Int), new(big.Int)
|
||||
qu, qx1, qy1 := new(big.Int), new(big.Int), new(big.Int)
|
||||
s, t := new(big.Int), new(big.Int)
|
||||
ri, ti := new(big.Int), new(big.Int)
|
||||
a1, b1, a2, b2 = new(big.Int), new(big.Int), new(big.Int), new(big.Int)
|
||||
found, oneMore := false, false
|
||||
for u.Sign() != 0 {
|
||||
// q = v/u
|
||||
q.Div(v, u)
|
||||
|
||||
// r = v - q*u
|
||||
qu.Mul(q, u)
|
||||
r.Sub(v, qu)
|
||||
|
||||
// s = x2 - q*x1
|
||||
qx1.Mul(q, x1)
|
||||
s.Sub(x2, qx1)
|
||||
|
||||
// t = y2 - q*y1
|
||||
qy1.Mul(q, y1)
|
||||
t.Sub(y2, qy1)
|
||||
|
||||
// v = u, u = r, x2 = x1, x1 = s, y2 = y1, y1 = t
|
||||
v.Set(u)
|
||||
u.Set(r)
|
||||
x2.Set(x1)
|
||||
x1.Set(s)
|
||||
y2.Set(y1)
|
||||
y1.Set(t)
|
||||
|
||||
// As soon as the remainder is less than the sqrt of n, the
|
||||
// values of a1 and b1 are known.
|
||||
if !found && r.Cmp(nSqrt) < 0 {
|
||||
// When this condition executes ri and ti represent the
|
||||
// r[i] and t[i] values such that i is the greatest
|
||||
// index for which r >= sqrt(n). Meanwhile, the current
|
||||
// r and t values are r[i+1] and t[i+1], respectively.
|
||||
|
||||
// a1 = r[i+1], b1 = -t[i+1]
|
||||
a1.Set(r)
|
||||
b1.Mul(t, bigMinus1)
|
||||
found = true
|
||||
oneMore = true
|
||||
|
||||
// Skip to the next iteration so ri and ti are not
|
||||
// modified.
|
||||
continue
|
||||
|
||||
} else if oneMore {
|
||||
// When this condition executes ri and ti still
|
||||
// represent the r[i] and t[i] values while the current
|
||||
// r and t are r[i+2] and t[i+2], respectively.
|
||||
|
||||
// sum1 = r[i]^2 + t[i]^2
|
||||
rSquared := new(big.Int).Mul(ri, ri)
|
||||
tSquared := new(big.Int).Mul(ti, ti)
|
||||
sum1 := new(big.Int).Add(rSquared, tSquared)
|
||||
|
||||
// sum2 = r[i+2]^2 + t[i+2]^2
|
||||
r2Squared := new(big.Int).Mul(r, r)
|
||||
t2Squared := new(big.Int).Mul(t, t)
|
||||
sum2 := new(big.Int).Add(r2Squared, t2Squared)
|
||||
|
||||
// if (r[i]^2 + t[i]^2) <= (r[i+2]^2 + t[i+2]^2)
|
||||
if sum1.Cmp(sum2) <= 0 {
|
||||
// a2 = r[i], b2 = -t[i]
|
||||
a2.Set(ri)
|
||||
b2.Mul(ti, bigMinus1)
|
||||
} else {
|
||||
// a2 = r[i+2], b2 = -t[i+2]
|
||||
a2.Set(r)
|
||||
b2.Mul(t, bigMinus1)
|
||||
}
|
||||
|
||||
// All done.
|
||||
break
|
||||
}
|
||||
|
||||
ri.Set(r)
|
||||
ti.Set(t)
|
||||
}
|
||||
|
||||
return a1, b1, a2, b2
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
// Copyright 2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"compress/zlib"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:generate go run -tags gensecp256k1 genprecomps.go
|
||||
|
||||
// loadS256BytePoints decompresses and deserializes the pre-computed byte points
|
||||
// used to accelerate scalar base multiplication for the secp256k1 curve. This
|
||||
// approach is used since it allows the compile to use significantly less ram
|
||||
// and be performed much faster than it is with hard-coding the final in-memory
|
||||
// data structure. At the same time, it is quite fast to generate the in-memory
|
||||
// data structure at init time with this approach versus computing the table.
|
||||
func loadS256BytePoints() error {
|
||||
// There will be no byte points to load when generating them.
|
||||
bp := secp256k1BytePoints
|
||||
if len(bp) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decompress the pre-computed table used to accelerate scalar base
|
||||
// multiplication.
|
||||
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(bp))
|
||||
r, err := zlib.NewReader(decoder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serialized, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deserialize the precomputed byte points and set the curve to them.
|
||||
offset := 0
|
||||
var bytePoints [32][256][3]fieldVal
|
||||
for byteNum := 0; byteNum < 32; byteNum++ {
|
||||
// All points in this window.
|
||||
for i := 0; i < 256; i++ {
|
||||
px := &bytePoints[byteNum][i][0]
|
||||
py := &bytePoints[byteNum][i][1]
|
||||
pz := &bytePoints[byteNum][i][2]
|
||||
for i := 0; i < 10; i++ {
|
||||
px.n[i] = binary.LittleEndian.Uint32(serialized[offset:])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
py.n[i] = binary.LittleEndian.Uint32(serialized[offset:])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
pz.n[i] = binary.LittleEndian.Uint32(serialized[offset:])
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
secp256k1.bytePoints = &bytePoints
|
||||
return nil
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// PrivateKey wraps an ecdsa.PrivateKey as a convenience mainly for signing
|
||||
// things with the the private key without having to directly import the ecdsa
|
||||
// package.
|
||||
type PrivateKey ecdsa.PrivateKey
|
||||
|
||||
// PrivKeyFromBytes returns a private and public key for `curve' based on the
|
||||
// private key passed as an argument as a byte slice.
|
||||
func PrivKeyFromBytes(curve elliptic.Curve, pk []byte) (*PrivateKey,
|
||||
*PublicKey) {
|
||||
x, y := curve.ScalarBaseMult(pk)
|
||||
|
||||
priv := &ecdsa.PrivateKey{
|
||||
PublicKey: ecdsa.PublicKey{
|
||||
Curve: curve,
|
||||
X: x,
|
||||
Y: y,
|
||||
},
|
||||
D: new(big.Int).SetBytes(pk),
|
||||
}
|
||||
|
||||
return (*PrivateKey)(priv), (*PublicKey)(&priv.PublicKey)
|
||||
}
|
||||
|
||||
// NewPrivateKey is a wrapper for ecdsa.GenerateKey that returns a PrivateKey
|
||||
// instead of the normal ecdsa.PrivateKey.
|
||||
func NewPrivateKey(curve elliptic.Curve) (*PrivateKey, error) {
|
||||
key, err := ecdsa.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*PrivateKey)(key), nil
|
||||
}
|
||||
|
||||
// PubKey returns the PublicKey corresponding to this private key.
|
||||
func (p *PrivateKey) PubKey() *PublicKey {
|
||||
return (*PublicKey)(&p.PublicKey)
|
||||
}
|
||||
|
||||
// ToECDSA returns the private key as a *ecdsa.PrivateKey.
|
||||
func (p *PrivateKey) ToECDSA() *ecdsa.PrivateKey {
|
||||
return (*ecdsa.PrivateKey)(p)
|
||||
}
|
||||
|
||||
// Sign generates a Schnorr signature for the provided hash (which should be the result
|
||||
// of hashing a larger message) using the private key. Produced signature
|
||||
// is deterministic (same message and same key yield the same signature).
|
||||
func (p *PrivateKey) Sign(hash []byte) (*Signature, error) {
|
||||
return sign(p, hash)
|
||||
}
|
||||
|
||||
// PrivKeyBytesLen defines the length in bytes of a serialized private key.
|
||||
const PrivKeyBytesLen = 32
|
||||
|
||||
// Serialize returns the private key number d as a big-endian binary-encoded
|
||||
// number, padded to a length of 32 bytes.
|
||||
func (p *PrivateKey) Serialize() []byte {
|
||||
b := make([]byte, 0, PrivKeyBytesLen)
|
||||
return paddedAppend(PrivKeyBytesLen, b, p.ToECDSA().D.Bytes())
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPrivKeys(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key []byte
|
||||
}{
|
||||
{
|
||||
name: "check curve",
|
||||
key: []byte{
|
||||
0xea, 0xf0, 0x2c, 0xa3, 0x48, 0xc5, 0x24, 0xe6,
|
||||
0x39, 0x26, 0x55, 0xba, 0x4d, 0x29, 0x60, 0x3c,
|
||||
0xd1, 0xa7, 0x34, 0x7d, 0x9d, 0x65, 0xcf, 0xe9,
|
||||
0x3c, 0xe1, 0xeb, 0xff, 0xdc, 0xa2, 0x26, 0x94,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
priv, pub := PrivKeyFromBytes(S256(), test.key)
|
||||
|
||||
_, err := ParsePubKey(pub.SerializeUncompressed(), S256())
|
||||
if err != nil {
|
||||
t.Errorf("%s privkey: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
hash := []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9}
|
||||
sig, err := priv.Sign(hash)
|
||||
if err != nil {
|
||||
t.Errorf("%s could not sign: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !sig.Verify(hash, pub) {
|
||||
t.Errorf("%s could not verify: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
serializedKey := priv.Serialize()
|
||||
if !bytes.Equal(serializedKey, test.key) {
|
||||
t.Errorf("%s unexpected serialized bytes - got: %x, "+
|
||||
"want: %x", test.name, serializedKey, test.key)
|
||||
}
|
||||
}
|
||||
}
|
189
ecc/pubkey.go
189
ecc/pubkey.go
@ -1,189 +0,0 @@
|
||||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"github.com/pkg/errors"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// These constants define the lengths of serialized public keys.
|
||||
const (
|
||||
PubKeyBytesLenCompressed = 33
|
||||
PubKeyBytesLenUncompressed = 65
|
||||
PubKeyBytesLenHybrid = 65
|
||||
)
|
||||
|
||||
func isOdd(a *big.Int) bool {
|
||||
return a.Bit(0) == 1
|
||||
}
|
||||
|
||||
// decompressPoint decompresses a point on the given curve given the X point and
|
||||
// the solution to use.
|
||||
func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, error) {
|
||||
// TODO: This will probably only work for secp256k1 due to
|
||||
// optimizations.
|
||||
|
||||
// Y = +-sqrt(x^3 + B)
|
||||
x3 := new(big.Int).Mul(x, x)
|
||||
x3.Mul(x3, x)
|
||||
x3.Add(x3, curve.Params().B)
|
||||
x3.Mod(x3, curve.Params().P)
|
||||
|
||||
// Now calculate sqrt mod p of x^3 + B
|
||||
// This code used to do a full sqrt based on tonelli/shanks,
|
||||
// but this was replaced by the algorithms referenced in
|
||||
// https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294
|
||||
y := new(big.Int).Exp(x3, curve.QPlus1Div4(), curve.Params().P)
|
||||
|
||||
// Check that y is a square root of x^3 + B.
|
||||
y2 := new(big.Int).Mul(y, y)
|
||||
y2.Mod(y2, curve.Params().P)
|
||||
if y2.Cmp(x3) != 0 {
|
||||
return nil, errors.Errorf("invalid square root")
|
||||
}
|
||||
|
||||
// Verify that y-coord has expected parity.
|
||||
if ybit != isOdd(y) {
|
||||
y.Sub(curve.Params().P, y)
|
||||
}
|
||||
if ybit != isOdd(y) {
|
||||
return nil, errors.Errorf("ybit doesn't match oddness")
|
||||
}
|
||||
return y, nil
|
||||
}
|
||||
|
||||
const (
|
||||
pubkeyCompressed byte = 0x2 // y_bit + x coord
|
||||
pubkeyUncompressed byte = 0x4 // x coord + y coord
|
||||
pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord
|
||||
)
|
||||
|
||||
// IsCompressedPubKey returns true the the passed serialized public key has
|
||||
// been encoded in compressed format, and false otherwise.
|
||||
func IsCompressedPubKey(pubKey []byte) bool {
|
||||
// The public key is only compressed if it is the correct length and
|
||||
// the format (first byte) is one of the compressed pubkey values.
|
||||
return len(pubKey) == PubKeyBytesLenCompressed &&
|
||||
(pubKey[0]&^byte(0x1) == pubkeyCompressed)
|
||||
}
|
||||
|
||||
// ParsePubKey parses a public key for a koblitz curve from a bytestring into a
|
||||
// ecdsa.Publickey, verifying that it is valid. It supports compressed,
|
||||
// uncompressed and hybrid signature formats.
|
||||
func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) {
|
||||
pubkey := PublicKey{}
|
||||
pubkey.Curve = curve
|
||||
|
||||
if len(pubKeyStr) == 0 {
|
||||
return nil, errors.New("pubkey string is empty")
|
||||
}
|
||||
|
||||
format := pubKeyStr[0]
|
||||
ybit := (format & 0x1) == 0x1
|
||||
format &= ^byte(0x1)
|
||||
|
||||
switch len(pubKeyStr) {
|
||||
case PubKeyBytesLenUncompressed:
|
||||
if format != pubkeyUncompressed && format != pubkeyHybrid {
|
||||
return nil, errors.Errorf("invalid magic in pubkey str: "+
|
||||
"%d", pubKeyStr[0])
|
||||
}
|
||||
|
||||
pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
|
||||
pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:])
|
||||
// hybrid keys have extra information, make use of it.
|
||||
if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) {
|
||||
return nil, errors.Errorf("ybit doesn't match oddness")
|
||||
}
|
||||
case PubKeyBytesLenCompressed:
|
||||
// format is 0x2 | solution, <X coordinate>
|
||||
// solution determines which solution of the curve we use.
|
||||
/// y^2 = x^3 + Curve.B
|
||||
if format != pubkeyCompressed {
|
||||
return nil, errors.Errorf("invalid magic in compressed "+
|
||||
"pubkey string: %d", pubKeyStr[0])
|
||||
}
|
||||
pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
|
||||
pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default: // wrong!
|
||||
return nil, errors.Errorf("invalid pub key length %d",
|
||||
len(pubKeyStr))
|
||||
}
|
||||
|
||||
if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 {
|
||||
return nil, errors.Errorf("pubkey X parameter is >= to P")
|
||||
}
|
||||
if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 {
|
||||
return nil, errors.Errorf("pubkey Y parameter is >= to P")
|
||||
}
|
||||
if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
|
||||
return nil, errors.Errorf("pubkey isn't on secp256k1 curve")
|
||||
}
|
||||
return &pubkey, nil
|
||||
}
|
||||
|
||||
// PublicKey is an ecdsa.PublicKey with additional functions to
|
||||
// serialize in uncompressed, compressed, and hybrid formats.
|
||||
type PublicKey ecdsa.PublicKey
|
||||
|
||||
// ToECDSA returns the public key as a *ecdsa.PublicKey.
|
||||
func (p *PublicKey) ToECDSA() *ecdsa.PublicKey {
|
||||
return (*ecdsa.PublicKey)(p)
|
||||
}
|
||||
|
||||
// SerializeUncompressed serializes a public key in a 65-byte uncompressed
|
||||
// format.
|
||||
func (p *PublicKey) SerializeUncompressed() []byte {
|
||||
b := make([]byte, 0, PubKeyBytesLenUncompressed)
|
||||
b = append(b, pubkeyUncompressed)
|
||||
b = paddedAppend(32, b, p.X.Bytes())
|
||||
return paddedAppend(32, b, p.Y.Bytes())
|
||||
}
|
||||
|
||||
// SerializeCompressed serializes a public key in a 33-byte compressed format.
|
||||
func (p *PublicKey) SerializeCompressed() []byte {
|
||||
b := make([]byte, 0, PubKeyBytesLenCompressed)
|
||||
format := pubkeyCompressed
|
||||
if isOdd(p.Y) {
|
||||
format |= 0x1
|
||||
}
|
||||
b = append(b, format)
|
||||
return paddedAppend(32, b, p.X.Bytes())
|
||||
}
|
||||
|
||||
// SerializeHybrid serializes a public key in a 65-byte hybrid format.
|
||||
func (p *PublicKey) SerializeHybrid() []byte {
|
||||
b := make([]byte, 0, PubKeyBytesLenHybrid)
|
||||
format := pubkeyHybrid
|
||||
if isOdd(p.Y) {
|
||||
format |= 0x1
|
||||
}
|
||||
b = append(b, format)
|
||||
b = paddedAppend(32, b, p.X.Bytes())
|
||||
return paddedAppend(32, b, p.Y.Bytes())
|
||||
}
|
||||
|
||||
// IsEqual compares this PublicKey instance to the one passed, returning true if
|
||||
// both PublicKeys are equivalent. A PublicKey is equivalent to another, if they
|
||||
// both have the same X and Y coordinate.
|
||||
func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool {
|
||||
return p.X.Cmp(otherPubKey.X) == 0 &&
|
||||
p.Y.Cmp(otherPubKey.Y) == 0
|
||||
}
|
||||
|
||||
// paddedAppend appends the src byte slice to dst, returning the new slice.
|
||||
// If the length of the source is smaller than the passed size, leading zero
|
||||
// bytes are appended to the dst slice before appending src.
|
||||
func paddedAppend(size uint, dst, src []byte) []byte {
|
||||
for i := 0; i < int(size)-len(src); i++ {
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
return append(dst, src...)
|
||||
}
|
@ -1,294 +0,0 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
type pubKeyTest struct {
|
||||
name string
|
||||
key []byte
|
||||
format byte
|
||||
isValid bool
|
||||
}
|
||||
|
||||
var pubKeyTests = []pubKeyTest{
|
||||
{
|
||||
name: "uncompressed ok",
|
||||
key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
|
||||
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
|
||||
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
|
||||
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0,
|
||||
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
|
||||
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
|
||||
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
|
||||
0xb4, 0x12, 0xa3,
|
||||
},
|
||||
isValid: true,
|
||||
format: pubkeyUncompressed,
|
||||
},
|
||||
{
|
||||
name: "uncompressed x changed",
|
||||
key: []byte{0x04, 0x15, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
|
||||
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
|
||||
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
|
||||
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0,
|
||||
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
|
||||
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
|
||||
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
|
||||
0xb4, 0x12, 0xa3,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "uncompressed y changed",
|
||||
key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
|
||||
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
|
||||
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
|
||||
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0,
|
||||
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
|
||||
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
|
||||
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
|
||||
0xb4, 0x12, 0xa4,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "uncompressed claims compressed",
|
||||
key: []byte{0x03, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
|
||||
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
|
||||
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
|
||||
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0,
|
||||
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
|
||||
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
|
||||
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
|
||||
0xb4, 0x12, 0xa3,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "uncompressed as hybrid ok",
|
||||
key: []byte{0x07, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
|
||||
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
|
||||
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
|
||||
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0,
|
||||
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
|
||||
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
|
||||
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
|
||||
0xb4, 0x12, 0xa3,
|
||||
},
|
||||
isValid: true,
|
||||
format: pubkeyHybrid,
|
||||
},
|
||||
{
|
||||
name: "uncompressed as hybrid wrong",
|
||||
key: []byte{0x06, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
|
||||
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
|
||||
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
|
||||
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0,
|
||||
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
|
||||
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
|
||||
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
|
||||
0xb4, 0x12, 0xa3,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
// from tx 0b09c51c51ff762f00fb26217269d2a18e77a4fa87d69b3c363ab4df16543f20
|
||||
{
|
||||
name: "compressed ok (ybit = 0)",
|
||||
key: []byte{0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b,
|
||||
0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1,
|
||||
0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21,
|
||||
0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d,
|
||||
},
|
||||
isValid: true,
|
||||
format: pubkeyCompressed,
|
||||
},
|
||||
// from tx fdeb8e72524e8dab0da507ddbaf5f88fe4a933eb10a66bc4745bb0aa11ea393c
|
||||
{
|
||||
name: "compressed ok (ybit = 1)",
|
||||
key: []byte{0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33,
|
||||
0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34,
|
||||
0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4,
|
||||
0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e,
|
||||
},
|
||||
isValid: true,
|
||||
format: pubkeyCompressed,
|
||||
},
|
||||
{
|
||||
name: "compressed claims uncompressed (ybit = 0)",
|
||||
key: []byte{0x04, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b,
|
||||
0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1,
|
||||
0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21,
|
||||
0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "compressed claims uncompressed (ybit = 1)",
|
||||
key: []byte{0x05, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33,
|
||||
0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34,
|
||||
0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4,
|
||||
0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "wrong length)",
|
||||
key: []byte{0x05},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "X == P",
|
||||
key: []byte{0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, 0xb2, 0xe0,
|
||||
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
|
||||
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
|
||||
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
|
||||
0xb4, 0x12, 0xa3,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "X > P",
|
||||
key: []byte{0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFD, 0x2F, 0xb2, 0xe0,
|
||||
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
|
||||
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
|
||||
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
|
||||
0xb4, 0x12, 0xa3,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "Y == P",
|
||||
key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
|
||||
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
|
||||
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
|
||||
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF,
|
||||
0xFF, 0xFC, 0x2F,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "Y > P",
|
||||
key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
|
||||
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
|
||||
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
|
||||
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF,
|
||||
0xFF, 0xFD, 0x2F,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "hybrid",
|
||||
key: []byte{0x06, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb,
|
||||
0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07,
|
||||
0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59,
|
||||
0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98, 0x48, 0x3a,
|
||||
0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb,
|
||||
0xfc, 0x0e, 0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48,
|
||||
0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47, 0xd0, 0x8f, 0xfb,
|
||||
0x10, 0xd4, 0xb8,
|
||||
},
|
||||
format: pubkeyHybrid,
|
||||
isValid: true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestPubKeys(t *testing.T) {
|
||||
for _, test := range pubKeyTests {
|
||||
pk, err := ParsePubKey(test.key, S256())
|
||||
if err != nil {
|
||||
if test.isValid {
|
||||
t.Errorf("%s pubkey failed when shouldn't %v",
|
||||
test.name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !test.isValid {
|
||||
t.Errorf("%s counted as valid when it should fail",
|
||||
test.name)
|
||||
continue
|
||||
}
|
||||
var pkStr []byte
|
||||
switch test.format {
|
||||
case pubkeyUncompressed:
|
||||
pkStr = (*PublicKey)(pk).SerializeUncompressed()
|
||||
case pubkeyCompressed:
|
||||
pkStr = (*PublicKey)(pk).SerializeCompressed()
|
||||
case pubkeyHybrid:
|
||||
pkStr = (*PublicKey)(pk).SerializeHybrid()
|
||||
}
|
||||
if !bytes.Equal(test.key, pkStr) {
|
||||
t.Errorf("%s pubkey: serialized keys do not match.",
|
||||
test.name)
|
||||
spew.Dump(test.key)
|
||||
spew.Dump(pkStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicKeyIsEqual(t *testing.T) {
|
||||
pubKey1, err := ParsePubKey(
|
||||
[]byte{0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33,
|
||||
0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34,
|
||||
0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4,
|
||||
0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e,
|
||||
},
|
||||
S256(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse raw bytes for pubKey1: %v", err)
|
||||
}
|
||||
|
||||
pubKey2, err := ParsePubKey(
|
||||
[]byte{0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b,
|
||||
0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1,
|
||||
0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21,
|
||||
0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d,
|
||||
},
|
||||
S256(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse raw bytes for pubKey2: %v", err)
|
||||
}
|
||||
|
||||
if !pubKey1.IsEqual(pubKey1) {
|
||||
t.Fatalf("value of IsEqual is incorrect, %v is "+
|
||||
"equal to %v", pubKey1, pubKey1)
|
||||
}
|
||||
|
||||
if pubKey1.IsEqual(pubKey2) {
|
||||
t.Fatalf("value of IsEqual is incorrect, %v is not "+
|
||||
"equal to %v", pubKey1, pubKey2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCompressed(t *testing.T) {
|
||||
for _, test := range pubKeyTests {
|
||||
isCompressed := IsCompressedPubKey(test.key)
|
||||
wantCompressed := (test.format == pubkeyCompressed)
|
||||
if isCompressed != wantCompressed {
|
||||
t.Fatalf("%s (%x) pubkey: unexpected compressed result, "+
|
||||
"got %v, want %v", test.name, test.key,
|
||||
isCompressed, wantCompressed)
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
261
ecc/signature.go
261
ecc/signature.go
@ -1,261 +0,0 @@
|
||||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"github.com/pkg/errors"
|
||||
"hash"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Signature is a type representing a Schnorr signature.
|
||||
type Signature struct {
|
||||
R *big.Int
|
||||
S *big.Int
|
||||
}
|
||||
|
||||
var (
|
||||
// Used in RFC6979 implementation when testing the nonce for correctness
|
||||
one = big.NewInt(1)
|
||||
|
||||
// oneInitializer is used to fill a byte slice with byte 0x01. It is provided
|
||||
// here to avoid the need to create it multiple times.
|
||||
oneInitializer = []byte{0x01}
|
||||
)
|
||||
|
||||
// Serialize returns a serialized signature (R and S concatenated).
|
||||
func (sig *Signature) Serialize() []byte {
|
||||
return append(bigIntTo32Bytes(sig.R), bigIntTo32Bytes(sig.S)...)
|
||||
}
|
||||
|
||||
// Verify verifies digital signatures. It returns true if the signature
|
||||
// is valid, false otherwise.
|
||||
func (sig *Signature) Verify(hash []byte, pubKey *PublicKey) bool {
|
||||
return verifySchnorr(pubKey, hash, sig.R, sig.S)
|
||||
}
|
||||
|
||||
// verifySchnorr verifies the schnorr signature of the hash using the pubkey key.
|
||||
// It returns true if the signature is valid, false otherwise.
|
||||
func verifySchnorr(pubKey *PublicKey, hash []byte, r *big.Int, s *big.Int) bool {
|
||||
// This schnorr specification is specific to the secp256k1 curve so if the
|
||||
// provided curve is not a KoblitizCurve then we'll just return false.
|
||||
curve, ok := pubKey.Curve.(*KoblitzCurve)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Signature is invalid if s >= order or r >= p.
|
||||
if s.Cmp(curve.Params().N) >= 0 || r.Cmp(curve.Params().P) >= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compute scalar e = Hash(r || compressed(P) || m) mod N
|
||||
eBytes := sha256.Sum256(append(append(bigIntTo32Bytes(r), pubKey.SerializeCompressed()...), hash...))
|
||||
e := new(big.Int).SetBytes(eBytes[:])
|
||||
e.Mod(e, curve.Params().N)
|
||||
|
||||
// Negate e
|
||||
e.Neg(e).Mod(e, curve.Params().N)
|
||||
|
||||
// Compute point R = s * G - e * P.
|
||||
sgx, sgy, sgz := curve.scalarBaseMultJacobian(s.Bytes())
|
||||
epx, epy, epz := curve.scalarMultJacobian(pubKey.X, pubKey.Y, e.Bytes())
|
||||
rx, ry, rz := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
curve.addJacobian(sgx, sgy, sgz, epx, epy, epz, rx, ry, rz)
|
||||
|
||||
// Check that R is not infinity
|
||||
if rz.Equals(new(fieldVal).SetInt(0)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if R.y is quadratic residue
|
||||
yz := ry.Mul(rz).Normalize()
|
||||
b := yz.Bytes()
|
||||
if big.Jacobi(new(big.Int).SetBytes(b[:]), curve.P) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check R values match
|
||||
// rx ≠ rz^2 * r mod p
|
||||
fieldR := new(fieldVal).SetByteSlice(r.Bytes())
|
||||
return rx.Normalize().Equals(rz.Square().Mul(fieldR).Normalize())
|
||||
}
|
||||
|
||||
// IsEqual compares this Signature instance to the one passed, returning true
|
||||
// if both Signatures are equivalent. A signature is equivalent to another, if
|
||||
// they both have the same scalar value for R and S.
|
||||
func (sig *Signature) IsEqual(otherSig *Signature) bool {
|
||||
return sig.R.Cmp(otherSig.R) == 0 &&
|
||||
sig.S.Cmp(otherSig.S) == 0
|
||||
}
|
||||
|
||||
// ParseSignature parses a 64 byte schnorr signature into a Signature type.
|
||||
func ParseSignature(sigStr []byte) (*Signature, error) {
|
||||
if len(sigStr) != 64 {
|
||||
return nil, errors.New("malformed schnorr signature: not 64 bytes")
|
||||
}
|
||||
bigR := new(big.Int).SetBytes(sigStr[:32])
|
||||
bigS := new(big.Int).SetBytes(sigStr[32:64])
|
||||
return &Signature{
|
||||
R: bigR,
|
||||
S: bigS,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// bigIntTo32Bytes pads a big int bytes with leading zeros if they
|
||||
// are missing to get the length up to 32 bytes.
|
||||
func bigIntTo32Bytes(val *big.Int) []byte {
|
||||
b := val.Bytes()
|
||||
pad := bytes.Repeat([]byte{0x00}, 32-len(b))
|
||||
return append(pad, b...)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// sign signs the hash using the schnorr signature algorithm.
|
||||
func sign(privateKey *PrivateKey, hash []byte) (*Signature, error) {
|
||||
// The rfc6979 nonce derivation function accepts additional entropy.
|
||||
// See https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/2019-05-15-schnorr.md#recommended-practices-for-secure-signature-generation
|
||||
additionalData := []byte{'S', 'c', 'h', 'n', 'o', 'r', 'r', '+', 'S', 'H', 'A', '2', '5', '6', ' ', ' '}
|
||||
k := nonceRFC6979(privateKey.D, hash, additionalData)
|
||||
// Compute point R = k * G
|
||||
rx, ry := privateKey.Curve.ScalarBaseMult(k.Bytes())
|
||||
|
||||
// Negate nonce if R.y is not a quadratic residue.
|
||||
if big.Jacobi(ry, privateKey.Params().P) != 1 {
|
||||
k = k.Neg(k)
|
||||
}
|
||||
|
||||
// Compute scalar e = Hash(R.x || compressed(P) || m) mod N
|
||||
eBytes := sha256.Sum256(append(append(bigIntTo32Bytes(rx), privateKey.PubKey().SerializeCompressed()...), hash...))
|
||||
e := new(big.Int).SetBytes(eBytes[:])
|
||||
e.Mod(e, privateKey.Params().N)
|
||||
|
||||
// Compute scalar s = (k + e * x) mod N
|
||||
x := new(big.Int).SetBytes(privateKey.Serialize())
|
||||
s := e.Mul(e, x)
|
||||
s.Add(s, k)
|
||||
s.Mod(s, privateKey.Params().N)
|
||||
return &Signature{
|
||||
R: rx,
|
||||
S: s,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nonceRFC6979 generates a nonce (`k`) deterministically according to RFC 6979.
|
||||
// It takes a 32-byte hash as an input and returns 32-byte nonce to be used in the digital signature algorithm.
|
||||
func nonceRFC6979(privkey *big.Int, hash []byte, additionalData []byte) *big.Int {
|
||||
|
||||
// Step A
|
||||
curve := S256()
|
||||
q := curve.Params().N
|
||||
x := privkey
|
||||
alg := sha256.New
|
||||
|
||||
qlen := q.BitLen()
|
||||
holen := alg().Size()
|
||||
rolen := (qlen + 7) >> 3
|
||||
bx := append(int2octets(x, rolen), bits2octets(hash, curve, rolen)...)
|
||||
|
||||
// Step B
|
||||
v := bytes.Repeat(oneInitializer, holen)
|
||||
|
||||
// Step C (Go zeroes the all allocated memory)
|
||||
k := make([]byte, holen)
|
||||
|
||||
// Step D
|
||||
k = mac(alg, k, append(append(append(v, 0x00), bx...), additionalData...))
|
||||
|
||||
// Step E
|
||||
v = mac(alg, k, v)
|
||||
|
||||
// Step F
|
||||
k = mac(alg, k, append(append(append(v, 0x01), bx...), additionalData...))
|
||||
|
||||
// Step G
|
||||
v = mac(alg, k, v)
|
||||
|
||||
// Step H
|
||||
for {
|
||||
// Step H1
|
||||
var t []byte
|
||||
|
||||
// Step H2
|
||||
for len(t)*8 < qlen {
|
||||
v = mac(alg, k, v)
|
||||
t = append(t, v...)
|
||||
}
|
||||
|
||||
// Step H3
|
||||
secret := hashToInt(t, curve)
|
||||
if secret.Cmp(one) >= 0 && secret.Cmp(q) < 0 {
|
||||
return secret
|
||||
}
|
||||
k = mac(alg, k, append(v, 0x00))
|
||||
v = mac(alg, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// mac returns an HMAC of the given key and message.
|
||||
func mac(alg func() hash.Hash, k, m []byte) []byte {
|
||||
h := hmac.New(alg, k)
|
||||
h.Write(m)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc6979#section-2.3.3
|
||||
func int2octets(v *big.Int, rolen int) []byte {
|
||||
out := v.Bytes()
|
||||
|
||||
// left pad with zeros if it's too short
|
||||
if len(out) < rolen {
|
||||
out2 := make([]byte, rolen)
|
||||
copy(out2[rolen-len(out):], out)
|
||||
return out2
|
||||
}
|
||||
|
||||
// drop most significant bytes if it's too long
|
||||
if len(out) > rolen {
|
||||
out2 := make([]byte, rolen)
|
||||
copy(out2, out[len(out)-rolen:])
|
||||
return out2
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func bits2octets(in []byte, curve elliptic.Curve, rolen int) []byte {
|
||||
// https://tools.ietf.org/html/rfc6979#section-2.3.4
|
||||
z1 := hashToInt(in, curve)
|
||||
z2 := new(big.Int).Sub(z1, curve.Params().N)
|
||||
if z2.Sign() < 0 {
|
||||
return int2octets(z1, rolen)
|
||||
}
|
||||
return int2octets(z2, rolen)
|
||||
}
|
@ -1,262 +0,0 @@
|
||||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// decodeHex decodes the passed hex string and returns the resulting bytes. It
|
||||
// panics if an error occurs. This is only used in the tests as a helper since
|
||||
// the only way it can fail is if there is an error in the test source code.
|
||||
func decodeHex(hexStr string) []byte {
|
||||
b, err := hex.DecodeString(hexStr)
|
||||
if err != nil {
|
||||
panic("invalid hex string in test source: err " + err.Error() +
|
||||
", hex: " + hexStr)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func TestRFC6979(t *testing.T) {
|
||||
// Test vectors matching Trezor and CoreBitcoin implementations.
|
||||
// - https://github.com/trezor/trezor-crypto/blob/9fea8f8ab377dc514e40c6fd1f7c89a74c1d8dc6/tests.c#L432-L453
|
||||
// - https://github.com/oleganza/CoreBitcoin/blob/e93dd71207861b5bf044415db5fa72405e7d8fbc/CoreBitcoin/BTCKey%2BTests.m#L23-L49
|
||||
tests := []struct {
|
||||
key string
|
||||
msg string
|
||||
nonce string
|
||||
signature string
|
||||
}{
|
||||
{
|
||||
"cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50",
|
||||
"sample",
|
||||
"bbed2b98f40ded8587b0615b8413d2aaf520215f369b098cea4ceec119b3f722",
|
||||
"e8d6af55a53a1ac260c89aa247cc677a6219132f0d031530c75c22bffde442d894a5edc9cfffa4362f0b9f2af84f4cd13ae67b0b742d18c46eb85dac8eea6171",
|
||||
},
|
||||
{
|
||||
// This signature hits the case when S is higher than halforder.
|
||||
// If S is not canonicalized (lowered by halforder), this test will fail.
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"Satoshi Nakamoto",
|
||||
"c2b802cb01789ed38de5cffd81a56374c39b7ee6f31bb36244650141bb3425fe",
|
||||
"2f78a0720cf85bef9a24aef691fce02002c59381133ee543055d24222e2797cc78531f684122d541bbb4afe536e0b19236ad83e2e97a6277626aa5e0d1428f64",
|
||||
},
|
||||
{
|
||||
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
||||
"Satoshi Nakamoto",
|
||||
"b962c5b6537356a8b9362d6a275614df9711dff7b94d803b9ba5b5f2d4d90f8c",
|
||||
"bebecd554a4c22ad202d074db855af4c974a55a5b9ef5ecf009eff232c806c63b9b37ef1d984feb2aa500777562d66f2ba468fab0491202ede12e76aa69d194a",
|
||||
},
|
||||
{
|
||||
"f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",
|
||||
"Alan Turing",
|
||||
"65e72825561dd93f7a0ab53cdce97d5e57b2235c056e8b8396857425a6a8ff54",
|
||||
"864487c21bc3a7823a9dbc99fc2a6de67cf7d7cf5f2feb6585319256393ff0f2192c3292df9979a01bf26f96017bc7e5f4a2e01fa5b9e1007af1a19a58f767d0",
|
||||
},
|
||||
{
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"All those moments will be lost in time, like tears in rain. Time to die...",
|
||||
"60eb74b1fbc2db67be649f56246cee8c2225803155c86ff72f29641958de471a",
|
||||
"66e3a89b2842c05cf57e5315bc2d063b1054979a4f0e5718ec5e0d68bde268296f855a1595aaa0625c822b526226a0e248fc0a1eae6f160cc97a783b8646b755",
|
||||
},
|
||||
{
|
||||
"e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",
|
||||
"There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",
|
||||
"a1b518d7f2c6d9b68672f5e2b66fc872ef67a9b0b422d8b70ce0292017a62f60",
|
||||
"c5290b3ecadc0ed0d674501ffc880f2f44b2ed829bfd101676f6fb55def15f23236ace4b651a52e9d590f421668e2b1b495c9039d27db848eb3edf1d1eb60d50",
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
privKey, _ := PrivKeyFromBytes(S256(), decodeHex(test.key))
|
||||
hash := sha256.Sum256([]byte(test.msg))
|
||||
|
||||
// Ensure deterministically generated nonce is the expected value.
|
||||
additionalData := []byte{'S', 'c', 'h', 'n', 'o', 'r', 'r', '+', 'S', 'H', 'A', '2', '5', '6', ' ', ' '}
|
||||
gotNonce := nonceRFC6979(privKey.D, hash[:], additionalData).Bytes()
|
||||
wantNonce := decodeHex(test.nonce)
|
||||
if !bytes.Equal(gotNonce, wantNonce) {
|
||||
t.Errorf("NonceRFC6979 #%d (%s): Nonce is incorrect: "+
|
||||
"%x (expected %x)", i, test.msg, gotNonce,
|
||||
wantNonce)
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure deterministically generated signature is the expected value.
|
||||
gotSig, err := privKey.Sign(hash[:])
|
||||
if err != nil {
|
||||
t.Errorf("Sign #%d (%s): unexpected error: %v", i,
|
||||
test.msg, err)
|
||||
continue
|
||||
}
|
||||
gotSigBytes := gotSig.Serialize()
|
||||
wantSigBytes := decodeHex(test.signature)
|
||||
if !bytes.Equal(gotSigBytes, wantSigBytes) {
|
||||
t.Errorf("Sign #%d (%s): mismatched signature: %x "+
|
||||
"(expected %x)", i, test.msg, gotSigBytes,
|
||||
wantSigBytes)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignatureIsEqual(t *testing.T) {
|
||||
sig1 := &Signature{
|
||||
R: fromHex("0082235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"),
|
||||
S: fromHex("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"),
|
||||
}
|
||||
sig2 := &Signature{
|
||||
R: fromHex("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"),
|
||||
S: fromHex("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"),
|
||||
}
|
||||
|
||||
if !sig1.IsEqual(sig1) {
|
||||
t.Fatalf("value of IsEqual is incorrect, %v is "+
|
||||
"equal to %v", sig1, sig1)
|
||||
}
|
||||
|
||||
if sig1.IsEqual(sig2) {
|
||||
t.Fatalf("value of IsEqual is incorrect, %v is not "+
|
||||
"equal to %v", sig1, sig2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchnorrSignatureVerify(t *testing.T) {
|
||||
// Test vectors taken from https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr/test-vectors.csv
|
||||
tests := []struct {
|
||||
pubKey []byte
|
||||
message []byte
|
||||
signature []byte
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
decodeHex("0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"),
|
||||
decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
decodeHex("787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF67031A98831859DC34DFFEEDDA86831842CCD0079E1F92AF177F7F22CC1DCED05"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
decodeHex("02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"),
|
||||
decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"),
|
||||
decodeHex("2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
decodeHex("03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B"),
|
||||
decodeHex("5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C"),
|
||||
decodeHex("00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BE00880371D01766935B92D2AB4CD5C8A2A5837EC57FED7660773A05F0DE142380"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
decodeHex("03DEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34"),
|
||||
decodeHex("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703"),
|
||||
decodeHex("00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6302A8DC32E64E86A333F20EF56EAC9BA30B7246D6D25E22ADB8C6BE1AEB08D49D"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
decodeHex("031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F"),
|
||||
decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
decodeHex("52818579ACA59767E3291D91B76B637BEF062083284992F2D95F564CA6CB4E3530B1DA849C8E8304ADC0CFE870660334B3CFC18E825EF1DB34CFAE3DFC5D8187"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
decodeHex("03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B"),
|
||||
decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
|
||||
decodeHex("570DD4CA83D4E6317B8EE6BAE83467A1BF419D0767122DE409394414B05080DCE9EE5F237CBD108EABAE1E37759AE47F8E4203DA3532EB28DB860F33D62D49BD"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
decodeHex("02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"),
|
||||
decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"),
|
||||
decodeHex("2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1DFA16AEE06609280A19B67A24E1977E4697712B5FD2943914ECD5F730901B4AB7"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
decodeHex("03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B"),
|
||||
decodeHex("5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C"),
|
||||
decodeHex("00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BED092F9D860F1776A1F7412AD8A1EB50DACCC222BC8C0E26B2056DF2F273EFDEC"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
decodeHex("0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"),
|
||||
decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
decodeHex("787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF68FCE5677CE7A623CB20011225797CE7A8DE1DC6CCD4F754A47DA6C600E59543C"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
decodeHex("03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"),
|
||||
decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"),
|
||||
decodeHex("2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
decodeHex("03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"),
|
||||
decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"),
|
||||
decodeHex("00000000000000000000000000000000000000000000000000000000000000009E9D01AF988B5CEDCE47221BFA9B222721F3FA408915444A4B489021DB55775F"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
decodeHex("03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"),
|
||||
decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"),
|
||||
decodeHex("0000000000000000000000000000000000000000000000000000000000000001D37DDF0254351836D84B1BD6A795FD5D523048F298C4214D187FE4892947F728"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
decodeHex("03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"),
|
||||
decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"),
|
||||
decodeHex("4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
decodeHex("03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"),
|
||||
decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"),
|
||||
decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2F1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
decodeHex("03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"),
|
||||
decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"),
|
||||
decodeHex("2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
pubkey, err := ParsePubKey(test.pubKey, S256())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sig, err := ParseSignature(test.signature)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
valid := sig.Verify(test.message, pubkey)
|
||||
if valid != test.valid {
|
||||
t.Errorf("Schnorr test vector %d didn't produce correct result", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeterministicSchnorrSignatureGen(t *testing.T) {
|
||||
// Test vector from Bitcoin-ABC
|
||||
privKeyBytes := decodeHex("12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747")
|
||||
privKey, _ := PrivKeyFromBytes(S256(), privKeyBytes)
|
||||
|
||||
h1 := sha256.Sum256([]byte("Very deterministic message"))
|
||||
h2 := sha256.Sum256(h1[:])
|
||||
sig, err := privKey.Sign(h2[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(sig.R.Bytes(), decodeHex("2c56731ac2f7a7e7f11518fc7722a166b02438924ca9d8b4d111347b81d07175")) ||
|
||||
!bytes.Equal(sig.S.Bytes(), decodeHex("71846de67ad3d913a8fdf9d8f3f73161a4c48ae81cb183b214765feb86e255ce")) {
|
||||
t.Error("Failed to generate deterministic schnorr signature")
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
hdkeychain
|
||||
==========
|
||||
|
||||
[](https://choosealicense.com/licenses/isc/)
|
||||
[](http://godoc.org/github.com/kaspanet/kaspad/util/hdkeychain)
|
||||
|
||||
Package hdkeychain provides an API for kaspa hierarchical deterministic
|
||||
extended keys.
|
||||
|
||||
## Feature Overview
|
||||
|
||||
- Full BIP0032-compatible implementation
|
||||
- Single type for private and public extended keys
|
||||
- Convenient cryptograpically secure seed generation
|
||||
- Simple creation of master nodes
|
||||
- Support for multi-layer derivation
|
||||
- Easy serialization and deserialization for both private and public extended
|
||||
keys
|
||||
- Support for custom networks by registering them with dagconfig
|
||||
- Obtaining the underlying EC pubkeys, EC privkeys, and associated kaspa
|
||||
addresses ties in seamlessly with existing `ecc` and `util` types which provide
|
||||
powerful tools for working with them to do things like sign transations and
|
||||
generate payment scripts
|
||||
- Uses the `ecc` package which is highly optimized for secp256k1
|
||||
- Code examples including:
|
||||
- Generating a cryptographically secure random seed and deriving a
|
||||
master node from it
|
||||
- Default HD wallet layout as described by BIP0032
|
||||
- Audits use case as described by BIP0032
|
||||
- Comprehensive test coverage including the BIP0032 test vectors
|
||||
- Benchmarks
|
||||
|
||||
## Examples
|
||||
|
||||
* [NewMaster Example](http://godoc.org/github.com/kaspanet/kaspad/util/hdkeychain#example-NewMaster)
|
||||
Demonstrates how to generate a cryptographically random seed then use it to
|
||||
create a new master node (extended key).
|
||||
* [Default Wallet Layout Example](http://godoc.org/github.com/kaspanet/kaspad/util/hdkeychain#example-package--DefaultWalletLayout)
|
||||
Demonstrates the default hierarchical deterministic wallet layout as described
|
||||
in BIP0032.
|
||||
* [Audits Use Case Example](http://godoc.org/github.com/kaspanet/kaspad/util/hdkeychain#example-package--Audits)
|
||||
Demonstrates the audits use case in BIP0032.
|
||||
|
@ -1,83 +0,0 @@
|
||||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hdkeychain_test
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/util/hdkeychain"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// bip0032MasterPriv1 is the master private extended key from the first set of
|
||||
// test vectors in BIP0032.
|
||||
const bip0032MasterPriv1 = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbP" +
|
||||
"y6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
|
||||
|
||||
// BenchmarkDeriveHardened benchmarks how long it takes to derive a hardened
|
||||
// child from a master private extended key.
|
||||
func BenchmarkDeriveHardened(b *testing.B) {
|
||||
b.StopTimer()
|
||||
masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
if err != nil {
|
||||
b.Errorf("Failed to decode master seed: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
masterKey.Child(hdkeychain.HardenedKeyStart)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkDeriveNormal benchmarks how long it takes to derive a normal
|
||||
// (non-hardened) child from a master private extended key.
|
||||
func BenchmarkDeriveNormal(b *testing.B) {
|
||||
b.StopTimer()
|
||||
masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
if err != nil {
|
||||
b.Errorf("Failed to decode master seed: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
masterKey.Child(0)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkPrivToPub benchmarks how long it takes to convert a private extended
|
||||
// key to a public extended key.
|
||||
func BenchmarkPrivToPub(b *testing.B) {
|
||||
b.StopTimer()
|
||||
masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
if err != nil {
|
||||
b.Errorf("Failed to decode master seed: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
masterKey.Neuter()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkDeserialize benchmarks how long it takes to deserialize a private
|
||||
// extended key.
|
||||
func BenchmarkDeserialize(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSerialize benchmarks how long it takes to serialize a private
|
||||
// extended key.
|
||||
func BenchmarkSerialize(b *testing.B) {
|
||||
b.StopTimer()
|
||||
masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
if err != nil {
|
||||
b.Errorf("Failed to decode master seed: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = masterKey.String()
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
Package hdkeychain provides an API for kaspa hierarchical deterministic
|
||||
extended keys (BIP0032).
|
||||
|
||||
Overview
|
||||
|
||||
The ability to implement hierarchical deterministic wallets depends on the
|
||||
ability to create and derive hierarchical deterministic extended keys.
|
||||
|
||||
At a high level, this package provides support for those hierarchical
|
||||
deterministic extended keys by providing an ExtendedKey type and supporting
|
||||
functions. Each extended key can either be a private or public extended key
|
||||
which itself is capable of deriving a child extended key.
|
||||
|
||||
Determining the Extended Key Type
|
||||
|
||||
Whether an extended key is a private or public extended key can be determined
|
||||
with the IsPrivate function.
|
||||
|
||||
Transaction Signing Keys and Payment Addresses
|
||||
|
||||
In order to create and sign transactions, or provide others with addresses to
|
||||
send funds to, the underlying key and address material must be accessible. This
|
||||
package provides the ECPubKey, ECPrivKey, and Address functions for this
|
||||
purpose.
|
||||
|
||||
The Master Node
|
||||
|
||||
As previously mentioned, the extended keys are hierarchical meaning they are
|
||||
used to form a tree. The root of that tree is called the master node and this
|
||||
package provides the NewMaster function to create it from a cryptographically
|
||||
random seed. The GenerateSeed function is provided as a convenient way to
|
||||
create a random seed for use with the NewMaster function.
|
||||
|
||||
Deriving Children
|
||||
|
||||
Once you have created a tree root (or have deserialized an extended key as
|
||||
discussed later), the child extended keys can be derived by using the Child
|
||||
function. The Child function supports deriving both normal (non-hardened) and
|
||||
hardened child extended keys. In order to derive a hardened extended key, use
|
||||
the HardenedKeyStart constant + the hardened key number as the index to the
|
||||
Child function. This provides the ability to cascade the keys into a tree and
|
||||
hence generate the hierarchical deterministic key chains.
|
||||
|
||||
Normal vs Hardened Child Extended Keys
|
||||
|
||||
A private extended key can be used to derive both hardened and non-hardened
|
||||
(normal) child private and public extended keys. A public extended key can only
|
||||
be used to derive non-hardened child public extended keys. As enumerated in
|
||||
BIP0032 "knowledge of the extended public key plus any non-hardened private key
|
||||
descending from it is equivalent to knowing the extended private key (and thus
|
||||
every private and public key descending from it). This means that extended
|
||||
public keys must be treated more carefully than regular public keys. It is also
|
||||
the reason for the existence of hardened keys, and why they are used for the
|
||||
account level in the tree. This way, a leak of an account-specific (or below)
|
||||
private key never risks compromising the master or other accounts."
|
||||
|
||||
Neutering a Private Extended Key
|
||||
|
||||
A private extended key can be converted to a new instance of the corresponding
|
||||
public extended key with the Neuter function. The original extended key is not
|
||||
modified. A public extended key is still capable of deriving non-hardened child
|
||||
public extended keys.
|
||||
|
||||
Serializing and Deserializing Extended Keys
|
||||
|
||||
Extended keys are serialized and deserialized with the String and
|
||||
NewKeyFromString functions. The serialized key is a Base58-encoded string which
|
||||
looks like the following:
|
||||
public key: xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw
|
||||
private key: xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7
|
||||
|
||||
Network
|
||||
|
||||
Extended keys are much like normal kaspa addresses in that they have version
|
||||
bytes which tie them to a specific network. The SetNet and IsForNet functions
|
||||
are provided to set and determinine which network an extended key is associated
|
||||
with.
|
||||
*/
|
||||
package hdkeychain
|
@ -1,182 +0,0 @@
|
||||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hdkeychain_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/hdkeychain"
|
||||
)
|
||||
|
||||
// This example demonstrates how to generate a cryptographically random seed
|
||||
// then use it to create a new master node (extended key).
|
||||
func ExampleNewMaster() {
|
||||
// Generate a random seed at the recommended length.
|
||||
seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate a new master node using the seed.
|
||||
key, err := hdkeychain.NewMaster(seed, hdkeychain.HDKeyPairMainnet.PrivateKeyID)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Show that the generated master node extended key is private.
|
||||
fmt.Println("Private Extended Key?:", key.IsPrivate())
|
||||
|
||||
// Output:
|
||||
// Private Extended Key?: true
|
||||
}
|
||||
|
||||
// This example demonstrates the default hierarchical deterministic wallet
|
||||
// layout as described in BIP0032.
|
||||
func Example_defaultWalletLayout() {
|
||||
// The default wallet layout described in BIP0032 is:
|
||||
//
|
||||
// Each account is composed of two keypair chains: an internal and an
|
||||
// external one. The external keychain is used to generate new public
|
||||
// addresses, while the internal keychain is used for all other
|
||||
// operations (change addresses, generation addresses, ..., anything
|
||||
// that doesn't need to be communicated).
|
||||
//
|
||||
// * m/iH/0/k
|
||||
// corresponds to the k'th keypair of the external chain of account
|
||||
// number i of the HDW derived from master m.
|
||||
// * m/iH/1/k
|
||||
// corresponds to the k'th keypair of the internal chain of account
|
||||
// number i of the HDW derived from master m.
|
||||
|
||||
// Ordinarily this would either be read from some encrypted source
|
||||
// and be decrypted or generated as the NewMaster example shows, but
|
||||
// for the purposes of this example, the private extended key for the
|
||||
// master node is being hard coded here.
|
||||
master := "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jP" +
|
||||
"PqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
|
||||
|
||||
// Start by getting an extended key instance for the master node.
|
||||
// This gives the path:
|
||||
// m
|
||||
masterKey, err := hdkeychain.NewKeyFromString(master)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive the extended key for account 0. This gives the path:
|
||||
// m/0H
|
||||
acct0, err := masterKey.Child(hdkeychain.HardenedKeyStart + 0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive the extended key for the account 0 external chain. This
|
||||
// gives the path:
|
||||
// m/0H/0
|
||||
acct0Ext, err := acct0.Child(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive the extended key for the account 0 internal chain. This gives
|
||||
// the path:
|
||||
// m/0H/1
|
||||
acct0Int, err := acct0.Child(1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, acct0Ext and acct0Int are ready to derive the keys for
|
||||
// the external and internal wallet chains.
|
||||
|
||||
// Derive the 10th extended key for the account 0 external chain. This
|
||||
// gives the path:
|
||||
// m/0H/0/10
|
||||
acct0Ext10, err := acct0Ext.Child(10)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive the 1st extended key for the account 0 internal chain. This
|
||||
// gives the path:
|
||||
// m/0H/1/0
|
||||
acct0Int0, err := acct0Int.Child(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get and show the address associated with the extended keys for the
|
||||
// main kaspa network.
|
||||
acct0ExtAddr, err := acct0Ext10.Address(util.Bech32PrefixKaspa)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
acct0IntAddr, err := acct0Int0.Address(util.Bech32PrefixKaspa)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("Account 0 External Address 10:", acct0ExtAddr)
|
||||
fmt.Println("Account 0 Internal Address 0:", acct0IntAddr)
|
||||
|
||||
// Output:
|
||||
// Account 0 External Address 10: kaspa:qz6wkeydwnmhsf0w9ac4rwhnx8jmlqsp9y9hv6hmlh
|
||||
// Account 0 Internal Address 0: kaspa:qzak20c4paxssre0a2zvgk8e0r22p06j9srtald0ta
|
||||
}
|
||||
|
||||
// This example demonstrates the audits use case in BIP0032.
|
||||
func Example_audits() {
|
||||
// The audits use case described in BIP0032 is:
|
||||
//
|
||||
// In case an auditor needs full access to the list of incoming and
|
||||
// outgoing payments, one can share all account public extended keys.
|
||||
// This will allow the auditor to see all transactions from and to the
|
||||
// wallet, in all accounts, but not a single secret key.
|
||||
//
|
||||
// * N(m/*)
|
||||
// corresponds to the neutered master extended key (also called
|
||||
// the master public extended key)
|
||||
|
||||
// Ordinarily this would either be read from some encrypted source
|
||||
// and be decrypted or generated as the NewMaster example shows, but
|
||||
// for the purposes of this example, the private extended key for the
|
||||
// master node is being hard coded here.
|
||||
master := "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jP" +
|
||||
"PqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
|
||||
|
||||
// Start by getting an extended key instance for the master node.
|
||||
// This gives the path:
|
||||
// m
|
||||
masterKey, err := hdkeychain.NewKeyFromString(master)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Neuter the master key to generate a master public extended key. This
|
||||
// gives the path:
|
||||
// N(m/*)
|
||||
masterPubKey, err := masterKey.Neuter()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Share the master public extended key with the auditor.
|
||||
fmt.Println("Audit key N(m/*):", masterPubKey)
|
||||
|
||||
// Output:
|
||||
// Audit key N(m/*): xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8
|
||||
}
|
@ -1,644 +0,0 @@
|
||||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hdkeychain
|
||||
|
||||
// References:
|
||||
// [BIP32]: BIP0032 - Hierarchical Deterministic Wallets
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"encoding/binary"
|
||||
"github.com/pkg/errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/kaspanet/kaspad/ecc"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/kaspanet/kaspad/util/base58"
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
)
|
||||
|
||||
const (
|
||||
// RecommendedSeedLen is the recommended length in bytes for a seed
|
||||
// to a master node.
|
||||
RecommendedSeedLen = 32 // 256 bits
|
||||
|
||||
// HardenedKeyStart is the index at which a hardended key starts. Each
|
||||
// extended key has 2^31 normal child keys and 2^31 hardned child keys.
|
||||
// Thus the range for normal child keys is [0, 2^31 - 1] and the range
|
||||
// for hardened child keys is [2^31, 2^32 - 1].
|
||||
HardenedKeyStart = 0x80000000 // 2^31
|
||||
|
||||
// MinSeedBytes is the minimum number of bytes allowed for a seed to
|
||||
// a master node.
|
||||
MinSeedBytes = 16 // 128 bits
|
||||
|
||||
// MaxSeedBytes is the maximum number of bytes allowed for a seed to
|
||||
// a master node.
|
||||
MaxSeedBytes = 64 // 512 bits
|
||||
|
||||
// serializedKeyLen is the length of a serialized public or private
|
||||
// extended key. It consists of 4 bytes version, 1 byte depth, 4 bytes
|
||||
// fingerprint, 4 bytes child number, 32 bytes chain code, and 33 bytes
|
||||
// public/private key data.
|
||||
serializedKeyLen = 4 + 1 + 4 + 4 + 32 + 33 // 78 bytes
|
||||
|
||||
// maxUint8 is the max positive integer which can be serialized in a uint8
|
||||
maxUint8 = 1<<8 - 1
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDeriveHardFromPublic describes an error in which the caller
|
||||
// attempted to derive a hardened extended key from a public key.
|
||||
ErrDeriveHardFromPublic = errors.New("cannot derive a hardened key " +
|
||||
"from a public key")
|
||||
|
||||
// ErrDeriveBeyondMaxDepth describes an error in which the caller
|
||||
// has attempted to derive more than 255 keys from a root key.
|
||||
ErrDeriveBeyondMaxDepth = errors.New("cannot derive a key with more than " +
|
||||
"255 indices in its path")
|
||||
|
||||
// ErrNotPrivExtKey describes an error in which the caller attempted
|
||||
// to extract a private key from a public extended key.
|
||||
ErrNotPrivExtKey = errors.New("unable to create private keys from a " +
|
||||
"public extended key")
|
||||
|
||||
// ErrInvalidChild describes an error in which the child at a specific
|
||||
// index is invalid due to the derived key falling outside of the valid
|
||||
// range for secp256k1 private keys. This error indicates the caller
|
||||
// should simply ignore the invalid child extended key at this index and
|
||||
// increment to the next index.
|
||||
ErrInvalidChild = errors.New("the extended key at this index is invalid")
|
||||
|
||||
// ErrUnusableSeed describes an error in which the provided seed is not
|
||||
// usable due to the derived key falling outside of the valid range for
|
||||
// secp256k1 private keys. This error indicates the caller must choose
|
||||
// another seed.
|
||||
ErrUnusableSeed = errors.New("unusable seed")
|
||||
|
||||
// ErrInvalidSeedLen describes an error in which the provided seed or
|
||||
// seed length is not in the allowed range.
|
||||
ErrInvalidSeedLen = errors.Errorf("seed length must be between %d and %d "+
|
||||
"bits", MinSeedBytes*8, MaxSeedBytes*8)
|
||||
|
||||
// ErrBadChecksum describes an error in which the checksum encoded with
|
||||
// a serialized extended key does not match the calculated value.
|
||||
ErrBadChecksum = errors.New("bad extended key checksum")
|
||||
|
||||
// ErrInvalidKeyLen describes an error in which the provided serialized
|
||||
// key is not the expected length.
|
||||
ErrInvalidKeyLen = errors.New("the provided serialized extended key " +
|
||||
"length is invalid")
|
||||
|
||||
// ErrUnknownHDKeyID describes an error where the provided id which
|
||||
// is intended to identify the network for a hierarchical deterministic
|
||||
// private extended key is not registered.
|
||||
ErrUnknownHDKeyID = errors.New("unknown hd private extended key bytes")
|
||||
)
|
||||
|
||||
// masterKey is the master key used along with a random seed used to generate
|
||||
// the master node in the hierarchical tree.
|
||||
var masterKey = []byte("Kaspa seed")
|
||||
|
||||
var hdPrivToPubKeyIDs = make(map[[4]byte][]byte)
|
||||
|
||||
// ExtendedKey houses all the information needed to support a hierarchical
|
||||
// deterministic extended key. See the package overview documentation for
|
||||
// more details on how to use extended keys.
|
||||
type ExtendedKey struct {
|
||||
key []byte // This will be the pubkey for extended pub keys
|
||||
pubKey []byte // This will only be set for extended priv keys
|
||||
chainCode []byte
|
||||
depth uint8
|
||||
parentFP []byte
|
||||
childNum uint32
|
||||
version []byte
|
||||
isPrivate bool
|
||||
}
|
||||
|
||||
// NewExtendedKey returns a new instance of an extended key with the given
|
||||
// fields. No error checking is performed here as it's only intended to be a
|
||||
// convenience method used to create a populated struct. This function should
|
||||
// only by used by applications that need to create custom ExtendedKeys. All
|
||||
// other applications should just use NewMaster, Child, or Neuter.
|
||||
func NewExtendedKey(version, key, chainCode, parentFP []byte, depth uint8,
|
||||
childNum uint32, isPrivate bool) *ExtendedKey {
|
||||
|
||||
// NOTE: The pubKey field is intentionally left nil so it is only
|
||||
// computed and memoized as required.
|
||||
return &ExtendedKey{
|
||||
key: key,
|
||||
chainCode: chainCode,
|
||||
depth: depth,
|
||||
parentFP: parentFP,
|
||||
childNum: childNum,
|
||||
version: version,
|
||||
isPrivate: isPrivate,
|
||||
}
|
||||
}
|
||||
|
||||
// pubKeyBytes returns bytes for the serialized compressed public key associated
|
||||
// with this extended key in an efficient manner including memoization as
|
||||
// necessary.
|
||||
//
|
||||
// When the extended key is already a public key, the key is simply returned as
|
||||
// is since it's already in the correct form. However, when the extended key is
|
||||
// a private key, the public key will be calculated and memoized so future
|
||||
// accesses can simply return the cached result.
|
||||
func (k *ExtendedKey) pubKeyBytes() []byte {
|
||||
// Just return the key if it's already an extended public key.
|
||||
if !k.isPrivate {
|
||||
return k.key
|
||||
}
|
||||
|
||||
// This is a private extended key, so calculate and memoize the public
|
||||
// key if needed.
|
||||
if len(k.pubKey) == 0 {
|
||||
pkx, pky := ecc.S256().ScalarBaseMult(k.key)
|
||||
pubKey := ecc.PublicKey{Curve: ecc.S256(), X: pkx, Y: pky}
|
||||
k.pubKey = pubKey.SerializeCompressed()
|
||||
}
|
||||
|
||||
return k.pubKey
|
||||
}
|
||||
|
||||
// IsPrivate returns whether or not the extended key is a private extended key.
|
||||
//
|
||||
// A private extended key can be used to derive both hardened and non-hardened
|
||||
// child private and public extended keys. A public extended key can only be
|
||||
// used to derive non-hardened child public extended keys.
|
||||
func (k *ExtendedKey) IsPrivate() bool {
|
||||
return k.isPrivate
|
||||
}
|
||||
|
||||
// Depth returns the current derivation level with respect to the root.
|
||||
//
|
||||
// The root key has depth zero, and the field has a maximum of 255 due to
|
||||
// how depth is serialized.
|
||||
func (k *ExtendedKey) Depth() uint8 {
|
||||
return k.depth
|
||||
}
|
||||
|
||||
// ParentFingerprint returns a fingerprint of the parent extended key from which
|
||||
// this one was derived.
|
||||
func (k *ExtendedKey) ParentFingerprint() uint32 {
|
||||
return binary.BigEndian.Uint32(k.parentFP)
|
||||
}
|
||||
|
||||
// Child returns a derived child extended key at the given index. When this
|
||||
// extended key is a private extended key (as determined by the IsPrivate
|
||||
// function), a private extended key will be derived. Otherwise, the derived
|
||||
// extended key will be also be a public extended key.
|
||||
//
|
||||
// When the index is greater to or equal than the HardenedKeyStart constant, the
|
||||
// derived extended key will be a hardened extended key. It is only possible to
|
||||
// derive a hardended extended key from a private extended key. Consequently,
|
||||
// this function will return ErrDeriveHardFromPublic if a hardened child
|
||||
// extended key is requested from a public extended key.
|
||||
//
|
||||
// A hardened extended key is useful since, as previously mentioned, it requires
|
||||
// a parent private extended key to derive. In other words, normal child
|
||||
// extended public keys can be derived from a parent public extended key (no
|
||||
// knowledge of the parent private key) whereas hardened extended keys may not
|
||||
// be.
|
||||
//
|
||||
// NOTE: There is an extremely small chance (< 1 in 2^127) the specific child
|
||||
// index does not derive to a usable child. The ErrInvalidChild error will be
|
||||
// returned if this should occur, and the caller is expected to ignore the
|
||||
// invalid child and simply increment to the next index.
|
||||
func (k *ExtendedKey) Child(i uint32) (*ExtendedKey, error) {
|
||||
// Prevent derivation of children beyond the max allowed depth.
|
||||
if k.depth == maxUint8 {
|
||||
return nil, ErrDeriveBeyondMaxDepth
|
||||
}
|
||||
|
||||
// There are four scenarios that could happen here:
|
||||
// 1) Private extended key -> Hardened child private extended key
|
||||
// 2) Private extended key -> Non-hardened child private extended key
|
||||
// 3) Public extended key -> Non-hardened child public extended key
|
||||
// 4) Public extended key -> Hardened child public extended key (INVALID!)
|
||||
|
||||
// Case #4 is invalid, so error out early.
|
||||
// A hardened child extended key may not be created from a public
|
||||
// extended key.
|
||||
isChildHardened := i >= HardenedKeyStart
|
||||
if !k.isPrivate && isChildHardened {
|
||||
return nil, ErrDeriveHardFromPublic
|
||||
}
|
||||
|
||||
// The data used to derive the child key depends on whether or not the
|
||||
// child is hardened per [BIP32].
|
||||
//
|
||||
// For hardened children:
|
||||
// 0x00 || ser256(parentKey) || ser32(i)
|
||||
//
|
||||
// For normal children:
|
||||
// serP(parentPubKey) || ser32(i)
|
||||
keyLen := 33
|
||||
data := make([]byte, keyLen+4)
|
||||
if isChildHardened {
|
||||
// Case #1.
|
||||
// When the child is a hardened child, the key is known to be a
|
||||
// private key due to the above early return. Pad it with a
|
||||
// leading zero as required by [BIP32] for deriving the child.
|
||||
copy(data[1:], k.key)
|
||||
} else {
|
||||
// Case #2 or #3.
|
||||
// This is either a public or private extended key, but in
|
||||
// either case, the data which is used to derive the child key
|
||||
// starts with the secp256k1 compressed public key bytes.
|
||||
copy(data, k.pubKeyBytes())
|
||||
}
|
||||
binary.BigEndian.PutUint32(data[keyLen:], i)
|
||||
|
||||
// Take the HMAC-SHA512 of the current key's chain code and the derived
|
||||
// data:
|
||||
// I = HMAC-SHA512(Key = chainCode, Data = data)
|
||||
hmac512 := hmac.New(sha512.New, k.chainCode)
|
||||
hmac512.Write(data)
|
||||
ilr := hmac512.Sum(nil)
|
||||
|
||||
// Split "I" into two 32-byte sequences Il and Ir where:
|
||||
// Il = intermediate key used to derive the child
|
||||
// Ir = child chain code
|
||||
il := ilr[:len(ilr)/2]
|
||||
childChainCode := ilr[len(ilr)/2:]
|
||||
|
||||
// Both derived public or private keys rely on treating the left 32-byte
|
||||
// sequence calculated above (Il) as a 256-bit integer that must be
|
||||
// within the valid range for a secp256k1 private key. There is a small
|
||||
// chance (< 1 in 2^127) this condition will not hold, and in that case,
|
||||
// a child extended key can't be created for this index and the caller
|
||||
// should simply increment to the next index.
|
||||
ilNum := new(big.Int).SetBytes(il)
|
||||
if ilNum.Cmp(ecc.S256().N) >= 0 || ilNum.Sign() == 0 {
|
||||
return nil, ErrInvalidChild
|
||||
}
|
||||
|
||||
// The algorithm used to derive the child key depends on whether or not
|
||||
// a private or public child is being derived.
|
||||
//
|
||||
// For private children:
|
||||
// childKey = parse256(Il) + parentKey
|
||||
//
|
||||
// For public children:
|
||||
// childKey = serP(point(parse256(Il)) + parentKey)
|
||||
var isPrivate bool
|
||||
var childKey []byte
|
||||
if k.isPrivate {
|
||||
// Case #1 or #2.
|
||||
// Add the parent private key to the intermediate private key to
|
||||
// derive the final child key.
|
||||
//
|
||||
// childKey = parse256(Il) + parenKey
|
||||
keyNum := new(big.Int).SetBytes(k.key)
|
||||
ilNum.Add(ilNum, keyNum)
|
||||
ilNum.Mod(ilNum, ecc.S256().N)
|
||||
childKey = ilNum.Bytes()
|
||||
isPrivate = true
|
||||
} else {
|
||||
// Case #3.
|
||||
// Calculate the corresponding intermediate public key for
|
||||
// intermediate private key.
|
||||
ilx, ily := ecc.S256().ScalarBaseMult(il)
|
||||
if ilx.Sign() == 0 || ily.Sign() == 0 {
|
||||
return nil, ErrInvalidChild
|
||||
}
|
||||
|
||||
// Convert the serialized compressed parent public key into X
|
||||
// and Y coordinates so it can be added to the intermediate
|
||||
// public key.
|
||||
pubKey, err := ecc.ParsePubKey(k.key, ecc.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add the intermediate public key to the parent public key to
|
||||
// derive the final child key.
|
||||
//
|
||||
// childKey = serP(point(parse256(Il)) + parentKey)
|
||||
childX, childY := ecc.S256().Add(ilx, ily, pubKey.X, pubKey.Y)
|
||||
pk := ecc.PublicKey{Curve: ecc.S256(), X: childX, Y: childY}
|
||||
childKey = pk.SerializeCompressed()
|
||||
}
|
||||
|
||||
// The fingerprint of the parent for the derived child is the first 4
|
||||
// bytes of the RIPEMD160(SHA256(parentPubKey)).
|
||||
parentFP := util.Hash160(k.pubKeyBytes())[:4]
|
||||
return NewExtendedKey(k.version, childKey, childChainCode, parentFP,
|
||||
k.depth+1, i, isPrivate), nil
|
||||
}
|
||||
|
||||
// Neuter returns a new extended public key from this extended private key. The
|
||||
// same extended key will be returned unaltered if it is already an extended
|
||||
// public key.
|
||||
//
|
||||
// As the name implies, an extended public key does not have access to the
|
||||
// private key, so it is not capable of signing transactions or deriving
|
||||
// child extended private keys. However, it is capable of deriving further
|
||||
// child extended public keys.
|
||||
func (k *ExtendedKey) Neuter() (*ExtendedKey, error) {
|
||||
// Already an extended public key.
|
||||
if !k.isPrivate {
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// Get the associated public extended key version bytes.
|
||||
version, err := HDPrivateKeyToPublicKeyID(k.version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert it to an extended public key. The key for the new extended
|
||||
// key will simply be the pubkey of the current extended private key.
|
||||
//
|
||||
// This is the function N((k,c)) -> (K, c) from [BIP32].
|
||||
return NewExtendedKey(version, k.pubKeyBytes(), k.chainCode, k.parentFP,
|
||||
k.depth, k.childNum, false), nil
|
||||
}
|
||||
|
||||
// HDKeyIDPair represents a pair of public and private key IDs
|
||||
type HDKeyIDPair struct {
|
||||
PrivateKeyID [4]byte
|
||||
PublicKeyID [4]byte
|
||||
}
|
||||
|
||||
// HDKeyPairs for all kinds of nets
|
||||
var (
|
||||
HDKeyPairMainnet = HDKeyIDPair{
|
||||
PrivateKeyID: [4]byte{0x04, 0x88, 0xad, 0xe4}, // starts with xprv
|
||||
PublicKeyID: [4]byte{0x04, 0x88, 0xb2, 0x1e}, // starts with xpub
|
||||
}
|
||||
HDKeyPairTestnet = HDKeyIDPair{
|
||||
PrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with tprv
|
||||
PublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with tpub
|
||||
}
|
||||
HDKeyPairRegressionNet = HDKeyIDPair{
|
||||
PrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with tprv
|
||||
PublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with tpub
|
||||
}
|
||||
HDKeyPairSimnet = HDKeyIDPair{
|
||||
PrivateKeyID: [4]byte{0x04, 0x20, 0xb9, 0x00}, // starts with sprv
|
||||
PublicKeyID: [4]byte{0x04, 0x20, 0xbd, 0x3a}, // starts with spub
|
||||
}
|
||||
HDKeyPairDevnet = HDKeyIDPair{
|
||||
PrivateKeyID: [4]byte{0x04, 0x88, 0xad, 0xe4}, // starts with xprv
|
||||
PublicKeyID: [4]byte{0x04, 0x88, 0xb2, 0x1e}, // starts with xpub
|
||||
}
|
||||
)
|
||||
|
||||
// RegisterHDKeyIDPair registers an HDKeyIDPair in the private to public key map
|
||||
func RegisterHDKeyIDPair(hdKeyIDPair HDKeyIDPair) {
|
||||
hdPrivToPubKeyIDs[hdKeyIDPair.PrivateKeyID] = hdKeyIDPair.PublicKeyID[:]
|
||||
}
|
||||
|
||||
// HDPrivateKeyToPublicKeyID accepts a private hierarchical deterministic
|
||||
// extended key id and returns the associated public key id. When the provided
|
||||
// id is not registered, the ErrUnknownHDKeyID error will be returned.
|
||||
func HDPrivateKeyToPublicKeyID(id []byte) ([]byte, error) {
|
||||
if len(id) != 4 {
|
||||
return nil, ErrUnknownHDKeyID
|
||||
}
|
||||
|
||||
var key [4]byte
|
||||
copy(key[:], id)
|
||||
pubBytes, ok := hdPrivToPubKeyIDs[key]
|
||||
if !ok {
|
||||
return nil, ErrUnknownHDKeyID
|
||||
}
|
||||
|
||||
return pubBytes, nil
|
||||
}
|
||||
|
||||
// ECPubKey converts the extended key to a ecc public key and returns it.
|
||||
func (k *ExtendedKey) ECPubKey() (*ecc.PublicKey, error) {
|
||||
return ecc.ParsePubKey(k.pubKeyBytes(), ecc.S256())
|
||||
}
|
||||
|
||||
// ECPrivKey converts the extended key to a ecc private key and returns it.
|
||||
// As you might imagine this is only possible if the extended key is a private
|
||||
// extended key (as determined by the IsPrivate function). The ErrNotPrivExtKey
|
||||
// error will be returned if this function is called on a public extended key.
|
||||
func (k *ExtendedKey) ECPrivKey() (*ecc.PrivateKey, error) {
|
||||
if !k.isPrivate {
|
||||
return nil, ErrNotPrivExtKey
|
||||
}
|
||||
|
||||
privKey, _ := ecc.PrivKeyFromBytes(ecc.S256(), k.key)
|
||||
return privKey, nil
|
||||
}
|
||||
|
||||
// Address converts the extended key to a standard kaspa pay-to-pubkey-hash
|
||||
// address for the passed network.
|
||||
func (k *ExtendedKey) Address(prefix util.Bech32Prefix) (*util.AddressPubKeyHash, error) {
|
||||
pkHash := util.Hash160(k.pubKeyBytes())
|
||||
return util.NewAddressPubKeyHash(pkHash, prefix)
|
||||
}
|
||||
|
||||
// paddedAppend appends the src byte slice to dst, returning the new slice.
|
||||
// If the length of the source is smaller than the passed size, leading zero
|
||||
// bytes are appended to the dst slice before appending src.
|
||||
func paddedAppend(size uint, dst, src []byte) []byte {
|
||||
for i := 0; i < int(size)-len(src); i++ {
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
return append(dst, src...)
|
||||
}
|
||||
|
||||
// String returns the extended key as a human-readable base58-encoded string.
|
||||
func (k *ExtendedKey) String() string {
|
||||
if len(k.key) == 0 {
|
||||
return "zeroed extended key"
|
||||
}
|
||||
|
||||
var childNumBytes [4]byte
|
||||
binary.BigEndian.PutUint32(childNumBytes[:], k.childNum)
|
||||
|
||||
// The serialized format is:
|
||||
// version (4) || depth (1) || parent fingerprint (4)) ||
|
||||
// child num (4) || chain code (32) || key data (33) || checksum (4)
|
||||
serializedBytes := make([]byte, 0, serializedKeyLen+4)
|
||||
serializedBytes = append(serializedBytes, k.version...)
|
||||
serializedBytes = append(serializedBytes, k.depth)
|
||||
serializedBytes = append(serializedBytes, k.parentFP...)
|
||||
serializedBytes = append(serializedBytes, childNumBytes[:]...)
|
||||
serializedBytes = append(serializedBytes, k.chainCode...)
|
||||
if k.isPrivate {
|
||||
serializedBytes = append(serializedBytes, 0x00)
|
||||
serializedBytes = paddedAppend(32, serializedBytes, k.key)
|
||||
} else {
|
||||
serializedBytes = append(serializedBytes, k.pubKeyBytes()...)
|
||||
}
|
||||
|
||||
checkSum := daghash.DoubleHashB(serializedBytes)[:4]
|
||||
serializedBytes = append(serializedBytes, checkSum...)
|
||||
return base58.Encode(serializedBytes)
|
||||
}
|
||||
|
||||
// IsForNet returns whether or not the extended key is associated with the
|
||||
// passed network keyIDs.
|
||||
func (k *ExtendedKey) IsForNet(hdKeyIDPair HDKeyIDPair) bool {
|
||||
return bytes.Equal(k.version, hdKeyIDPair.PrivateKeyID[:]) ||
|
||||
bytes.Equal(k.version, hdKeyIDPair.PublicKeyID[:])
|
||||
}
|
||||
|
||||
// SetNet associates the extended key, and any child keys yet to be derived from
|
||||
// it, with the passed key IDs.
|
||||
func (k *ExtendedKey) SetNet(hdKeyIDPair HDKeyIDPair) {
|
||||
if k.isPrivate {
|
||||
k.version = hdKeyIDPair.PrivateKeyID[:]
|
||||
} else {
|
||||
k.version = hdKeyIDPair.PublicKeyID[:]
|
||||
}
|
||||
}
|
||||
|
||||
// zero sets all bytes in the passed slice to zero. This is used to
|
||||
// explicitly clear private key material from memory.
|
||||
func zero(b []byte) {
|
||||
lenb := len(b)
|
||||
for i := 0; i < lenb; i++ {
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Zero manually clears all fields and bytes in the extended key. This can be
|
||||
// used to explicitly clear key material from memory for enhanced security
|
||||
// against memory scraping. This function only clears this particular key and
|
||||
// not any children that have already been derived.
|
||||
func (k *ExtendedKey) Zero() {
|
||||
zero(k.key)
|
||||
zero(k.pubKey)
|
||||
zero(k.chainCode)
|
||||
zero(k.parentFP)
|
||||
k.version = nil
|
||||
k.key = nil
|
||||
k.depth = 0
|
||||
k.childNum = 0
|
||||
k.isPrivate = false
|
||||
}
|
||||
|
||||
// NewMaster creates a new master node for use in creating a hierarchical
|
||||
// deterministic key chain. The seed must be between 128 and 512 bits and
|
||||
// should be generated by a cryptographically secure random generation source.
|
||||
//
|
||||
// NOTE: There is an extremely small chance (< 1 in 2^127) the provided seed
|
||||
// will derive to an unusable secret key. The ErrUnusable error will be
|
||||
// returned if this should occur, so the caller must check for it and generate a
|
||||
// new seed accordingly.
|
||||
func NewMaster(seed []byte, hdPrivateKeyID [4]byte) (*ExtendedKey, error) {
|
||||
// Per [BIP32], the seed must be in range [MinSeedBytes, MaxSeedBytes].
|
||||
if len(seed) < MinSeedBytes || len(seed) > MaxSeedBytes {
|
||||
return nil, ErrInvalidSeedLen
|
||||
}
|
||||
|
||||
// First take the HMAC-SHA512 of the master key and the seed data:
|
||||
// I = HMAC-SHA512(Key = "Kaspa seed", Data = S)
|
||||
hmac512 := hmac.New(sha512.New, masterKey)
|
||||
hmac512.Write(seed)
|
||||
lr := hmac512.Sum(nil)
|
||||
|
||||
// Split "I" into two 32-byte sequences Il and Ir where:
|
||||
// Il = master secret key
|
||||
// Ir = master chain code
|
||||
secretKey := lr[:len(lr)/2]
|
||||
chainCode := lr[len(lr)/2:]
|
||||
|
||||
// Ensure the key in usable.
|
||||
secretKeyNum := new(big.Int).SetBytes(secretKey)
|
||||
if secretKeyNum.Cmp(ecc.S256().N) >= 0 || secretKeyNum.Sign() == 0 {
|
||||
return nil, ErrUnusableSeed
|
||||
}
|
||||
|
||||
parentFP := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
return NewExtendedKey(hdPrivateKeyID[:], secretKey, chainCode,
|
||||
parentFP, 0, 0, true), nil
|
||||
}
|
||||
|
||||
// NewKeyFromString returns a new extended key instance from a base58-encoded
|
||||
// extended key.
|
||||
func NewKeyFromString(key string) (*ExtendedKey, error) {
|
||||
// The base58-decoded extended key must consist of a serialized payload
|
||||
// plus an additional 4 bytes for the checksum.
|
||||
decoded := base58.Decode(key)
|
||||
if len(decoded) != serializedKeyLen+4 {
|
||||
return nil, ErrInvalidKeyLen
|
||||
}
|
||||
|
||||
// The serialized format is:
|
||||
// version (4) || depth (1) || parent fingerprint (4)) ||
|
||||
// child num (4) || chain code (32) || key data (33) || checksum (4)
|
||||
|
||||
// Split the payload and checksum up and ensure the checksum matches.
|
||||
payload := decoded[:len(decoded)-4]
|
||||
checkSum := decoded[len(decoded)-4:]
|
||||
expectedCheckSum := daghash.DoubleHashB(payload)[:4]
|
||||
if !bytes.Equal(checkSum, expectedCheckSum) {
|
||||
return nil, ErrBadChecksum
|
||||
}
|
||||
|
||||
// Deserialize each of the payload fields.
|
||||
version := payload[:4]
|
||||
depth := payload[4:5][0]
|
||||
parentFP := payload[5:9]
|
||||
childNum := binary.BigEndian.Uint32(payload[9:13])
|
||||
chainCode := payload[13:45]
|
||||
keyData := payload[45:78]
|
||||
|
||||
// The key data is a private key if it starts with 0x00. Serialized
|
||||
// compressed pubkeys either start with 0x02 or 0x03.
|
||||
isPrivate := keyData[0] == 0x00
|
||||
if isPrivate {
|
||||
// Ensure the private key is valid. It must be within the range
|
||||
// of the order of the secp256k1 curve and not be 0.
|
||||
keyData = keyData[1:]
|
||||
keyNum := new(big.Int).SetBytes(keyData)
|
||||
if keyNum.Cmp(ecc.S256().N) >= 0 || keyNum.Sign() == 0 {
|
||||
return nil, ErrUnusableSeed
|
||||
}
|
||||
} else {
|
||||
// Ensure the public key parses correctly and is actually on the
|
||||
// secp256k1 curve.
|
||||
_, err := ecc.ParsePubKey(keyData, ecc.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return NewExtendedKey(version, keyData, chainCode, parentFP, depth,
|
||||
childNum, isPrivate), nil
|
||||
}
|
||||
|
||||
// GenerateSeed returns a cryptographically secure random seed that can be used
|
||||
// as the input for the NewMaster function to generate a new master node.
|
||||
//
|
||||
// The length is in bytes and it must be between 16 and 64 (128 to 512 bits).
|
||||
// The recommended length is 32 (256 bits) as defined by the RecommendedSeedLen
|
||||
// constant.
|
||||
func GenerateSeed(length uint8) ([]byte, error) {
|
||||
// Per [BIP32], the seed must be in range [MinSeedBytes, MaxSeedBytes].
|
||||
if length < MinSeedBytes || length > MaxSeedBytes {
|
||||
return nil, ErrInvalidSeedLen
|
||||
}
|
||||
|
||||
buf := make([]byte, length)
|
||||
_, err := rand.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register all default keyPairIDs when the package is initialized.
|
||||
RegisterHDKeyIDPair(HDKeyPairMainnet)
|
||||
RegisterHDKeyIDPair(HDKeyPairTestnet)
|
||||
RegisterHDKeyIDPair(HDKeyPairRegressionNet)
|
||||
RegisterHDKeyIDPair(HDKeyPairSimnet)
|
||||
RegisterHDKeyIDPair(HDKeyPairDevnet)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user