From ca42ede9a2580725453c3b97d3c4b1e84cecf3dd Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 14 Sep 2018 17:52:34 +0700 Subject: RFC 8032: Implement Ed25519ctx, Ed25519ph, Ed448ph variants --- crypto/src/math/ec/rfc8032/Ed25519.cs | 255 +++++++++++++++++++++++++--------- 1 file changed, 191 insertions(+), 64 deletions(-) (limited to 'crypto/src/math/ec/rfc8032/Ed25519.cs') diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs index ff4587cb2..adb56ca9b 100644 --- a/crypto/src/math/ec/rfc8032/Ed25519.cs +++ b/crypto/src/math/ec/rfc8032/Ed25519.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Math.EC.Rfc7748; using Org.BouncyCastle.Math.Raw; @@ -18,11 +19,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private const int ScalarUints = 8; private const int ScalarBytes = ScalarUints * 4; + public static readonly int PrehashSize = 64; public static readonly int PublicKeySize = PointBytes; public static readonly int SecretKeySize = 32; public static readonly int SignatureSize = PointBytes + ScalarBytes; - //private static readonly byte[] Dom2Prefix = Strings.ToByteArray("SigEd25519 no Ed25519 collisions"); + private static readonly byte[] Dom2Prefix = Strings.ToByteArray("SigEd25519 no Ed25519 collisions"); private static readonly uint[] P = { 0xFFFFFFEDU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0x7FFFFFFFU }; private static readonly uint[] L = { 0x5CF5D3EDU, 0x5812631AU, 0xA2F79CD6U, 0x14DEF9DEU, 0x00000000U, 0x00000000U, 0x00000000U, 0x10000000U }; @@ -96,6 +98,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ReduceScalar(result); } + private static bool CheckContextVar(byte[] ctx, byte phflag) + { + return ctx == null && phflag == 0x00 + || ctx != null && ctx.Length < 256; + } + private static bool CheckPointVar(byte[] p) { uint[] t = new uint[8]; @@ -111,6 +119,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return !Nat256.Gte(n, L); } + private static IDigest CreateDigest() + { + return new Sha512Digest(); + } + + public static IDigest CreatePrehash() + { + return CreateDigest(); + } + private static uint Decode24(byte[] bs, int off) { uint n = bs[off]; @@ -140,9 +158,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { byte[] py = Arrays.CopyOfRange(p, pOff, pOff + PointBytes); if (!CheckPointVar(py)) - { return false; - } int x_0 = (py[PointBytes - 1] & 0x80) >> 7; py[PointBytes - 1] &= 0x7F; @@ -158,15 +174,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 X25519Field.AddOne(v); if (!X25519Field.SqrtRatioVar(u, v, r.x)) - { return false; - } X25519Field.Normalize(r.x); if (x_0 == 1 && X25519Field.IsZeroVar(r.x)) - { return false; - } if (negate ^ (x_0 != (r.x[0] & 1))) { @@ -182,6 +194,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Decode32(k, kOff, n, 0, ScalarUints); } + private static void Dom2(IDigest d, byte phflag, byte[] ctx) + { + if (ctx != null) + { + d.BlockUpdate(Dom2Prefix, 0, Dom2Prefix.Length); + d.Update(phflag); + d.Update((byte)ctx.Length); + d.BlockUpdate(ctx, 0, ctx.Length); + } + } + private static void Encode24(uint n, byte[] bs, int off) { bs[off] = (byte)(n); @@ -220,7 +243,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff) { - Sha512Digest d = new Sha512Digest(); + IDigest d = CreateDigest(); byte[] h = new byte[d.GetDigestSize()]; d.BlockUpdate(sk, skOff, SecretKeySize); @@ -286,8 +309,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ws; } - private static void ImplSign(Sha512Digest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + private static void ImplSign(IDigest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { + Dom2(d, phflag, ctx); d.BlockUpdate(h, ScalarBytes, ScalarBytes); d.BlockUpdate(m, mOff, mLen); d.DoFinal(h, 0); @@ -296,6 +321,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 byte[] R = new byte[PointBytes]; ScalarMultBaseEncoded(r, R, 0); + Dom2(d, phflag, ctx); d.BlockUpdate(R, 0, PointBytes); d.BlockUpdate(pk, 0, PointBytes); d.BlockUpdate(m, mOff, mLen); @@ -308,6 +334,90 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Array.Copy(S, 0, sig, sigOff + PointBytes, ScalarBytes); } + private static void ImplSign(byte[] sk, int skOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen, + byte[] sig, int sigOff) + { + if (!CheckContextVar(ctx, phflag)) + throw new ArgumentException("ctx"); + + IDigest d = CreateDigest(); + byte[] h = new byte[d.GetDigestSize()]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + byte[] pk = new byte[PointBytes]; + ScalarMultBaseEncoded(s, pk, 0); + + ImplSign(d, h, s, pk, 0, ctx, phflag, m, mOff, mLen, sig, sigOff); + } + + private static void ImplSign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + { + if (!CheckContextVar(ctx, phflag)) + throw new ArgumentException("ctx"); + + IDigest d = CreateDigest(); + byte[] h = new byte[d.GetDigestSize()]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + ImplSign(d, h, s, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } + + private static bool ImplVerify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen) + { + if (!CheckContextVar(ctx, phflag)) + throw new ArgumentException("ctx"); + + byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes); + byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize); + + if (!CheckPointVar(R)) + return false; + + if (!CheckScalarVar(S)) + return false; + + PointExt pA = new PointExt(); + if (!DecodePointVar(pk, pkOff, true, pA)) + return false; + + IDigest d = CreateDigest(); + byte[] h = new byte[d.GetDigestSize()]; + + Dom2(d, phflag, ctx); + d.BlockUpdate(R, 0, PointBytes); + d.BlockUpdate(pk, pkOff, PointBytes); + d.BlockUpdate(m, mOff, mLen); + d.DoFinal(h, 0); + + byte[] k = ReduceScalar(h); + + uint[] nS = new uint[ScalarUints]; + DecodeScalar(S, 0, nS); + + uint[] nA = new uint[ScalarUints]; + DecodeScalar(k, 0, nA); + + PointAccum pR = new PointAccum(); + ScalarMultStraussVar(nS, nA, pA, pR); + + byte[] check = new byte[PointBytes]; + EncodePoint(pR, check, 0); + + return Arrays.AreEqual(check, R); + } + private static void PointAddVar(bool negate, PointExt p, PointAccum r) { int[] A = X25519Field.Create(); @@ -518,9 +628,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void Precompute() { if (precompBase != null) - { return; - } // Precomputed table for the base point in verification ladder { @@ -795,9 +903,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } if ((cOff -= PrecompTeeth) < 0) - { break; - } PointDouble(r); } @@ -850,9 +956,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } if (--bit < 0) - { break; - } PointDouble(r); } @@ -860,78 +964,101 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void Sign(byte[] sk, int skOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { - Sha512Digest d = new Sha512Digest(); - byte[] h = new byte[d.GetDigestSize()]; + byte[] ctx = null; + byte phflag = 0x00; - d.BlockUpdate(sk, skOff, SecretKeySize); - d.DoFinal(h, 0); + ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } - byte[] s = new byte[ScalarBytes]; - PruneScalar(h, 0, s); + public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + { + byte[] ctx = null; + byte phflag = 0x00; - byte[] pk = new byte[PointBytes]; - ScalarMultBaseEncoded(s, pk, 0); + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } - ImplSign(d, h, s, pk, 0, m, mOff, mLen, sig, sigOff); + public static void Sign(byte[] sk, int skOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + { + byte phflag = 0x00; + + ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff); } - public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { - Sha512Digest d = new Sha512Digest(); - byte[] h = new byte[d.GetDigestSize()]; + byte phflag = 0x00; - d.BlockUpdate(sk, skOff, SecretKeySize); - d.DoFinal(h, 0); + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } - byte[] s = new byte[ScalarBytes]; - PruneScalar(h, 0, s); + public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff) + { + byte phflag = 0x01; - ImplSign(d, h, s, pk, pkOff, m, mOff, mLen, sig, sigOff); + ImplSign(sk, skOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff); } - public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen) + public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff) { - byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes); - byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize); + byte phflag = 0x01; - if (!CheckPointVar(R)) - { - return false; - } - if (!CheckScalarVar(S)) - { - return false; - } + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff); + } - PointExt pA = new PointExt(); - if (!DecodePointVar(pk, pkOff, true, pA)) - { - return false; - } + public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, IDigest ph, byte[] sig, int sigOff) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0)) + throw new ArgumentException("ph"); - Sha512Digest d = new Sha512Digest(); - byte[] h = new byte[d.GetDigestSize()]; + byte phflag = 0x01; - d.BlockUpdate(R, 0, PointBytes); - d.BlockUpdate(pk, pkOff, PointBytes); - d.BlockUpdate(m, mOff, mLen); - d.DoFinal(h, 0); + ImplSign(sk, skOff, ctx, phflag, m, 0, m.Length, sig, sigOff); + } - byte[] k = ReduceScalar(h); + public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, IDigest ph, byte[] sig, int sigOff) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0)) + throw new ArgumentException("ph"); - uint[] nS = new uint[ScalarUints]; - DecodeScalar(S, 0, nS); + byte phflag = 0x01; - uint[] nA = new uint[ScalarUints]; - DecodeScalar(k, 0, nA); + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, 0, m.Length, sig, sigOff); + } - PointAccum pR = new PointAccum(); - ScalarMultStraussVar(nS, nA, pA, pR); + public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen) + { + byte[] ctx = null; + byte phflag = 0x00; - byte[] check = new byte[PointBytes]; - EncodePoint(pR, check, 0); + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); + } - return Arrays.AreEqual(check, R); + 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 VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff) + { + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, pk, pkOff, 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]; + if (PrehashSize != ph.DoFinal(m, 0)) + throw new ArgumentException("ph"); + + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length); } } } -- cgit 1.4.1