...

Source file src/github.com/decred/dcrd/dcrec/secp256k1/v4/pubkey_test.go

Documentation: github.com/decred/dcrd/dcrec/secp256k1/v4

     1  // Copyright (c) 2013-2016 The btcsuite developers
     2  // Copyright (c) 2015-2020 The Decred developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package secp256k1
     7  
     8  import (
     9  	"bytes"
    10  	"errors"
    11  	"testing"
    12  )
    13  
    14  // TestParsePubKey ensures that public keys are properly parsed according
    15  // to the spec including both the positive and negative cases.
    16  func TestParsePubKey(t *testing.T) {
    17  	tests := []struct {
    18  		name  string // test description
    19  		key   string // hex encoded public key
    20  		err   error  // expected error
    21  		wantX string // expected x coordinate
    22  		wantY string // expected y coordinate
    23  	}{{
    24  		name: "uncompressed ok",
    25  		key: "04" +
    26  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
    27  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
    28  		err:   nil,
    29  		wantX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
    30  		wantY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
    31  	}, {
    32  		name: "uncompressed x changed (not on curve)",
    33  		key: "04" +
    34  			"15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
    35  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
    36  		err: ErrPubKeyNotOnCurve,
    37  	}, {
    38  		name: "uncompressed y changed (not on curve)",
    39  		key: "04" +
    40  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
    41  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4",
    42  		err: ErrPubKeyNotOnCurve,
    43  	}, {
    44  		name: "uncompressed claims compressed",
    45  		key: "03" +
    46  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
    47  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
    48  		err: ErrPubKeyInvalidFormat,
    49  	}, {
    50  		name: "uncompressed as hybrid ok (ybit = 0)",
    51  		key: "06" +
    52  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
    53  			"4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
    54  		err:   nil,
    55  		wantX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
    56  		wantY: "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
    57  	}, {
    58  		name: "uncompressed as hybrid ok (ybit = 1)",
    59  		key: "07" +
    60  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
    61  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
    62  		err:   nil,
    63  		wantX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
    64  		wantY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
    65  	}, {
    66  		name: "uncompressed as hybrid wrong oddness",
    67  		key: "06" +
    68  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
    69  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
    70  		err: ErrPubKeyMismatchedOddness,
    71  	}, {
    72  		name: "compressed ok (ybit = 0)",
    73  		key: "02" +
    74  			"ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
    75  		err:   nil,
    76  		wantX: "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
    77  		wantY: "0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032",
    78  	}, {
    79  		name: "compressed ok (ybit = 1)",
    80  		key: "03" +
    81  			"2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
    82  		err:   nil,
    83  		wantX: "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
    84  		wantY: "499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f",
    85  	}, {
    86  		name: "compressed claims uncompressed (ybit = 0)",
    87  		key: "04" +
    88  			"ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
    89  		err: ErrPubKeyInvalidFormat,
    90  	}, {
    91  		name: "compressed claims uncompressed (ybit = 1)",
    92  		key: "04" +
    93  			"2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
    94  		err: ErrPubKeyInvalidFormat,
    95  	}, {
    96  		name: "compressed claims hybrid (ybit = 0)",
    97  		key: "06" +
    98  			"ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
    99  		err: ErrPubKeyInvalidFormat,
   100  	}, {
   101  		name: "compressed claims hybrid (ybit = 1)",
   102  		key: "07" +
   103  			"2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
   104  		err: ErrPubKeyInvalidFormat,
   105  	}, {
   106  		name: "compressed with invalid x coord (ybit = 0)",
   107  		key: "03" +
   108  			"ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c",
   109  		err: ErrPubKeyNotOnCurve,
   110  	}, {
   111  		name: "compressed with invalid x coord (ybit = 1)",
   112  		key: "03" +
   113  			"2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d",
   114  		err: ErrPubKeyNotOnCurve,
   115  	}, {
   116  		name: "empty",
   117  		key:  "",
   118  		err:  ErrPubKeyInvalidLen,
   119  	}, {
   120  		name: "wrong length",
   121  		key:  "05",
   122  		err:  ErrPubKeyInvalidLen,
   123  	}, {
   124  		name: "uncompressed x == p",
   125  		key: "04" +
   126  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" +
   127  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
   128  		err: ErrPubKeyXTooBig,
   129  	}, {
   130  		// The y coordinate produces a valid point for x == 1 (mod p), but it
   131  		// should fail to parse instead of wrapping around.
   132  		name: "uncompressed x > p (p + 1 -- aka 1)",
   133  		key: "04" +
   134  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30" +
   135  			"bde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441",
   136  		err: ErrPubKeyXTooBig,
   137  	}, {
   138  		name: "uncompressed y == p",
   139  		key: "04" +
   140  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
   141  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
   142  		err: ErrPubKeyYTooBig,
   143  	}, {
   144  		// The x coordinate produces a valid point for y == 1 (mod p), but it
   145  		// should fail to parse instead of wrapping around.
   146  		name: "uncompressed y > p (p + 1 -- aka 1)",
   147  		key: "04" +
   148  			"1fe1e5ef3fceb5c135ab7741333ce5a6e80d68167653f6b2b24bcbcfaaaff507" +
   149  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30",
   150  		err: ErrPubKeyYTooBig,
   151  	}, {
   152  		name: "compressed x == p (ybit = 0)",
   153  		key: "02" +
   154  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
   155  		err: ErrPubKeyXTooBig,
   156  	}, {
   157  		name: "compressed x == p (ybit = 1)",
   158  		key: "03" +
   159  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
   160  		err: ErrPubKeyXTooBig,
   161  	}, {
   162  		// This would be valid for x == 2 (mod p), but it should fail to parse
   163  		// instead of wrapping around.
   164  		name: "compressed x > p (p + 2 -- aka 2) (ybit = 0)",
   165  		key: "02" +
   166  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc31",
   167  		err: ErrPubKeyXTooBig,
   168  	}, {
   169  		// This would be valid for x == 1 (mod p), but it should fail to parse
   170  		// instead of wrapping around.
   171  		name: "compressed x > p (p + 1 -- aka 1) (ybit = 1)",
   172  		key: "03" +
   173  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30",
   174  		err: ErrPubKeyXTooBig,
   175  	}, {
   176  		name: "hybrid x == p (ybit = 1)",
   177  		key: "07" +
   178  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" +
   179  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
   180  		err: ErrPubKeyXTooBig,
   181  	}, {
   182  		// The y coordinate produces a valid point for x == 1 (mod p), but it
   183  		// should fail to parse instead of wrapping around.
   184  		name: "hybrid x > p (p + 1 -- aka 1) (ybit = 0)",
   185  		key: "06" +
   186  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30" +
   187  			"bde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441",
   188  		err: ErrPubKeyXTooBig,
   189  	}, {
   190  		name: "hybrid y == p (ybit = 0 when mod p)",
   191  		key: "06" +
   192  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
   193  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
   194  		err: ErrPubKeyYTooBig,
   195  	}, {
   196  		// The x coordinate produces a valid point for y == 1 (mod p), but it
   197  		// should fail to parse instead of wrapping around.
   198  		name: "hybrid y > p (p + 1 -- aka 1) (ybit = 1 when mod p)",
   199  		key: "07" +
   200  			"1fe1e5ef3fceb5c135ab7741333ce5a6e80d68167653f6b2b24bcbcfaaaff507" +
   201  			"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30",
   202  		err: ErrPubKeyYTooBig,
   203  	}}
   204  
   205  	for _, test := range tests {
   206  		pubKeyBytes := hexToBytes(test.key)
   207  		pubKey, err := ParsePubKey(pubKeyBytes)
   208  		if !errors.Is(err, test.err) {
   209  			t.Errorf("%s mismatched err -- got %v, want %v", test.name, err,
   210  				test.err)
   211  			continue
   212  		}
   213  		if err != nil {
   214  			continue
   215  		}
   216  
   217  		// Ensure the x and y coordinates match the expected values upon
   218  		// successful parse.
   219  		wantX, wantY := hexToFieldVal(test.wantX), hexToFieldVal(test.wantY)
   220  		if !pubKey.x.Equals(wantX) {
   221  			t.Errorf("%s: mismatched x coordinate -- got %v, want %v",
   222  				test.name, pubKey.x, wantX)
   223  			continue
   224  		}
   225  		if !pubKey.y.Equals(wantY) {
   226  			t.Errorf("%s: mismatched y coordinate -- got %v, want %v",
   227  				test.name, pubKey.y, wantY)
   228  			continue
   229  		}
   230  	}
   231  }
   232  
   233  // TestPubKeySerialize ensures that serializing public keys works as expected
   234  // for both the compressed and uncompressed cases.
   235  func TestPubKeySerialize(t *testing.T) {
   236  	tests := []struct {
   237  		name     string // test description
   238  		pubX     string // hex encoded x coordinate for pubkey to serialize
   239  		pubY     string // hex encoded y coordinate for pubkey to serialize
   240  		compress bool   // whether to serialize compressed or uncompressed
   241  		expected string // hex encoded expected pubkey serialization
   242  	}{{
   243  		name:     "uncompressed (ybit = 0)",
   244  		pubX:     "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
   245  		pubY:     "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
   246  		compress: false,
   247  		expected: "04" +
   248  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
   249  			"4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
   250  	}, {
   251  		name:     "uncompressed (ybit = 1)",
   252  		pubX:     "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
   253  		pubY:     "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
   254  		compress: false,
   255  		expected: "04" +
   256  			"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
   257  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
   258  	}, {
   259  		// It's invalid to parse pubkeys that are not on the curve, however it
   260  		// is possible to manually create them and they should serialize
   261  		// correctly.
   262  		name:     "uncompressed not on the curve due to x coord",
   263  		pubX:     "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
   264  		pubY:     "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
   265  		compress: false,
   266  		expected: "04" +
   267  			"15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
   268  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
   269  	}, {
   270  		// It's invalid to parse pubkeys that are not on the curve, however it
   271  		// is possible to manually create them and they should serialize
   272  		// correctly.
   273  		name:     "uncompressed not on the curve due to y coord",
   274  		pubX:     "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
   275  		pubY:     "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4",
   276  		compress: false,
   277  		expected: "04" +
   278  			"15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
   279  			"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4",
   280  	}, {
   281  		name:     "compressed (ybit = 0)",
   282  		pubX:     "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
   283  		pubY:     "0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032",
   284  		compress: true,
   285  		expected: "02" +
   286  			"ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
   287  	}, {
   288  		name:     "compressed (ybit = 1)",
   289  		pubX:     "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
   290  		pubY:     "499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f",
   291  		compress: true,
   292  		expected: "03" +
   293  			"2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
   294  	}, {
   295  		// It's invalid to parse pubkeys that are not on the curve, however it
   296  		// is possible to manually create them and they should serialize
   297  		// correctly.
   298  		name:     "compressed not on curve (ybit = 0)",
   299  		pubX:     "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c",
   300  		pubY:     "0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032",
   301  		compress: true,
   302  		expected: "02" +
   303  			"ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c",
   304  	}, {
   305  		// It's invalid to parse pubkeys that are not on the curve, however it
   306  		// is possible to manually create them and they should serialize
   307  		// correctly.
   308  		name:     "compressed not on curve (ybit = 1)",
   309  		pubX:     "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d",
   310  		pubY:     "499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f",
   311  		compress: true,
   312  		expected: "03" +
   313  			"2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d",
   314  	}}
   315  
   316  	for _, test := range tests {
   317  		// Parse the test data.
   318  		x, y := hexToFieldVal(test.pubX), hexToFieldVal(test.pubY)
   319  		pubKey := NewPublicKey(x, y)
   320  
   321  		// Serialize with the correct method and ensure the result matches the
   322  		// expected value.
   323  		var serialized []byte
   324  		if test.compress {
   325  			serialized = pubKey.SerializeCompressed()
   326  		} else {
   327  			serialized = pubKey.SerializeUncompressed()
   328  		}
   329  		expected := hexToBytes(test.expected)
   330  		if !bytes.Equal(serialized, expected) {
   331  			t.Errorf("%s: mismatched serialized public key -- got %x, want %x",
   332  				test.name, serialized, expected)
   333  			continue
   334  		}
   335  	}
   336  }
   337  
   338  // TestPublicKeyIsEqual ensures that equality testing between two public keys
   339  // works as expected.
   340  func TestPublicKeyIsEqual(t *testing.T) {
   341  	pubKey1 := &PublicKey{
   342  		x: *hexToFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"),
   343  		y: *hexToFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"),
   344  	}
   345  	pubKey1Copy := &PublicKey{
   346  		x: *hexToFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"),
   347  		y: *hexToFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"),
   348  	}
   349  	pubKey2 := &PublicKey{
   350  		x: *hexToFieldVal("ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d"),
   351  		y: *hexToFieldVal("0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032"),
   352  	}
   353  
   354  	if !pubKey1.IsEqual(pubKey1) {
   355  		t.Fatalf("bad self public key equality check: (%v, %v)", pubKey1.x,
   356  			pubKey1.y)
   357  	}
   358  	if !pubKey1.IsEqual(pubKey1Copy) {
   359  		t.Fatalf("bad public key equality check: (%v, %v) == (%v, %v)",
   360  			pubKey1.x, pubKey1.y, pubKey1Copy.x, pubKey1Copy.y)
   361  	}
   362  
   363  	if pubKey1.IsEqual(pubKey2) {
   364  		t.Fatalf("bad public key equality check: (%v, %v) != (%v, %v)",
   365  			pubKey1.x, pubKey1.y, pubKey2.x, pubKey2.y)
   366  	}
   367  }
   368  
   369  // TestPublicKeyAsJacobian ensures converting a public key to a jacobian point
   370  // with a Z coordinate of 1 works as expected.
   371  func TestPublicKeyAsJacobian(t *testing.T) {
   372  	tests := []struct {
   373  		name   string // test description
   374  		pubKey string // hex encoded serialized compressed pubkey
   375  		wantX  string // hex encoded expected X coordinate
   376  		wantY  string // hex encoded expected Y coordinate
   377  
   378  	}{{
   379  		name:   "public key for private key 0x01",
   380  		pubKey: "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
   381  		wantX:  "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
   382  		wantY:  "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
   383  	}, {
   384  		name:   "public for private key 0x03",
   385  		pubKey: "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
   386  		wantX:  "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
   387  		wantY:  "388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672",
   388  	}, {
   389  		name:   "public for private key 0x06",
   390  		pubKey: "03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556",
   391  		wantX:  "fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556",
   392  		wantY:  "ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297",
   393  	}}
   394  
   395  	for _, test := range tests {
   396  		// Parse the test data.
   397  		pubKeyBytes := hexToBytes(test.pubKey)
   398  		wantX := hexToFieldVal(test.wantX)
   399  		wantY := hexToFieldVal(test.wantY)
   400  		pubKey, err := ParsePubKey(pubKeyBytes)
   401  		if err != nil {
   402  			t.Errorf("%s: failed to parse public key: %v", test.name, err)
   403  			continue
   404  		}
   405  
   406  		// Convert the public key to a jacobian point and ensure the coordinates
   407  		// match the expected values.
   408  		var point JacobianPoint
   409  		pubKey.AsJacobian(&point)
   410  		if !point.Z.IsOne() {
   411  			t.Errorf("%s: invalid Z coordinate -- got %v, want 1", test.name,
   412  				point.Z)
   413  			continue
   414  		}
   415  		if !point.X.Equals(wantX) {
   416  			t.Errorf("%s: invalid X coordinate - got %v, want %v", test.name,
   417  				point.X, wantX)
   418  			continue
   419  		}
   420  		if !point.Y.Equals(wantY) {
   421  			t.Errorf("%s: invalid Y coordinate - got %v, want %v", test.name,
   422  				point.Y, wantY)
   423  			continue
   424  		}
   425  	}
   426  }
   427  
   428  // TestPublicKeyIsOnCurve ensures testing if a public key is on the curve works
   429  // as expected.
   430  func TestPublicKeyIsOnCurve(t *testing.T) {
   431  	tests := []struct {
   432  		name string // test description
   433  		pubX string // hex encoded x coordinate for pubkey to serialize
   434  		pubY string // hex encoded y coordinate for pubkey to serialize
   435  		want bool   // expected result
   436  	}{{
   437  		name: "valid with even y",
   438  		pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
   439  		pubY: "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
   440  		want: true,
   441  	}, {
   442  		name: "valid with odd y",
   443  		pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
   444  		pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
   445  		want: true,
   446  	}, {
   447  		name: "invalid due to x coord",
   448  		pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
   449  		pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
   450  		want: false,
   451  	}, {
   452  		name: "invalid due to y coord",
   453  		pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
   454  		pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4",
   455  		want: false,
   456  	}}
   457  
   458  	for _, test := range tests {
   459  		// Parse the test data.
   460  		x, y := hexToFieldVal(test.pubX), hexToFieldVal(test.pubY)
   461  		pubKey := NewPublicKey(x, y)
   462  
   463  		result := pubKey.IsOnCurve()
   464  		if result != test.want {
   465  			t.Errorf("%s: mismatched is on curve result -- got %v, want %v",
   466  				test.name, result, test.want)
   467  			continue
   468  		}
   469  	}
   470  }
   471  

View as plain text