diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-11-26 22:12:14 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-11-26 22:12:14 +0700 |
commit | 11529c83fbdd81f9a25658ec8d3c526618576be6 (patch) | |
tree | 517803479099edff45dedc6799fe184b0f9952c1 /crypto/src/math/ec/rfc8032 | |
parent | Refactoring in Math.EC.Rfc8032 (diff) | |
download | BouncyCastle.NET-ed25519-11529c83fbdd81f9a25658ec8d3c526618576be6.tar.xz |
EdDSA: Hold decoded pubilc point in public keys
Diffstat (limited to 'crypto/src/math/ec/rfc8032')
-rw-r--r-- | crypto/src/math/ec/rfc8032/Ed25519.cs | 444 | ||||
-rw-r--r-- | crypto/src/math/ec/rfc8032/Ed448.cs | 499 |
2 files changed, 827 insertions, 116 deletions
diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs index c1d8fa624..2479d94d4 100644 --- a/crypto/src/math/ec/rfc8032/Ed25519.cs +++ b/crypto/src/math/ec/rfc8032/Ed25519.cs @@ -34,6 +34,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Ed25519ph = 2, } + public sealed class PublicPoint + { + internal readonly int[] m_data; + + internal PublicPoint(int[] data) + { + m_data = data; + } + } + private const int CoordUints = 8; private const int PointBytes = CoordUints * 4; private const int ScalarUints = 8; @@ -151,14 +161,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 || ctx != null && ctx.Length < 256; } - private static int CheckPoint(int[] x, int[] y) + private static int CheckPoint(ref PointAffine p) { int[] t = F.Create(); int[] u = F.Create(); int[] v = F.Create(); - F.Sqr(x, u); - F.Sqr(y, v); + F.Sqr(p.x, u); + F.Sqr(p.y, v); F.Mul(u, v, t); F.Sub(v, u, v); F.Mul(t, C_d, t); @@ -191,6 +201,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return F.IsZero(t); } + private static bool CheckPointOrderVar(ref PointAffine p) + { + Init(out PointAccum r); + ScalarMultOrderVar(ref p, ref r); + return NormalizeToNeutralElementVar(ref r); + } + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private static bool CheckPointVar(ReadOnlySpan<byte> p) { @@ -339,6 +356,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (negate ^ (x_0 != (r.x[0] & 1))) { F.Negate(r.x, r.x); + F.Normalize(r.x); } return true; @@ -369,50 +387,73 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 #endif } - private static int EncodePoint(ref PointAccum p, byte[] r, int rOff) + private static void EncodePoint(ref PointAffine p, byte[] r, int rOff) { + F.Encode(p.y, r, rOff); + r[rOff + PointBytes - 1] |= (byte)((p.x[0] & 1) << 7); + } + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - return EncodePoint(ref p, r.AsSpan(rOff)); -#else - int[] x = F.Create(); - int[] y = F.Create(); + private static void EncodePoint(ref PointAffine p, Span<byte> r) + { + F.Encode(p.y, r); + r[PointBytes - 1] |= (byte)((p.x[0] & 1) << 7); + } +#endif + + public static void EncodePublicPoint(PublicPoint publicPoint, byte[] pk, int pkOff) + { + F.Encode(publicPoint.m_data, F.Size, pk, pkOff); + pk[pkOff + PointBytes - 1] |= (byte)((publicPoint.m_data[0] & 1) << 7); + } - F.Inv(p.z, y); - F.Mul(p.x, y, x); - F.Mul(p.y, y, y); - F.Normalize(x); - F.Normalize(y); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void EncodePublicPoint(PublicPoint publicPoint, Span<byte> pk) + { + F.Encode(publicPoint.m_data.AsSpan(F.Size), pk); + pk[PointBytes - 1] |= (byte)((publicPoint.m_data[0] & 1) << 7); + } +#endif + + private static int EncodeResult(ref PointAccum p, byte[] r, int rOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return EncodeResult(ref p, r.AsSpan(rOff)); +#else + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); - int result = CheckPoint(x, y); + int result = CheckPoint(ref q); - F.Encode(y, r, rOff); - r[rOff + PointBytes - 1] |= (byte)((x[0] & 1) << 7); + EncodePoint(ref q, r, rOff); return result; #endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - private static int EncodePoint(ref PointAccum p, Span<byte> r) + private static int EncodeResult(ref PointAccum p, Span<byte> r) { - int[] x = F.Create(); - int[] y = F.Create(); - - F.Inv(p.z, y); - F.Mul(p.x, y, x); - F.Mul(p.y, y, y); - F.Normalize(x); - F.Normalize(y); + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); - int result = CheckPoint(x, y); + int result = CheckPoint(ref q); - F.Encode(y, r); - r[PointBytes - 1] |= (byte)((x[0] & 1) << 7); + EncodePoint(ref q, r); return result; } #endif + private static void ExportPoint(ref PointAffine p, out PublicPoint publicPoint) + { + int[] data = new int[F.Size * 2]; + F.Copy(p.x, 0, data, 0); + F.Copy(p.y, 0, data, F.Size); + + publicPoint = new PublicPoint(data); + } + public static void GeneratePrivateKey(SecureRandom random, byte[] k) { if (k.Length != SecretKeySize) @@ -465,6 +506,58 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #endif + public static void GeneratePublicKey(byte[] sk, int skOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + GeneratePublicKey(sk.AsSpan(skOff), out publicPoint); +#else + IDigest d = CreateDigest(); + byte[] h = new byte[64]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + Init(out PointAccum p); + ScalarMultBase(s, ref p); + + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + if (0 == CheckPoint(ref q)) + throw new InvalidOperationException(); + + ExportPoint(ref q, out publicPoint); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePublicKey(ReadOnlySpan<byte> sk, out PublicPoint publicPoint) + { + IDigest d = CreateDigest(); + Span<byte> h = stackalloc byte[64]; + + d.BlockUpdate(sk[..SecretKeySize]); + d.DoFinal(h); + + Span<byte> s = stackalloc byte[ScalarBytes]; + PruneScalar(h, s); + + Init(out PointAccum p); + ScalarMultBase(s, ref p); + + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + if (0 == CheckPoint(ref q)) + throw new InvalidOperationException(); + + ExportPoint(ref q, out publicPoint); + } +#endif + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private static uint GetWindow4(ReadOnlySpan<uint> x, int n) #else @@ -663,12 +756,108 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Init(out PointAccum pZ); ScalarMultStraus128Var(nS, v0, ref pA, v1, ref pR, ref pZ); + return NormalizeToNeutralElementVar(ref pZ); + } + + private static bool ImplVerify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen) + { + if (!CheckContextVar(ctx, phflag)) + throw new ArgumentException("ctx"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span<byte> signature = stackalloc byte[SignatureSize]; + signature.CopyFrom(sig.AsSpan(sigOff, SignatureSize)); + var R = signature[..PointBytes]; + var S = signature[PointBytes..]; + + if (!CheckPointVar(R)) + return false; + + Span<uint> nS = stackalloc uint[ScalarUints]; + if (!Scalar25519.CheckVar(S, nS)) + return false; + + Init(out PointAffine pR); + if (!DecodePointVar(R, true, ref pR)) + return false; + + Init(out PointAffine pA); + F.Negate(publicPoint.m_data, pA.x); + F.Copy(publicPoint.m_data.AsSpan(F.Size), pA.y); + + Span<byte> A = stackalloc byte[PublicKeySize]; + EncodePublicPoint(publicPoint, A); - F.Normalize(pZ.x); - F.Normalize(pZ.y); - F.Normalize(pZ.z); + IDigest d = CreateDigest(); + Span<byte> h = stackalloc byte[64]; - return IsNeutralElementVar(pZ.x, pZ.y, pZ.z); + if (ctx != null) + { + Dom2(d, phflag, ctx); + } + d.BlockUpdate(R); + d.BlockUpdate(A); + d.BlockUpdate(m.AsSpan(mOff, mLen)); + d.DoFinal(h); + + Span<byte> k = stackalloc byte[ScalarBytes]; + Scalar25519.Reduce(h, k); + + Span<uint> nA = stackalloc uint[ScalarUints]; + Scalar25519.Decode(k, nA); + + Span<uint> v0 = stackalloc uint[4]; + Span<uint> v1 = stackalloc uint[4]; +#else + byte[] R = Copy(sig, sigOff, PointBytes); + byte[] S = Copy(sig, sigOff + PointBytes, ScalarBytes); + + if (!CheckPointVar(R)) + return false; + + uint[] nS = new uint[ScalarUints]; + if (!Scalar25519.CheckVar(S, nS)) + return false; + + Init(out PointAffine pR); + if (!DecodePointVar(R, true, ref pR)) + return false; + + Init(out PointAffine pA); + F.Negate(publicPoint.m_data, pA.x); + F.Copy(publicPoint.m_data, F.Size, pA.y, 0); + + byte[] A = new byte[PublicKeySize]; + EncodePublicPoint(publicPoint, A, 0); + + IDigest d = CreateDigest(); + byte[] h = new byte[64]; + + if (ctx != null) + { + Dom2(d, phflag, ctx); + } + d.BlockUpdate(R, 0, PointBytes); + d.BlockUpdate(A, 0, PointBytes); + d.BlockUpdate(m, mOff, mLen); + d.DoFinal(h, 0); + + byte[] k = Scalar25519.Reduce(h); + + uint[] nA = new uint[ScalarUints]; + Scalar25519.Decode(k, nA); + + uint[] v0 = new uint[4]; + uint[] v1 = new uint[4]; +#endif + + Scalar25519.ReduceBasisVar(nA, v0, v1); + Scalar25519.Multiply128Var(nS, v1, nS); + + Init(out PointAccum pZ); + ScalarMultStraus128Var(nS, v0, ref pA, v1, ref pR, ref pZ); + return NormalizeToNeutralElementVar(ref pZ); } private static void Init(out PointAccum r) @@ -759,6 +948,24 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return F.IsZeroVar(x) && F.AreEqualVar(y, z); } + private static void NormalizeToAffine(ref PointAccum p, ref PointAffine r) + { + F.Inv(p.z, r.y); + F.Mul(r.y, p.x, r.x); + F.Mul(r.y, p.y, r.y); + F.Normalize(r.x); + F.Normalize(r.y); + } + + private static bool NormalizeToNeutralElementVar(ref PointAccum p) + { + F.Normalize(p.x); + F.Normalize(p.y); + F.Normalize(p.z); + + return IsNeutralElementVar(p.x, p.y, p.z); + } + private static void PointAdd(ref PointExtended p, ref PointExtended q, ref PointExtended r, ref PointTemp t) { // p may ref the same point as r (or q), but q may not ref the same point as r. @@ -1143,6 +1350,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { Init(out toothPowers[tooth]); } + Init(out PointExtended u); for (int block = 0; block < PrecompBlocks; ++block) { @@ -1393,7 +1601,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 #else Init(out PointAccum p); ScalarMultBase(k, ref p); - if (0 == EncodePoint(ref p, r, rOff)) + if (0 == EncodeResult(ref p, r, rOff)) throw new InvalidOperationException(); #endif } @@ -1403,7 +1611,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { Init(out PointAccum p); ScalarMultBase(k, ref p); - if (0 == EncodePoint(ref p, r)) + if (0 == EncodeResult(ref p, r)) throw new InvalidOperationException(); } #endif @@ -1624,11 +1832,26 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static bool ValidatePublicKeyFull(byte[] pk, int pkOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - Span<byte> A = stackalloc byte[PublicKeySize]; - A.CopyFrom(pk.AsSpan(pkOff)); + return ValidatePublicKeyFull(pk.AsSpan(pkOff)); #else byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (!CheckPointFullVar(A)) + return false; + + Init(out PointAffine pA); + if (!DecodePointVar(A, false, ref pA)) + return false; + + return CheckPointOrderVar(ref pA); #endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyFull(ReadOnlySpan<byte> pk) + { + Span<byte> A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); if (!CheckPointFullVar(A)) return false; @@ -1637,24 +1860,79 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (!DecodePointVar(A, false, ref pA)) return false; - Init(out PointAccum pR); - ScalarMultOrderVar(ref pA, ref pR); + return CheckPointOrderVar(ref pA); + } +#endif - F.Normalize(pR.x); - F.Normalize(pR.y); - F.Normalize(pR.z); + public static bool ValidatePublicKeyFull(byte[] pk, int pkOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ValidatePublicKeyFull(pk.AsSpan(pkOff), out publicPoint); +#else + byte[] A = Copy(pk, pkOff, PublicKeySize); - return IsNeutralElementVar(pR.x, pR.y, pR.z); + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + if (CheckPointOrderVar(ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + } + + publicPoint = null; + return false; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyFull(ReadOnlySpan<byte> pk, out PublicPoint publicPoint) + { + Span<byte> A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + if (CheckPointOrderVar(ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + } + + publicPoint = null; + return false; + } +#endif + public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - Span<byte> A = stackalloc byte[PublicKeySize]; - A.CopyFrom(pk.AsSpan(pkOff)); + return ValidatePublicKeyPartial(pk.AsSpan(pkOff)); #else byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (!CheckPointFullVar(A)) + return false; + + Init(out PointAffine pA); + return DecodePointVar(A, false, ref pA); #endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyPartial(ReadOnlySpan<byte> pk) + { + Span<byte> A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); if (!CheckPointFullVar(A)) return false; @@ -1662,6 +1940,50 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Init(out PointAffine pA); return DecodePointVar(A, false, ref pA); } +#endif + + public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ValidatePublicKeyPartial(pk.AsSpan(pkOff), out publicPoint); +#else + byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + + publicPoint = null; + return false; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyPartial(ReadOnlySpan<byte> pk, out PublicPoint publicPoint) + { + Span<byte> A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + + publicPoint = null; + return false; + } +#endif public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen) { @@ -1671,6 +1993,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); } + public static bool Verify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] m, int mOff, int mLen) + { + byte[] ctx = null; + byte phflag = 0x00; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, mOff, mLen); + } + public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) { byte phflag = 0x00; @@ -1678,6 +2008,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); } + public static bool Verify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] m, int mOff, int mLen) + { + byte phflag = 0x00; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, mOff, mLen); + } + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff) { byte phflag = 0x01; @@ -1685,6 +2022,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize); } + public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] ph, + int phOff) + { + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, ph, phOff, PrehashSize); + } + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IDigest ph) { byte[] m = new byte[PrehashSize]; @@ -1695,5 +2040,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length); } + + public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, IDigest ph) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0)) + throw new ArgumentException("ph"); + + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, 0, m.Length); + } } } diff --git a/crypto/src/math/ec/rfc8032/Ed448.cs b/crypto/src/math/ec/rfc8032/Ed448.cs index d2c29a41c..8ffbccfa4 100644 --- a/crypto/src/math/ec/rfc8032/Ed448.cs +++ b/crypto/src/math/ec/rfc8032/Ed448.cs @@ -31,6 +31,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Ed448ph = 1, } + public sealed class PublicPoint + { + internal readonly uint[] m_data; + + internal PublicPoint(uint[] data) + { + m_data = data; + } + } + private const int CoordUints = 14; private const int PointBytes = CoordUints * 4 + 1; private const int ScalarUints = 14; @@ -113,14 +123,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ctx != null && ctx.Length < 256; } - private static int CheckPoint(uint[] x, uint[] y) + private static int CheckPoint(ref PointAffine p) { uint[] t = F.Create(); uint[] u = F.Create(); uint[] v = F.Create(); - F.Sqr(x, u); - F.Sqr(y, v); + F.Sqr(p.x, u); + F.Sqr(p.y, v); F.Mul(u, v, t); F.Add(u, v, u); F.Mul(t, -C_d, t); @@ -153,6 +163,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return F.IsZero(t); } + private static bool CheckPointOrderVar(ref PointAffine p) + { + Init(out PointProjective r); + ScalarMultOrderVar(ref p, ref r); + return NormalizeToNeutralElementVar(ref r); + } + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private static bool CheckPointVar(ReadOnlySpan<byte> p) { @@ -277,9 +294,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - private static bool DecodePointVar(ReadOnlySpan<byte> p, bool negate, ref PointProjective r) + private static bool DecodePointVar(ReadOnlySpan<byte> p, bool negate, ref PointAffine r) #else - private static bool DecodePointVar(byte[] p, bool negate, ref PointProjective r) + private static bool DecodePointVar(byte[] p, bool negate, ref PointAffine r) #endif { int x_0 = (p[PointBytes - 1] & 0x80) >> 7; @@ -305,9 +322,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (negate ^ (x_0 != (r.x[0] & 1))) { F.Negate(r.x, r.x); + F.Normalize(r.x); } - F.One(r.z); return true; } @@ -334,46 +351,73 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 #endif } - private static int EncodePoint(ref PointProjective p, byte[] r, int rOff) + private static void EncodePoint(ref PointAffine p, byte[] r, int rOff) { - uint[] x = F.Create(); - uint[] y = F.Create(); + F.Encode(p.y, r, rOff); + r[rOff + PointBytes - 1] = (byte)((p.x[0] & 1) << 7); + } - F.Inv(p.z, y); - F.Mul(p.x, y, x); - F.Mul(p.y, y, y); - F.Normalize(x); - F.Normalize(y); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void EncodePoint(ref PointAffine p, Span<byte> r) + { + F.Encode(p.y, r); + r[PointBytes - 1] = (byte)((p.x[0] & 1) << 7); + } +#endif - int result = CheckPoint(x, y); + public static void EncodePublicPoint(PublicPoint publicPoint, byte[] pk, int pkOff) + { + F.Encode(publicPoint.m_data, F.Size, pk, pkOff); + pk[pkOff + PointBytes - 1] = (byte)((publicPoint.m_data[0] & 1) << 7); + } - F.Encode(y, r, rOff); - r[rOff + PointBytes - 1] = (byte)((x[0] & 1) << 7); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void EncodePublicPoint(PublicPoint publicPoint, Span<byte> pk) + { + F.Encode(publicPoint.m_data.AsSpan(F.Size), pk); + pk[PointBytes - 1] = (byte)((publicPoint.m_data[0] & 1) << 7); + } +#endif + + private static int EncodeResult(ref PointProjective p, byte[] r, int rOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return EncodeResult(ref p, r.AsSpan(rOff)); +#else + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + int result = CheckPoint(ref q); + + EncodePoint(ref q, r, rOff); return result; +#endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - private static int EncodePoint(ref PointProjective p, Span<byte> r) + private static int EncodeResult(ref PointProjective p, Span<byte> r) { - uint[] x = F.Create(); - uint[] y = F.Create(); - - F.Inv(p.z, y); - F.Mul(p.x, y, x); - F.Mul(p.y, y, y); - F.Normalize(x); - F.Normalize(y); + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); - int result = CheckPoint(x, y); + int result = CheckPoint(ref q); - F.Encode(y, r); - r[PointBytes - 1] = (byte)((x[0] & 1) << 7); + EncodePoint(ref q, r); return result; } #endif + private static void ExportPoint(ref PointAffine p, out PublicPoint publicPoint) + { + uint[] data = new uint[F.Size * 2]; + F.Copy(p.x, 0, data, 0); + F.Copy(p.y, 0, data, F.Size); + + publicPoint = new PublicPoint(data); + } + public static void GeneratePrivateKey(SecureRandom random, byte[] k) { if (k.Length != SecretKeySize) @@ -426,6 +470,58 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #endif + public static void GeneratePublicKey(byte[] sk, int skOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + GeneratePublicKey(sk.AsSpan(skOff), out publicPoint); +#else + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.OutputFinal(h, 0, h.Length); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + Init(out PointProjective p); + ScalarMultBase(s, ref p); + + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + if (0 == CheckPoint(ref q)) + throw new InvalidOperationException(); + + ExportPoint(ref q, out publicPoint); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePublicKey(ReadOnlySpan<byte> sk, out PublicPoint publicPoint) + { + IXof d = CreateXof(); + Span<byte> h = stackalloc byte[ScalarBytes * 2]; + + d.BlockUpdate(sk[..SecretKeySize]); + d.OutputFinal(h); + + Span<byte> s = stackalloc byte[ScalarBytes]; + PruneScalar(h, s); + + Init(out PointProjective p); + ScalarMultBase(s, ref p); + + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + if (0 == CheckPoint(ref q)) + throw new InvalidOperationException(); + + ExportPoint(ref q, out publicPoint); + } +#endif + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private static uint GetWindow4(ReadOnlySpan<uint> x, int n) #else @@ -525,11 +621,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (!CheckPointFullVar(A)) return false; - Init(out PointProjective pR); + Init(out PointAffine pR); if (!DecodePointVar(R, true, ref pR)) return false; - Init(out PointProjective pA); + Init(out PointAffine pA); if (!DecodePointVar(A, true, ref pA)) return false; @@ -565,11 +661,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (!CheckPointFullVar(A)) return false; - Init(out PointProjective pR); + Init(out PointAffine pR); if (!DecodePointVar(R, true, ref pR)) return false; - Init(out PointProjective pA); + Init(out PointAffine pA); if (!DecodePointVar(A, true, ref pA)) return false; @@ -596,12 +692,102 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Init(out PointProjective pZ); ScalarMultStraus225Var(nS, v0, ref pA, v1, ref pR, ref pZ); + return NormalizeToNeutralElementVar(ref pZ); + } + + private static bool ImplVerify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen) + { + if (!CheckContextVar(ctx)) + throw new ArgumentException("ctx"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span<byte> signature = stackalloc byte[SignatureSize]; + signature.CopyFrom(sig.AsSpan(sigOff, SignatureSize)); + var R = signature[..PointBytes]; + var S = signature[PointBytes..]; + + if (!CheckPointVar(R)) + return false; + + Span<uint> nS = stackalloc uint[ScalarUints]; + if (!Scalar448.CheckVar(S, nS)) + return false; + + Init(out PointAffine pR); + if (!DecodePointVar(R, true, ref pR)) + return false; + + Init(out PointAffine pA); + F.Negate(publicPoint.m_data, pA.x); + F.Copy(publicPoint.m_data.AsSpan(F.Size), pA.y); + + Span<byte> A = stackalloc byte[PublicKeySize]; + EncodePublicPoint(publicPoint, A); + + IXof d = CreateXof(); + Span<byte> h = stackalloc byte[ScalarBytes * 2]; + + Dom4(d, phflag, ctx); + d.BlockUpdate(R); + d.BlockUpdate(A); + d.BlockUpdate(m.AsSpan(mOff, mLen)); + d.OutputFinal(h); + + Span<byte> k = stackalloc byte[ScalarBytes]; + Scalar448.Reduce(h, k); + + Span<uint> nA = stackalloc uint[ScalarUints]; + Scalar448.Decode(k, nA); + + Span<uint> v0 = stackalloc uint[8]; + Span<uint> v1 = stackalloc uint[8]; +#else + byte[] R = Copy(sig, sigOff, PointBytes); + byte[] S = Copy(sig, sigOff + PointBytes, ScalarBytes); + + if (!CheckPointVar(R)) + return false; + + uint[] nS = new uint[ScalarUints]; + if (!Scalar448.CheckVar(S, nS)) + return false; + + Init(out PointAffine pR); + if (!DecodePointVar(R, true, ref pR)) + return false; + + Init(out PointAffine pA); + F.Negate(publicPoint.m_data, pA.x); + F.Copy(publicPoint.m_data, F.Size, pA.y, 0); + + byte[] A = new byte[PublicKeySize]; + EncodePublicPoint(publicPoint, A, 0); + + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + Dom4(d, phflag, ctx); + d.BlockUpdate(R, 0, PointBytes); + d.BlockUpdate(A, 0, PointBytes); + d.BlockUpdate(m, mOff, mLen); + d.OutputFinal(h, 0, h.Length); + + byte[] k = Scalar448.Reduce(h); + + uint[] nA = new uint[ScalarUints]; + Scalar448.Decode(k, nA); + + uint[] v0 = new uint[8]; + uint[] v1 = new uint[8]; +#endif - F.Normalize(pZ.x); - F.Normalize(pZ.y); - F.Normalize(pZ.z); + Scalar448.ReduceBasisVar(nA, v0, v1); + Scalar448.Multiply225Var(nS, v1, nS); - return IsNeutralElementVar(pZ.x, pZ.y, pZ.z); + Init(out PointProjective pZ); + ScalarMultStraus225Var(nS, v0, ref pA, v1, ref pR, ref pZ); + return NormalizeToNeutralElementVar(ref pZ); } private static void Init(out PointAffine r) @@ -655,6 +841,24 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return F.IsZeroVar(x) && F.AreEqualVar(y, z); } + private static void NormalizeToAffine(ref PointProjective p, ref PointAffine r) + { + F.Inv(p.z, r.y); + F.Mul(r.y, p.x, r.x); + F.Mul(r.y, p.y, r.y); + F.Normalize(r.x); + F.Normalize(r.y); + } + + private static bool NormalizeToNeutralElementVar(ref PointProjective p) + { + F.Normalize(p.x); + F.Normalize(p.y); + F.Normalize(p.z); + + return IsNeutralElementVar(p.x, p.y, p.z); + } + private static void PointAdd(ref PointAffine p, ref PointProjective r) { uint[] b = F.Create(); @@ -813,6 +1017,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 F.Mul(f, g, r.z); } + private static void PointCopy(ref PointAffine p, ref PointProjective r) + { + F.Copy(p.x, 0, r.x, 0); + F.Copy(p.y, 0, r.y, 0); + F.One(r.z); + } + private static void PointCopy(ref PointProjective p, ref PointProjective r) { F.Copy(p.x, 0, r.x, 0); @@ -948,8 +1159,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return table; } - private static void PointPrecomputeVar(ref PointProjective p, PointProjective[] points, int pointsOff, - int pointsLen) + private static void PointPrecompute(ref PointAffine p, PointProjective[] points, int pointsOff, int pointsLen) { Debug.Assert(pointsLen > 0); @@ -959,6 +1169,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Init(out points[pointsOff]); PointCopy(ref p, ref points[pointsOff]); + for (int i = 1; i < pointsLen; ++i) { Init(out points[pointsOff + i]); @@ -990,19 +1201,20 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 PointProjective[] points = new PointProjective[totalPoints]; - Init(out PointProjective p); - F.Copy(B_x, 0, p.x, 0); - F.Copy(B_y, 0, p.y, 0); - F.One(p.z); + Init(out PointAffine B); + F.Copy(B_x, 0, B.x, 0); + F.Copy(B_y, 0, B.y, 0); - PointPrecomputeVar(ref p, points, 0, wnafPoints); + PointPrecompute(ref B, points, 0, wnafPoints); - Init(out PointProjective p225); - F.Copy(B225_x, 0, p225.x, 0); - F.Copy(B225_y, 0, p225.y, 0); - F.One(p225.z); + Init(out PointAffine B225); + F.Copy(B225_x, 0, B225.x, 0); + F.Copy(B225_y, 0, B225.y, 0); - PointPrecomputeVar(ref p225, points, wnafPoints, wnafPoints); + PointPrecompute(ref B225, points, wnafPoints, wnafPoints); + + Init(out PointProjective p); + PointCopy(ref B, ref p); int pointsIndex = wnafPoints * 2; PointProjective[] toothPowers = new PointProjective[PrecompTeeth]; @@ -1010,6 +1222,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { Init(out toothPowers[tooth]); } + for (int block = 0; block < PrecompBlocks; ++block) { ref PointProjective sum = ref points[pointsIndex++]; @@ -1225,7 +1438,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 #else Init(out PointProjective p); ScalarMultBase(k, ref p); - if (0 == EncodePoint(ref p, r, rOff)) + if (0 == EncodeResult(ref p, r, rOff)) throw new InvalidOperationException(); #endif } @@ -1235,7 +1448,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { Init(out PointProjective p); ScalarMultBase(k, ref p); - if (0 == EncodePoint(ref p, r)) + if (0 == EncodeResult(ref p, r)) throw new InvalidOperationException(); } #endif @@ -1276,7 +1489,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #endif - private static void ScalarMultOrderVar(ref PointProjective p, ref PointProjective r) + private static void ScalarMultOrderVar(ref PointAffine p, ref PointProjective r) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER Span<sbyte> ws_p = stackalloc sbyte[447]; @@ -1288,7 +1501,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 int count = 1 << (WnafWidth225 - 2); PointProjective[] tp = new PointProjective[count]; - PointPrecomputeVar(ref p, tp, 0, count); + PointPrecompute(ref p, tp, 0, count); PointSetNeutral(ref r); @@ -1309,11 +1522,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - private static void ScalarMultStraus225Var(ReadOnlySpan<uint> nb, ReadOnlySpan<uint> np, ref PointProjective p, - ReadOnlySpan<uint> nq, ref PointProjective q, ref PointProjective r) + private static void ScalarMultStraus225Var(ReadOnlySpan<uint> nb, ReadOnlySpan<uint> np, ref PointAffine p, + ReadOnlySpan<uint> nq, ref PointAffine q, ref PointProjective r) #else - private static void ScalarMultStraus225Var(uint[] nb, uint[] np, ref PointProjective p, uint[] nq, - ref PointProjective q, ref PointProjective r) + private static void ScalarMultStraus225Var(uint[] nb, uint[] np, ref PointAffine p, uint[] nq, + ref PointAffine q, ref PointProjective r) #endif { Debug.Assert(nb.Length == ScalarUints); @@ -1342,8 +1555,8 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 int count = 1 << (WnafWidth225 - 2); PointProjective[] tp = new PointProjective[count]; PointProjective[] tq = new PointProjective[count]; - PointPrecomputeVar(ref p, tp, 0, count); - PointPrecomputeVar(ref q, tq, 0, count); + PointPrecompute(ref p, tp, 0, count); + PointPrecompute(ref q, tq, 0, count); PointSetNeutral(ref r); @@ -1438,52 +1651,175 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static bool ValidatePublicKeyFull(byte[] pk, int pkOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - Span<byte> A = stackalloc byte[PublicKeySize]; - A.CopyFrom(pk.AsSpan(pkOff)); + return ValidatePublicKeyFull(pk.AsSpan(pkOff)); #else byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (!CheckPointFullVar(A)) + return false; + + Init(out PointAffine pA); + if (!DecodePointVar(A, false, ref pA)) + return false; + + return CheckPointOrderVar(ref pA); #endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyFull(ReadOnlySpan<byte> pk) + { + Span<byte> A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); if (!CheckPointFullVar(A)) return false; - Init(out PointProjective pA); + Init(out PointAffine pA); if (!DecodePointVar(A, false, ref pA)) return false; - Init(out PointProjective pR); - ScalarMultOrderVar(ref pA, ref pR); + return CheckPointOrderVar(ref pA); + } +#endif - F.Normalize(pR.x); - F.Normalize(pR.y); - F.Normalize(pR.z); + public static bool ValidatePublicKeyFull(byte[] pk, int pkOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ValidatePublicKeyFull(pk.AsSpan(pkOff), out publicPoint); +#else + byte[] A = Copy(pk, pkOff, PublicKeySize); - return IsNeutralElementVar(pR.x, pR.y, pR.z); + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + if (CheckPointOrderVar(ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + } + + publicPoint = null; + return false; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyFull(ReadOnlySpan<byte> pk, out PublicPoint publicPoint) + { + Span<byte> A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + if (CheckPointOrderVar(ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + } + + publicPoint = null; + return false; + } +#endif + public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - Span<byte> A = stackalloc byte[PublicKeySize]; - A.CopyFrom(pk.AsSpan(pkOff)); + return ValidatePublicKeyPartial(pk.AsSpan(pkOff)); #else byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (!CheckPointFullVar(A)) + return false; + + Init(out PointAffine pA); + return DecodePointVar(A, false, ref pA); #endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyPartial(ReadOnlySpan<byte> pk) + { + Span<byte> A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); if (!CheckPointFullVar(A)) return false; - Init(out PointProjective pA); + Init(out PointAffine pA); return DecodePointVar(A, false, ref pA); } +#endif - public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) + public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ValidatePublicKeyPartial(pk.AsSpan(pkOff), out publicPoint); +#else + byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + + publicPoint = null; + return false; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyPartial(ReadOnlySpan<byte> pk, out PublicPoint publicPoint) + { + Span<byte> A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + + publicPoint = null; + return false; + } +#endif + + public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, + int mLen) { byte phflag = 0x00; return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); } + public static bool Verify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] m, int mOff, + int mLen) + { + byte phflag = 0x00; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, mOff, mLen); + } + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff) { byte phflag = 0x01; @@ -1491,6 +1827,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize); } + public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] ph, + int phOff) + { + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, ph, phOff, PrehashSize); + } + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IXof ph) { byte[] m = new byte[PrehashSize]; @@ -1501,5 +1845,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length); } + + public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, IXof ph) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.OutputFinal(m, 0, PrehashSize)) + throw new ArgumentException("ph"); + + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, 0, m.Length); + } } } |