diff --git a/btcec_test.go b/btcec_test.go index 59c50697..92465f2f 100644 --- a/btcec_test.go +++ b/btcec_test.go @@ -18,6 +18,483 @@ import ( "testing" ) +// 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 := btcec.NewFieldVal().SetHex(test.x1) + y1 := btcec.NewFieldVal().SetHex(test.y1) + z1 := btcec.NewFieldVal().SetHex(test.z1) + x2 := btcec.NewFieldVal().SetHex(test.x2) + y2 := btcec.NewFieldVal().SetHex(test.y2) + z2 := btcec.NewFieldVal().SetHex(test.z2) + x3 := btcec.NewFieldVal().SetHex(test.x3) + y3 := btcec.NewFieldVal().SetHex(test.y3) + z3 := btcec.NewFieldVal().SetHex(test.z3) + + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !z1.IsZero() && !btcec.S256().TstIsJacobianOnCurve(x1, y1, z1) { + t.Errorf("#%d first point is not on the curve -- "+ + "invalid test data", i) + continue + } + if !z2.IsZero() && !btcec.S256().TstIsJacobianOnCurve(x2, y2, z2) { + t.Errorf("#%d second point is not on the curve -- "+ + "invalid test data", i) + continue + } + if !z3.IsZero() && !btcec.S256().TstIsJacobianOnCurve(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 := btcec.NewFieldVal(), btcec.NewFieldVal(), btcec.NewFieldVal() + btcec.S256().TstAddJacobian(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) && !btcec.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) && !btcec.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) && !btcec.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 := btcec.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", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Convert hex to field values. + x1 := btcec.NewFieldVal().SetHex(test.x1) + y1 := btcec.NewFieldVal().SetHex(test.y1) + z1 := btcec.NewFieldVal().SetHex(test.z1) + x3 := btcec.NewFieldVal().SetHex(test.x3) + y3 := btcec.NewFieldVal().SetHex(test.y3) + z3 := btcec.NewFieldVal().SetHex(test.z3) + + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !z1.IsZero() && !btcec.S256().TstIsJacobianOnCurve(x1, y1, z1) { + t.Errorf("#%d first point is not on the curve -- "+ + "invalid test data", i) + continue + } + if !z3.IsZero() && !btcec.S256().TstIsJacobianOnCurve(x3, y3, z3) { + t.Errorf("#%d expected point is not on the curve -- "+ + "invalid test data", i) + continue + } + + // Double the point. + rx, ry, rz := btcec.NewFieldVal(), btcec.NewFieldVal(), btcec.NewFieldVal() + btcec.S256().TstDoubleJacobian(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) && !btcec.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) && !btcec.S256().IsOnCurve(x3, y3) { + t.Errorf("#%d expected point is not on the curve -- "+ + "invalid test data", i) + continue + } + + // Double the point. + rx, ry := btcec.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 := btcec.S256() if !s256.IsOnCurve(s256.Params().Gx, s256.Params().Gy) { diff --git a/field_test.go b/field_test.go new file mode 100644 index 00000000..7c381175 --- /dev/null +++ b/field_test.go @@ -0,0 +1,743 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Copyright (c) 2013 Dave Collins +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec_test + +import ( + "github.com/conformal/btcec" + "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 := btcec.NewFieldVal().SetInt(test.in) + result := f.TstRawInts() + if !reflect.DeepEqual(result, test.raw) { + t.Errorf("fieldVal.Set #%d wrong result\ngot: %v\n"+ + "want: %v", i, result, test.raw) + continue + } + } +} + +// TestZero ensures that zeroing a field value zero works as expected. +func TestZero(t *testing.T) { + f := btcec.NewFieldVal().SetInt(2) + f.Zero() + for idx, rawInt := range f.TstRawInts() { + 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 := btcec.NewFieldVal() + if !f.IsZero() { + t.Errorf("new field value is not zero - got %v (rawints %x)", f, + f.TstRawInts()) + } + + f.SetInt(1) + if f.IsZero() { + t.Errorf("field claims it's zero when it's not - got %v "+ + "(raw rawints %x)", f, f.TstRawInts()) + } + + f.Zero() + if !f.IsZero() { + t.Errorf("field claims it's not zero when it is - got %v "+ + "(raw rawints %x)", f, f.TstRawInts()) + } +} + +// 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 btcec 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 := btcec.NewFieldVal().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}, + }, + // 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}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().TstSetRawInts(test.raw).Normalize() + result := f.TstRawInts() + if !reflect.DeepEqual(result, test.normalized) { + t.Errorf("fieldVal.Set #%d wrong normalized result\n"+ + "got: %x\nwant: %x", i, result, 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 := btcec.NewFieldVal().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 := btcec.NewFieldVal().SetHex(test.in1).Normalize() + f2 := btcec.NewFieldVal().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 := btcec.NewFieldVal().SetHex(test.in).Normalize() + expected := btcec.NewFieldVal().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 := btcec.NewFieldVal().SetHex(test.in1).Normalize() + expected := btcec.NewFieldVal().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 := btcec.NewFieldVal().SetHex(test.in1).Normalize() + f2 := btcec.NewFieldVal().SetHex(test.in2).Normalize() + expected := btcec.NewFieldVal().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"}, + // 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 := btcec.NewFieldVal().SetHex(test.in1).Normalize() + f2 := btcec.NewFieldVal().SetHex(test.in2).Normalize() + expected := btcec.NewFieldVal().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 := btcec.NewFieldVal().SetHex(test.in1).Normalize() + expected := btcec.NewFieldVal().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"}, + // 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 := btcec.NewFieldVal().SetHex(test.in1).Normalize() + f2 := btcec.NewFieldVal().SetHex(test.in2).Normalize() + expected := btcec.NewFieldVal().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 := btcec.NewFieldVal().SetHex(test.in).Normalize() + expected := btcec.NewFieldVal().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 := btcec.NewFieldVal().SetHex(test.in).Normalize() + expected := btcec.NewFieldVal().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 + } + } +} diff --git a/internal_test.go b/internal_test.go index 6615fd36..c388225b 100644 --- a/internal_test.go +++ b/internal_test.go @@ -4,8 +4,62 @@ package btcec +import ( + "math/big" +) + const ( TstPubkeyUncompressed = pubkeyUncompressed TstPubkeyCompressed = pubkeyCompressed TstPubkeyHybrid = pubkeyHybrid ) + +// TstRawInts allows the test package to get the integers from the internal +// field representation for ensuring correctness. It is only available during +// the tests. +func (f *fieldVal) TstRawInts() [10]uint32 { + return f.n +} + +// TstSetRawInts allows the test package to directly set the integers used by +// the internal field representation. It is only available during the tests. +func (f *fieldVal) TstSetRawInts(raw [10]uint32) *fieldVal { + for i := 0; i < len(raw); i++ { + f.n[i] = raw[i] + } + return f +} + +// TstFieldJacobianToBigAffine makes the internal fieldJacobianToBigAffine +// function available to the test package. +func (curve *KoblitzCurve) TstFieldJacobianToBigAffine(x, y, z *fieldVal) (*big.Int, *big.Int) { + return curve.fieldJacobianToBigAffine(x, y, z) +} + +// TstIsJacobianOnCurve returns boolean if the point (x,y,z) is on the curve. +func (curve *KoblitzCurve) TstIsJacobianOnCurve(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) +} + +// TstAddJacobian makes the internal addJacobian function available to the test +// package. +func (curve *KoblitzCurve) TstAddJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3 *fieldVal) { + curve.addJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3) +} + +// TstDoubleJacobian makes the internal doubleJacobian function available to the test +// package. +func (curve *KoblitzCurve) TstDoubleJacobian(x1, y1, z1, x3, y3, z3 *fieldVal) { + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) +} diff --git a/test_coverage.txt b/test_coverage.txt index 316be9bd..2295eefc 100644 --- a/test_coverage.txt +++ b/test_coverage.txt @@ -1,26 +1,58 @@ -github.com/conformal/btcec/signature.go parseSig 100.00% (51/51) -github.com/conformal/btcec/btcec.go KoblitzCurve.doubleJacobian 100.00% (21/21) -github.com/conformal/btcec/btcec.go KoblitzCurve.ScalarMult 100.00% (9/9) -github.com/conformal/btcec/pubkey.go PublicKey.SerializeHybrid 100.00% (8/8) -github.com/conformal/btcec/btcec.go initS256 100.00% (7/7) -github.com/conformal/btcec/pubkey.go PublicKey.SerializeCompressed 100.00% (7/7) -github.com/conformal/btcec/btcec.go KoblitzCurve.IsOnCurve 100.00% (7/7) -github.com/conformal/btcec/pubkey.go PublicKey.SerializeUncompressed 100.00% (5/5) -github.com/conformal/btcec/btcec.go zForAffine 100.00% (4/4) -github.com/conformal/btcec/signature.go canonicalPadding 100.00% (4/4) -github.com/conformal/btcec/btcec.go KoblitzCurve.QPlus1Div4 100.00% (3/3) -github.com/conformal/btcec/btcec.go KoblitzCurve.Add 100.00% (3/3) -github.com/conformal/btcec/btcec.go S256 100.00% (2/2) -github.com/conformal/btcec/btcec.go initAll 100.00% (1/1) -github.com/conformal/btcec/btcec.go KoblitzCurve.ScalarBaseMult 100.00% (1/1) -github.com/conformal/btcec/pubkey.go isOdd 100.00% (1/1) -github.com/conformal/btcec/signature.go ParseSignature 100.00% (1/1) -github.com/conformal/btcec/signature.go ParseDERSignature 100.00% (1/1) -github.com/conformal/btcec/btcec.go KoblitzCurve.Params 100.00% (1/1) -github.com/conformal/btcec/pubkey.go ParsePubKey 96.88% (31/32) -github.com/conformal/btcec/btcec.go KoblitzCurve.addJacobian 91.67% (55/60) -github.com/conformal/btcec/btcec.go KoblitzCurve.affineFromJacobian 90.00% (9/10) -github.com/conformal/btcec/btcec.go KoblitzCurve.Double 0.00% (0/2) -github.com/conformal/btcec ------------------------------- 96.27% (232/241) +github.com/conformal/btcec/field.go fieldVal.Normalize 100.00% (93/93) +github.com/conformal/btcec/field.go fieldVal.Inverse 100.00% (88/88) +github.com/conformal/btcec/field.go fieldVal.Mul2 100.00% (73/73) +github.com/conformal/btcec/field.go fieldVal.SquareVal 100.00% (73/73) +github.com/conformal/btcec/signature.go parseSig 100.00% (51/51) +github.com/conformal/btcec/btcec.go KoblitzCurve.addGeneric 100.00% (35/35) +github.com/conformal/btcec/btcec.go KoblitzCurve.addZ2EqualsOne 100.00% (35/35) +github.com/conformal/btcec/field.go fieldVal.PutBytes 100.00% (32/32) +github.com/conformal/btcec/btcec.go KoblitzCurve.addZ1EqualsZ2 100.00% (30/30) +github.com/conformal/btcec/btcec.go KoblitzCurve.addZ1AndZ2EqualsOne 100.00% (29/29) +github.com/conformal/btcec/btcec.go KoblitzCurve.addJacobian 100.00% (22/22) +github.com/conformal/btcec/btcec.go KoblitzCurve.doubleZ1EqualsOne 100.00% (18/18) +github.com/conformal/btcec/btcec.go KoblitzCurve.doubleGeneric 100.00% (18/18) +github.com/conformal/btcec/field.go fieldVal.MulInt 100.00% (12/12) +github.com/conformal/btcec/btcec.go KoblitzCurve.fieldJacobianToBigAffine 100.00% (12/12) +github.com/conformal/btcec/field.go fieldVal.Add 100.00% (11/11) +github.com/conformal/btcec/field.go fieldVal.Add2 100.00% (11/11) +github.com/conformal/btcec/field.go fieldVal.SetBytes 100.00% (11/11) +github.com/conformal/btcec/field.go fieldVal.NegateVal 100.00% (11/11) +github.com/conformal/btcec/btcec.go KoblitzCurve.ScalarMult 100.00% (10/10) +github.com/conformal/btcec/field.go fieldVal.Zero 100.00% (10/10) +github.com/conformal/btcec/btcec.go KoblitzCurve.doubleJacobian 100.00% (9/9) +github.com/conformal/btcec/btcec.go KoblitzCurve.Add 100.00% (9/9) +github.com/conformal/btcec/btcec.go initS256 100.00% (8/8) +github.com/conformal/btcec/pubkey.go PublicKey.SerializeHybrid 100.00% (8/8) +github.com/conformal/btcec/pubkey.go PublicKey.SerializeCompressed 100.00% (7/7) +github.com/conformal/btcec/btcec.go KoblitzCurve.Double 100.00% (6/6) +github.com/conformal/btcec/field.go fieldVal.SetByteSlice 100.00% (5/5) +github.com/conformal/btcec/pubkey.go PublicKey.SerializeUncompressed 100.00% (5/5) +github.com/conformal/btcec/signature.go canonicalPadding 100.00% (4/4) +github.com/conformal/btcec/field.go fieldVal.SetHex 100.00% (4/4) +github.com/conformal/btcec/btcec.go KoblitzCurve.bigAffineToField 100.00% (4/4) +github.com/conformal/btcec/btcec.go KoblitzCurve.IsOnCurve 100.00% (4/4) +github.com/conformal/btcec/field.go fieldVal.SetInt 100.00% (3/3) +github.com/conformal/btcec/field.go fieldVal.Bytes 100.00% (3/3) +github.com/conformal/btcec/field.go fieldVal.Set 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.String 100.00% (2/2) +github.com/conformal/btcec/btcec.go S256 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.IsZero 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.Equals 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.AddInt 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.Square 100.00% (1/1) +github.com/conformal/btcec/btcec.go KoblitzCurve.ScalarBaseMult 100.00% (1/1) +github.com/conformal/btcec/btcec.go KoblitzCurve.Params 100.00% (1/1) +github.com/conformal/btcec/field.go NewFieldVal 100.00% (1/1) +github.com/conformal/btcec/pubkey.go isOdd 100.00% (1/1) +github.com/conformal/btcec/signature.go ParseDERSignature 100.00% (1/1) +github.com/conformal/btcec/field.go fieldVal.Mul 100.00% (1/1) +github.com/conformal/btcec/btcec.go KoblitzCurve.QPlus1Div4 100.00% (1/1) +github.com/conformal/btcec/btcec.go initAll 100.00% (1/1) +github.com/conformal/btcec/field.go fieldVal.Negate 100.00% (1/1) +github.com/conformal/btcec/field.go fieldVal.IsOdd 100.00% (1/1) +github.com/conformal/btcec/signature.go ParseSignature 100.00% (1/1) +github.com/conformal/btcec/pubkey.go ParsePubKey 96.88% (31/32) +github.com/conformal/btcec/pubkey.go pad 80.00% (4/5) +github.com/conformal/btcec ------------------------------------- 99.76% (823/825)