diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-10-02 01:49:41 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-10-02 01:49:41 +0700 |
commit | 8682ed8e39998e340ce43c5955da1e1689857124 (patch) | |
tree | a1867916f7aff1266b6cfe6c3bb10c4f75a096cc /crypto/src | |
parent | Span-based variant of ECCurve.DecodePoint (diff) | |
download | BouncyCastle.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.cs | 233 |
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 } } } |