summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-10-02 01:49:41 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-10-02 01:49:41 +0700
commit8682ed8e39998e340ce43c5955da1e1689857124 (patch)
treea1867916f7aff1266b6cfe6c3bb10c4f75a096cc /crypto/src
parentSpan-based variant of ECCurve.DecodePoint (diff)
downloadBouncyCastle.NET-ed25519-8682ed8e39998e340ce43c5955da1e1689857124.tar.xz
Add support for C1C3C2 mode to SM2Engine
- Refactoring, including span-based variants
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/crypto/engines/SM2Engine.cs233
1 files changed, 214 insertions, 19 deletions
diff --git a/crypto/src/crypto/engines/SM2Engine.cs b/crypto/src/crypto/engines/SM2Engine.cs
index 1a121c192..36593cddc 100644
--- a/crypto/src/crypto/engines/SM2Engine.cs
+++ b/crypto/src/crypto/engines/SM2Engine.cs
@@ -16,7 +16,13 @@ namespace Org.BouncyCastle.Crypto.Engines
     /// </summary>
     public class SM2Engine
     {
+        public enum Mode
+        {
+            C1C2C3, C1C3C2
+        }
+
         private readonly IDigest mDigest;
+        private readonly Mode mMode;
 
         private bool mForEncryption;
         private ECKeyParameters mECKey;
@@ -29,9 +35,20 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
         }
 
+        public SM2Engine(Mode mode)
+            : this(new SM3Digest(), mode)
+        {
+        }
+
         public SM2Engine(IDigest digest)
+            : this(digest, Mode.C1C2C3)
+        {
+        }
+
+        public SM2Engine(IDigest digest, Mode mode)
         {
-            this.mDigest = digest;
+            mDigest = digest;
+            mMode = mode;
         }
 
         public virtual void Init(bool forEncryption, ICipherParameters param)
