diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs
index 350e39120..c3e2b5b7e 100644
--- a/crypto/src/math/ec/rfc8032/Ed25519.cs
+++ b/crypto/src/math/ec/rfc8032/Ed25519.cs
@@ -64,6 +64,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
private const int L3 = -0x006215D1; // L3:23/--
private const int L4 = 0x000014DF; // L4:12/11
+ private static readonly uint[] Order8_y1 = { 0x706A17C7, 0x4FD84D3D, 0x760B3CBA, 0x0F67100D, 0xFA53202A,
+ 0xC6CC392C, 0x77FDC74E, 0x7A03AC92 };
+ private static readonly uint[] Order8_y2 = { 0x8F95E826, 0xB027B2C2, 0x89F4C345, 0xF098EFF2, 0x05ACDFD5,
+ 0x3933C6D3, 0x880238B1, 0x05FC536D };
+
private static readonly int[] B_x = { 0x0325D51A, 0x018B5823, 0x007B2C95, 0x0304A92D, 0x00D2598E, 0x01D6DC5C,
0x01388C7F, 0x013FEC0A, 0x029E6B72, 0x0042D26D };
private static readonly int[] B_y = { 0x02666658, 0x01999999, 0x00666666, 0x03333333, 0x00CCCCCC, 0x02666666,
@@ -209,12 +214,6 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
}
return false;
}
-
- private static bool CheckScalarVar(ReadOnlySpan<byte> s, Span<uint> n)
- {
- DecodeScalar(s, n);
- return !Nat.Gte(ScalarUints, n, L);
- }
#else
private static bool CheckPointVar(byte[] p)
{
@@ -227,7 +226,51 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
}
return false;
}
+#endif
+ private static bool CheckPointFullVar(byte[] p)
+ {
+ uint y7 = Codec.Decode32(p, 28) & 0x7FFFFFFFU;
+
+ uint t0 = y7;
+ uint t1 = y7 ^ P[7];
+ uint t2 = y7 ^ Order8_y1[7];
+ uint t3 = y7 ^ Order8_y2[7];
+
+ for (int i = CoordUints - 2; i > 0; --i)
+ {
+ uint yi = Codec.Decode32(p, i * 4);
+
+ t0 |= yi;
+ t1 |= yi ^ P[i];
+ t2 |= yi ^ Order8_y1[i];
+ t3 |= yi ^ Order8_y2[i];
+ }
+
+ uint y0 = Codec.Decode32(p, 0);
+
+ // Reject 0 and 1
+ if (t0 == 0 && y0 <= 1U)
+ return false;
+
+ // Reject P - 1 and non-canonical encodings (i.e. >= P)
+ if (t1 == 0 && y0 >= (P[0] - 1U))
+ return false;
+
+ t2 |= y0 ^ Order8_y1[0];
+ t3 |= y0 ^ Order8_y2[0];
+
+ // Reject order 8 points
+ return (t2 != 0) & (t3 != 0);
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private static bool CheckScalarVar(ReadOnlySpan<byte> s, Span<uint> n)
+ {
+ DecodeScalar(s, n);
+ return !Nat.Gte(ScalarUints, n, L);
+ }
+#else
private static bool CheckScalarVar(byte[] s, uint[] n)
{
DecodeScalar(s, 0, n);
@@ -258,7 +301,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
private static bool DecodePointVar(byte[] p, int pOff, bool negate, ref PointAffine r)
{
byte[] py = Copy(p, pOff, PointBytes);
- if (!CheckPointVar(py))
+ if (!CheckPointFullVar(py))
return false;
int x_0 = (py[PointBytes - 1] & 0x80) >> 7;
@@ -1910,12 +1953,6 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
if (!DecodePointVar(pk, pkOff, false, ref p))
return false;
- F.Normalize(p.x);
- F.Normalize(p.y);
-
- if (IsNeutralElementVar(p.x, p.y))
- return false;
-
Init(out PointAccum r);
ScalarMultOrderVar(ref p, ref r);
diff --git a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs
index 04b9c3cce..d042aff9f 100644
--- a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs
+++ b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs
@@ -15,8 +15,6 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
{
private static readonly SecureRandom Random = new SecureRandom();
- private static readonly byte[] Neutral = Hex.DecodeStrict("0100000000000000000000000000000000000000000000000000000000000000");
-
[SetUp]
public void SetUp()
{
@@ -371,8 +369,6 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
[Test]
public void TestPublicKeyValidationFull()
{
- Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Neutral, 0));
-
byte[] sk = new byte[Ed25519.SecretKeySize];
byte[] pk = new byte[Ed25519.PublicKeySize];
@@ -383,9 +379,21 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
Assert.IsTrue(Ed25519.ValidatePublicKeyFull(pk, 0));
}
+ // Small order points (canonical encodings)
+ Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("0000000000000000000000000000000000000000000000000000000000000000"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("0000000000000000000000000000000000000000000000000000000000000080"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("0100000000000000000000000000000000000000000000000000000000000000"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("ECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("C7176A703D4DD84FBA3C0B760D10670F2A2053FA2C39CCC64EC7FD7792AC037A"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("C7176A703D4DD84FBA3C0B760D10670F2A2053FA2C39CCC64EC7FD7792AC03FA"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("26E8958FC2B227B045C3F489F2EF98F0D5DFAC05D3C63339B13802886D53FC05"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("26E8958FC2B227B045C3F489F2EF98F0D5DFAC05D3C63339B13802886D53FC85"), 0));
+
+ // Small order points (non-canonical encodings)
Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("0100000000000000000000000000000000000000000000000000000000000080"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("ECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 0));
- Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("ECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F"), 0));
+ // Non-canonical encodings
Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F"), 0));
Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 0));
Assert.IsFalse(Ed25519.ValidatePublicKeyFull(Hex.DecodeStrict("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F"), 0));
@@ -417,8 +425,6 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
[Test]
public void TestPublicKeyValidationPartial()
{
- Assert.IsTrue(Ed25519.ValidatePublicKeyPartial(Neutral, 0));
-
byte[] sk = new byte[Ed25519.SecretKeySize];
byte[] pk = new byte[Ed25519.PublicKeySize];
@@ -429,9 +435,21 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
Assert.IsTrue(Ed25519.ValidatePublicKeyPartial(pk, 0));
}
+ // Small order points (canonical encodings)
+ Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("0000000000000000000000000000000000000000000000000000000000000000"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("0000000000000000000000000000000000000000000000000000000000000080"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("0100000000000000000000000000000000000000000000000000000000000000"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("ECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("C7176A703D4DD84FBA3C0B760D10670F2A2053FA2C39CCC64EC7FD7792AC037A"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("C7176A703D4DD84FBA3C0B760D10670F2A2053FA2C39CCC64EC7FD7792AC03FA"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("26E8958FC2B227B045C3F489F2EF98F0D5DFAC05D3C63339B13802886D53FC05"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("26E8958FC2B227B045C3F489F2EF98F0D5DFAC05D3C63339B13802886D53FC85"), 0));
+
+ // Small order points (non-canonical encodings)
Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("0100000000000000000000000000000000000000000000000000000000000080"), 0));
+ Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("ECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 0));
- Assert.IsTrue (Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("ECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F"), 0));
+ // Non-canonical encodings
Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F"), 0));
Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 0));
Assert.IsFalse(Ed25519.ValidatePublicKeyPartial(Hex.DecodeStrict("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F"), 0));
@@ -469,23 +487,20 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
[Test]
public void TamingNonRepudiation()
{
- // TODO Algorithm 2 rejects this because A is one of 8 small order points
-
byte[] msg1 = Encoding.UTF8.GetBytes("Send 100 USD to Alice");
byte[] msg2 = Encoding.UTF8.GetBytes("Send 100000 USD to Alice");
byte[] pub = Hex.DecodeStrict("ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f");
byte[] sig = Hex.DecodeStrict("a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dc" +
"a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04");
- Assert.IsTrue(Ed25519.Verify(sig, 0, pub, 0, msg1, 0, msg1.Length));
- Assert.IsTrue(Ed25519.Verify(sig, 0, pub, 0, msg2, 0, msg2.Length));
+ Assert.IsFalse(Ed25519.Verify(sig, 0, pub, 0, msg1, 0, msg1.Length));
+ Assert.IsFalse(Ed25519.Verify(sig, 0, pub, 0, msg2, 0, msg2.Length));
}
[Test]
public void TamingVector_00()
{
- // TODO Algorithm 2 rejects this because A is one of 8 small order points
- ImplTamingVector(0, true,
+ ImplTamingVector(0, false,
"8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a" +
@@ -495,8 +510,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
[Test]
public void TamingVector_01()
{
- // TODO Algorithm 2 rejects this because A is one of 8 small order points
- ImplTamingVector(1, true,
+ ImplTamingVector(1, false,
"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43" +
|