Skip to content

Commit 3fc9cde

Browse files
Add scalar inverse methods
1 parent ec01d6b commit 3fc9cde

File tree

1 file changed

+293
-0
lines changed

1 file changed

+293
-0
lines changed

Src/Tests/Bitcoin/Cryptography/EllipticCurve/Scalar8x32Tests.cs

+293
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,299 @@ public void Libsecp256k1_StaticTest(byte[] xba, byte[] yba, byte[] r1ba, byte[]
14981498
}
14991499
}
15001500

1501+
1502+
private static readonly Scalar8x32 _m1 = new(0xD0364140, 0xBFD25E8C, 0xAF48A03B, 0xBAAEDCE6, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
1503+
internal static ref readonly Scalar8x32 Minus_One => ref _m1;
1504+
1505+
1506+
// These tests test the following identities:
1507+
//
1508+
// for x==0: 1/x == 0
1509+
// for x!=0: x*(1/x) == 1
1510+
// for x!=0 and x!=1: 1/(1/x - 1) + 1 == -1/(x-1)
1511+
private static void TestInverseScalar(in Scalar8x32 x, int useVar, out Scalar8x32 _out)
1512+
{
1513+
Scalar8x32 l = useVar == 0 ? x.Inverse() : x.InverseVar(); // l = 1/x
1514+
_out = l;
1515+
if (x.IsZero)
1516+
{
1517+
Assert.True(l.IsZero);
1518+
return;
1519+
}
1520+
Scalar8x32 t = x.Multiply(l); // t = x*(1/x)
1521+
Assert.True(t.IsOne); // x*(1/x) == 1
1522+
Scalar8x32 r = x.Add(Minus_One, out _); // r = x-1
1523+
if (r.IsZero)
1524+
{
1525+
return;
1526+
}
1527+
r = useVar == 0 ? r.Inverse() : r.InverseVar(); // r = 1/(x-1)
1528+
l = Minus_One.Add(l, out _); // l = 1/x-1
1529+
l = useVar == 0 ? l.Inverse() : l.InverseVar(); // l = 1/(1/x-1)
1530+
l = l.Add(Scalar8x32.One, out _); // l = 1/(1/x-1)+1
1531+
l = r.Add(l, out _); // l = 1/(1/x-1)+1 + 1/(x-1)
1532+
Assert.True(l.IsZero); // l == 0
1533+
}
1534+
1535+
public static IEnumerable<object[]> GetInvCases()
1536+
{
1537+
// Fixed test cases for scalar inverses: pairs of (x, 1/x) mod n.
1538+
yield return new object[]
1539+
{
1540+
// 0
1541+
new Scalar8x32(0, 0, 0, 0, 0, 0, 0, 0),
1542+
new Scalar8x32(0, 0, 0, 0, 0, 0, 0, 0)
1543+
};
1544+
yield return new object[]
1545+
{
1546+
// 1
1547+
new Scalar8x32(1, 0, 0, 0, 0, 0, 0, 0),
1548+
new Scalar8x32(1, 0, 0, 0, 0, 0, 0, 0)
1549+
};
1550+
yield return new object[]
1551+
{
1552+
// -1
1553+
new Scalar8x32(0xd0364140, 0xbfd25e8c, 0xaf48a03b, 0xbaaedce6, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff),
1554+
new Scalar8x32(0xd0364140, 0xbfd25e8c, 0xaf48a03b, 0xbaaedce6, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff)
1555+
};
1556+
yield return new object[]
1557+
{
1558+
// 2
1559+
new Scalar8x32(2, 0, 0, 0, 0, 0, 0, 0),
1560+
new Scalar8x32(0x681b20a1, 0xdfe92f46, 0x57a4501d, 0x5d576e73, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff)
1561+
};
1562+
yield return new object[]
1563+
{
1564+
// 2**128
1565+
new Scalar8x32(0, 0, 0, 0, 1, 0, 0, 0),
1566+
new Scalar8x32(0xa3ee9f22, 0xd6a23766, 0xcf80ef0f, 0x9984d5b3, 0x5588b13e, 0x4b0dff66, 0x34b9ec24, 0x50a51ac8)
1567+
};
1568+
yield return new object[]
1569+
{
1570+
// Input known to need 635 divsteps
1571+
new Scalar8x32(0x19199ec3, 0x32a5ef60, 0x8feb7ae9, 0x3c9b3376, 0x6365da66, 0xcd71bf3f, 0xdd4416c2, 0xcb9f1d35),
1572+
new Scalar8x32(0x8e9bc708, 0x5e189791, 0xec72c428, 0x42c2e42f, 0x36b411dc, 0xb834bd09, 0xf1893d53, 0x1d7c7bba)
1573+
};
1574+
yield return new object[]
1575+
{
1576+
// Input known to need 566 divsteps starting with delta=1/2.
1577+
new Scalar8x32(0xcd132c72, 0x80f82eff, 0xebe6db35, 0xd382083a, 0x2db54174, 0xbc015b49, 0xa4272488, 0x7e3c993d),
1578+
new Scalar8x32(0xd0b934f8, 0x312c6ded, 0x365db268, 0x6304439d, 0xcc84ac95, 0x77418f28, 0x3e631f76, 0x086f34a0)
1579+
};
1580+
yield return new object[]
1581+
{
1582+
// Input known to need 565 divsteps starting with delta=1/2.
1583+
new Scalar8x32(0x7dd6edc4, 0x4b1f0e4b, 0x254350d3, 0xb38a9fd5, 0x8a18491e, 0x60d93147, 0x3f307859, 0xbad7e587),
1584+
new Scalar8x32(0x515f467b, 0x6632d908, 0x29c4943e, 0xc2223add, 0xd039c8ac, 0xf19bd876, 0x39e2b041, 0x89f2df26)
1585+
};
1586+
yield return new object[]
1587+
{
1588+
// Selection of randomly generated inputs that reach low/high d/e values in various configurations.
1589+
new Scalar8x32(0xdbde180e, 0x6a0ed8e3, 0x5e5d7d2c, 0x07e1e3c8, 0x0bb8997e, 0x435059bb, 0xb37a5809, 0x1950d757),
1590+
new Scalar8x32(0x3bcb6f8c, 0x633d894b, 0x4388251e, 0x7e25e475, 0xfe432b93, 0x8dda230b, 0x750309e2, 0xbf72af9b)
1591+
};
1592+
yield return new object[]
1593+
{
1594+
new Scalar8x32(0x3cd10ce0, 0xf6d7eabb, 0x62de7d4e, 0x391749a1, 0xbb65a13f, 0x50637aa9, 0xc5a515e3, 0x9bccf4e7),
1595+
new Scalar8x32(0x20092f67, 0x8f9cc83b, 0x3baaf30f, 0x7f09179c, 0x5e92a70d, 0xcd0365be, 0xb6385a33, 0xaf2d5623)
1596+
};
1597+
yield return new object[]
1598+
{
1599+
new Scalar8x32(0x39e642c9, 0xd21267b1, 0xa46e5f47, 0xa30a7659, 0xf3be2ace, 0x5c5dee59, 0xb242952a, 0x73a57111),
1600+
new Scalar8x32(0xcd5fa004, 0x86d0feb3, 0x272d4a18, 0xb02cf157, 0xbcd058ce, 0xd61cc6be, 0xcbcf13ef, 0xa711df07)
1601+
};
1602+
yield return new object[]
1603+
{
1604+
new Scalar8x32(0x3e785ec2, 0x97ebfdba, 0x24c7cebd, 0x9cd2c84f, 0x3c691db3, 0xba547030, 0xce0580b1, 0x04884963),
1605+
new Scalar8x32(0x0ec4dc5d, 0x4cc28f33, 0xe5c5a03d, 0x3769a851, 0x78a28b4c, 0x517ce2c1, 0xd7c99ba7, 0xaaaaaf14)
1606+
};
1607+
yield return new object[]
1608+
{
1609+
new Scalar8x32(0x6c4a9905, 0x6c985281, 0x0b0f275e, 0x5b9fa037, 0x9efc511c, 0x815cb8ae, 0x21f537b1, 0x1679ed49),
1610+
new Scalar8x32(0x302eea62, 0xea1624f9, 0x1f8172aa, 0x0294341a, 0xffca4998, 0xef34ead1, 0x62b52999, 0xb14ac3d5)
1611+
};
1612+
yield return new object[]
1613+
{
1614+
new Scalar8x32(0x4ad5f6ec, 0x1d2c4799, 0x48b08a9d, 0xea826506, 0x452a1fd3, 0xee982f83, 0xf0057c35, 0x626b37c0),
1615+
new Scalar8x32(0xdb71c1e8, 0xfd98e77a, 0x69509283, 0x07112443, 0xe327239c, 0x5d2f1c15, 0x567bfc2f, 0xe38643b7)
1616+
};
1617+
yield return new object[]
1618+
{
1619+
new Scalar8x32(0x965bd816, 0xd5f85f27, 0xcf4d8927, 0xe263bbc9, 0x14d1234b, 0x54f287b2, 0x759efc56, 0x1850a3a7),
1620+
new Scalar8x32(0xabfa0009, 0x8eaa2621, 0xc6a00901, 0x95d0db2f, 0xf614d63b, 0xcceb0596, 0xcac9619a, 0x3b071831)
1621+
};
1622+
yield return new object[]
1623+
{
1624+
new Scalar8x32(0xdca4222f, 0xf4df627a, 0xea675ffc, 0xe475b5c0, 0xaa51ebed, 0x487d72be, 0xa27dc400, 0x94ae5d06),
1625+
new Scalar8x32(0x594438a5, 0x32ef5760, 0x54f8d067, 0x8fd3930a, 0xe5e3dc99, 0x1532537e, 0xd7830956, 0x01b412ed)
1626+
};
1627+
yield return new object[]
1628+
{
1629+
new Scalar8x32(0x8815f63a, 0xb5a6bf84, 0xd58bb1b4, 0x6620e009, 0xebe35f48, 0xa328dbbc, 0xb5bfe374, 0x1f24278a),
1630+
new Scalar8x32(0xa608c914, 0x083a3843, 0x8783bee4, 0x9e58ad8a, 0x903a60c7, 0xfde513da, 0xca5ba2d3, 0xfe928416)
1631+
};
1632+
yield return new object[]
1633+
{
1634+
new Scalar8x32(0xf2146504, 0xf34d1bd4, 0x968ce3f5, 0x5201dfb8, 0x26093111, 0x67dba8bc, 0x274f6330, 0xdc107d58),
1635+
new Scalar8x32(0xaa78fcfb, 0xdd08edc3, 0x7a3d2cdb, 0x6d9c9d10, 0xedd09e71, 0x7023b1e5, 0x13c3d93e, 0x660cfa90)
1636+
};
1637+
yield return new object[]
1638+
{
1639+
new Scalar8x32(0x894fd4f7, 0x631c7442, 0x1119d5a4, 0x7da05389, 0x5da61cff, 0x2f551cc7, 0xc6f02776, 0x7cd1e905),
1640+
new Scalar8x32(0x181bdc46, 0xd94ece5e, 0xa89a1109, 0xaa23282a, 0x3004ccae, 0x1628803b, 0x9d3b1a37, 0xff20862a)
1641+
};
1642+
yield return new object[]
1643+
{
1644+
new Scalar8x32(0x9e453640, 0xa062f254, 0xf482c96b, 0x3dea04af, 0x25b8ae97, 0xcd12d818, 0x23d26c58, 0x5b9dade8),
1645+
new Scalar8x32(0x8cb5db46, 0x5f2baab4, 0x18fa0816, 0x262c22c7, 0x5c9b120a, 0xbe1e5392, 0x15fa53f4, 0x50c38800)
1646+
};
1647+
yield return new object[]
1648+
{
1649+
new Scalar8x32(0x0c19ff03, 0x65cdbe7a, 0x882bea84, 0x656fd098, 0x5b01d22e, 0xef1f4ab0, 0x969c464b, 0x11cdaeda),
1650+
new Scalar8x32(0x295ccdfb, 0x7e38e1fb, 0x4b1ae0ad, 0xec6b359a, 0xb3820bed, 0xb55f1f72, 0xac46f103, 0x1968d0fa)
1651+
};
1652+
yield return new object[]
1653+
{
1654+
new Scalar8x32(0x157266b4, 0x134d1c03, 0x10914454, 0x0cb97b7f, 0x06561f66, 0x194f8a1e, 0x26e91589, 0x2c351aa1),
1655+
new Scalar8x32(0x22af80f5, 0xcc4b847d, 0x9d1cd6f3, 0x14883434, 0xa478ba95, 0x41b176c4, 0x92bd8711, 0xbe49ada6)
1656+
};
1657+
yield return new object[]
1658+
{
1659+
new Scalar8x32(0x623ba730, 0xf65ec2ae, 0x80426395, 0x76fe3ec5, 0x84b5fa56, 0x6247f5c3, 0x13a60edb, 0x6ba07c6e),
1660+
new Scalar8x32(0x1c515595, 0x345f4f52, 0x4c8a8655, 0x24c8ebfe, 0x4a11c7bf, 0x98376f9d, 0x418cd747, 0x25ac23f7)
1661+
};
1662+
yield return new object[]
1663+
{
1664+
new Scalar8x32(0xd2ab2948, 0xaed4d24b, 0xd75c9b31, 0x0661dca8, 0x703b1c2a, 0x2d4a3d54, 0x8abb6951, 0x9397a712),
1665+
new Scalar8x32(0xffd0dcf3, 0x6a87489e, 0x18c51cc2, 0x36b9cd57, 0xeb9fb606, 0x1c897739, 0xd55ce3eb, 0xc52e8bef)
1666+
};
1667+
yield return new object[]
1668+
{
1669+
new Scalar8x32(0xc6738b6f, 0x889f3212, 0x207c1653, 0x7e3b380a, 0x4e224e44, 0xe97798df, 0xeb437888, 0xe6a808cc),
1670+
new Scalar8x32(0xda70aa55, 0xb910f26b, 0x19bb6122, 0x8ae35f73, 0x5243a0eb, 0x757a2e5e, 0xd1e08b20, 0x31f9ae13)
1671+
};
1672+
yield return new object[]
1673+
{
1674+
new Scalar8x32(0x80000000, 0x2ee588a6, 0x9d5281f8, 0xb8c1e010, 0x61a347a6, 0xa70779e0, 0xab0effe7, 0xd0320548),
1675+
new Scalar8x32(0x4425fcaf, 0x5dcd9e45, 0x7a53b471, 0xbce8bc6d, 0x728b6100, 0x7583dd9e, 0x78195c90, 0x1541897e)
1676+
};
1677+
yield return new object[]
1678+
{
1679+
new Scalar8x32(0xba503de9, 0x250494e7, 0xef6304cf, 0xd30edc20, 0x9eac9407, 0x796e9186, 0xd45b50b0, 0x93d623f1),
1680+
new Scalar8x32(0x80b9b957, 0x612fc387, 0x31d82b01, 0xcd3ea236, 0x3c7fb47c, 0x92043952, 0x1178b548, 0x7026d638)
1681+
};
1682+
yield return new object[]
1683+
{
1684+
new Scalar8x32(0xdd28d6a3, 0x8fba84cc, 0x13ca10be, 0xc248ffd3, 0x3b48bd90, 0xa4d73bcc, 0x55f5d412, 0xf860ab39),
1685+
new Scalar8x32(0x7649ab59, 0x52155cd0, 0x7a4433d9, 0xeacdb229, 0xfe62be4d, 0x76694700, 0xe0b15d67, 0x5c32fc70)
1686+
};
1687+
yield return new object[]
1688+
{
1689+
new Scalar8x32(0x6ac1f189, 0x5c1f5e63, 0xac532ea8, 0x6981ab73, 0xe175c9ba, 0x7a690a8e, 0x0800af58, 0x4e41311c),
1690+
new Scalar8x32(0x842324dc, 0xb280b28f, 0x94ec5f45, 0x4533b91f, 0xc05038a2, 0x7fbd3825, 0xd075982c, 0xfffffff9)
1691+
};
1692+
yield return new object[]
1693+
{
1694+
new Scalar8x32(0xd0364141, 0xb7a43a69, 0x2dc796d8, 0x0a99397c, 0x2424c4e4, 0xad5d7089, 0x3555eade, 0x48e473bf),
1695+
new Scalar8x32(0xe23a4daa, 0x7180c704, 0x9bb762f1, 0x6f602644, 0x266d6fd0, 0x1ec38593, 0xa0e47895, 0x634976b2)
1696+
};
1697+
yield return new object[]
1698+
{
1699+
new Scalar8x32(0xe4d6bd13, 0x34ead589, 0x4032a720, 0x7cbb8810, 0x556ccedc, 0x26e71c62, 0x3292fc54, 0xbe83878d),
1700+
new Scalar8x32(0x8480abb3, 0xe1e5d712, 0xb71b9d18, 0x1e6562e8, 0x6377534a, 0x74cbae3d, 0x25e59d0f, 0x6cd150ad)
1701+
};
1702+
yield return new object[]
1703+
{
1704+
new Scalar8x32(0xeb1782d0, 0xd1cf3451, 0x68daa5fb, 0x28561581, 0x8a846ca9, 0xc9ee06de, 0xefc15f88, 0xcdddf2e5),
1705+
new Scalar8x32(0x61b55c70, 0xbd86bd87, 0xe6d5a46e, 0x3ca3a3dc, 0x23e9681a, 0x993c865a, 0xed8d2af4, 0xffffffd9)
1706+
};
1707+
yield return new object[]
1708+
{
1709+
new Scalar8x32(0xca8b7785, 0xc2ef8082, 0x1f7118fb, 0x6c0359ab, 0x319ca19c, 0x08165ec4, 0x04872df9, 0xb6a18f1f),
1710+
new Scalar8x32(0xffbf272b, 0x274dc153, 0x3330b72f, 0x5f455e4e, 0x2358d5ad, 0x0f0c88c2, 0x0f1ac78c, 0xff55b19b)
1711+
};
1712+
yield return new object[]
1713+
{
1714+
new Scalar8x32(0x1decafa0, 0xc5d08f4c, 0x75636225, 0x01e26fb6, 0x06ec6844, 0xcf0e5c3d, 0x30eba3e8, 0xea4898e5),
1715+
new Scalar8x32(0xb067d90e, 0x6407ccc2, 0x12c08d02, 0x00630806, 0xcfc7b386, 0xea4f9b32, 0xe3c4ec1e, 0xe5a014a8)
1716+
};
1717+
yield return new object[]
1718+
{
1719+
new Scalar8x32(0x2ca58265, 0x6bebc918, 0x5ffcf47d, 0xff951863, 0x23e4b772, 0x8a23bfab, 0x7e933af0, 0x70e9aea9),
1720+
new Scalar8x32(0x989d8f77, 0x8b6bdbe0, 0xba4e9afb, 0x80ad7c48, 0xc194a859, 0x4eb6ec02, 0x81bc6441, 0xf4e00006)
1721+
};
1722+
yield return new object[]
1723+
{
1724+
new Scalar8x32(0xeefc1bf5, 0x06dc8bcc, 0x83b1e215, 0xd247df61, 0xf9b5a846, 0xe93618b8, 0x46efe6f0, 0x3c56c774),
1725+
new Scalar8x32(0xafb93f00, 0x8ad0de5b, 0x95b6533d, 0x9fb91ed3, 0xd1cefa7a, 0x43c25e57, 0x2cd9586b, 0xfff8937a)
1726+
};
1727+
yield return new object[]
1728+
{
1729+
new Scalar8x32(0xb50711a8, 0x51279ac5, 0xa68756a1, 0x392aa92e, 0xe4e3ebf3, 0xe38264df, 0x5cb30e83, 0xfb5c2772),
1730+
new Scalar8x32(0x2e0fd0ea, 0x39251130, 0x072fb8bc, 0x3b266b02, 0x3d638f99, 0xa6bbd7fb, 0x1105bfe7, 0x000013af)
1731+
};
1732+
}
1733+
[Theory]
1734+
[MemberData(nameof(GetInvCases))]
1735+
public void Libsecp256k1_InverseTest(in Scalar8x32 a, in Scalar8x32 b)
1736+
{
1737+
// Test fixed test cases through test_inverse_{scalar,field}, both ways.
1738+
for (int useVar = 0; useVar <= 1; useVar++)
1739+
{
1740+
TestInverseScalar(a, useVar, out Scalar8x32 x_scalar);
1741+
Assert.True(b.Equals(x_scalar));
1742+
TestInverseScalar(b, useVar, out x_scalar);
1743+
Assert.True(a.Equals(x_scalar));
1744+
}
1745+
}
1746+
1747+
[Fact]
1748+
public void Libsecp256k1_InverseRandomTest()
1749+
{
1750+
Scalar8x32 x_scalar;
1751+
// Test inputs 0..999 and their respective negations.
1752+
byte[] b32 = new byte[32];
1753+
for (int i = 0; i < 1000; i++)
1754+
{
1755+
b32[31] = (byte)i;
1756+
b32[30] = (byte)(i >> 8);
1757+
x_scalar = new(b32, out _);
1758+
for (int var = 0; var <= 1; ++var)
1759+
{
1760+
TestInverseScalar(x_scalar, var, out _);
1761+
}
1762+
x_scalar = x_scalar.Negate();
1763+
for (int var = 0; var <= 1; ++var)
1764+
{
1765+
TestInverseScalar(x_scalar, var, out _);
1766+
}
1767+
}
1768+
1769+
TestRNG rng = new();
1770+
rng.Init(null);
1771+
// test 128*count random inputs; half with testrand256_test, half with testrand256 */
1772+
for (int testrand = 0; testrand <= 1; ++testrand)
1773+
{
1774+
for (int i = 0; i < 64 * Count; ++i)
1775+
{
1776+
if (testrand == 0)
1777+
{
1778+
rng.Rand256(b32);
1779+
}
1780+
else
1781+
{
1782+
rng.Rand256Test(b32);
1783+
}
1784+
1785+
x_scalar = new(b32, out _);
1786+
for (int var = 0; var <= 1; ++var)
1787+
{
1788+
TestInverseScalar(x_scalar, var, out _);
1789+
}
1790+
}
1791+
}
1792+
}
1793+
15011794
#endregion
15021795
}
15031796
}

0 commit comments

Comments
 (0)