@@ -63,10 +80,11 @@ namespace Org.BouncyCastle.Crypto.Engines
         public virtual byte[] ProcessBlock(byte[] input, int inOff, int inLen)
         {
             if ((inOff + inLen) > input.Length || inLen == 0)
-            {
                 throw new DataLengthException("input buffer too short");
-            }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return ProcessBlock(input.AsSpan(inOff, inLen));
+#else
             if (mForEncryption)
             {
                 return Encrypt(input, inOff, inLen);
@@ -75,13 +93,141 @@ namespace Org.BouncyCastle.Crypto.Engines
             {
                 return Decrypt(input, inOff, inLen);
             }
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual byte[] ProcessBlock(ReadOnlySpan<byte> input)
+        {
+            if (input.Length == 0)
+                throw new DataLengthException("input buffer too short");
+
+            if (mForEncryption)
+            {
+                return Encrypt(input);
+            }
+            else
+            {
+                return Decrypt(input);
+            }
+        }
+#endif
+
         protected virtual ECMultiplier CreateBasePointMultiplier()
         {
             return new FixedPointCombMultiplier();
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private byte[] Encrypt(ReadOnlySpan<byte> input)
+        {
+            byte[] c2 = input.ToArray();
+
+            ECMultiplier multiplier = CreateBasePointMultiplier();
+
+            BigInteger k;
+            ECPoint kPB;
+            do
+            {
+                k = NextK();
+                kPB = ((ECPublicKeyParameters)mECKey).Q.Multiply(k).Normalize();
+
+                Kdf(mDigest, kPB, c2);
+            }
+            while (NotEncrypted(c2, input));
+
+            ECPoint c1P = multiplier.Multiply(mECParams.G, k).Normalize();
+
+            Span<byte> c1 = stackalloc byte[c1P.GetEncodedLength(false)];
+            c1P.EncodeTo(false, c1);
+
+            AddFieldElement(mDigest, kPB.AffineXCoord);
+            mDigest.BlockUpdate(input);
+            AddFieldElement(mDigest, kPB.AffineYCoord);
+
+            Span<byte> c3 = stackalloc byte[mDigest.GetDigestSize()];
+            mDigest.DoFinal(c3);
+
+            switch (mMode)
+            {
+            case Mode.C1C3C2:
+                return Arrays.Concatenate(c1, c3, c2);
+            default:
+                return Arrays.Concatenate(c1, c2, c3);
+            }
+        }
+
+        private byte[] Decrypt(ReadOnlySpan<byte> input)
+        {
+            int c1Length = mCurveLength * 2 + 1;
+            ECPoint c1P = mECParams.Curve.DecodePoint(input[..c1Length]);
+
+            ECPoint s = c1P.Multiply(mECParams.H);
+            if (s.IsInfinity)
+                throw new InvalidCipherTextException("[h]C1 at infinity");
+
+            c1P = c1P.Multiply(((ECPrivateKeyParameters)mECKey).D).Normalize();
+
+            int digestSize = mDigest.GetDigestSize();
+            int c2Length = input.Length - c1Length - digestSize;
+            byte[] c2 = new byte[c2Length];
+
+            if (mMode == Mode.C1C3C2)
+            {
+                input[(c1Length + digestSize)..].CopyTo(c2);
+            }
+            else
+            {
+                input[c1Length..(c1Length + c2Length)].CopyTo(c2);
+            }
+
+            Kdf(mDigest, c1P, c2);
+
+            AddFieldElement(mDigest, c1P.AffineXCoord);
+            mDigest.BlockUpdate(c2);
+            AddFieldElement(mDigest, c1P.AffineYCoord);
+
+            Span<byte> c3 = stackalloc byte[mDigest.GetDigestSize()];
+            mDigest.DoFinal(c3);
+
+            int check = 0;
+            if (mMode == Mode.C1C3C2)
+            {
+                for (int i = 0; i != c3.Length; i++)
+                {
+                    check |= c3[i] ^ input[c1Length + i];
+                }
+            }
+            else
+            {
+                for (int i = 0; i != c3.Length; i++)
+                {
+                    check |= c3[i] ^ input[c1Length + c2.Length + i];
+                }
+            }
+
+            c3.Fill(0);
+
+            if (check != 0)
+            {
+                Arrays.Fill(c2, 0);
+                throw new InvalidCipherTextException("invalid cipher text");
+            }
+
+            return c2;
+        }
+
+        private bool NotEncrypted(ReadOnlySpan<byte> encData, ReadOnlySpan<byte> input)
+        {
+            for (int i = 0; i != encData.Length; i++)
+            {
+                if (encData[i] != input[i])
+                    return false;
+            }
+
+            return true;
+        }
+#else
         private byte[] Encrypt(byte[] input, int inOff, int inLen)
         {
             byte[] c2 = new byte[inLen];
@@ -90,29 +236,34 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             ECMultiplier multiplier = CreateBasePointMultiplier();
 
-            byte[] c1;
+            BigInteger k;
             ECPoint kPB;
             do
             {
-                BigInteger k = NextK();
-
-                ECPoint c1P = multiplier.Multiply(mECParams.G, k).Normalize();
-
-                c1 = c1P.GetEncoded(false);
-
+                k = NextK();
                 kPB = ((ECPublicKeyParameters)mECKey).Q.Multiply(k).Normalize();
 
                 Kdf(mDigest, kPB, c2);
             }
             while (NotEncrypted(c2, input, inOff));
 
+            ECPoint c1P = multiplier.Multiply(mECParams.G, k).Normalize();
+
+            byte[] c1 = c1P.GetEncoded(false);
+
             AddFieldElement(mDigest, kPB.AffineXCoord);
             mDigest.BlockUpdate(input, inOff, inLen);
             AddFieldElement(mDigest, kPB.AffineYCoord);
 
             byte[] c3 = DigestUtilities.DoFinal(mDigest);
 
-            return Arrays.ConcatenateAll(c1, c2, c3);
+            switch (mMode)
+            {
+            case Mode.C1C3C2:
+                return Arrays.ConcatenateAll(c1, c3, c2);
+            default:
+                return Arrays.ConcatenateAll(c1, c2, c3);
+            }
         }
 
         private byte[] Decrypt(byte[] input, int inOff, int inLen)
@@ -129,9 +280,17 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             c1P = c1P.Multiply(((ECPrivateKeyParameters)mECKey).D).Normalize();
 
-            byte[] c2 = new byte[inLen - c1.Length - mDigest.GetDigestSize()];
+            int digestSize = mDigest.GetDigestSize();
+            byte[] c2 = new byte[inLen - c1.Length - digestSize];
 
-            Array.Copy(input, inOff + c1.Length, c2, 0, c2.Length);
+            if (mMode == Mode.C1C3C2)
+            {
+                Array.Copy(input, inOff + c1.Length + digestSize, c2, 0, c2.Length);
+            }
+            else
+            {
+                Array.Copy(input, inOff + c1.Length, c2, 0, c2.Length);
+            }
 
             Kdf(mDigest, c1P, c2);
 
@@ -142,9 +301,19 @@ namespace Org.BouncyCastle.Crypto.Engines
             byte[] c3 = DigestUtilities.DoFinal(mDigest);
 
             int check = 0;
-            for (int i = 0; i != c3.Length; i++)
+            if (mMode == Mode.C1C3C2)
+            {
+                for (int i = 0; i != c3.Length; i++)
+                {
+                    check |= c3[i] ^ input[inOff + c1.Length + i];
+                }
+            }
+            else
             {
-                check |= c3[i] ^ input[inOff + c1.Length + c2.Length + i];
+                for (int i = 0; i != c3.Length; i++)
+                {
+                    check |= c3[i] ^ input[inOff + c1.Length + c2.Length + i];
+                }
             }
 
             Arrays.Fill(c1, 0);
@@ -164,18 +333,21 @@ namespace Org.BouncyCastle.Crypto.Engines
             for (int i = 0; i != encData.Length; i++)
             {
                 if (encData[i] != input[inOff + i])
-                {
                     return false;
-                }
             }
 
             return true;
         }
+#endif
 
         private void Kdf(IDigest digest, ECPoint c1, byte[] encData)
         {
             int digestSize = digest.GetDigestSize();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> buf = stackalloc byte[System.Math.Max(4, digestSize)];
+#else
             byte[] buf = new byte[System.Math.Max(4, digestSize)];
+#endif
             int off = 0;
 
             IMemoable memo = digest as IMemoable;
@@ -202,16 +374,32 @@ namespace Org.BouncyCastle.Crypto.Engines
                     AddFieldElement(digest, c1.AffineYCoord);
                 }
 
+                int xorLen = System.Math.Min(digestSize, encData.Length - off);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                Pack.UInt32_To_BE(++ct, buf);
+                digest.BlockUpdate(buf[..4]);
+                digest.DoFinal(buf);
+                Xor(encData.AsSpan(off, xorLen), buf);
+#else
                 Pack.UInt32_To_BE(++ct, buf, 0);
                 digest.BlockUpdate(buf, 0, 4);
                 digest.DoFinal(buf, 0);
-
-                int xorLen = System.Math.Min(digestSize, encData.Length - off);
                 Xor(encData, buf, off, xorLen);
+#endif
                 off += xorLen;
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void Xor(Span<byte> data, ReadOnlySpan<byte> kdfOut)
+        {
+            for (int i = 0; i != data.Length; i++)
+            {
+                data[i] ^= kdfOut[i];
+            }
+        }
+#else
         private void Xor(byte[] data, byte[] kdfOut, int dOff, int dRemaining)
         {
             for (int i = 0; i != dRemaining; i++)
@@ -219,6 +407,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                 data[dOff + i] ^= kdfOut[i];
             }
         }
+#endif
 
         private BigInteger NextK()
         {
@@ -236,8 +425,14 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         private void AddFieldElement(IDigest digest, ECFieldElement v)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> p = stackalloc byte[v.GetEncodedLength()];
+            v.EncodeTo(p);
+            digest.BlockUpdate(p);
+#else
             byte[] p = v.GetEncoded();
             digest.BlockUpdate(p, 0, p.Length);
+#endif
         }
     }
 }