From 04dfef28a2eef656afd846edfc318452cc7a27bc Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 14 Sep 2018 15:52:15 +0700 Subject: Add asymmetric multiplication --- crypto/src/math/raw/Nat.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crypto/src/math/raw/Nat.cs b/crypto/src/math/raw/Nat.cs index 7ca60278a..040ade74f 100644 --- a/crypto/src/math/raw/Nat.cs +++ b/crypto/src/math/raw/Nat.cs @@ -488,21 +488,31 @@ namespace Org.BouncyCastle.Math.Raw public static void Mul(int len, uint[] x, uint[] y, uint[] zz) { - zz[len] = (uint)MulWord(len, x[0], y, zz); + zz[len] = MulWord(len, x[0], y, zz); for (int i = 1; i < len; ++i) { - zz[i + len] = (uint)MulWordAddTo(len, x[i], y, 0, zz, i); + zz[i + len] = MulWordAddTo(len, x[i], y, 0, zz, i); } } public static void Mul(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) { - zz[zzOff + len] = (uint)MulWord(len, x[xOff], y, yOff, zz, zzOff); + zz[zzOff + len] = MulWord(len, x[xOff], y, yOff, zz, zzOff); for (int i = 1; i < len; ++i) { - zz[zzOff + i + len] = (uint)MulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff + i); + zz[zzOff + i + len] = MulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff + i); + } + } + + public static void Mul(uint[] x, int xOff, int xLen, uint[] y, int yOff, int yLen, uint[] zz, int zzOff) + { + zz[zzOff + yLen] = MulWord(yLen, x[xOff], y, yOff, zz, zzOff); + + for (int i = 1; i < xLen; ++i) + { + zz[zzOff + i + yLen] = MulWordAddTo(yLen, x[xOff + i], y, yOff, zz, zzOff + i); } } -- cgit 1.4.1 From 56d58cba091c7e03253b1b43b81e21d69c82c143 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 14 Sep 2018 16:10:44 +0700 Subject: RFC 7748: Export size constants for scalars, points --- crypto/src/math/ec/rfc7748/X25519.cs | 3 ++ crypto/src/math/ec/rfc7748/X448.cs | 3 ++ crypto/test/src/math/ec/rfc7748/test/X25519Test.cs | 49 ++++++++++++-------- crypto/test/src/math/ec/rfc7748/test/X448Test.cs | 53 +++++++++++++--------- 4 files changed, 66 insertions(+), 42 deletions(-) diff --git a/crypto/src/math/ec/rfc7748/X25519.cs b/crypto/src/math/ec/rfc7748/X25519.cs index a10d53da5..d63cc5a3e 100644 --- a/crypto/src/math/ec/rfc7748/X25519.cs +++ b/crypto/src/math/ec/rfc7748/X25519.cs @@ -6,6 +6,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 { public abstract class X25519 { + public const int PointSize = 32; + public const int ScalarSize = 32; + private const int C_A = 486662; private const int C_A24 = (C_A + 2)/4; diff --git a/crypto/src/math/ec/rfc7748/X448.cs b/crypto/src/math/ec/rfc7748/X448.cs index 88e8a5d76..aac603b08 100644 --- a/crypto/src/math/ec/rfc7748/X448.cs +++ b/crypto/src/math/ec/rfc7748/X448.cs @@ -6,6 +6,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 { public abstract class X448 { + public const int PointSize = 56; + public const int ScalarSize = 56; + private const uint C_A = 156326; private const uint C_A24 = (C_A + 2)/4; diff --git a/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs b/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs index 89c325fd5..562e0e423 100644 --- a/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs +++ b/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs @@ -22,10 +22,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests [Test] public void TestConsistency() { - byte[] u = new byte[32]; u[0] = 9; - byte[] k = new byte[32]; - byte[] rF = new byte[32]; - byte[] rV = new byte[32]; + byte[] u = new byte[X25519.PointSize]; u[0] = 9; + byte[] k = new byte[X25519.ScalarSize]; + byte[] rF = new byte[X25519.PointSize]; + byte[] rV = new byte[X25519.PointSize]; for (int i = 1; i <= 100; ++i) { @@ -39,12 +39,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests [Test] public void TestECDH() { - byte[] kA = new byte[32]; - byte[] kB = new byte[32]; - byte[] qA = new byte[32]; - byte[] qB = new byte[32]; - byte[] sA = new byte[32]; - byte[] sB = new byte[32]; + byte[] kA = new byte[X25519.ScalarSize]; + byte[] kB = new byte[X25519.ScalarSize]; + byte[] qA = new byte[X25519.PointSize]; + byte[] qB = new byte[X25519.PointSize]; + byte[] sA = new byte[X25519.PointSize]; + byte[] sB = new byte[X25519.PointSize]; for (int i = 1; i <= 100; ++i) { @@ -116,38 +116,43 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests private static void CheckECDHVector(string sA, string sAPub, string sB, string sBPub, string sK, string text) { byte[] a = Hex.Decode(sA); + Assert.AreEqual(X25519.ScalarSize, a.Length); + byte[] b = Hex.Decode(sB); + Assert.AreEqual(X25519.ScalarSize, b.Length); - byte[] aPub = new byte[32]; + byte[] aPub = new byte[X25519.PointSize]; X25519.ScalarMultBase(a, 0, aPub, 0); CheckValue(aPub, text, sAPub); - byte[] bPub = new byte[32]; + byte[] bPub = new byte[X25519.PointSize]; X25519.ScalarMultBase(b, 0, bPub, 0); CheckValue(bPub, text, sBPub); - byte[] aK = new byte[32]; + byte[] aK = new byte[X25519.PointSize]; X25519.ScalarMult(a, 0, bPub, 0, aK, 0); CheckValue(aK, text, sK); - byte[] bK = new byte[32]; + byte[] bK = new byte[X25519.PointSize]; X25519.ScalarMult(b, 0, aPub, 0, bK, 0); CheckValue(bK, text, sK); } private static void CheckIterated(int count) { - byte[] k = new byte[32]; k[0] = 9; - byte[] u = new byte[32]; u[0] = 9; - byte[] r = new byte[32]; + Assert.AreEqual(X25519.PointSize, X25519.ScalarSize); + + byte[] k = new byte[X25519.PointSize]; k[0] = 9; + byte[] u = new byte[X25519.PointSize]; u[0] = 9; + byte[] r = new byte[X25519.PointSize]; int iterations = 0; while (iterations < count) { X25519.ScalarMult(k, 0, u, 0, r, 0); - Array.Copy(k, 0, u, 0, 32); - Array.Copy(r, 0, k, 0, 32); + Array.Copy(k, 0, u, 0, X25519.PointSize); + Array.Copy(r, 0, k, 0, X25519.PointSize); switch (++iterations) { @@ -175,8 +180,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests private static void CheckX25519Vector(string sk, string su, string se, string text) { byte[] k = Hex.Decode(sk); + Assert.AreEqual(X25519.ScalarSize, k.Length); + byte[] u = Hex.Decode(su); - byte[] r = new byte[32]; + Assert.AreEqual(X25519.PointSize, u.Length); + + byte[] r = new byte[X25519.PointSize]; X25519.ScalarMult(k, 0, u, 0, r, 0); CheckValue(r, text, se); } diff --git a/crypto/test/src/math/ec/rfc7748/test/X448Test.cs b/crypto/test/src/math/ec/rfc7748/test/X448Test.cs index b095eade0..df0158b96 100644 --- a/crypto/test/src/math/ec/rfc7748/test/X448Test.cs +++ b/crypto/test/src/math/ec/rfc7748/test/X448Test.cs @@ -22,10 +22,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests [Test] public void TestConsistency() { - byte[] u = new byte[56]; u[0] = 5; - byte[] k = new byte[56]; - byte[] rF = new byte[56]; - byte[] rV = new byte[56]; + byte[] u = new byte[X448.PointSize]; u[0] = 5; + byte[] k = new byte[X448.ScalarSize]; + byte[] rF = new byte[X448.PointSize]; + byte[] rV = new byte[X448.PointSize]; for (int i = 1; i <= 100; ++i) { @@ -39,12 +39,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests [Test] public void TestECDH() { - byte[] kA = new byte[56]; - byte[] kB = new byte[56]; - byte[] qA = new byte[56]; - byte[] qB = new byte[56]; - byte[] sA = new byte[56]; - byte[] sB = new byte[56]; + byte[] kA = new byte[X448.ScalarSize]; + byte[] kB = new byte[X448.ScalarSize]; + byte[] qA = new byte[X448.PointSize]; + byte[] qB = new byte[X448.PointSize]; + byte[] sA = new byte[X448.PointSize]; + byte[] sB = new byte[X448.PointSize]; for (int i = 1; i <= 100; ++i) { @@ -112,38 +112,43 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests private static void CheckECDHVector(string sA, string sAPub, string sB, string sBPub, string sK, string text) { byte[] a = Hex.Decode(sA); + Assert.AreEqual(X448.ScalarSize, a.Length); + byte[] b = Hex.Decode(sB); + Assert.AreEqual(X448.ScalarSize, b.Length); - byte[] aPub = new byte[56]; + byte[] aPub = new byte[X448.PointSize]; X448.ScalarMultBase(a, 0, aPub, 0); CheckValue(aPub, text, sAPub); - byte[] bPub = new byte[56]; + byte[] bPub = new byte[X448.PointSize]; X448.ScalarMultBase(b, 0, bPub, 0); CheckValue(bPub, text, sBPub); - byte[] aK = new byte[56]; + byte[] aK = new byte[X448.PointSize]; X448.ScalarMult(a, 0, bPub, 0, aK, 0); CheckValue(aK, text, sK); - byte[] bK = new byte[56]; + byte[] bK = new byte[X448.PointSize]; X448.ScalarMult(b, 0, aPub, 0, bK, 0); CheckValue(bK, text, sK); } private static void CheckIterated(int count) { - byte[] k = new byte[56]; k[0] = 5; - byte[] u = new byte[56]; u[0] = 5; - byte[] r = new byte[56]; + Assert.AreEqual(X448.PointSize, X448.ScalarSize); + + byte[] k = new byte[X448.PointSize]; k[0] = 5; + byte[] u = new byte[X448.PointSize]; u[0] = 5; + byte[] r = new byte[X448.PointSize]; int iterations = 0; while (iterations < count) { X448.ScalarMult(k, 0, u, 0, r, 0); - Array.Copy(k, 0, u, 0, 56); - Array.Copy(r, 0, k, 0, 56); + Array.Copy(k, 0, u, 0, X448.PointSize); + Array.Copy(r, 0, k, 0, X448.PointSize); switch (++iterations) { @@ -165,17 +170,21 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests } } - private static void CheckValue(byte[] n, String text, String se) + private static void CheckValue(byte[] n, string text, string se) { byte[] e = Hex.Decode(se); Assert.IsTrue(Arrays.AreEqual(e, n), text); } - private static void CheckX448Vector(String sk, String su, String se, String text) + private static void CheckX448Vector(string sk, string su, string se, string text) { byte[] k = Hex.Decode(sk); + Assert.AreEqual(X448.ScalarSize, k.Length); + byte[] u = Hex.Decode(su); - byte[] r = new byte[56]; + Assert.AreEqual(X448.PointSize, u.Length); + + byte[] r = new byte[X448.PointSize]; X448.ScalarMult(k, 0, u, 0, r, 0); CheckValue(r, text, se); } -- cgit 1.4.1 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 ++++++++++++++----- crypto/src/math/ec/rfc8032/Ed448.cs | 232 ++++++++++------- .../test/src/math/ec/rfc8032/test/Ed25519Test.cs | 281 ++++++++++++++++++++- crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs | 178 ++++++++++++- 4 files changed, 783 insertions(+), 163 deletions(-) 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); } } } diff --git a/crypto/src/math/ec/rfc8032/Ed448.cs b/crypto/src/math/ec/rfc8032/Ed448.cs index 52c215160..0e56b12a8 100644 --- a/crypto/src/math/ec/rfc8032/Ed448.cs +++ b/crypto/src/math/ec/rfc8032/Ed448.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,6 +19,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private const int ScalarUints = 14; private const int ScalarBytes = ScalarUints * 4 + 1; + public static readonly int PrehashSize = 64; public static readonly int PublicKeySize = PointBytes; public static readonly int SecretKeySize = 57; public static readonly int SignatureSize = PointBytes + ScalarBytes; @@ -103,9 +105,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private static bool CheckPointVar(byte[] p) { if ((p[PointBytes - 1] & 0x7F) != 0x00) - { return false; - } uint[] t = new uint[14]; Decode32(p, 0, t, 0, 14); @@ -115,15 +115,23 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private static bool CheckScalarVar(byte[] s) { if (s[ScalarBytes - 1] != 0x00) - { return false; - } uint[] n = new uint[ScalarUints]; DecodeScalar(s, 0, n); return !Nat.Gte(ScalarUints, n, L); } + public static IXof CreatePrehash() + { + return CreateXof(); + } + + private static IXof CreateXof() + { + return new ShakeDigest(256); + } + private static uint Decode16(byte[] bs, int off) { uint n = bs[off]; @@ -160,9 +168,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; @@ -179,15 +185,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 X448Field.AddOne(v); if (!X448Field.SqrtRatioVar(u, v, r.x)) - { return false; - } X448Field.Normalize(r.x); if (x_0 == 1 && X448Field.IsZeroVar(r.x)) - { return false; - } if (negate ^ (x_0 != (r.x[0] & 1))) { @@ -205,7 +207,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Decode32(k, kOff, n, 0, ScalarUints); } - private static void Dom4(ShakeDigest d, byte x, byte[] y) + private static void Dom4(IXof d, byte x, byte[] y) { d.BlockUpdate(Dom4Prefix, 0, Dom4Prefix.Length); d.Update(x); @@ -251,7 +253,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff) { - ShakeDigest d = new ShakeDigest(256); + IXof d = CreateXof(); byte[] h = new byte[ScalarBytes * 2]; d.BlockUpdate(sk, skOff, SecretKeySize); @@ -317,10 +319,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ws; } - private static void ImplSign(ShakeDigest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + private static void ImplSign(IXof d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { - byte phflag = 0x00; - Dom4(d, phflag, ctx); d.BlockUpdate(h, ScalarBytes, ScalarBytes); d.BlockUpdate(m, mOff, mLen); @@ -343,6 +344,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)) + throw new ArgumentException("ctx"); + + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0, h.Length); + + 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)) + throw new ArgumentException("ctx"); + + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0, h.Length); + + 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)) + 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; + + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + Dom4(d, phflag, ctx); + d.BlockUpdate(R, 0, PointBytes); + d.BlockUpdate(pk, pkOff, PointBytes); + d.BlockUpdate(m, mOff, mLen); + d.DoFinal(h, 0, h.Length); + + byte[] k = ReduceScalar(h); + + uint[] nS = new uint[ScalarUints]; + DecodeScalar(S, 0, nS); + + uint[] nA = new uint[ScalarUints]; + DecodeScalar(k, 0, nA); + + PointExt pR = new PointExt(); + 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, PointExt r) { uint[] A = X448Field.Create(); @@ -505,9 +590,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void Precompute() { if (precompBase != null) - { return; - } PointExt p = new PointExt(); X448Field.Copy(B_x, 0, p.x, 0); @@ -907,9 +990,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } if (--cOff < 0) - { break; - } PointDouble(r); } @@ -962,9 +1043,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } if (--bit < 0) - { break; - } PointDouble(r); } @@ -972,96 +1051,77 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static void Sign(byte[] sk, int skOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { - if (!CheckContextVar(ctx)) - { - throw new ArgumentException("ctx"); - } + byte phflag = 0x00; - ShakeDigest d = new ShakeDigest(256); - byte[] h = new byte[ScalarBytes * 2]; + ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } - d.BlockUpdate(sk, skOff, SecretKeySize); - d.DoFinal(h, 0, h.Length); + public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) + { + byte phflag = 0x00; - byte[] s = new byte[ScalarBytes]; - PruneScalar(h, 0, s); + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); + } - byte[] pk = new byte[PointBytes]; - ScalarMultBaseEncoded(s, pk, 0); + 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, 0, ctx, m, mOff, mLen, sig, sigOff); + ImplSign(sk, skOff, ctx, phflag, ph, phOff, PrehashSize, sig, 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) + public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff) { - if (!CheckContextVar(ctx)) - { - throw new ArgumentException("ctx"); - } + byte phflag = 0x01; - ShakeDigest d = new ShakeDigest(256); - byte[] h = new byte[ScalarBytes * 2]; + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff); + } - d.BlockUpdate(sk, skOff, SecretKeySize); - d.DoFinal(h, 0, h.Length); + public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, IXof ph, byte[] sig, int sigOff) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0, PrehashSize)) + throw new ArgumentException("ph"); - byte[] s = new byte[ScalarBytes]; - PruneScalar(h, 0, s); + byte phflag = 0x01; - ImplSign(d, h, s, pk, pkOff, ctx, m, mOff, mLen, sig, sigOff); + ImplSign(sk, skOff, ctx, phflag, m, 0, m.Length, sig, sigOff); } - public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) + public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, IXof ph, byte[] sig, int sigOff) { - if (!CheckContextVar(ctx)) - { - throw new ArgumentException("ctx"); - } - - byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes); - byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize); + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0, PrehashSize)) + throw new ArgumentException("ph"); - if (!CheckPointVar(R)) - { - return false; - } - if (!CheckScalarVar(S)) - { - return false; - } + byte phflag = 0x01; - PointExt pA = new PointExt(); - if (!DecodePointVar(pk, pkOff, true, pA)) - { - return false; - } + ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, 0, m.Length, sig, sigOff); + } + public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) + { byte phflag = 0x00; - ShakeDigest d = new ShakeDigest(256); - byte[] h = new byte[ScalarBytes * 2]; - - Dom4(d, phflag, ctx); - d.BlockUpdate(R, 0, PointBytes); - d.BlockUpdate(pk, pkOff, PointBytes); - d.BlockUpdate(m, mOff, mLen); - d.DoFinal(h, 0, h.Length); - - byte[] k = ReduceScalar(h); + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); + } - uint[] nS = new uint[ScalarUints]; - DecodeScalar(S, 0, nS); + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff) + { + byte phflag = 0x01; - uint[] nA = new uint[ScalarUints]; - DecodeScalar(k, 0, nA); + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize); + } - PointExt pR = new PointExt(); - ScalarMultStraussVar(nS, nA, pA, pR); + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IXof ph) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0, PrehashSize)) + throw new ArgumentException("ph"); - byte[] check = new byte[PointBytes]; - EncodePoint(pR, check, 0); + byte phflag = 0x01; - return Arrays.AreEqual(check, R); + return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length); } } } diff --git a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs index 43ea23988..5a42daeae 100644 --- a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs +++ b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs @@ -2,6 +2,7 @@ using NUnit.Framework; +using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; @@ -40,16 +41,93 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Ed25519.Sign(sk, 0, m, 0, mLen, sig1, 0); Ed25519.Sign(sk, 0, pk, 0, m, 0, mLen, sig2, 0); - Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Consistent signatures #" + i); + Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed25519 consistent signatures #" + i); bool shouldVerify = Ed25519.Verify(sig1, 0, pk, 0, m, 0, mLen); - Assert.IsTrue(shouldVerify, "Consistent sign/verify #" + i); + Assert.IsTrue(shouldVerify, "Ed25519 consistent sign/verify #" + i); sig1[Ed25519.PublicKeySize - 1] ^= 0x80; bool shouldNotVerify = Ed25519.Verify(sig1, 0, pk, 0, m, 0, mLen); - Assert.IsFalse(shouldNotVerify, "Consistent verification failure #" + i); + Assert.IsFalse(shouldNotVerify, "Ed25519 consistent verification failure #" + i); + } + } + + [Test] + public void TestEd25519ctxConsistency() + { + byte[] sk = new byte[Ed25519.SecretKeySize]; + byte[] pk = new byte[Ed25519.PublicKeySize]; + byte[] ctx = new byte[Random.Next() & 7]; + byte[] m = new byte[255]; + byte[] sig1 = new byte[Ed25519.SignatureSize]; + byte[] sig2 = new byte[Ed25519.SignatureSize]; + + Random.NextBytes(ctx); + Random.NextBytes(m); + + for (int i = 0; i < 10; ++i) + { + Random.NextBytes(sk); + Ed25519.GeneratePublicKey(sk, 0, pk, 0); + + int mLen = Random.Next() & 255; + + Ed25519.Sign(sk, 0, ctx, m, 0, mLen, sig1, 0); + Ed25519.Sign(sk, 0, pk, 0, ctx, m, 0, mLen, sig2, 0); + + Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed25519ctx consistent signatures #" + i); + + bool shouldVerify = Ed25519.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); + + Assert.IsTrue(shouldVerify, "Ed25519ctx consistent sign/verify #" + i); + + sig1[Ed25519.PublicKeySize - 1] ^= 0x80; + bool shouldNotVerify = Ed25519.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); + + Assert.IsFalse(shouldNotVerify, "Ed25519ctx consistent verification failure #" + i); + } + } + + [Test] + public void TestEd25519phConsistency() + { + byte[] sk = new byte[Ed25519.SecretKeySize]; + byte[] pk = new byte[Ed25519.PublicKeySize]; + byte[] ctx = new byte[Random.Next() & 7]; + byte[] m = new byte[255]; + byte[] ph = new byte[Ed25519.PrehashSize]; + byte[] sig1 = new byte[Ed25519.SignatureSize]; + byte[] sig2 = new byte[Ed25519.SignatureSize]; + + Random.NextBytes(ctx); + Random.NextBytes(m); + + for (int i = 0; i < 10; ++i) + { + Random.NextBytes(sk); + Ed25519.GeneratePublicKey(sk, 0, pk, 0); + + int mLen = Random.Next() & 255; + + IDigest prehash = Ed25519.CreatePrehash(); + prehash.BlockUpdate(m, 0, mLen); + prehash.DoFinal(ph, 0); + + Ed25519.SignPrehash(sk, 0, ctx, ph, 0, sig1, 0); + Ed25519.SignPrehash(sk, 0, pk, 0, ctx, ph, 0, sig2, 0); + + Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed25519ph consistent signatures #" + i); + + bool shouldVerify = Ed25519.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); + + Assert.IsTrue(shouldVerify, "Ed25519ph consistent sign/verify #" + i); + + sig1[Ed25519.PublicKeySize - 1] ^= 0x80; + bool shouldNotVerify = Ed25519.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); + + Assert.IsFalse(shouldNotVerify, "Ed25519ph consistent verification failure #" + i); } } @@ -201,18 +279,107 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests + "3dca179c138ac17ad9bef1177331a704"), "Ed25519 Vector SHA(abc)"); } - + + [Test] + public void TestEd25519ctxVector1() + { + CheckEd25519ctxVector( + ("0305334e381af78f141cb666f6199f57" + + "bc3495335a256a95bd2a55bf546663f6"), + ("dfc9425e4f968f7f0c29f0259cf5f9ae" + + "d6851c2bb4ad8bfb860cfee0ab248292"), + "f726936d19c800494e3fdaff20b276a8", + "666f6f", + ("55a4cc2f70a54e04288c5f4cd1e45a7b" + + "b520b36292911876cada7323198dd87a" + + "8b36950b95130022907a7fb7c4e9b2d5" + + "f6cca685a587b4b21f4b888e4e7edb0d"), + "Ed25519ctx Vector #1"); + } + + [Test] + public void TestEd25519ctxVector2() + { + CheckEd25519ctxVector( + ("0305334e381af78f141cb666f6199f57" + + "bc3495335a256a95bd2a55bf546663f6"), + ("dfc9425e4f968f7f0c29f0259cf5f9ae" + + "d6851c2bb4ad8bfb860cfee0ab248292"), + "f726936d19c800494e3fdaff20b276a8", + "626172", + ("fc60d5872fc46b3aa69f8b5b4351d580" + + "8f92bcc044606db097abab6dbcb1aee3" + + "216c48e8b3b66431b5b186d1d28f8ee1" + + "5a5ca2df6668346291c2043d4eb3e90d"), + "Ed25519ctx Vector #2"); + } + + [Test] + public void TestEd25519ctxVector3() + { + CheckEd25519ctxVector( + ("0305334e381af78f141cb666f6199f57" + + "bc3495335a256a95bd2a55bf546663f6"), + ("dfc9425e4f968f7f0c29f0259cf5f9ae" + + "d6851c2bb4ad8bfb860cfee0ab248292"), + "508e9e6882b979fea900f62adceaca35", + "666f6f", + ("8b70c1cc8310e1de20ac53ce28ae6e72" + + "07f33c3295e03bb5c0732a1d20dc6490" + + "8922a8b052cf99b7c4fe107a5abb5b2c" + + "4085ae75890d02df26269d8945f84b0b"), + "Ed25519ctx Vector #3"); + } + + [Test] + public void TestEd25519ctxVector4() + { + CheckEd25519ctxVector( + ("ab9c2853ce297ddab85c993b3ae14bca" + + "d39b2c682beabc27d6d4eb20711d6560"), + ("0f1d1274943b91415889152e893d80e9" + + "3275a1fc0b65fd71b4b0dda10ad7d772"), + "f726936d19c800494e3fdaff20b276a8", + "666f6f", + ("21655b5f1aa965996b3f97b3c849eafb" + + "a922a0a62992f73b3d1b73106a84ad85" + + "e9b86a7b6005ea868337ff2d20a7f5fb" + + "d4cd10b0be49a68da2b2e0dc0ad8960f"), + "Ed25519ctx Vector #4"); + } + + [Test] + public void TestEd25519phVector1() + { + CheckEd25519phVector( + ("833fe62409237b9d62ec77587520911e" + + "9a759cec1d19755b7da901b96dca3d42"), + ("ec172b93ad5e563bf4932c70e1245034" + + "c35467ef2efd4d64ebf819683467e2bf"), + "616263", + "", + ("98a70222f0b8121aa9d30f813d683f80" + + "9e462b469c7ff87639499bb94e6dae41" + + "31f85042463c2a355a2003d062adf5aa" + + "a10b8c61e636062aaad11c2a26083406"), + "Ed25519ph Vector #1"); + } + private static void CheckEd25519Vector(string sSK, string sPK, string sM, string sSig, string text) { byte[] sk = Hex.Decode(sSK); - byte[] pk = Hex.Decode(sPK); + byte[] pkGen = new byte[Ed25519.PublicKeySize]; Ed25519.GeneratePublicKey(sk, 0, pkGen, 0); Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text); byte[] m = Hex.Decode(sM); byte[] sig = Hex.Decode(sSig); + + byte[] badsig = Arrays.Clone(sig); + badsig[Ed25519.SignatureSize - 1] ^= 0x80; + byte[] sigGen = new byte[Ed25519.SignatureSize]; Ed25519.Sign(sk, 0, m, 0, m.Length, sigGen, 0); Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); @@ -223,9 +390,109 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests bool shouldVerify = Ed25519.Verify(sig, 0, pk, 0, m, 0, m.Length); Assert.IsTrue(shouldVerify, text); - sig[Ed25519.SignatureSize - 1] ^= 0x80; - bool shouldNotVerify = Ed25519.Verify(sig, 0, pk, 0, m, 0, m.Length); + bool shouldNotVerify = Ed25519.Verify(badsig, 0, pk, 0, m, 0, m.Length); + Assert.IsFalse(shouldNotVerify, text); + } + + private static void CheckEd25519ctxVector(string sSK, string sPK, string sM, string sCTX, string sSig, string text) + { + byte[] sk = Hex.Decode(sSK); + byte[] pk = Hex.Decode(sPK); + + byte[] pkGen = new byte[Ed25519.PublicKeySize]; + Ed25519.GeneratePublicKey(sk, 0, pkGen, 0); + Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text); + + byte[] m = Hex.Decode(sM); + byte[] ctx = Hex.Decode(sCTX); + byte[] sig = Hex.Decode(sSig); + + byte[] badsig = Arrays.Clone(sig); + badsig[Ed25519.SignatureSize - 1] ^= 0x80; + + byte[] sigGen = new byte[Ed25519.SignatureSize]; + Ed25519.Sign(sk, 0, ctx, m, 0, m.Length, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + + Ed25519.Sign(sk, 0, pk, 0, ctx, m, 0, m.Length, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + + bool shouldVerify = Ed25519.Verify(sig, 0, pk, 0, ctx, m, 0, m.Length); + Assert.IsTrue(shouldVerify, text); + + bool shouldNotVerify = Ed25519.Verify(badsig, 0, pk, 0, ctx, m, 0, m.Length); Assert.IsFalse(shouldNotVerify, text); } + + private static void CheckEd25519phVector(string sSK, string sPK, string sM, string sCTX, string sSig, string text) + { + byte[] sk = Hex.Decode(sSK); + byte[] pk = Hex.Decode(sPK); + + byte[] pkGen = new byte[Ed25519.PublicKeySize]; + Ed25519.GeneratePublicKey(sk, 0, pkGen, 0); + Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text); + + byte[] m = Hex.Decode(sM); + byte[] ctx = Hex.Decode(sCTX); + byte[] sig = Hex.Decode(sSig); + + byte[] badsig = Arrays.Clone(sig); + badsig[Ed25519.SignatureSize - 1] ^= 0x80; + + byte[] sigGen = new byte[Ed25519.SignatureSize]; + + { + IDigest prehash = Ed25519.CreatePrehash(); + prehash.BlockUpdate(m, 0, m.Length); + + byte[] ph = new byte[Ed25519.PrehashSize]; + prehash.DoFinal(ph, 0); + + Ed25519.SignPrehash(sk, 0, ctx, ph, 0, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + + Ed25519.SignPrehash(sk, 0, pk, 0, ctx, ph, 0, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + + bool shouldVerify = Ed25519.VerifyPrehash(sig, 0, pk, 0, ctx, ph, 0); + Assert.IsTrue(shouldVerify, text); + + bool shouldNotVerify = Ed25519.VerifyPrehash(badsig, 0, pk, 0, ctx, ph, 0); + Assert.IsFalse(shouldNotVerify, text); + } + + { + IDigest ph = Ed25519.CreatePrehash(); + ph.BlockUpdate(m, 0, m.Length); + + Ed25519.SignPrehash(sk, 0, ctx, ph, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + } + + { + IDigest ph = Ed25519.CreatePrehash(); + ph.BlockUpdate(m, 0, m.Length); + + Ed25519.SignPrehash(sk, 0, pk, 0, ctx, ph, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + } + + { + IDigest ph = Ed25519.CreatePrehash(); + ph.BlockUpdate(m, 0, m.Length); + + bool shouldVerify = Ed25519.VerifyPrehash(sig, 0, pk, 0, ctx, ph); + Assert.IsTrue(shouldVerify, text); + } + + { + IDigest ph = Ed25519.CreatePrehash(); + ph.BlockUpdate(m, 0, m.Length); + + bool shouldNotVerify = Ed25519.VerifyPrehash(badsig, 0, pk, 0, ctx, ph); + Assert.IsFalse(shouldNotVerify, text); + } + } } } diff --git a/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs b/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs index 98c487c09..826f76345 100644 --- a/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs +++ b/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs @@ -2,6 +2,7 @@ using NUnit.Framework; +using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; @@ -42,16 +43,57 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Ed448.Sign(sk, 0, ctx, m, 0, mLen, sig1, 0); Ed448.Sign(sk, 0, pk, 0, ctx, m, 0, mLen, sig2, 0); - Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Consistent signatures #" + i); + Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed448 consistent signatures #" + i); bool shouldVerify = Ed448.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); - Assert.IsTrue(shouldVerify, "Consistent sign/verify #" + i); + Assert.IsTrue(shouldVerify, "Ed448 consistent sign/verify #" + i); sig1[Ed448.PublicKeySize - 1] ^= 0x80; bool shouldNotVerify = Ed448.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); - Assert.IsFalse(shouldNotVerify, "Consistent verification failure #" + i); + Assert.IsFalse(shouldNotVerify, "Ed448 consistent verification failure #" + i); + } + } + + [Test] + public void TestEd448phConsistency() + { + byte[] sk = new byte[Ed448.SecretKeySize]; + byte[] pk = new byte[Ed448.PublicKeySize]; + byte[] ctx = new byte[Random.Next() & 7]; + byte[] m = new byte[255]; + byte[] ph = new byte[Ed448.PrehashSize]; + byte[] sig1 = new byte[Ed448.SignatureSize]; + byte[] sig2 = new byte[Ed448.SignatureSize]; + + Random.NextBytes(ctx); + Random.NextBytes(m); + + for (int i = 0; i < 10; ++i) + { + Random.NextBytes(sk); + Ed448.GeneratePublicKey(sk, 0, pk, 0); + + int mLen = Random.Next() & 255; + + IXof prehash = Ed448.CreatePrehash(); + prehash.BlockUpdate(m, 0, mLen); + prehash.DoFinal(ph, 0, ph.Length); + + Ed448.SignPrehash(sk, 0, ctx, ph, 0, sig1, 0); + Ed448.SignPrehash(sk, 0, pk, 0, ctx, ph, 0, sig2, 0); + + Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed448ph consistent signatures #" + i); + + bool shouldVerify = Ed448.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); + + Assert.IsTrue(shouldVerify, "Ed448ph consistent sign/verify #" + i); + + sig1[Ed448.PublicKeySize - 1] ^= 0x80; + bool shouldNotVerify = Ed448.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); + + Assert.IsFalse(shouldNotVerify, "Ed448ph consistent verification failure #" + i); } } @@ -370,11 +412,61 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests "Ed448 Vector #1023"); } + [Test] + public void TestEd448phVector1() + { + CheckEd448phVector( + ("833fe62409237b9d62ec77587520911e" + + "9a759cec1d19755b7da901b96dca3d42" + + "ef7822e0d5104127dc05d6dbefde69e3" + + "ab2cec7c867c6e2c49"), + ("259b71c19f83ef77a7abd26524cbdb31" + + "61b590a48f7d17de3ee0ba9c52beb743" + + "c09428a131d6b1b57303d90d8132c276" + + "d5ed3d5d01c0f53880"), + "616263", + "", + ("822f6901f7480f3d5f562c592994d969" + + "3602875614483256505600bbc281ae38" + + "1f54d6bce2ea911574932f52a4e6cadd" + + "78769375ec3ffd1b801a0d9b3f4030cd" + + "433964b6457ea39476511214f97469b5" + + "7dd32dbc560a9a94d00bff07620464a3" + + "ad203df7dc7ce360c3cd3696d9d9fab9" + + "0f00"), + "Ed448ph Vector #1"); + } + + [Test] + public void TestEd448phVector2() + { + CheckEd448phVector( + ("833fe62409237b9d62ec77587520911e" + + "9a759cec1d19755b7da901b96dca3d42" + + "ef7822e0d5104127dc05d6dbefde69e3" + + "ab2cec7c867c6e2c49"), + ("259b71c19f83ef77a7abd26524cbdb31" + + "61b590a48f7d17de3ee0ba9c52beb743" + + "c09428a131d6b1b57303d90d8132c276" + + "d5ed3d5d01c0f53880"), + "616263", + "666f6f", + ("c32299d46ec8ff02b54540982814dce9" + + "a05812f81962b649d528095916a2aa48" + + "1065b1580423ef927ecf0af5888f90da" + + "0f6a9a85ad5dc3f280d91224ba9911a3" + + "653d00e484e2ce232521481c8658df30" + + "4bb7745a73514cdb9bf3e15784ab7128" + + "4f8d0704a608c54a6b62d97beb511d13" + + "2100"), + "Ed448ph Vector #2"); + } + private static void CheckEd448Vector(string sSK, string sPK, string sM, string sCTX, string sSig, string text) { byte[] sk = Hex.Decode(sSK); - byte[] pk = Hex.Decode(sPK); + byte[] pkGen = new byte[Ed448.PublicKeySize]; Ed448.GeneratePublicKey(sk, 0, pkGen, 0); Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text); @@ -383,6 +475,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests byte[] ctx = Hex.Decode(sCTX); byte[] sig = Hex.Decode(sSig); byte[] sigGen = new byte[Ed448.SignatureSize]; + + byte[] badsig = Arrays.Clone(sig); + badsig[Ed448.SignatureSize - 1] ^= 0x80; + Ed448.Sign(sk, 0, ctx, m, 0, m.Length, sigGen, 0); Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); @@ -392,9 +488,79 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests bool shouldVerify = Ed448.Verify(sig, 0, pk, 0, ctx, m, 0, m.Length); Assert.IsTrue(shouldVerify, text); - sig[Ed448.SignatureSize - 1] ^= 0x80; - bool shouldNotVerify = Ed448.Verify(sig, 0, pk, 0, ctx, m, 0, m.Length); + bool shouldNotVerify = Ed448.Verify(badsig, 0, pk, 0, ctx, m, 0, m.Length); Assert.IsFalse(shouldNotVerify, text); } + + private static void CheckEd448phVector(string sSK, string sPK, string sM, string sCTX, string sSig, string text) + { + byte[] sk = Hex.Decode(sSK); + byte[] pk = Hex.Decode(sPK); + + byte[] pkGen = new byte[Ed448.PublicKeySize]; + Ed448.GeneratePublicKey(sk, 0, pkGen, 0); + Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text); + + byte[] m = Hex.Decode(sM); + byte[] ctx = Hex.Decode(sCTX); + byte[] sig = Hex.Decode(sSig); + + byte[] badsig = Arrays.Clone(sig); + badsig[Ed448.SignatureSize - 1] ^= 0x80; + + byte[] sigGen = new byte[Ed448.SignatureSize]; + + { + IXof prehash = Ed448.CreatePrehash(); + prehash.BlockUpdate(m, 0, m.Length); + + byte[] ph = new byte[Ed448.PrehashSize]; + prehash.DoFinal(ph, 0, ph.Length); + + Ed448.SignPrehash(sk, 0, ctx, ph, 0, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + + Ed448.SignPrehash(sk, 0, pk, 0, ctx, ph, 0, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + + bool shouldVerify = Ed448.VerifyPrehash(sig, 0, pk, 0, ctx, ph, 0); + Assert.IsTrue(shouldVerify, text); + + bool shouldNotVerify = Ed448.VerifyPrehash(badsig, 0, pk, 0, ctx, ph, 0); + Assert.IsFalse(shouldNotVerify, text); + } + + { + IXof ph = Ed448.CreatePrehash(); + ph.BlockUpdate(m, 0, m.Length); + + Ed448.SignPrehash(sk, 0, ctx, ph, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + } + + { + IXof ph = Ed448.CreatePrehash(); + ph.BlockUpdate(m, 0, m.Length); + + Ed448.SignPrehash(sk, 0, pk, 0, ctx, ph, sigGen, 0); + Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text); + } + + { + IXof ph = Ed448.CreatePrehash(); + ph.BlockUpdate(m, 0, m.Length); + + bool shouldVerify = Ed448.VerifyPrehash(sig, 0, pk, 0, ctx, ph); + Assert.IsTrue(shouldVerify, text); + } + + { + IXof ph = Ed448.CreatePrehash(); + ph.BlockUpdate(m, 0, m.Length); + + bool shouldNotVerify = Ed448.VerifyPrehash(badsig, 0, pk, 0, ctx, ph); + Assert.IsFalse(shouldNotVerify, text); + } + } } } -- cgit 1.4.1 From 297ccc3cc304defd9ecb0ba944bede4f89696b3b Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 14 Sep 2018 17:54:02 +0700 Subject: RFC 5958: Update PrivateKeyInfo - now supports optional 'publicKey' field --- crypto/src/asn1/pkcs/PrivateKeyInfo.cs | 193 ++++++++++++++------- crypto/src/util/collections/CollectionUtilities.cs | 8 + 2 files changed, 139 insertions(+), 62 deletions(-) diff --git a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs index c5be7a315..dfb332fdd 100644 --- a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs +++ b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs @@ -4,15 +4,55 @@ using System.IO; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Asn1.Pkcs { + /** + * RFC 5958 + * + *
+     *  [IMPLICIT TAGS]
+     *
+     *  OneAsymmetricKey ::= SEQUENCE {
+     *      version                   Version,
+     *      privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
+     *      privateKey                PrivateKey,
+     *      attributes            [0] Attributes OPTIONAL,
+     *      ...,
+     *      [[2: publicKey        [1] PublicKey OPTIONAL ]],
+     *      ...
+     *  }
+     *
+     *  PrivateKeyInfo ::= OneAsymmetricKey
+     *
+     *  Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2)
+     *
+     *  PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+     *                                     { PUBLIC-KEY,
+     *                                       { PrivateKeyAlgorithms } }
+     *
+     *  PrivateKey ::= OCTET STRING
+     *                     -- Content varies based on type of key.  The
+     *                     -- algorithm identifier dictates the format of
+     *                     -- the key.
+     *
+     *  PublicKey ::= BIT STRING
+     *                     -- Content varies based on type of key.  The
+     *                     -- algorithm identifier dictates the format of
+     *                     -- the key.
+     *
+     *  Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
+     *  
+ */ public class PrivateKeyInfo : Asn1Encodable { - private readonly Asn1OctetString privKey; - private readonly AlgorithmIdentifier algID; - private readonly Asn1Set attributes; + private readonly DerInteger version; + private readonly AlgorithmIdentifier privateKeyAlgorithm; + private readonly Asn1OctetString privateKey; + private readonly Asn1Set attributes; + private readonly DerBitString publicKey; public static PrivateKeyInfo GetInstance(Asn1TaggedObject obj, bool explicitly) { @@ -25,110 +65,139 @@ namespace Org.BouncyCastle.Asn1.Pkcs if (obj == null) return null; if (obj is PrivateKeyInfo) - return (PrivateKeyInfo) obj; + return (PrivateKeyInfo)obj; return new PrivateKeyInfo(Asn1Sequence.GetInstance(obj)); } - public PrivateKeyInfo(AlgorithmIdentifier algID, Asn1Encodable privateKey) - : this(algID, privateKey, null) + private static int GetVersionValue(DerInteger version) + { + BigInteger bigValue = version.Value; + if (bigValue.CompareTo(BigInteger.Zero) < 0 || bigValue.CompareTo(BigInteger.One) > 0) + throw new ArgumentException("invalid version for private key info", "version"); + + return bigValue.IntValue; + } + + public PrivateKeyInfo( + AlgorithmIdentifier privateKeyAlgorithm, + Asn1Encodable privateKey) + : this(privateKeyAlgorithm, privateKey, null, null) { } public PrivateKeyInfo( - AlgorithmIdentifier algID, - Asn1Encodable privateKey, - Asn1Set attributes) + AlgorithmIdentifier privateKeyAlgorithm, + Asn1Encodable privateKey, + Asn1Set attributes) + : this(privateKeyAlgorithm, privateKey, attributes, null) { - this.algID = algID; - this.privKey = new DerOctetString(privateKey.GetEncoded(Asn1Encodable.Der)); + } + + public PrivateKeyInfo( + AlgorithmIdentifier privateKeyAlgorithm, + Asn1Encodable privateKey, + Asn1Set attributes, + byte[] publicKey) + { + this.version = new DerInteger(publicKey != null ? BigInteger.One : BigInteger.Zero); + this.privateKeyAlgorithm = privateKeyAlgorithm; + this.privateKey = new DerOctetString(privateKey); this.attributes = attributes; + this.publicKey = publicKey == null ? null : new DerBitString(publicKey); } private PrivateKeyInfo(Asn1Sequence seq) { IEnumerator e = seq.GetEnumerator(); - e.MoveNext(); - BigInteger version = ((DerInteger)e.Current).Value; - if (version.IntValue != 0) - { - throw new ArgumentException("wrong version for private key info: " + version.IntValue); - } + this.version = DerInteger.GetInstance(CollectionUtilities.RequireNext(e)); - e.MoveNext(); - algID = AlgorithmIdentifier.GetInstance(e.Current); - e.MoveNext(); - privKey = Asn1OctetString.GetInstance(e.Current); + int versionValue = GetVersionValue(version); - if (e.MoveNext()) + this.privateKeyAlgorithm = AlgorithmIdentifier.GetInstance(CollectionUtilities.RequireNext(e)); + this.privateKey = Asn1OctetString.GetInstance(CollectionUtilities.RequireNext(e)); + + int lastTag = -1; + while (e.MoveNext()) { - attributes = Asn1Set.GetInstance((Asn1TaggedObject)e.Current, false); + Asn1TaggedObject tagged = (Asn1TaggedObject)e.Current; + + int tag = tagged.TagNo; + if (tag <= lastTag) + throw new ArgumentException("invalid optional field in private key info", "seq"); + + lastTag = tag; + + switch (tag) + { + case 0: + { + this.attributes = Asn1Set.GetInstance(tagged, false); + break; + } + case 1: + { + if (versionValue < 1) + throw new ArgumentException("'publicKey' requires version v2(1) or later", "seq"); + + this.publicKey = DerBitString.GetInstance(tagged, false); + break; + } + default: + { + throw new ArgumentException("unknown optional field in private key info", "seq"); + } + } } } - public virtual AlgorithmIdentifier PrivateKeyAlgorithm + public virtual Asn1Set Attributes { - get { return algID; } + get { return attributes; } } - [Obsolete("Use 'PrivateKeyAlgorithm' property instead")] - public virtual AlgorithmIdentifier AlgorithmID + /// Return true if a public key is present, false otherwise. + public virtual bool HasPublicKey { - get { return algID; } + get { return publicKey != null; } + } + + public virtual AlgorithmIdentifier PrivateKeyAlgorithm + { + get { return privateKeyAlgorithm; } } public virtual Asn1Object ParsePrivateKey() { - return Asn1Object.FromByteArray(privKey.GetOctets()); + return Asn1Object.FromByteArray(privateKey.GetOctets()); } - [Obsolete("Use 'ParsePrivateKey' instead")] - public virtual Asn1Object PrivateKey + /// For when the public key is an ASN.1 encoding. + public virtual Asn1Object ParsePublicKey() { - get - { - try - { - return ParsePrivateKey(); - } - catch (IOException) - { - throw new InvalidOperationException("unable to parse private key"); - } - } + return publicKey == null ? null : Asn1Object.FromByteArray(publicKey.GetOctets()); } - public virtual Asn1Set Attributes + /// Return the public key as a raw bit string. + public virtual DerBitString PublicKeyData { - get { return attributes; } + get { return publicKey; } } - /** - * write out an RSA private key with its associated information - * as described in Pkcs8. - *
-         *      PrivateKeyInfo ::= Sequence {
-         *                              version Version,
-         *                              privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}},
-         *                              privateKey PrivateKey,
-         *                              attributes [0] IMPLICIT Attributes OPTIONAL
-         *                          }
-         *      Version ::= Integer {v1(0)} (v1,...)
-         *
-         *      PrivateKey ::= OCTET STRING
-         *
-         *      Attributes ::= Set OF Attr
-         * 
- */ public override Asn1Object ToAsn1Object() { - Asn1EncodableVector v = new Asn1EncodableVector(new DerInteger(0), algID, privKey); + Asn1EncodableVector v = new Asn1EncodableVector(version, privateKeyAlgorithm, privateKey); if (attributes != null) { v.Add(new DerTaggedObject(false, 0, attributes)); } + if (publicKey != null) + { + v.Add(new DerTaggedObject(false, 1, publicKey)); + } + return new DerSequence(v); } } diff --git a/crypto/src/util/collections/CollectionUtilities.cs b/crypto/src/util/collections/CollectionUtilities.cs index 18fcb6774..cac158226 100644 --- a/crypto/src/util/collections/CollectionUtilities.cs +++ b/crypto/src/util/collections/CollectionUtilities.cs @@ -39,6 +39,14 @@ namespace Org.BouncyCastle.Utilities.Collections return new UnmodifiableSetProxy(s); } + public static object RequireNext(IEnumerator e) + { + if (!e.MoveNext()) + throw new InvalidOperationException(); + + return e.Current; + } + public static string ToString(IEnumerable c) { StringBuilder sb = new StringBuilder("["); -- cgit 1.4.1 From d78d99738fc727f2efea7b99f6bc3cc9a083dca1 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 14 Sep 2018 18:04:22 +0700 Subject: Reduce single-bit extractions from scalars --- crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs | 8 +++++--- crypto/src/math/ec/rfc8032/Ed448.cs | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs index 505832442..37e5b5c29 100644 --- a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs +++ b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs @@ -37,15 +37,17 @@ namespace Org.BouncyCastle.Math.EC.Multiplier int top = fullComb - 1; for (int i = 0; i < d; ++i) { - int secretIndex = 0; + uint secretIndex = 0; for (int j = top - i; j >= 0; j -= d) { + uint secretBit = K[j >> 5] >> (j & 0x1F); + secretIndex ^= secretBit >> 1; secretIndex <<= 1; - secretIndex |= (int)Nat.GetBit(K, j); + secretIndex ^= secretBit; } - ECPoint add = lookupTable.Lookup(secretIndex); + ECPoint add = lookupTable.Lookup((int)secretIndex); R = R.TwicePlus(add); } diff --git a/crypto/src/math/ec/rfc8032/Ed448.cs b/crypto/src/math/ec/rfc8032/Ed448.cs index 0e56b12a8..c1c0788a7 100644 --- a/crypto/src/math/ec/rfc8032/Ed448.cs +++ b/crypto/src/math/ec/rfc8032/Ed448.cs @@ -971,8 +971,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 uint w = 0; for (int t = 0; t < PrecompTeeth; ++t) { - uint tBit = (n[tPos >> 5] >> (tPos & 0x1F)) & 1U; - w |= tBit << t; + uint tBit = n[tPos >> 5] >> (tPos & 0x1F); + w &= ~(1U << t); + w ^= (tBit << t); tPos += PrecompSpacing; } -- cgit 1.4.1 From ba3292784635dc3f06070e4c99c01ed630818940 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sun, 16 Sep 2018 16:21:56 +0700 Subject: Fixed Rfc3211WrapEngine processing of messages over 127 bytes. --- crypto/Readme.html | 9 ++++ crypto/src/crypto/engines/RFC3211WrapEngine.cs | 59 +++++++++++++++----------- crypto/test/src/crypto/test/RFC3211WrapTest.cs | 2 +- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/crypto/Readme.html b/crypto/Readme.html index 72e97516f..1d1b74f1c 100644 --- a/crypto/Readme.html +++ b/crypto/Readme.html @@ -30,6 +30,8 @@
  • Notes:
      +
    1. + Release 1.8.4
    2. Release 1.8.3
    3. @@ -294,6 +296,13 @@ We state, where EC MQV has not otherwise been disabled or removed:

      Notes:

      +

      Release 1.8.4, TBD

      + +
      Defects Fixed
      +
        +
      • Rfc3211WrapEngine would not properly handle messages longer than 127 bytes. This has been fixed.
      • +
      +

      Release 1.8.3, Saturday August 11, 2018

      IMPORTANT
      diff --git a/crypto/src/crypto/engines/RFC3211WrapEngine.cs b/crypto/src/crypto/engines/RFC3211WrapEngine.cs index 4e3af5227..86480145c 100644 --- a/crypto/src/crypto/engines/RFC3211WrapEngine.cs +++ b/crypto/src/crypto/engines/RFC3211WrapEngine.cs @@ -32,10 +32,10 @@ namespace Org.BouncyCastle.Crypto.Engines if (param is ParametersWithRandom) { - ParametersWithRandom p = (ParametersWithRandom) param; + ParametersWithRandom p = (ParametersWithRandom)param; - this.rand = p.Random; - this.param = (ParametersWithIV) p.Parameters; + this.rand = p.Random; + this.param = p.Parameters as ParametersWithIV; } else { @@ -44,9 +44,12 @@ namespace Org.BouncyCastle.Crypto.Engines rand = new SecureRandom(); } - this.param = (ParametersWithIV) param; - } - } + this.param = param as ParametersWithIV; + } + + if (null == this.param) + throw new ArgumentException("RFC3211Wrap requires an IV", "param"); + } public virtual string AlgorithmName { @@ -59,11 +62,11 @@ namespace Org.BouncyCastle.Crypto.Engines int inLen) { if (!forWrapping) - { throw new InvalidOperationException("not set for wrapping"); - } + if (inLen > 255 || inLen < 0) + throw new ArgumentException("input must be from 0 to 255 bytes", "inLen"); - engine.Init(true, param); + engine.Init(true, param); int blockSize = engine.GetBlockSize(); byte[] cekBlock; @@ -78,15 +81,16 @@ namespace Org.BouncyCastle.Crypto.Engines } cekBlock[0] = (byte)inLen; - cekBlock[1] = (byte)~inBytes[inOff]; - cekBlock[2] = (byte)~inBytes[inOff + 1]; - cekBlock[3] = (byte)~inBytes[inOff + 2]; Array.Copy(inBytes, inOff, cekBlock, 4, inLen); rand.NextBytes(cekBlock, inLen + 4, cekBlock.Length - inLen - 4); - for (int i = 0; i < cekBlock.Length; i += blockSize) + cekBlock[1] = (byte)~cekBlock[4]; + cekBlock[2] = (byte)~cekBlock[4 + 1]; + cekBlock[3] = (byte)~cekBlock[4 + 2]; + + for (int i = 0; i < cekBlock.Length; i += blockSize) { engine.ProcessBlock(cekBlock, i, cekBlock, i); } @@ -142,27 +146,34 @@ namespace Org.BouncyCastle.Crypto.Engines engine.ProcessBlock(cekBlock, i, cekBlock, i); } - if ((cekBlock[0] & 0xff) > cekBlock.Length - 4) - { - throw new InvalidCipherTextException("wrapped key corrupted"); - } + bool invalidLength = (int)cekBlock[0] > (cekBlock.Length - 4); - byte[] key = new byte[cekBlock[0] & 0xff]; + byte[] key; + if (invalidLength) + { + key = new byte[cekBlock.Length - 4]; + } + else + { + key = new byte[cekBlock[0]]; + } - Array.Copy(cekBlock, 4, key, 0, cekBlock[0]); + Array.Copy(cekBlock, 4, key, 0, key.Length); // Note: Using constant time comparison int nonEqual = 0; for (int i = 0; i != 3; i++) { byte check = (byte)~cekBlock[1 + i]; - nonEqual |= (check ^ key[i]); - } + nonEqual |= (check ^ cekBlock[4 + i]); + } + + Array.Clear(cekBlock, 0, cekBlock.Length); - if (nonEqual != 0) - throw new InvalidCipherTextException("wrapped key fails checksum"); + if (nonEqual != 0 | invalidLength) + throw new InvalidCipherTextException("wrapped key corrupted"); - return key; + return key; } } } diff --git a/crypto/test/src/crypto/test/RFC3211WrapTest.cs b/crypto/test/src/crypto/test/RFC3211WrapTest.cs index bdef7c999..91dea34dd 100644 --- a/crypto/test/src/crypto/test/RFC3211WrapTest.cs +++ b/crypto/test/src/crypto/test/RFC3211WrapTest.cs @@ -115,7 +115,7 @@ namespace Org.BouncyCastle.Crypto.Tests } catch (InvalidCipherTextException e) { - if (!e.Message.Equals("wrapped key fails checksum")) + if (!e.Message.Equals("wrapped key corrupted")) { Fail("wrong exception"); } -- cgit 1.4.1 From af7355a81832318dd9b3125be82c12389ec0cb90 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sun, 16 Sep 2018 17:09:50 +0700 Subject: Blake2b/s: relax length-only constructor constraints - addresses https://github.com/bcgit/bc-csharp/issues/142 --- crypto/Readme.html | 4 + crypto/src/crypto/digests/Blake2bDigest.cs | 4 +- crypto/src/crypto/digests/Blake2sDigest.cs | 7 +- crypto/test/src/crypto/test/Blake2bDigestTest.cs | 496 +++++++++++++---------- crypto/test/src/crypto/test/Blake2sDigestTest.cs | 120 ++++-- crypto/test/src/util/test/SimpleTest.cs | 57 ++- 6 files changed, 435 insertions(+), 253 deletions(-) diff --git a/crypto/Readme.html b/crypto/Readme.html index 1d1b74f1c..59c333290 100644 --- a/crypto/Readme.html +++ b/crypto/Readme.html @@ -302,6 +302,10 @@ We state, where EC MQV has not otherwise been disabled or removed:
      • Rfc3211WrapEngine would not properly handle messages longer than 127 bytes. This has been fixed.
      +
      Additional Features and Functionality
      +
        +
      • Restrictions on the output sizes of the Blake2b/s digests have been removed.
      • +

      Release 1.8.3, Saturday August 11, 2018

      diff --git a/crypto/src/crypto/digests/Blake2bDigest.cs b/crypto/src/crypto/digests/Blake2bDigest.cs index b8e4f272e..770e35caf 100644 --- a/crypto/src/crypto/digests/Blake2bDigest.cs +++ b/crypto/src/crypto/digests/Blake2bDigest.cs @@ -136,8 +136,8 @@ namespace Org.BouncyCastle.Crypto.Digests */ public Blake2bDigest(int digestSize) { - if (digestSize != 160 && digestSize != 256 && digestSize != 384 && digestSize != 512) - throw new ArgumentException("BLAKE2b digest restricted to one of [160, 256, 384, 512]"); + if (digestSize < 8 || digestSize > 512 || digestSize % 8 != 0) + throw new ArgumentException("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512"); buffer = new byte[BLOCK_LENGTH_BYTES]; keyLength = 0; diff --git a/crypto/src/crypto/digests/Blake2sDigest.cs b/crypto/src/crypto/digests/Blake2sDigest.cs index f31032874..432b0f4d2 100644 --- a/crypto/src/crypto/digests/Blake2sDigest.cs +++ b/crypto/src/crypto/digests/Blake2sDigest.cs @@ -152,13 +152,12 @@ namespace Org.BouncyCastle.Crypto.Digests /** * BLAKE2s for hashing. * - * @param digestBits the desired digest length in bits. Must be one of - * [128, 160, 224, 256]. + * @param digestBits the desired digest length in bits. Must be a multiple of 8 and less than 256. */ public Blake2sDigest(int digestBits) { - if (digestBits != 128 && digestBits != 160 && digestBits != 224 && digestBits != 256) - throw new ArgumentException("BLAKE2s digest restricted to one of [128, 160, 224, 256]"); + if (digestBits < 8 || digestBits > 256 || digestBits % 8 != 0) + throw new ArgumentException("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256"); buffer = new byte[BLOCK_LENGTH_BYTES]; keyLength = 0; diff --git a/crypto/test/src/crypto/test/Blake2bDigestTest.cs b/crypto/test/src/crypto/test/Blake2bDigestTest.cs index 0d0853977..c9dbfc9d5 100644 --- a/crypto/test/src/crypto/test/Blake2bDigestTest.cs +++ b/crypto/test/src/crypto/test/Blake2bDigestTest.cs @@ -14,226 +14,300 @@ namespace Org.BouncyCastle.Crypto.Tests public class Blake2bDigestTest : SimpleTest { - private static readonly string[][] keyedTestVectors = - { // input/message, key, hash - - // Vectors from BLAKE2 web site: https://blake2.net/blake2b-test.txt - new string[]{ - "", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", - "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568" }, - - new string[]{ - "00", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", - "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd" }, - - new string[]{ - "0001", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", - "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965" }, - - new string[]{ - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", - "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd" }, - - new string[]{ - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", - "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f" }, - - new string[]{ - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", - "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461" } + private static readonly string[,] keyedTestVectors = { // input/message, key, hash + // Vectors from BLAKE2 web site: https://blake2.net/blake2b-test.txt + { + "", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568" + }, + { + "00", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd" + }, + { + "0001", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965" + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd" + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f" + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461" + } }; - private static readonly string[][] unkeyedTestVectors = - { // from: http://fossies.org/linux/john/src/rawBLAKE2_512_fmt_plug.c - // hash, input/message - // digests without leading $BLAKE2$ - new string[]{ - "4245af08b46fbb290222ab8a68613621d92ce78577152d712467742417ebc1153668f1c9e1ec1e152a32a9c242dc686d175e087906377f0c483c5be2cb68953e", - "blake2" }, - new string[]{ - "021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0", - "hello world" }, - new string[]{ - "1f7d9b7c9a90f7bfc66e52b69f3b6c3befbd6aee11aac860e99347a495526f30c9e51f6b0db01c24825092a09dd1a15740f0ade8def87e60c15da487571bcef7", - "verystrongandlongpassword" }, - new string[]{ - "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", - "The quick brown fox jumps over the lazy dog" }, - new string[]{ - "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", - "" }, - new string[]{ - "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", - "abc" }, - }; - - public override string Name - { - get { return "BLAKE2b"; } - } - - private void offsetTest( - IDigest digest, - byte[] input, - byte[] expected) - { - byte[] resBuf = new byte[expected.Length + 11]; - - digest.BlockUpdate(input, 0, input.Length); + // from: http://fossies.org/linux/john/src/rawBLAKE2_512_fmt_plug.c + private static readonly string[,] unkeyedTestVectors = { // hash, input/message + // digests without leading $BLAKE2$ + { + "4245af08b46fbb290222ab8a68613621d92ce78577152d712467742417ebc1153668f1c9e1ec1e152a32a9c242dc686d175e087906377f0c483c5be2cb68953e", + "blake2" + }, + { + "021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0", + "hello world" + }, + { + "1f7d9b7c9a90f7bfc66e52b69f3b6c3befbd6aee11aac860e99347a495526f30c9e51f6b0db01c24825092a09dd1a15740f0ade8def87e60c15da487571bcef7", + "verystrongandlongpassword" + }, + { + "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", + "The quick brown fox jumps over the lazy dog" + }, + { + "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", + "" + }, + { + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", + "abc" + }, + }; + + public override string Name + { + get { return "BLAKE2b"; } + } + + private void offsetTest( + IDigest digest, + byte[] input, + byte[] expected) + { + byte[] resBuf = new byte[expected.Length + 11]; + + digest.BlockUpdate(input, 0, input.Length); digest.DoFinal(resBuf, 11); - if (!AreEqual(Arrays.CopyOfRange(resBuf, 11, resBuf.Length), expected)) - { - Fail("Offset failed got " + Hex.ToHexString(resBuf)); - } - } - - public override void PerformTest() - { - // test keyed test vectors: - - Blake2bDigest blake2bkeyed = new Blake2bDigest(Hex.Decode(keyedTestVectors[0][1])); - for (int tv = 0; tv < keyedTestVectors.Length; tv++) - { - - byte[] input = Hex.Decode(keyedTestVectors[tv][0]); - blake2bkeyed.Reset(); - - blake2bkeyed.BlockUpdate(input, 0, input.Length); - byte[] keyedHash = new byte[64]; - blake2bkeyed.DoFinal(keyedHash, 0); - - if (!Arrays.AreEqual(Hex.Decode(keyedTestVectors[tv][2]), keyedHash)) - { - Fail("BLAKE2b mismatch on test vector ", - keyedTestVectors[tv][2], - Hex.ToHexString(keyedHash)); - } - - offsetTest(blake2bkeyed, input, keyedHash); - } - - Blake2bDigest blake2bunkeyed = new Blake2bDigest(); - // test unkeyed test vectors: - for (int i = 0; i < unkeyedTestVectors.Length; i++) - { - // blake2bunkeyed.update( - // unkeyedTestVectors[i][1].getBytes("UTF-8")); - // test update(byte b) - byte[] unkeyedInput = Encoding.UTF8.GetBytes(unkeyedTestVectors[i][1]); - for (int j = 0; j < unkeyedInput.Length; j++) - { - blake2bunkeyed.Update(unkeyedInput[j]); - } + if (!AreEqual(Arrays.CopyOfRange(resBuf, 11, resBuf.Length), expected)) + { + Fail("Offset failed got " + Hex.ToHexString(resBuf)); + } + } + + public override void PerformTest() + { + // test keyed test vectors: + + Blake2bDigest blake2bkeyed = new Blake2bDigest(Hex.Decode(keyedTestVectors[0, 1])); + for (int tv = 0; tv < keyedTestVectors.GetLength(0); tv++) + { + byte[] input = Hex.Decode(keyedTestVectors[tv, 0]); + blake2bkeyed.Reset(); + + blake2bkeyed.BlockUpdate(input, 0, input.Length); + byte[] keyedHash = new byte[64]; + blake2bkeyed.DoFinal(keyedHash, 0); + + if (!Arrays.AreEqual(Hex.Decode(keyedTestVectors[tv, 2]), keyedHash)) + { + Fail("BLAKE2b mismatch on test vector ", keyedTestVectors[tv, 2], Hex.ToHexString(keyedHash)); + } + + offsetTest(blake2bkeyed, input, keyedHash); + } + + Blake2bDigest blake2bunkeyed = new Blake2bDigest(); + // test unkeyed test vectors: + for (int i = 0; i < unkeyedTestVectors.GetLength(0); i++) + { + // test update(byte b) + byte[] unkeyedInput = Encoding.UTF8.GetBytes(unkeyedTestVectors[i, 1]); + for (int j = 0; j < unkeyedInput.Length; j++) + { + blake2bunkeyed.Update(unkeyedInput[j]); + } byte[] unkeyedHash = new byte[64]; - blake2bunkeyed.DoFinal(unkeyedHash, 0); - blake2bunkeyed.Reset(); - - if (!Arrays.AreEqual(Hex.Decode(unkeyedTestVectors[i][0]), - unkeyedHash)) - { - Fail("BLAKE2b mismatch on test vector ", - unkeyedTestVectors[i][0], - Hex.ToHexString(unkeyedHash)); - } - } - - cloneTest(); - resetTest(); - } - - private void cloneTest() - { - Blake2bDigest blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3][1]), 16, Hex.Decode("000102030405060708090a0b0c0d0e0f"), Hex.Decode("101112131415161718191a1b1c1d1e1f")); - byte[] expected = Hex.Decode("b6d48ed5771b17414c4e08bd8d8a3bc4"); - - checkClone(blake2bCloneSource, expected); - - // just digest size - blake2bCloneSource = new Blake2bDigest(160); - expected = Hex.Decode("64202454e538279b21cea0f5a7688be656f8f484"); - checkClone(blake2bCloneSource, expected); - - // null salt and personalisation - blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3][1]), 16, null, null); - expected = Hex.Decode("2b4a081fae2d7b488f5eed7e83e42a20"); - checkClone(blake2bCloneSource, expected); - - // null personalisation - blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3][1]), 16, Hex.Decode("000102030405060708090a0b0c0d0e0f"), null); - expected = Hex.Decode("00c3a2a02fcb9f389857626e19d706f6"); - checkClone(blake2bCloneSource, expected); - - // null salt - blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3][1]), 16, null, Hex.Decode("101112131415161718191a1b1c1d1e1f")); - expected = Hex.Decode("f445ec9c062a3c724f8fdef824417abb"); - checkClone(blake2bCloneSource, expected); - } - - private void checkClone(Blake2bDigest blake2bCloneSource, byte[] expected) - { - byte[] message = Hex.Decode(keyedTestVectors[3][0]); - - blake2bCloneSource.BlockUpdate(message, 0, message.Length); - - byte[] hash = new byte[blake2bCloneSource.GetDigestSize()]; - - Blake2bDigest digClone = new Blake2bDigest(blake2bCloneSource); - - blake2bCloneSource.DoFinal(hash, 0); - if (!AreEqual(expected, hash)) - { - Fail("clone source not correct"); - } - - digClone.DoFinal(hash, 0); - if (!AreEqual(expected, hash)) - { - Fail("clone not correct"); - } - } - - private void resetTest() - { - // Generate a non-zero key - byte[] key = new byte[32]; - for (byte i = 0; i < key.Length; i++) - { - key[i] = i; - } - // Generate some non-zero input longer than the key - byte[] input = new byte[key.Length + 1]; - for (byte i = 0; i < input.Length; i++) - { - input[i] = i; - } - // Hash the input - Blake2bDigest digest = new Blake2bDigest(key); + blake2bunkeyed.DoFinal(unkeyedHash, 0); + blake2bunkeyed.Reset(); + + if (!Arrays.AreEqual(Hex.Decode(unkeyedTestVectors[i, 0]), unkeyedHash)) + { + Fail("BLAKE2b mismatch on test vector ", unkeyedTestVectors[i, 0], Hex.ToHexString(unkeyedHash)); + } + } + + CloneTest(); + ResetTest(); + DoTestNullKeyVsUnkeyed(); + DoTestLengthConstruction(); + } + + private void CloneTest() + { + Blake2bDigest blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3, 1]), 16, + Hex.Decode("000102030405060708090a0b0c0d0e0f"), Hex.Decode("101112131415161718191a1b1c1d1e1f")); + byte[] expected = Hex.Decode("b6d48ed5771b17414c4e08bd8d8a3bc4"); + + CheckClone(blake2bCloneSource, expected); + + // just digest size + blake2bCloneSource = new Blake2bDigest(160); + expected = Hex.Decode("64202454e538279b21cea0f5a7688be656f8f484"); + CheckClone(blake2bCloneSource, expected); + + // null salt and personalisation + blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3, 1]), 16, null, null); + expected = Hex.Decode("2b4a081fae2d7b488f5eed7e83e42a20"); + CheckClone(blake2bCloneSource, expected); + + // null personalisation + blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3, 1]), 16, Hex.Decode("000102030405060708090a0b0c0d0e0f"), null); + expected = Hex.Decode("00c3a2a02fcb9f389857626e19d706f6"); + CheckClone(blake2bCloneSource, expected); + + // null salt + blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3, 1]), 16, null, Hex.Decode("101112131415161718191a1b1c1d1e1f")); + expected = Hex.Decode("f445ec9c062a3c724f8fdef824417abb"); + CheckClone(blake2bCloneSource, expected); + } + + private void CheckClone(Blake2bDigest blake2bCloneSource, byte[] expected) + { + byte[] message = Hex.Decode(keyedTestVectors[3, 0]); + + blake2bCloneSource.BlockUpdate(message, 0, message.Length); + + byte[] hash = new byte[blake2bCloneSource.GetDigestSize()]; + + Blake2bDigest digClone = new Blake2bDigest(blake2bCloneSource); + + blake2bCloneSource.DoFinal(hash, 0); + if (!AreEqual(expected, hash)) + { + Fail("clone source not correct"); + } + + digClone.DoFinal(hash, 0); + if (!AreEqual(expected, hash)) + { + Fail("clone not correct"); + } + } + + private void DoTestLengthConstruction() + { + try + { + new Blake2bDigest(-1); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512", e.Message); + } + + try + { + new Blake2bDigest(9); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512", e.Message); + } + + try + { + new Blake2bDigest(520); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512", e.Message); + } + + try + { + new Blake2bDigest(null, -1, null, null); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("Invalid digest length (required: 1 - 64)", e.Message); + } + + try + { + new Blake2bDigest(null, 65, null, null); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("Invalid digest length (required: 1 - 64)", e.Message); + } + } + + private void DoTestNullKeyVsUnkeyed() + { + byte[] abc = Strings.ToByteArray("abc"); + + for (int i = 1; i != 64; i++) + { + Blake2bDigest dig1 = new Blake2bDigest(i * 8); + Blake2bDigest dig2 = new Blake2bDigest(null, i, null, null); + + byte[] out1 = new byte[i]; + byte[] out2 = new byte[i]; + + dig1.BlockUpdate(abc, 0, abc.Length); + dig2.BlockUpdate(abc, 0, abc.Length); + + dig1.DoFinal(out1, 0); + dig2.DoFinal(out2, 0); + + IsTrue(Arrays.AreEqual(out1, out2)); + } + } + + private void ResetTest() + { + // Generate a non-zero key + byte[] key = new byte[32]; + for (byte i = 0; i < key.Length; i++) + { + key[i] = i; + } + // Generate some non-zero input longer than the key + byte[] input = new byte[key.Length + 1]; + for (byte i = 0; i < input.Length; i++) + { + input[i] = i; + } + // Hash the input + Blake2bDigest digest = new Blake2bDigest(key); digest.BlockUpdate(input, 0, input.Length); - byte[] hash = new byte[digest.GetDigestSize()]; - digest.DoFinal(hash, 0); - // Using a second instance, hash the input without calling doFinal() - Blake2bDigest digest1 = new Blake2bDigest(key); + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + // Using a second instance, hash the input without calling doFinal() + Blake2bDigest digest1 = new Blake2bDigest(key); + digest1.BlockUpdate(input, 0, input.Length); + // Reset the second instance and hash the input again + digest1.Reset(); digest1.BlockUpdate(input, 0, input.Length); - // Reset the second instance and hash the input again - digest1.Reset(); - digest1.BlockUpdate(input, 0, input.Length); - byte[] hash1 = new byte[digest.GetDigestSize()]; - digest1.DoFinal(hash1, 0); - // The hashes should be identical - if (!Arrays.AreEqual(hash, hash1)) - { - Fail("state was not reset"); - } - } + byte[] hash1 = new byte[digest.GetDigestSize()]; + digest1.DoFinal(hash1, 0); + // The hashes should be identical + if (!Arrays.AreEqual(hash, hash1)) + { + Fail("state was not reset"); + } + } public static void Main(string[] args) { diff --git a/crypto/test/src/crypto/test/Blake2sDigestTest.cs b/crypto/test/src/crypto/test/Blake2sDigestTest.cs index 2365071dd..cb075807c 100644 --- a/crypto/test/src/crypto/test/Blake2sDigestTest.cs +++ b/crypto/test/src/crypto/test/Blake2sDigestTest.cs @@ -15,34 +15,33 @@ namespace Org.BouncyCastle.Crypto.Tests : SimpleTest { // Vectors from BLAKE2 web site: https://blake2.net/blake2s-test.txt - private static readonly string[][] keyedTestVectors = { - // input/message, key, hash - new string[]{ + private static readonly string[,] keyedTestVectors = { // input/message, key, hash + { "", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49", }, - new string[]{ + { "00", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "40d15fee7c328830166ac3f918650f807e7e01e177258cdc0a39b11f598066f1", }, - new string[]{ + { "0001", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "6bb71300644cd3991b26ccd4d274acd1adeab8b1d7914546c1198bbe9fc9d803", }, - new string[]{ + { "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "172ffc67153d12e0ca76a8b6cd5d4731885b39ce0cac93a8972a18006c8b8baf", }, - new string[]{ + { "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "4f8ce1e51d2fe7f24043a904d898ebfc91975418753413aa099b795ecb35cedb", }, - new string[]{ + { "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "3fb735061abc519dfe979e54c1ee5bfad0a9d858b3315bad34bde999efd724dd", @@ -56,38 +55,32 @@ namespace Org.BouncyCastle.Crypto.Tests public void DoTestDigestWithKeyedTestVectors() { - Blake2sDigest digest = new Blake2sDigest(Hex.Decode( - keyedTestVectors[0][1])); - for (int i = 0; i != keyedTestVectors.Length; i++) + Blake2sDigest digest = new Blake2sDigest(Hex.Decode(keyedTestVectors[0, 1])); + for (int i = 0; i != keyedTestVectors.GetLength(0); i++) { - String[] keyedTestVector = keyedTestVectors[i]; - byte[] input = Hex.Decode(keyedTestVector[0]); + byte[] input = Hex.Decode(keyedTestVectors[i, 0]); digest.Reset(); digest.BlockUpdate(input, 0, input.Length); byte[] hash = new byte[32]; digest.DoFinal(hash, 0); - if (!AreEqual(Hex.Decode(keyedTestVector[2]), hash)) + if (!AreEqual(Hex.Decode(keyedTestVectors[i, 2]), hash)) { - Fail("BLAKE2s mismatch on test vector ", - keyedTestVector[2], - Hex.ToHexString(hash)); + Fail("BLAKE2s mismatch on test vector ", keyedTestVectors[i, 2], Hex.ToHexString(hash)); } } } public void DoTestDigestWithKeyedTestVectorsAndRandomUpdate() { - Blake2sDigest digest = new Blake2sDigest(Hex.Decode( - keyedTestVectors[0][1])); + Blake2sDigest digest = new Blake2sDigest(Hex.Decode(keyedTestVectors[0, 1])); Random random = new Random(); for (int i = 0; i < 100; i++) { - for (int j = 0; j != keyedTestVectors.Length; j++) + for (int j = 0; j != keyedTestVectors.GetLength(0); j++) { - String[] keyedTestVector = keyedTestVectors[j]; - byte[] input = Hex.Decode(keyedTestVector[0]); + byte[] input = Hex.Decode(keyedTestVectors[j, 0]); if (input.Length < 3) { continue; @@ -108,16 +101,89 @@ namespace Org.BouncyCastle.Crypto.Tests byte[] hash = new byte[32]; digest.DoFinal(hash, 0); - if (!AreEqual(Hex.Decode(keyedTestVector[2]), hash)) + if (!AreEqual(Hex.Decode(keyedTestVectors[j, 2]), hash)) { - Fail("BLAKE2s mismatch on test vector ", - keyedTestVector[2], - Hex.ToHexString(hash)); + Fail("BLAKE2s mismatch on test vector ", keyedTestVectors[j, 2], Hex.ToHexString(hash)); } } } } + private void DoTestLengthConstruction() + { + try + { + new Blake2sDigest(-1); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256", e.Message); + } + + try + { + new Blake2sDigest(9); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256", e.Message); + } + + try + { + new Blake2sDigest(512); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256", e.Message); + } + + try + { + new Blake2sDigest(null, -1, null, null); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("Invalid digest length (required: 1 - 32)", e.Message); + } + + try + { + new Blake2sDigest(null, 33, null, null); + Fail("no exception"); + } + catch (ArgumentException e) + { + IsEquals("Invalid digest length (required: 1 - 32)", e.Message); + } + } + + private void DoTestNullKeyVsUnkeyed() + { + byte[] abc = Strings.ToByteArray("abc"); + + for (int i = 1; i != 32; i++) + { + Blake2sDigest dig1 = new Blake2sDigest(i * 8); + Blake2sDigest dig2 = new Blake2sDigest(null, i, null, null); + + byte[] out1 = new byte[i]; + byte[] out2 = new byte[i]; + + dig1.BlockUpdate(abc, 0, abc.Length); + dig2.BlockUpdate(abc, 0, abc.Length); + + dig1.DoFinal(out1, 0); + dig2.DoFinal(out2, 0); + + IsTrue(Arrays.AreEqual(out1, out2)); + } + } + public void DoTestReset() { // Generate a non-zero key @@ -225,6 +291,8 @@ namespace Org.BouncyCastle.Crypto.Tests DoTestDigestWithKeyedTestVectorsAndRandomUpdate(); DoTestReset(); RunSelfTest(); + DoTestNullKeyVsUnkeyed(); + DoTestLengthConstruction(); } public static void Main(string[] args) diff --git a/crypto/test/src/util/test/SimpleTest.cs b/crypto/test/src/util/test/SimpleTest.cs index 154da27f4..dd27205c8 100644 --- a/crypto/test/src/util/test/SimpleTest.cs +++ b/crypto/test/src/util/test/SimpleTest.cs @@ -27,6 +27,21 @@ namespace Org.BouncyCastle.Utilities.Test throw new TestFailedException(SimpleTestResult.Failed(this, message)); } + internal void Fail( + string message, + Exception throwable) + { + throw new TestFailedException(SimpleTestResult.Failed(this, message, throwable)); + } + + internal void Fail( + string message, + object expected, + object found) + { + throw new TestFailedException(SimpleTestResult.Failed(this, message, expected, found)); + } + internal void IsTrue(bool value) { if (!value) @@ -39,22 +54,44 @@ namespace Org.BouncyCastle.Utilities.Test throw new TestFailedException(SimpleTestResult.Failed(this, message)); } - internal void Fail( - string message, - Exception throwable) + internal void IsEquals(object a, object b) { - throw new TestFailedException(SimpleTestResult.Failed(this, message, throwable)); + if (!a.Equals(b)) + throw new TestFailedException(SimpleTestResult.Failed(this, "no message")); } - internal void Fail( - string message, - object expected, - object found) + internal void IsEquals(int a, int b) { - throw new TestFailedException(SimpleTestResult.Failed(this, message, expected, found)); + if (a != b) + throw new TestFailedException(SimpleTestResult.Failed(this, "no message")); + } + + internal void IsEquals(string message, bool a, bool b) + { + if (a != b) + throw new TestFailedException(SimpleTestResult.Failed(this, message)); + } + + internal void IsEquals(string message, long a, long b) + { + if (a != b) + throw new TestFailedException(SimpleTestResult.Failed(this, message)); + } + + internal void IsEquals(string message, object a, object b) + { + if (a == null && b == null) + return; + + if (a == null) + throw new TestFailedException(SimpleTestResult.Failed(this, message)); + if (b == null) + throw new TestFailedException(SimpleTestResult.Failed(this, message)); + if (!a.Equals(b)) + throw new TestFailedException(SimpleTestResult.Failed(this, message)); } - internal bool AreEqual( + internal bool AreEqual( byte[] a, byte[] b) { -- cgit 1.4.1 From e5ce7d7fa8a16984adfff485196d3a7ce212f6a6 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 18 Sep 2018 14:02:55 +0700 Subject: Link to "Prime and Prejudice" paper --- crypto/Readme.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/Readme.html b/crypto/Readme.html index 59c333290..a89e7e535 100644 --- a/crypto/Readme.html +++ b/crypto/Readme.html @@ -315,7 +315,8 @@ We state, where EC MQV has not otherwise been disabled or removed: In this release, the TLS library has moved to a whitelisting approach for client-side validation of server-presented Diffie-Hellman (DH) parameters. In the default configuration, if a ciphersuite using ephemeral DH is selected by the server, the client will abort the handshake if the proposed DH group is not one of those specified in RFC 3526 or RFC 7919, - or if the DH prime is < 2048 bits. The client therefore no longer offers DH ciphersuites by default. + or if the DH prime is < 2048 bits. The client therefore no longer offers DH ciphersuites by default. See also the paper + "Prime and Prejudice: Primality Testing Under Adversarial Conditions".
    4. -- cgit 1.4.1 From 3dd782fe79791e7e2cbafcf40caa986d09ee1c8e Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 18 Sep 2018 14:04:00 +0700 Subject: Add additional constructor to EncryptionScheme --- crypto/src/asn1/pkcs/EncryptionScheme.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crypto/src/asn1/pkcs/EncryptionScheme.cs b/crypto/src/asn1/pkcs/EncryptionScheme.cs index 7b90ece53..34d26e172 100644 --- a/crypto/src/asn1/pkcs/EncryptionScheme.cs +++ b/crypto/src/asn1/pkcs/EncryptionScheme.cs @@ -8,7 +8,13 @@ namespace Org.BouncyCastle.Asn1.Pkcs public class EncryptionScheme : AlgorithmIdentifier { - public EncryptionScheme( + public EncryptionScheme( + DerObjectIdentifier objectID) + : base(objectID) + { + } + + public EncryptionScheme( DerObjectIdentifier objectID, Asn1Encodable parameters) : base(objectID, parameters) -- cgit 1.4.1 From 46eed4965f84712cfee05de4a823075c488315e8 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 18 Sep 2018 14:15:21 +0700 Subject: Code cleanup --- crypto/src/asn1/DerApplicationSpecific.cs | 33 +++++++++++-------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs index 52467fabe..a2d57bf9d 100644 --- a/crypto/src/asn1/DerApplicationSpecific.cs +++ b/crypto/src/asn1/DerApplicationSpecific.cs @@ -199,38 +199,27 @@ namespace Org.BouncyCastle.Asn1 { int tagNo = input[0] & 0x1f; int index = 1; - // - // with tagged object tag number is bottom 5 bits, or stored at the start of the content - // + + // with tagged object tag number is bottom 5 bits, or stored at the start of the content if (tagNo == 0x1f) { - tagNo = 0; - - int b = input[index++] & 0xff; + int b = input[index++]; - // X.690-0207 8.1.2.4.2 + // X.690-0207 8.1.2.4.2 // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." if ((b & 0x7f) == 0) // Note: -1 will pass - { - throw new InvalidOperationException("corrupted stream - invalid high tag number found"); - } + throw new IOException("corrupted stream - invalid high tag number found"); - while ((b >= 0) && ((b & 0x80) != 0)) + while ((b & 0x80) != 0) { - tagNo |= (b & 0x7f); - tagNo <<= 7; - b = input[index++] & 0xff; + b = input[index++]; } - - tagNo |= (b & 0x7f); } - byte[] tmp = new byte[input.Length - index + 1]; - - Array.Copy(input, index, tmp, 1, tmp.Length - 1); - - tmp[0] = (byte)newTag; - + int remaining = input.Length - index; + byte[] tmp = new byte[1 + remaining]; + tmp[0] = (byte)newTag; + Array.Copy(input, index, tmp, 1, remaining); return tmp; } } -- cgit 1.4.1 From f7b6049eb3cc4687ca0d0791da80ed42b0b2327c Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 18 Sep 2018 14:21:19 +0700 Subject: Add missing default and improve extensions handling --- crypto/src/asn1/x509/TBSCertificateStructure.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crypto/src/asn1/x509/TBSCertificateStructure.cs b/crypto/src/asn1/x509/TBSCertificateStructure.cs index 9df078539..e69e985f5 100644 --- a/crypto/src/asn1/x509/TBSCertificateStructure.cs +++ b/crypto/src/asn1/x509/TBSCertificateStructure.cs @@ -121,7 +121,7 @@ namespace Org.BouncyCastle.Asn1.X509 while (extras > 0) { - DerTaggedObject extra = (DerTaggedObject) seq[seqStart + 6 + extras]; + DerTaggedObject extra = (DerTaggedObject)seq[seqStart + 6 + extras]; switch (extra.TagNo) { @@ -140,9 +140,13 @@ namespace Org.BouncyCastle.Asn1.X509 if (isV2) throw new ArgumentException("version 2 certificate cannot contain extensions"); - extensions = X509Extensions.GetInstance(extra); + extensions = X509Extensions.GetInstance(Asn1Sequence.GetInstance(extra, true)); break; } + default: + { + throw new ArgumentException("Unknown tag encountered in structure: " + extra.TagNo); + } } extras--; } -- cgit 1.4.1 From 92c99a5571303e4aa46dc730f45ae2e34db769f1 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 18 Sep 2018 21:16:28 +0700 Subject: Code cleanup (from Java build) --- crypto/src/bcpg/ECPublicBCPGKey.cs | 2 +- crypto/src/openpgp/PgpSignatureSubpacketVector.cs | 8 +++++++- crypto/test/src/openpgp/examples/DirectKeySignature.cs | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crypto/src/bcpg/ECPublicBCPGKey.cs b/crypto/src/bcpg/ECPublicBCPGKey.cs index f328f9dc3..c473139e7 100644 --- a/crypto/src/bcpg/ECPublicBCPGKey.cs +++ b/crypto/src/bcpg/ECPublicBCPGKey.cs @@ -26,7 +26,7 @@ namespace Org.BouncyCastle.Bcpg DerObjectIdentifier oid, ECPoint point) { - this.point = new BigInteger(1, point.GetEncoded()); + this.point = new BigInteger(1, point.GetEncoded(false)); this.oid = oid; } diff --git a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs index 156243f4e..1d3d75941 100644 --- a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs +++ b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs @@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return result; } - public NotationData[] GetNotationDataOccurences() + public NotationData[] GetNotationDataOccurrences() { SignatureSubpacket[] notations = GetSubpackets(SignatureSubpacketTag.NotationData); NotationData[] vals = new NotationData[notations.Length]; @@ -87,6 +87,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return vals; } + [Obsolete("Use 'GetNotationDataOccurrences' instead")] + public NotationData[] GetNotationDataOccurences() + { + return GetNotationDataOccurrences(); + } + public long GetIssuerKeyId() { SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IssuerKeyId); diff --git a/crypto/test/src/openpgp/examples/DirectKeySignature.cs b/crypto/test/src/openpgp/examples/DirectKeySignature.cs index a6bf8e755..3926a787b 100644 --- a/crypto/test/src/openpgp/examples/DirectKeySignature.cs +++ b/crypto/test/src/openpgp/examples/DirectKeySignature.cs @@ -38,11 +38,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples Console.WriteLine("Signature date is: " + sig.GetHashedSubPackets().GetSignatureCreationTime()); - NotationData[] data = sig.GetHashedSubPackets().GetNotationDataOccurences(); + NotationData[] data = sig.GetHashedSubPackets().GetNotationDataOccurrences(); for (int i = 0; i < data.Length; i++) { - Console.WriteLine("Found Notaion named '" + data[i].GetNotationName() + Console.WriteLine("Found Notation named '" + data[i].GetNotationName() +"' with content '" + data[i].GetNotationValue() + "'."); } } -- cgit 1.4.1 From aafad562a09077d87e5fc1de630b26a937618086 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 20 Sep 2018 23:47:43 +0700 Subject: Refactoring --- crypto/src/math/ec/multiplier/WNafUtilities.cs | 12 ++++++------ crypto/src/util/Arrays.cs | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crypto/src/math/ec/multiplier/WNafUtilities.cs b/crypto/src/math/ec/multiplier/WNafUtilities.cs index e893abd49..24646deb2 100644 --- a/crypto/src/math/ec/multiplier/WNafUtilities.cs +++ b/crypto/src/math/ec/multiplier/WNafUtilities.cs @@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Math.EC.Multiplier { public abstract class WNafUtilities @@ -8,8 +10,6 @@ namespace Org.BouncyCastle.Math.EC.Multiplier private static readonly int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 }; - private static readonly byte[] EMPTY_BYTES = new byte[0]; - private static readonly int[] EMPTY_INTS = new int[0]; private static readonly ECPoint[] EMPTY_POINTS = new ECPoint[0]; public static int[] GenerateCompactNaf(BigInteger k) @@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier if ((k.BitLength >> 16) != 0) throw new ArgumentException("must have bitlength < 2^16", "k"); if (k.SignValue == 0) - return EMPTY_INTS; + return Arrays.EmptyInts; BigInteger _3k = k.ShiftLeft(1).Add(k); @@ -63,7 +63,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier if ((k.BitLength >> 16) != 0) throw new ArgumentException("must have bitlength < 2^16", "k"); if (k.SignValue == 0) - return EMPTY_INTS; + return Arrays.EmptyInts; int[] wnaf = new int[k.BitLength / width + 1]; @@ -176,7 +176,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier public static byte[] GenerateNaf(BigInteger k) { if (k.SignValue == 0) - return EMPTY_BYTES; + return Arrays.EmptyBytes; BigInteger _3k = k.ShiftLeft(1).Add(k); @@ -221,7 +221,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier if (width < 2 || width > 8) throw new ArgumentException("must be in the range [2, 8]", "width"); if (k.SignValue == 0) - return EMPTY_BYTES; + return Arrays.EmptyBytes; byte[] wnaf = new byte[k.BitLength + 1]; diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index df9b4e7ee..3df908240 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -8,6 +8,9 @@ namespace Org.BouncyCastle.Utilities /// General array utilities. public abstract class Arrays { + public static readonly byte[] EmptyBytes = new byte[0]; + public static readonly int[] EmptyInts = new int[0]; + public static bool AreEqual( bool[] a, bool[] b) -- cgit 1.4.1 From fbae27fb1edcea6b0924dba977a6d94f0a3655db Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 21 Sep 2018 15:17:56 +0700 Subject: Higher-level API support for Ed25519/Ed448/X25519/X448 --- crypto/BouncyCastle.Android.csproj | 25 ++++ crypto/BouncyCastle.csproj | 25 ++++ crypto/BouncyCastle.iOS.csproj | 25 ++++ crypto/crypto.csproj | 150 +++++++++++++++++++++ crypto/src/asn1/edec/EdECObjectIdentifiers.cs | 17 +++ crypto/src/crypto/IRawAgreement.cs | 13 ++ crypto/src/crypto/agreement/X25519Agreement.cs | 27 ++++ crypto/src/crypto/agreement/X448Agreement.cs | 27 ++++ .../crypto/generators/Ed25519KeyPairGenerator.cs | 25 ++++ .../src/crypto/generators/Ed448KeyPairGenerator.cs | 25 ++++ .../crypto/generators/X25519KeyPairGenerator.cs | 25 ++++ .../src/crypto/generators/X448KeyPairGenerator.cs | 25 ++++ .../parameters/Ed25519KeyGenerationParameters.cs | 15 +++ .../parameters/Ed25519PrivateKeyParameters.cs | 98 ++++++++++++++ .../parameters/Ed25519PublicKeyParameters.cs | 40 ++++++ .../parameters/Ed448KeyGenerationParameters.cs | 15 +++ .../crypto/parameters/Ed448PrivateKeyParameters.cs | 90 +++++++++++++ .../crypto/parameters/Ed448PublicKeyParameters.cs | 40 ++++++ .../parameters/X25519KeyGenerationParameters.cs | 15 +++ .../parameters/X25519PrivateKeyParameters.cs | 62 +++++++++ .../crypto/parameters/X25519PublicKeyParameters.cs | 40 ++++++ .../parameters/X448KeyGenerationParameters.cs | 15 +++ .../crypto/parameters/X448PrivateKeyParameters.cs | 62 +++++++++ .../crypto/parameters/X448PublicKeyParameters.cs | 40 ++++++ crypto/src/crypto/signers/Ed25519Signer.cs | 128 ++++++++++++++++++ crypto/src/crypto/signers/Ed25519ctxSigner.cs | 130 ++++++++++++++++++ crypto/src/crypto/signers/Ed25519phSigner.cs | 89 ++++++++++++ crypto/src/crypto/signers/Ed448Signer.cs | 130 ++++++++++++++++++ crypto/src/crypto/signers/Ed448phSigner.cs | 89 ++++++++++++ crypto/src/math/ec/rfc8032/Ed25519.cs | 7 + crypto/src/math/ec/rfc8032/Ed448.cs | 6 + crypto/src/pkcs/PrivateKeyInfoFactory.cs | 116 +++++++++++----- crypto/src/security/AgreementUtilities.cs | 55 +++++--- crypto/src/security/GeneratorUtilities.cs | 34 ++++- crypto/src/security/PrivateKeyFactory.cs | 28 +++- crypto/src/security/PublicKeyFactory.cs | 32 ++++- crypto/src/security/SignerUtilities.cs | 36 ++++- crypto/src/x509/SubjectPublicKeyInfoFactory.cs | 67 ++++++--- crypto/test/UnitTests.csproj | 5 + crypto/test/src/asn1/test/PrivateKeyInfoTest.cs | 60 +++++++++ crypto/test/src/asn1/test/RegressionTest.cs | 1 + crypto/test/src/crypto/test/Ed25519Test.cs | 111 +++++++++++++++ crypto/test/src/crypto/test/Ed448Test.cs | 107 +++++++++++++++ crypto/test/src/crypto/test/RegressionTest.cs | 4 + crypto/test/src/crypto/test/X25519Test.cs | 69 ++++++++++ crypto/test/src/crypto/test/X448Test.cs | 69 ++++++++++ .../test/src/math/ec/rfc8032/test/Ed25519Test.cs | 10 +- crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs | 8 +- crypto/test/src/security/test/TestSignerUtil.cs | 18 +++ 49 files changed, 2259 insertions(+), 91 deletions(-) create mode 100644 crypto/src/asn1/edec/EdECObjectIdentifiers.cs create mode 100644 crypto/src/crypto/IRawAgreement.cs create mode 100644 crypto/src/crypto/agreement/X25519Agreement.cs create mode 100644 crypto/src/crypto/agreement/X448Agreement.cs create mode 100644 crypto/src/crypto/generators/Ed25519KeyPairGenerator.cs create mode 100644 crypto/src/crypto/generators/Ed448KeyPairGenerator.cs create mode 100644 crypto/src/crypto/generators/X25519KeyPairGenerator.cs create mode 100644 crypto/src/crypto/generators/X448KeyPairGenerator.cs create mode 100644 crypto/src/crypto/parameters/Ed25519KeyGenerationParameters.cs create mode 100644 crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs create mode 100644 crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs create mode 100644 crypto/src/crypto/parameters/Ed448KeyGenerationParameters.cs create mode 100644 crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs create mode 100644 crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs create mode 100644 crypto/src/crypto/parameters/X25519KeyGenerationParameters.cs create mode 100644 crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs create mode 100644 crypto/src/crypto/parameters/X25519PublicKeyParameters.cs create mode 100644 crypto/src/crypto/parameters/X448KeyGenerationParameters.cs create mode 100644 crypto/src/crypto/parameters/X448PrivateKeyParameters.cs create mode 100644 crypto/src/crypto/parameters/X448PublicKeyParameters.cs create mode 100644 crypto/src/crypto/signers/Ed25519Signer.cs create mode 100644 crypto/src/crypto/signers/Ed25519ctxSigner.cs create mode 100644 crypto/src/crypto/signers/Ed25519phSigner.cs create mode 100644 crypto/src/crypto/signers/Ed448Signer.cs create mode 100644 crypto/src/crypto/signers/Ed448phSigner.cs create mode 100644 crypto/test/src/asn1/test/PrivateKeyInfoTest.cs create mode 100644 crypto/test/src/crypto/test/Ed25519Test.cs create mode 100644 crypto/test/src/crypto/test/Ed448Test.cs create mode 100644 crypto/test/src/crypto/test/X25519Test.cs create mode 100644 crypto/test/src/crypto/test/X448Test.cs diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj index 3c34c5e1b..c5eb8e6f5 100644 --- a/crypto/BouncyCastle.Android.csproj +++ b/crypto/BouncyCastle.Android.csproj @@ -258,6 +258,7 @@ + @@ -659,6 +660,7 @@ + @@ -699,6 +701,8 @@ + + @@ -815,6 +819,8 @@ + + @@ -831,6 +837,8 @@ + + @@ -901,6 +909,12 @@ + + + + + + @@ -939,6 +953,12 @@ + + + + + + @@ -950,6 +970,11 @@ + + + + + diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj index 95d42bc8b..76da30095 100644 --- a/crypto/BouncyCastle.csproj +++ b/crypto/BouncyCastle.csproj @@ -252,6 +252,7 @@ + @@ -653,6 +654,7 @@ + @@ -693,6 +695,8 @@ + + @@ -809,6 +813,8 @@ + + @@ -825,6 +831,8 @@ + + @@ -895,6 +903,12 @@ + + + + + + @@ -933,6 +947,12 @@ + + + + + + @@ -944,6 +964,11 @@ + + + + + diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj index f9cebdc86..52dae0f4f 100644 --- a/crypto/BouncyCastle.iOS.csproj +++ b/crypto/BouncyCastle.iOS.csproj @@ -253,6 +253,7 @@ + @@ -654,6 +655,7 @@ + @@ -694,6 +696,8 @@ + + @@ -810,6 +814,8 @@ + + @@ -826,6 +832,8 @@ + + @@ -896,6 +904,12 @@ + + + + + + @@ -934,6 +948,12 @@ + + + + + + @@ -945,6 +965,11 @@ + + + + + diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index b6dfb3963..2ffb66d8c 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -1148,6 +1148,11 @@ SubType = "Code" BuildAction = "Compile" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -361,6 +371,30 @@ namespace Org.BouncyCastle.Security if (mechanism == null) mechanism = algorithm; + if (Platform.StartsWith(mechanism, "Ed")) + { + if (mechanism.Equals("Ed25519")) + { + return new Ed25519Signer(); + } + if (mechanism.Equals("Ed25519ctx")) + { + return new Ed25519ctxSigner(Arrays.EmptyBytes); + } + if (mechanism.Equals("Ed25519ph")) + { + return new Ed25519phSigner(Arrays.EmptyBytes); + } + if (mechanism.Equals("Ed448")) + { + return new Ed448Signer(Arrays.EmptyBytes); + } + if (mechanism.Equals("Ed448ph")) + { + return new Ed448phSigner(Arrays.EmptyBytes); + } + } + if (mechanism.Equals("RSA")) { return (new RsaDigestSigner(new NullDigest(), (AlgorithmIdentifier)null)); diff --git a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs index 7614321d4..fca5da3f5 100644 --- a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs +++ b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs @@ -3,6 +3,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.X509; @@ -26,20 +27,20 @@ namespace Org.BouncyCastle.X509 /// /// Create a Subject Public Key Info object for a given public key. /// - /// One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters + /// One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters /// A subject public key info object. /// Throw exception if object provided is not one of the above. public static SubjectPublicKeyInfo CreateSubjectPublicKeyInfo( - AsymmetricKeyParameter key) + AsymmetricKeyParameter publicKey) { - if (key == null) - throw new ArgumentNullException("key"); - if (key.IsPrivate) - throw new ArgumentException("Private key passed - public key expected.", "key"); + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + if (publicKey.IsPrivate) + throw new ArgumentException("Private key passed - public key expected.", "publicKey"); - if (key is ElGamalPublicKeyParameters) + if (publicKey is ElGamalPublicKeyParameters) { - ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)key; + ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)publicKey; ElGamalParameters kp = _key.Parameters; SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( @@ -51,9 +52,9 @@ namespace Org.BouncyCastle.X509 return info; } - if (key is DsaPublicKeyParameters) + if (publicKey is DsaPublicKeyParameters) { - DsaPublicKeyParameters _key = (DsaPublicKeyParameters) key; + DsaPublicKeyParameters _key = (DsaPublicKeyParameters) publicKey; DsaParameters kp = _key.Parameters; Asn1Encodable ae = kp == null ? null @@ -64,9 +65,9 @@ namespace Org.BouncyCastle.X509 new DerInteger(_key.Y)); } - if (key is DHPublicKeyParameters) + if (publicKey is DHPublicKeyParameters) { - DHPublicKeyParameters _key = (DHPublicKeyParameters) key; + DHPublicKeyParameters _key = (DHPublicKeyParameters) publicKey; DHParameters kp = _key.Parameters; SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( @@ -78,9 +79,9 @@ namespace Org.BouncyCastle.X509 return info; } // End of DH - if (key is RsaKeyParameters) + if (publicKey is RsaKeyParameters) { - RsaKeyParameters _key = (RsaKeyParameters) key; + RsaKeyParameters _key = (RsaKeyParameters) publicKey; SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( new AlgorithmIdentifier(PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance), @@ -89,9 +90,9 @@ namespace Org.BouncyCastle.X509 return info; } // End of RSA. - if (key is ECPublicKeyParameters) + if (publicKey is ECPublicKeyParameters) { - ECPublicKeyParameters _key = (ECPublicKeyParameters) key; + ECPublicKeyParameters _key = (ECPublicKeyParameters) publicKey; if (_key.AlgorithmName == "ECGOST3410") { @@ -139,9 +140,9 @@ namespace Org.BouncyCastle.X509 } } // End of EC - if (key is Gost3410PublicKeyParameters) + if (publicKey is Gost3410PublicKeyParameters) { - Gost3410PublicKeyParameters _key = (Gost3410PublicKeyParameters) key; + Gost3410PublicKeyParameters _key = (Gost3410PublicKeyParameters) publicKey; if (_key.PublicKeyParamSet == null) throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); @@ -164,7 +165,35 @@ namespace Org.BouncyCastle.X509 return new SubjectPublicKeyInfo(algID, new DerOctetString(keyBytes)); } - throw new ArgumentException("Class provided no convertible: " + Platform.GetTypeName(key)); + if (publicKey is X448PublicKeyParameters) + { + X448PublicKeyParameters key = (X448PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), key.GetEncoded()); + } + + if (publicKey is X25519PublicKeyParameters) + { + X25519PublicKeyParameters key = (X25519PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), key.GetEncoded()); + } + + if (publicKey is Ed448PublicKeyParameters) + { + Ed448PublicKeyParameters key = (Ed448PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448), key.GetEncoded()); + } + + if (publicKey is Ed25519PublicKeyParameters) + { + Ed25519PublicKeyParameters key = (Ed25519PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), key.GetEncoded()); + } + + throw new ArgumentException("Class provided no convertible: " + Platform.GetTypeName(publicKey)); } private static void ExtractBytes( diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj index 5f0e7af57..bb046c2b6 100644 --- a/crypto/test/UnitTests.csproj +++ b/crypto/test/UnitTests.csproj @@ -101,6 +101,7 @@ + @@ -186,6 +187,8 @@ + + @@ -284,6 +287,8 @@ + + diff --git a/crypto/test/src/asn1/test/PrivateKeyInfoTest.cs b/crypto/test/src/asn1/test/PrivateKeyInfoTest.cs new file mode 100644 index 000000000..eb17a54c3 --- /dev/null +++ b/crypto/test/src/asn1/test/PrivateKeyInfoTest.cs @@ -0,0 +1,60 @@ +using System; + +using NUnit.Framework; + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.Test; + +namespace Org.BouncyCastle.Asn1.Tests +{ + [TestFixture] + public class PrivateKeyInfoTest + : SimpleTest + { + private static readonly byte[] priv = Base64.Decode( + "MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC"); + + private static readonly byte[] privWithPub = Base64.Decode( + "MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC" + + "oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB" + + "Z9w7lshQhqowtrbLDFw4rXAxZuE="); + + public override string Name + { + get { return "PrivateKeyInfoTest"; } + } + + public override void PerformTest() + { + PrivateKeyInfo privInfo1 = PrivateKeyInfo.GetInstance(priv); + + IsTrue(!privInfo1.HasPublicKey); + + PrivateKeyInfo privInfo2 = new PrivateKeyInfo(privInfo1.PrivateKeyAlgorithm, privInfo1.ParsePrivateKey()); + + IsTrue("enc 1 failed", AreEqual(priv, privInfo2.GetEncoded())); + + privInfo1 = PrivateKeyInfo.GetInstance(privWithPub); + + IsTrue(privInfo1.HasPublicKey); + + privInfo2 = new PrivateKeyInfo(privInfo1.PrivateKeyAlgorithm, privInfo1.ParsePrivateKey(), privInfo1.Attributes, privInfo1.PublicKeyData.GetOctets()); + + IsTrue("enc 2 failed", AreEqual(privWithPub, privInfo2.GetEncoded())); + } + + public static void Main(string[] args) + { + RunTest(new PrivateKeyInfoTest()); + } + + [Test] + public void TestFunction() + { + string resultText = Perform().ToString(); + + Assert.AreEqual(Name + ": Okay", resultText); + } + } +} diff --git a/crypto/test/src/asn1/test/RegressionTest.cs b/crypto/test/src/asn1/test/RegressionTest.cs index 873ab4396..eeca9ccd0 100644 --- a/crypto/test/src/asn1/test/RegressionTest.cs +++ b/crypto/test/src/asn1/test/RegressionTest.cs @@ -51,6 +51,7 @@ namespace Org.BouncyCastle.Asn1.Tests new Pkcs10Test(), new Pkcs12Test(), new PkiFailureInfoTest(), + new PrivateKeyInfoTest(), new ProcurationSyntaxUnitTest(), new ProfessionInfoUnitTest(), new QCStatementUnitTest(), diff --git a/crypto/test/src/crypto/test/Ed25519Test.cs b/crypto/test/src/crypto/test/Ed25519Test.cs new file mode 100644 index 000000000..82e36d991 --- /dev/null +++ b/crypto/test/src/crypto/test/Ed25519Test.cs @@ -0,0 +1,111 @@ +using System; + +using NUnit.Framework; + +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Test; + +namespace Org.BouncyCastle.Crypto.Tests +{ + [TestFixture] + public class Ed25519Test + : SimpleTest + { + private static readonly SecureRandom Random = new SecureRandom(); + + public override string Name + { + get { return "Ed25519"; } + } + + public static void Main(string[] args) + { + RunTest(new Ed25519Test()); + } + + [Test] + public void TestFunction() + { + string resultText = Perform().ToString(); + + Assert.AreEqual(Name + ": Okay", resultText); + } + + public override void PerformTest() + { + for (int i = 0; i < 10; ++i) + { + DoTestConsistency(Ed25519.Algorithm.Ed25519, null); + + byte[] context = RandomContext(Random.NextInt() & 255); + DoTestConsistency(Ed25519.Algorithm.Ed25519ctx, context); + DoTestConsistency(Ed25519.Algorithm.Ed25519ph, context); + } + } + + private ISigner CreateSigner(Ed25519.Algorithm algorithm, byte[] context) + { + switch (algorithm) + { + case Ed25519.Algorithm.Ed25519: + return new Ed25519Signer(); + case Ed25519.Algorithm.Ed25519ctx: + return new Ed25519ctxSigner(context); + case Ed25519.Algorithm.Ed25519ph: + return new Ed25519phSigner(context); + default: + throw new ArgumentException("algorithm"); + } + } + + private byte[] RandomContext(int length) + { + byte[] context = new byte[length]; + Random.NextBytes(context); + return context; + } + + private void DoTestConsistency(Ed25519.Algorithm algorithm, byte[] context) + { + Ed25519KeyPairGenerator kpg = new Ed25519KeyPairGenerator(); + kpg.Init(new Ed25519KeyGenerationParameters(Random)); + + AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair(); + Ed25519PrivateKeyParameters privateKey = (Ed25519PrivateKeyParameters)kp.Private; + Ed25519PublicKeyParameters publicKey = (Ed25519PublicKeyParameters)kp.Public; + + byte[] msg = new byte[Random.NextInt() & 255]; + Random.NextBytes(msg); + + ISigner signer = CreateSigner(algorithm, context); + signer.Init(true, privateKey); + signer.BlockUpdate(msg, 0, msg.Length); + byte[] signature = signer.GenerateSignature(); + + ISigner verifier = CreateSigner(algorithm, context); + verifier.Init(false, publicKey); + verifier.BlockUpdate(msg, 0, msg.Length); + bool shouldVerify = verifier.VerifySignature(signature); + + if (!shouldVerify) + { + Fail("Ed25519(" + algorithm + ") signature failed to verify"); + } + + signature[Random.Next() % signature.Length] ^= (byte)(1 << (Random.NextInt() & 7)); + + verifier.Init(false, publicKey); + verifier.BlockUpdate(msg, 0, msg.Length); + bool shouldNotVerify = verifier.VerifySignature(signature); + + if (shouldNotVerify) + { + Fail("Ed25519(" + algorithm + ") bad signature incorrectly verified"); + } + } + } +} diff --git a/crypto/test/src/crypto/test/Ed448Test.cs b/crypto/test/src/crypto/test/Ed448Test.cs new file mode 100644 index 000000000..b035f554e --- /dev/null +++ b/crypto/test/src/crypto/test/Ed448Test.cs @@ -0,0 +1,107 @@ +using System; + +using NUnit.Framework; + +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Test; + +namespace Org.BouncyCastle.Crypto.Tests +{ + [TestFixture] + public class Ed448Test + : SimpleTest + { + private static readonly SecureRandom Random = new SecureRandom(); + + public override string Name + { + get { return "Ed448"; } + } + + public static void Main(string[] args) + { + RunTest(new Ed448Test()); + } + + [Test] + public void TestFunction() + { + string resultText = Perform().ToString(); + + Assert.AreEqual(Name + ": Okay", resultText); + } + + public override void PerformTest() + { + for (int i = 0; i < 10; ++i) + { + byte[] context = RandomContext(Random.NextInt() & 255); + DoTestConsistency(Ed448.Algorithm.Ed448, context); + DoTestConsistency(Ed448.Algorithm.Ed448ph, context); + } + } + + private ISigner CreateSigner(Ed448.Algorithm algorithm, byte[] context) + { + switch (algorithm) + { + case Ed448.Algorithm.Ed448: + return new Ed448Signer(context); + case Ed448.Algorithm.Ed448ph: + return new Ed448phSigner(context); + default: + throw new ArgumentException("algorithm"); + } + } + + private byte[] RandomContext(int length) + { + byte[] context = new byte[length]; + Random.NextBytes(context); + return context; + } + + private void DoTestConsistency(Ed448.Algorithm algorithm, byte[] context) + { + Ed448KeyPairGenerator kpg = new Ed448KeyPairGenerator(); + kpg.Init(new Ed448KeyGenerationParameters(Random)); + + AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair(); + Ed448PrivateKeyParameters privateKey = (Ed448PrivateKeyParameters)kp.Private; + Ed448PublicKeyParameters publicKey = (Ed448PublicKeyParameters)kp.Public; + + byte[] msg = new byte[Random.NextInt() & 255]; + Random.NextBytes(msg); + + ISigner signer = CreateSigner(algorithm, context); + signer.Init(true, privateKey); + signer.BlockUpdate(msg, 0, msg.Length); + byte[] signature = signer.GenerateSignature(); + + ISigner verifier = CreateSigner(algorithm, context); + verifier.Init(false, publicKey); + verifier.BlockUpdate(msg, 0, msg.Length); + bool shouldVerify = verifier.VerifySignature(signature); + + if (!shouldVerify) + { + Fail("Ed448(" + algorithm + ") signature failed to verify"); + } + + signature[Random.Next() % signature.Length] ^= (byte)(1 << (Random.NextInt() & 7)); + + verifier.Init(false, publicKey); + verifier.BlockUpdate(msg, 0, msg.Length); + bool shouldNotVerify = verifier.VerifySignature(signature); + + if (shouldNotVerify) + { + Fail("Ed448(" + algorithm + ") bad signature incorrectly verified"); + } + } + } +} diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs index 13fe23ecc..18aa62d97 100644 --- a/crypto/test/src/crypto/test/RegressionTest.cs +++ b/crypto/test/src/crypto/test/RegressionTest.cs @@ -133,6 +133,10 @@ namespace Org.BouncyCastle.Crypto.Tests new SM2EngineTest(), new SM2KeyExchangeTest(), new SM2SignerTest(), + new X25519Test(), + new X448Test(), + new Ed25519Test(), + new Ed448Test(), }; public static void Main(string[] args) diff --git a/crypto/test/src/crypto/test/X25519Test.cs b/crypto/test/src/crypto/test/X25519Test.cs new file mode 100644 index 000000000..29466e0c6 --- /dev/null +++ b/crypto/test/src/crypto/test/X25519Test.cs @@ -0,0 +1,69 @@ +using System; + +using NUnit.Framework; + +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Test; + +namespace Org.BouncyCastle.Crypto.Tests +{ + [TestFixture] + public class X25519Test + : SimpleTest + { + private static readonly SecureRandom Random = new SecureRandom(); + + public override string Name + { + get { return "X25519"; } + } + + public static void Main(string[] args) + { + RunTest(new X25519Test()); + } + + [Test] + public void TestFunction() + { + string resultText = Perform().ToString(); + + Assert.AreEqual(Name + ": Okay", resultText); + } + + public override void PerformTest() + { + for (int i = 0; i < 10; ++i) + { + DoTestAgreement(); + } + } + + private void DoTestAgreement() + { + IAsymmetricCipherKeyPairGenerator kpGen = new X25519KeyPairGenerator(); + kpGen.Init(new X25519KeyGenerationParameters(Random)); + + AsymmetricCipherKeyPair kpA = kpGen.GenerateKeyPair(); + AsymmetricCipherKeyPair kpB = kpGen.GenerateKeyPair(); + + X25519Agreement agreeA = new X25519Agreement(); + agreeA.Init(kpA.Private); + byte[] secretA = new byte[agreeA.AgreementSize]; + agreeA.CalculateAgreement(kpB.Public, secretA, 0); + + X25519Agreement agreeB = new X25519Agreement(); + agreeB.Init(kpB.Private); + byte[] secretB = new byte[agreeB.AgreementSize]; + agreeB.CalculateAgreement(kpA.Public, secretB, 0); + + if (!AreEqual(secretA, secretB)) + { + Fail("X25519 agreement failed"); + } + } + } +} diff --git a/crypto/test/src/crypto/test/X448Test.cs b/crypto/test/src/crypto/test/X448Test.cs new file mode 100644 index 000000000..5d4b14b63 --- /dev/null +++ b/crypto/test/src/crypto/test/X448Test.cs @@ -0,0 +1,69 @@ +using System; + +using NUnit.Framework; + +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Test; + +namespace Org.BouncyCastle.Crypto.Tests +{ + [TestFixture] + public class X448Test + : SimpleTest + { + private static readonly SecureRandom Random = new SecureRandom(); + + public override string Name + { + get { return "X448"; } + } + + public static void Main(string[] args) + { + RunTest(new X448Test()); + } + + [Test] + public void TestFunction() + { + string resultText = Perform().ToString(); + + Assert.AreEqual(Name + ": Okay", resultText); + } + + public override void PerformTest() + { + for (int i = 0; i < 10; ++i) + { + DoTestAgreement(); + } + } + + private void DoTestAgreement() + { + IAsymmetricCipherKeyPairGenerator kpGen = new X448KeyPairGenerator(); + kpGen.Init(new X448KeyGenerationParameters(Random)); + + AsymmetricCipherKeyPair kpA = kpGen.GenerateKeyPair(); + AsymmetricCipherKeyPair kpB = kpGen.GenerateKeyPair(); + + X448Agreement agreeA = new X448Agreement(); + agreeA.Init(kpA.Private); + byte[] secretA = new byte[agreeA.AgreementSize]; + agreeA.CalculateAgreement(kpB.Public, secretA, 0); + + X448Agreement agreeB = new X448Agreement(); + agreeB.Init(kpB.Private); + byte[] secretB = new byte[agreeB.AgreementSize]; + agreeB.CalculateAgreement(kpA.Public, secretB, 0); + + if (!AreEqual(secretA, secretB)) + { + Fail("X448 agreement failed"); + } + } + } +} diff --git a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs index 5a42daeae..8a61609af 100644 --- a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs +++ b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs @@ -36,7 +36,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Random.NextBytes(sk); Ed25519.GeneratePublicKey(sk, 0, pk, 0); - int mLen = Random.Next() & 255; + int mLen = Random.NextInt() & 255; Ed25519.Sign(sk, 0, m, 0, mLen, sig1, 0); Ed25519.Sign(sk, 0, pk, 0, m, 0, mLen, sig2, 0); @@ -59,7 +59,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests { byte[] sk = new byte[Ed25519.SecretKeySize]; byte[] pk = new byte[Ed25519.PublicKeySize]; - byte[] ctx = new byte[Random.Next() & 7]; + byte[] ctx = new byte[Random.NextInt() & 7]; byte[] m = new byte[255]; byte[] sig1 = new byte[Ed25519.SignatureSize]; byte[] sig2 = new byte[Ed25519.SignatureSize]; @@ -72,7 +72,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Random.NextBytes(sk); Ed25519.GeneratePublicKey(sk, 0, pk, 0); - int mLen = Random.Next() & 255; + int mLen = Random.NextInt() & 255; Ed25519.Sign(sk, 0, ctx, m, 0, mLen, sig1, 0); Ed25519.Sign(sk, 0, pk, 0, ctx, m, 0, mLen, sig2, 0); @@ -95,7 +95,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests { byte[] sk = new byte[Ed25519.SecretKeySize]; byte[] pk = new byte[Ed25519.PublicKeySize]; - byte[] ctx = new byte[Random.Next() & 7]; + byte[] ctx = new byte[Random.NextInt() & 7]; byte[] m = new byte[255]; byte[] ph = new byte[Ed25519.PrehashSize]; byte[] sig1 = new byte[Ed25519.SignatureSize]; @@ -109,7 +109,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Random.NextBytes(sk); Ed25519.GeneratePublicKey(sk, 0, pk, 0); - int mLen = Random.Next() & 255; + int mLen = Random.NextInt() & 255; IDigest prehash = Ed25519.CreatePrehash(); prehash.BlockUpdate(m, 0, mLen); diff --git a/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs b/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs index 826f76345..cc8e82de0 100644 --- a/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs +++ b/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs @@ -25,7 +25,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests { byte[] sk = new byte[Ed448.SecretKeySize]; byte[] pk = new byte[Ed448.PublicKeySize]; - byte[] ctx = new byte[Random.Next() & 7]; + byte[] ctx = new byte[Random.NextInt() & 7]; byte[] m = new byte[255]; byte[] sig1 = new byte[Ed448.SignatureSize]; byte[] sig2 = new byte[Ed448.SignatureSize]; @@ -38,7 +38,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Random.NextBytes(sk); Ed448.GeneratePublicKey(sk, 0, pk, 0); - int mLen = Random.Next() & 255; + int mLen = Random.NextInt() & 255; Ed448.Sign(sk, 0, ctx, m, 0, mLen, sig1, 0); Ed448.Sign(sk, 0, pk, 0, ctx, m, 0, mLen, sig2, 0); @@ -61,7 +61,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests { byte[] sk = new byte[Ed448.SecretKeySize]; byte[] pk = new byte[Ed448.PublicKeySize]; - byte[] ctx = new byte[Random.Next() & 7]; + byte[] ctx = new byte[Random.NextInt() & 7]; byte[] m = new byte[255]; byte[] ph = new byte[Ed448.PrehashSize]; byte[] sig1 = new byte[Ed448.SignatureSize]; @@ -75,7 +75,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Random.NextBytes(sk); Ed448.GeneratePublicKey(sk, 0, pk, 0); - int mLen = Random.Next() & 255; + int mLen = Random.NextInt() & 255; IXof prehash = Ed448.CreatePrehash(); prehash.BlockUpdate(m, 0, mLen); diff --git a/crypto/test/src/security/test/TestSignerUtil.cs b/crypto/test/src/security/test/TestSignerUtil.cs index f2ee4b048..dc3c1c81b 100644 --- a/crypto/test/src/security/test/TestSignerUtil.cs +++ b/crypto/test/src/security/test/TestSignerUtil.cs @@ -94,6 +94,14 @@ namespace Org.BouncyCastle.Security.Tests AsymmetricCipherKeyPair ecGostPair = ecGostKpg.GenerateKeyPair(); + IAsymmetricCipherKeyPairGenerator ed25519Kpg = GeneratorUtilities.GetKeyPairGenerator("Ed25519"); + ed25519Kpg.Init(new Ed25519KeyGenerationParameters(new SecureRandom())); + AsymmetricCipherKeyPair ed25519Pair = ed25519Kpg.GenerateKeyPair(); + + IAsymmetricCipherKeyPairGenerator ed448Kpg = GeneratorUtilities.GetKeyPairGenerator("Ed448"); + ed448Kpg.Init(new Ed448KeyGenerationParameters(new SecureRandom())); + AsymmetricCipherKeyPair ed448Pair = ed448Kpg.GenerateKeyPair(); + // // GOST3410 parameters // @@ -147,6 +155,16 @@ namespace Org.BouncyCastle.Security.Tests signParams = ecGostPair.Private; verifyParams = ecGostPair.Public; } + else if (cipherName == "ED25519") + { + signParams = ed25519Pair.Private; + verifyParams = ed25519Pair.Public; + } + else if (cipherName == "ED448") + { + signParams = ed448Pair.Private; + verifyParams = ed448Pair.Public; + } else if (cipherName == "GOST3410") { signParams = gostPair.Private; -- cgit 1.4.1 From c52855a65b0886935c8bd93a5d6079ff74d7abac Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 21 Sep 2018 20:14:03 +0700 Subject: Fix ed25519 ignoring the public key offset - Thanks to https://github.com/TimoRoth --- crypto/src/math/ec/rfc8032/Ed25519.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs index 0b0e649d7..2dd9e2f6f 100644 --- a/crypto/src/math/ec/rfc8032/Ed25519.cs +++ b/crypto/src/math/ec/rfc8032/Ed25519.cs @@ -330,7 +330,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Dom2(d, phflag, ctx); d.BlockUpdate(R, 0, PointBytes); - d.BlockUpdate(pk, 0, PointBytes); + d.BlockUpdate(pk, pkOff, PointBytes); d.BlockUpdate(m, mOff, mLen); d.DoFinal(h, 0); -- cgit 1.4.1 From 7951b6781f03204652fd7a6ff023732b010ebd59 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 25 Sep 2018 21:47:28 +0700 Subject: Port of SM4 from Java API --- crypto/BouncyCastle.Android.csproj | 1 + crypto/BouncyCastle.csproj | 1 + crypto/BouncyCastle.iOS.csproj | 1 + crypto/crypto.csproj | 15 ++ crypto/src/crypto/engines/SM4Engine.cs | 189 ++++++++++++++++++++++++++ crypto/src/security/CipherUtilities.cs | 5 + crypto/src/security/GeneratorUtilities.cs | 3 +- crypto/src/security/ParameterUtilities.cs | 4 +- crypto/src/util/Integers.cs | 12 ++ crypto/test/UnitTests.csproj | 2 + crypto/test/src/crypto/test/RegressionTest.cs | 1 + crypto/test/src/crypto/test/SM4Test.cs | 93 +++++++++++++ crypto/test/src/test/RegressionTest.cs | 1 + crypto/test/src/test/SM4Test.cs | 149 ++++++++++++++++++++ 14 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 crypto/src/crypto/engines/SM4Engine.cs create mode 100644 crypto/test/src/crypto/test/SM4Test.cs create mode 100644 crypto/test/src/test/SM4Test.cs diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj index c5eb8e6f5..8936ecbc1 100644 --- a/crypto/BouncyCastle.Android.csproj +++ b/crypto/BouncyCastle.Android.csproj @@ -801,6 +801,7 @@ + diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj index 76da30095..e66bb4e5d 100644 --- a/crypto/BouncyCastle.csproj +++ b/crypto/BouncyCastle.csproj @@ -795,6 +795,7 @@ + diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj index 52dae0f4f..ad433e3dc 100644 --- a/crypto/BouncyCastle.iOS.csproj +++ b/crypto/BouncyCastle.iOS.csproj @@ -796,6 +796,7 @@ + diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index 2ffb66d8c..d0f672e8e 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -3768,6 +3768,11 @@ SubType = "Code" BuildAction = "Compile" /> + + + SM4 Block Cipher - SM4 is a 128 bit block cipher with a 128 bit key. + /// + /// The implementation here is based on the document http://eprint.iacr.org/2008/329.pdf + /// by Whitfield Diffie and George Ledin, which is a translation of Prof. LU Shu-wang's original standard. + /// + public class SM4Engine + : IBlockCipher + { + private const int BlockSize = 16; + + private static readonly byte[] Sbox = + { + 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05, + 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, + 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62, + 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6, + 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8, + 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35, + 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, + 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, + 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, + 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, + 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, + 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, + 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, + 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, + 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84, + 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48 + }; + + private static readonly uint[] CK = + { + 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, + 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, + 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, + 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9, + 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, + 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299, + 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, + 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279 + }; + + private static readonly uint[] FK = + { + 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc + }; + + private uint[] rk; + + // non-linear substitution tau. + private static uint tau(uint A) + { + uint b0 = Sbox[A >> 24]; + uint b1 = Sbox[(A >> 16) & 0xFF]; + uint b2 = Sbox[(A >> 8) & 0xFF]; + uint b3 = Sbox[A & 0xFF]; + + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + + private static uint L_ap(uint B) + { + return B ^ Integers.RotateLeft(B, 13) ^ Integers.RotateLeft(B, 23); + } + + private uint T_ap(uint Z) + { + return L_ap(tau(Z)); + } + + // Key expansion + private void ExpandKey(bool forEncryption, byte[] key) + { + uint K0 = Pack.BE_To_UInt32(key, 0) ^ FK[0]; + uint K1 = Pack.BE_To_UInt32(key, 4) ^ FK[1]; + uint K2 = Pack.BE_To_UInt32(key, 8) ^ FK[2]; + uint K3 = Pack.BE_To_UInt32(key, 12) ^ FK[3]; + + if (forEncryption) + { + rk[0] = K0 ^ T_ap(K1 ^ K2 ^ K3 ^ CK[0]); + rk[1] = K1 ^ T_ap(K2 ^ K3 ^ rk[0] ^ CK[1]); + rk[2] = K2 ^ T_ap(K3 ^ rk[0] ^ rk[1] ^ CK[2]); + rk[3] = K3 ^ T_ap(rk[0] ^ rk[1] ^ rk[2] ^ CK[3]); + for (int i = 4; i < 32; ++i) + { + rk[i] = rk[i - 4] ^ T_ap(rk[i - 3] ^ rk[i - 2] ^ rk[i - 1] ^ CK[i]); + } + } + else + { + rk[31] = K0 ^ T_ap(K1 ^ K2 ^ K3 ^ CK[0]); + rk[30] = K1 ^ T_ap(K2 ^ K3 ^ rk[31] ^ CK[1]); + rk[29] = K2 ^ T_ap(K3 ^ rk[31] ^ rk[30] ^ CK[2]); + rk[28] = K3 ^ T_ap(rk[31] ^ rk[30] ^ rk[29] ^ CK[3]); + for (int i = 27; i >= 0; --i) + { + rk[i] = rk[i + 4] ^ T_ap(rk[i + 3] ^ rk[i + 2] ^ rk[i + 1] ^ CK[31 - i]); + } + } + } + + // Linear substitution L + private static uint L(uint B) + { + return B ^ Integers.RotateLeft(B, 2) ^ Integers.RotateLeft(B, 10) ^ Integers.RotateLeft(B, 18) ^ Integers.RotateLeft(B, 24); + } + + // Mixer-substitution T + private static uint T(uint Z) + { + return L(tau(Z)); + } + + public virtual void Init(bool forEncryption, ICipherParameters parameters) + { + KeyParameter keyParameter = parameters as KeyParameter; + if (null == keyParameter) + throw new ArgumentException("invalid parameter passed to SM4 init - " + Platform.GetTypeName(parameters), "parameters"); + + byte[] key = keyParameter.GetKey(); + if (key.Length != 16) + throw new ArgumentException("SM4 requires a 128 bit key", "parameters"); + + if (null == rk) + { + rk = new uint[32]; + } + + ExpandKey(forEncryption, key); + } + + public virtual string AlgorithmName + { + get { return "SM4"; } + } + + public virtual bool IsPartialBlockOkay + { + get { return false; } + } + + public virtual int GetBlockSize() + { + return BlockSize; + } + + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) + { + if (null == rk) + throw new InvalidOperationException("SM4 not initialised"); + + Check.DataLength(input, inOff, BlockSize, "input buffer too short"); + Check.OutputLength(output, outOff, BlockSize, "output buffer too short"); + + uint X0 = Pack.BE_To_UInt32(input, inOff); + uint X1 = Pack.BE_To_UInt32(input, inOff + 4); + uint X2 = Pack.BE_To_UInt32(input, inOff + 8); + uint X3 = Pack.BE_To_UInt32(input, inOff + 12); + + for (int i = 0; i < 32; i += 4) + { + X0 ^= T(X1 ^ X2 ^ X3 ^ rk[i ]); // F0 + X1 ^= T(X2 ^ X3 ^ X0 ^ rk[i + 1]); // F1 + X2 ^= T(X3 ^ X0 ^ X1 ^ rk[i + 2]); // F2 + X3 ^= T(X0 ^ X1 ^ X2 ^ rk[i + 3]); // F3 + } + + Pack.UInt32_To_BE(X3, output, outOff); + Pack.UInt32_To_BE(X2, output, outOff + 4); + Pack.UInt32_To_BE(X1, output, outOff + 8); + Pack.UInt32_To_BE(X0, output, outOff + 12); + + return BlockSize; + } + + public virtual void Reset() + { + } + } +} diff --git a/crypto/src/security/CipherUtilities.cs b/crypto/src/security/CipherUtilities.cs index de05bc9ef..eb10baec8 100644 --- a/crypto/src/security/CipherUtilities.cs +++ b/crypto/src/security/CipherUtilities.cs @@ -53,6 +53,7 @@ namespace Org.BouncyCastle.Security SEED, SERPENT, SKIPJACK, + SM4, TEA, THREEFISH_256, THREEFISH_512, @@ -433,6 +434,9 @@ namespace Org.BouncyCastle.Security case CipherAlgorithm.SKIPJACK: blockCipher = new SkipjackEngine(); break; + case CipherAlgorithm.SM4: + blockCipher = new SM4Engine(); + break; case CipherAlgorithm.TEA: blockCipher = new TeaEngine(); break; @@ -740,6 +744,7 @@ namespace Org.BouncyCastle.Security case CipherAlgorithm.SEED: return new SeedEngine(); case CipherAlgorithm.SERPENT: return new SerpentEngine(); case CipherAlgorithm.SKIPJACK: return new SkipjackEngine(); + case CipherAlgorithm.SM4: return new SM4Engine(); case CipherAlgorithm.TEA: return new TeaEngine(); case CipherAlgorithm.THREEFISH_256: return new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256); case CipherAlgorithm.THREEFISH_512: return new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512); diff --git a/crypto/src/security/GeneratorUtilities.cs b/crypto/src/security/GeneratorUtilities.cs index 26ddb396b..08281493a 100644 --- a/crypto/src/security/GeneratorUtilities.cs +++ b/crypto/src/security/GeneratorUtilities.cs @@ -110,6 +110,7 @@ namespace Org.BouncyCastle.Security KisaObjectIdentifiers.IdSeedCbc); AddKgAlgorithm("SERPENT"); AddKgAlgorithm("SKIPJACK"); + AddKgAlgorithm("SM4"); AddKgAlgorithm("TEA"); AddKgAlgorithm("THREEFISH-256"); AddKgAlgorithm("THREEFISH-512"); @@ -203,7 +204,7 @@ namespace Org.BouncyCastle.Security AddDefaultKeySizeEntries(80, "SKIPJACK"); AddDefaultKeySizeEntries(128, "AES128", "BLOWFISH", "CAMELLIA128", "CAST5", "DESEDE", "HC128", "HMACMD2", "HMACMD4", "HMACMD5", "HMACRIPEMD128", "IDEA", "NOEKEON", - "RC2", "RC4", "RC5", "SALSA20", "SEED", "TEA", "XTEA", "VMPC", "VMPC-KSA3"); + "RC2", "RC4", "RC5", "SALSA20", "SEED", "SM4", "TEA", "XTEA", "VMPC", "VMPC-KSA3"); AddDefaultKeySizeEntries(160, "HMACRIPEMD160", "HMACSHA1"); AddDefaultKeySizeEntries(192, "AES", "AES192", "CAMELLIA192", "DESEDE3", "HMACTIGER", "RIJNDAEL", "SERPENT", "TNEPRES"); diff --git a/crypto/src/security/ParameterUtilities.cs b/crypto/src/security/ParameterUtilities.cs index c12155878..792067bba 100644 --- a/crypto/src/security/ParameterUtilities.cs +++ b/crypto/src/security/ParameterUtilities.cs @@ -103,6 +103,7 @@ namespace Org.BouncyCastle.Security KisaObjectIdentifiers.IdSeedCbc); AddAlgorithm("SERPENT"); AddAlgorithm("SKIPJACK"); + AddAlgorithm("SM4"); AddAlgorithm("TEA"); AddAlgorithm("THREEFISH-256"); AddAlgorithm("THREEFISH-512"); @@ -115,7 +116,8 @@ namespace Org.BouncyCastle.Security AddBasicIVSizeEntries(8, "BLOWFISH", "DES", "DESEDE", "DESEDE3"); AddBasicIVSizeEntries(16, "AES", "AES128", "AES192", "AES256", - "CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "NOEKEON", "SEED"); + "CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", + "NOEKEON", "SEED", "SM4"); // TODO These algorithms support an IV // but JCE doesn't seem to provide an AlgorithmParametersGenerator for them diff --git a/crypto/src/util/Integers.cs b/crypto/src/util/Integers.cs index ccbf872c4..e746b0ef4 100644 --- a/crypto/src/util/Integers.cs +++ b/crypto/src/util/Integers.cs @@ -9,9 +9,21 @@ namespace Org.BouncyCastle.Utilities return (i << distance) ^ (int)((uint)i >> -distance); } + [CLSCompliantAttribute(false)] + public static uint RotateLeft(uint i, int distance) + { + return (i << distance) ^ (i >> -distance); + } + public static int RotateRight(int i, int distance) { return (int)((uint)i >> distance) ^ (i << -distance); } + + [CLSCompliantAttribute(false)] + public static uint RotateRight(uint i, int distance) + { + return (i >> distance) ^ (i << -distance); + } } } diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj index bb046c2b6..d89cd6d56 100644 --- a/crypto/test/UnitTests.csproj +++ b/crypto/test/UnitTests.csproj @@ -265,6 +265,7 @@ + @@ -434,6 +435,7 @@ + diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs index 18aa62d97..f8b5c3c79 100644 --- a/crypto/test/src/crypto/test/RegressionTest.cs +++ b/crypto/test/src/crypto/test/RegressionTest.cs @@ -133,6 +133,7 @@ namespace Org.BouncyCastle.Crypto.Tests new SM2EngineTest(), new SM2KeyExchangeTest(), new SM2SignerTest(), + new SM4Test(), new X25519Test(), new X448Test(), new Ed25519Test(), diff --git a/crypto/test/src/crypto/test/SM4Test.cs b/crypto/test/src/crypto/test/SM4Test.cs new file mode 100644 index 000000000..ae2f18b00 --- /dev/null +++ b/crypto/test/src/crypto/test/SM4Test.cs @@ -0,0 +1,93 @@ +using System; + +using NUnit.Framework; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.Test; + +namespace Org.BouncyCastle.Crypto.Tests +{ + /** + * SM4 tester, vectors from http://eprint.iacr.org/2008/329.pdf + */ + [TestFixture] + public class SM4Test + : CipherTest + { + static SimpleTest[] tests = + { + new BlockCipherVectorTest(0, new SM4Engine(), + new KeyParameter(Hex.Decode("0123456789abcdeffedcba9876543210")), + "0123456789abcdeffedcba9876543210", + "681edf34d206965e86b3e94f536e4246") + }; + + public SM4Test() + : base(tests, new SM4Engine(), new KeyParameter(new byte[16])) + { + } + + public override void PerformTest() + { + base.PerformTest(); + + DoTest1000000(); + } + + private void DoTest1000000() + { + byte[] plain = Hex.Decode("0123456789abcdeffedcba9876543210"); + byte[] key = Hex.Decode("0123456789abcdeffedcba9876543210"); + byte[] cipher = Hex.Decode("595298c7c6fd271f0402f804c33d3f66"); + byte[] buf = new byte[16]; + + IBlockCipher engine = new SM4Engine(); + + engine.Init(true, new KeyParameter(key)); + + Array.Copy(plain, 0, buf, 0, buf.Length); + + for (int i = 0; i != 1000000; i++) + { + engine.ProcessBlock(buf, 0, buf, 0); + } + + if (!AreEqual(cipher, buf)) + { + Fail("1000000 encryption test failed"); + } + + engine.Init(false, new KeyParameter(key)); + + for (int i = 0; i != 1000000; i++) + { + engine.ProcessBlock(buf, 0, buf, 0); + } + + if (!AreEqual(plain, buf)) + { + Fail("1000000 decryption test failed"); + } + } + + public override string Name + { + get { return "SM4"; } + } + + public static void Main(string[] args) + { + RunTest(new SM4Test()); + } + + [Test] + public void TestFunction() + { + string resultText = Perform().ToString(); + + Assert.AreEqual(Name + ": Okay", resultText); + } + } +} diff --git a/crypto/test/src/test/RegressionTest.cs b/crypto/test/src/test/RegressionTest.cs index 0ffde72e4..7f4f38f00 100644 --- a/crypto/test/src/test/RegressionTest.cs +++ b/crypto/test/src/test/RegressionTest.cs @@ -64,6 +64,7 @@ namespace Org.BouncyCastle.Tests new MqvTest(), new CMacTest(), new Crl5Test(), + new SM4Test() }; public static void Main( diff --git a/crypto/test/src/test/SM4Test.cs b/crypto/test/src/test/SM4Test.cs new file mode 100644 index 000000000..5d36b3431 --- /dev/null +++ b/crypto/test/src/test/SM4Test.cs @@ -0,0 +1,149 @@ +using System; +using System.IO; + +using NUnit.Framework; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tests +{ + /** + * basic test class for SM4 + */ + [TestFixture] + public class SM4Test + : BaseBlockCipherTest + { + internal static readonly string[] cipherTests = + { + "128", + "0123456789abcdeffedcba9876543210", + "0123456789abcdeffedcba9876543210", + "681edf34d206965e86b3e94f536e4246" + }; + + public SM4Test() + : base("SM4") + { + } + + public void DoTest( + int strength, + byte[] keyBytes, + byte[] input, + byte[] output) + { + KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes); + + IBufferedCipher inCipher = CipherUtilities.GetCipher("SM4/ECB/NoPadding"); + IBufferedCipher outCipher = CipherUtilities.GetCipher("SM4/ECB/NoPadding"); + + try + { + outCipher.Init(true, key); + } + catch (Exception e) + { + Fail("SM4 failed initialisation - " + e, e); + } + + try + { + inCipher.Init(false, key); + } + catch (Exception e) + { + Fail("SM4 failed initialisation - " + e, e); + } + + // + // encryption pass + // + MemoryStream bOut = new MemoryStream(); + + CipherStream cOut = new CipherStream(bOut, null, outCipher); + + try + { + for (int i = 0; i != input.Length / 2; i++) + { + cOut.WriteByte(input[i]); + } + cOut.Write(input, input.Length / 2, input.Length - input.Length / 2); + cOut.Close(); + } + catch (IOException e) + { + Fail("SM4 failed encryption - " + e, e); + } + + byte[] bytes = bOut.ToArray(); + + if (!AreEqual(bytes, output)) + { + Fail("SM4 failed encryption - expected " + + Hex.ToHexString(output) + " got " + + Hex.ToHexString(bytes)); + } + + // + // decryption pass + // + MemoryStream bIn = new MemoryStream(bytes, false); + + CipherStream cIn = new CipherStream(bIn, inCipher, null); + + try + { +// DataInputStream dIn = new DataInputStream(cIn); + BinaryReader dIn = new BinaryReader(cIn); + + bytes = new byte[input.Length]; + + for (int i = 0; i != input.Length / 2; i++) + { +// bytes[i] = (byte)dIn.read(); + bytes[i] = dIn.ReadByte(); + } + + int remaining = bytes.Length - input.Length / 2; +// dIn.readFully(bytes, input.Length / 2, remaining); + byte[] extra = dIn.ReadBytes(remaining); + if (extra.Length < remaining) + throw new EndOfStreamException(); + extra.CopyTo(bytes, input.Length / 2); + } + catch (Exception e) + { + Fail("SM4 failed encryption - " + e, e); + } + + if (!AreEqual(bytes, input)) + { + Fail("SM4 failed decryption - expected " + + Hex.ToHexString(input) + " got " + + Hex.ToHexString(bytes)); + } + } + + public override void PerformTest() + { + for (int i = 0; i != cipherTests.Length; i += 4) + { + DoTest(int.Parse(cipherTests[i]), + Hex.Decode(cipherTests[i + 1]), + Hex.Decode(cipherTests[i + 2]), + Hex.Decode(cipherTests[i + 3])); + } + } + + public static void Main(string[] args) + { + RunTest(new SM4Test()); + } + } +} -- cgit 1.4.1 From 04e57f9ff6d5f18189e7bfe322caa63d4a8fde0b Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 25 Sep 2018 21:55:19 +0700 Subject: RFC 8032: Avoid unnecessary doublings in precomputation --- crypto/src/math/ec/rfc8032/Ed25519.cs | 7 +++++-- crypto/src/math/ec/rfc8032/Ed448.cs | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs index 2dd9e2f6f..f9ba1ff97 100644 --- a/crypto/src/math/ec/rfc8032/Ed25519.cs +++ b/crypto/src/math/ec/rfc8032/Ed25519.cs @@ -670,9 +670,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 ds[t] = PointCopy(p); - for (int s = 1; s < PrecompSpacing; ++s) + if (b + t != PrecompBlocks + PrecompTeeth - 2) { - PointDouble(p); + for (int s = 1; s < PrecompSpacing; ++s) + { + PointDouble(p); + } } } diff --git a/crypto/src/math/ec/rfc8032/Ed448.cs b/crypto/src/math/ec/rfc8032/Ed448.cs index f12aa0807..a1f0e93b0 100644 --- a/crypto/src/math/ec/rfc8032/Ed448.cs +++ b/crypto/src/math/ec/rfc8032/Ed448.cs @@ -622,9 +622,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 ds[t] = PointCopy(p); - for (int s = 1; s < PrecompSpacing; ++s) + if (b + t != PrecompBlocks + PrecompTeeth - 2) { - PointDouble(p); + for (int s = 1; s < PrecompSpacing; ++s) + { + PointDouble(p); + } } } -- cgit 1.4.1