diff --git a/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs b/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs
index 32d3103af..9c366503d 100644
--- a/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs
+++ b/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections;
-using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Rosstandart;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
@@ -10,9 +10,9 @@ using Org.BouncyCastle.Utilities.Collections;
namespace Org.BouncyCastle.Asn1.CryptoPro
{
- /**
- * table of the available named parameters for GOST 3410-2001.
- */
+ /// <summary>
+ /// Table of the available named parameters for GOST 3410-2001 / 2012.
+ /// </summary>
public sealed class ECGost3410NamedCurves
{
private ECGost3410NamedCurves()
@@ -119,17 +119,98 @@ namespace Org.BouncyCastle.Asn1.CryptoPro
parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProC] = ecParams;
+ //GOST34.10 2012
+ mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319"); //p
+ mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323"); //q
+ curve = new FpCurve(
+ mod_p, // p
+ new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a
+ new BigInteger("166"), // b
+ mod_q,
+ BigInteger.One);
+
+ ecParams = new ECDomainParameters(
+ curve,
+ curve.CreatePoint(
+ new BigInteger("1"), // x
+ new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y
+ mod_q); // q
+
+ parameters[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA] = ecParams;
+
+ mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7",16); //p
+ mod_q = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275",16); //q
+ curve = new FpCurve(
+ mod_p, // p
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4",16), // a
+ new BigInteger("E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760",16), // b
+ mod_q,
+ BigInteger.One);
+
+ ecParams = new ECDomainParameters(
+ curve,
+ curve.CreatePoint(
+ new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"), // x
+ new BigInteger("7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4",16)), // y
+ mod_q); // q
+
+ parameters[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA] = ecParams;
+
+ mod_p = new BigInteger("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F",16); //p
+ mod_q = new BigInteger("800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD",16); //q
+ curve = new FpCurve(
+ mod_p, // p
+ new BigInteger("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C",16), // a
+ new BigInteger("687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116",16), // b
+ mod_q,
+ BigInteger.One);
+
+ ecParams = new ECDomainParameters(
+ curve,
+ curve.CreatePoint(
+ new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"), // x
+ new BigInteger("1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD",16)), // y
+ mod_q); // q
+
+ parameters[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB] = ecParams;
+
+ mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7",16); //p
+ mod_q = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC98CDBA46506AB004C33A9FF5147502CC8EDA9E7A769A12694623CEF47F023ED",16); //q
+ curve = new FpCurve(
+ mod_p, // p
+ new BigInteger("DC9203E514A721875485A529D2C722FB187BC8980EB866644DE41C68E143064546E861C0E2C9EDD92ADE71F46FCF50FF2AD97F951FDA9F2A2EB6546F39689BD3",16), // a
+ new BigInteger("B4C4EE28CEBC6C2C8AC12952CF37F16AC7EFB6A9F69F4B57FFDA2E4F0DE5ADE038CBC2FFF719D2C18DE0284B8BFEF3B52B8CC7A5F5BF0A3C8D2319A5312557E1",16), // b
+ mod_q,
+ BigInteger.One);
+
+ ecParams = new ECDomainParameters(
+ curve,
+ curve.CreatePoint(
+ new BigInteger("E2E31EDFC23DE7BDEBE241CE593EF5DE2295B7A9CBAEF021D385F7074CEA043AA27272A7AE602BF2A7B9033DB9ED3610C6FB85487EAE97AAC5BC7928C1950148", 16), // x
+ new BigInteger("F5CE40D95B5EB899ABBCCFF5911CB8577939804D6527378B8C108C3D2090FF9BE18E2D33E3021ED2EF32D85822423B6304F726AA854BAE07D0396E9A9ADDC40F",16)), // y
+ mod_q); // q
+
+ parameters[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC] = ecParams;
+
objIds["GostR3410-2001-CryptoPro-A"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProA;
objIds["GostR3410-2001-CryptoPro-B"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProB;
objIds["GostR3410-2001-CryptoPro-C"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProC;
objIds["GostR3410-2001-CryptoPro-XchA"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchA;
objIds["GostR3410-2001-CryptoPro-XchB"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB;
+ objIds["Tc26-Gost-3410-12-256-paramSetA"] = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA;
+ objIds["Tc26-Gost-3410-12-512-paramSetA"] = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA;
+ objIds["Tc26-Gost-3410-12-512-paramSetB"] = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB;
+ objIds["Tc26-Gost-3410-12-512-paramSetC"] = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC;
names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProA] = "GostR3410-2001-CryptoPro-A";
names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProB] = "GostR3410-2001-CryptoPro-B";
names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProC] = "GostR3410-2001-CryptoPro-C";
names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchA] = "GostR3410-2001-CryptoPro-XchA";
names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB] = "GostR3410-2001-CryptoPro-XchB";
+ names[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA] = "Tc26-Gost-3410-12-256-paramSetA";
+ names[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA] = "Tc26-Gost-3410-12-512-paramSetA";
+ names[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB] = "Tc26-Gost-3410-12-512-paramSetB";
+ names[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC] = "Tc26-Gost-3410-12-512-paramSetC";
}
/**
diff --git a/crypto/src/asn1/esf/SignerLocation.cs b/crypto/src/asn1/esf/SignerLocation.cs
index d2cef51bb..16dbcd01f 100644
--- a/crypto/src/asn1/esf/SignerLocation.cs
+++ b/crypto/src/asn1/esf/SignerLocation.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections;
-using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X500;
namespace Org.BouncyCastle.Asn1.Esf
{
@@ -20,10 +20,9 @@ namespace Org.BouncyCastle.Asn1.Esf
public class SignerLocation
: Asn1Encodable
{
- // TODO Should these be using DirectoryString?
- private DerUtf8String countryName;
- private DerUtf8String localityName;
- private Asn1Sequence postalAddress;
+ private DirectoryString countryName;
+ private DirectoryString localityName;
+ private Asn1Sequence postalAddress;
public SignerLocation(
Asn1Sequence seq)
@@ -33,10 +32,10 @@ namespace Org.BouncyCastle.Asn1.Esf
switch (obj.TagNo)
{
case 0:
- this.countryName = DerUtf8String.GetInstance(obj, true);
+ this.countryName = DirectoryString.GetInstance(obj, true);
break;
case 1:
- this.localityName = DerUtf8String.GetInstance(obj, true);
+ this.localityName = DirectoryString.GetInstance(obj, true);
break;
case 2:
bool isExplicit = obj.IsExplicit(); // handle erroneous implicitly tagged sequences
@@ -50,33 +49,36 @@ namespace Org.BouncyCastle.Asn1.Esf
}
}
- public SignerLocation(
- DerUtf8String countryName,
- DerUtf8String localityName,
- Asn1Sequence postalAddress)
- {
- if (postalAddress != null && postalAddress.Count > 6)
- {
- throw new ArgumentException("postal address must contain less than 6 strings");
- }
-
- if (countryName != null)
- {
- this.countryName = DerUtf8String.GetInstance(countryName.ToAsn1Object());
- }
-
- if (localityName != null)
- {
- this.localityName = DerUtf8String.GetInstance(localityName.ToAsn1Object());
- }
-
- if (postalAddress != null)
- {
- this.postalAddress = (Asn1Sequence) postalAddress.ToAsn1Object();
- }
- }
-
- public static SignerLocation GetInstance(
+ private SignerLocation(
+ DirectoryString countryName,
+ DirectoryString localityName,
+ Asn1Sequence postalAddress)
+ {
+ if (postalAddress != null && postalAddress.Count > 6)
+ throw new ArgumentException("postal address must contain less than 6 strings");
+
+ this.countryName = countryName;
+ this.localityName = localityName;
+ this.postalAddress = postalAddress;
+ }
+
+ public SignerLocation(
+ DirectoryString countryName,
+ DirectoryString localityName,
+ DirectoryString[] postalAddress)
+ : this(countryName, localityName, new DerSequence(postalAddress))
+ {
+ }
+
+ public SignerLocation(
+ DerUtf8String countryName,
+ DerUtf8String localityName,
+ Asn1Sequence postalAddress)
+ : this(DirectoryString.GetInstance(countryName), DirectoryString.GetInstance(localityName), postalAddress)
+ {
+ }
+
+ public static SignerLocation GetInstance(
object obj)
{
if (obj == null || obj is SignerLocation)
@@ -87,15 +89,41 @@ namespace Org.BouncyCastle.Asn1.Esf
return new SignerLocation(Asn1Sequence.GetInstance(obj));
}
+ public DirectoryString Country
+ {
+ get { return countryName; }
+ }
+
+ public DirectoryString Locality
+ {
+ get { return localityName; }
+ }
+
+ public DirectoryString[] GetPostal()
+ {
+ if (postalAddress == null)
+ return null;
+
+ DirectoryString[] dirStrings = new DirectoryString[postalAddress.Count];
+ for (int i = 0; i != dirStrings.Length; i++)
+ {
+ dirStrings[i] = DirectoryString.GetInstance(postalAddress[i]);
+ }
+
+ return dirStrings;
+ }
+
+ [Obsolete("Use 'Country' property instead")]
public DerUtf8String CountryName
{
- get { return countryName; }
+ get { return countryName == null ? null : new DerUtf8String(countryName.GetString()); }
}
- public DerUtf8String LocalityName
- {
- get { return localityName; }
- }
+ [Obsolete("Use 'Locality' property instead")]
+ public DerUtf8String LocalityName
+ {
+ get { return localityName == null ? null : new DerUtf8String(localityName.GetString()); }
+ }
public Asn1Sequence PostalAddress
{
diff --git a/crypto/src/asn1/rosstandart/RosstandartObjectIdentifiers.cs b/crypto/src/asn1/rosstandart/RosstandartObjectIdentifiers.cs
new file mode 100644
index 000000000..86fedb7bb
--- /dev/null
+++ b/crypto/src/asn1/rosstandart/RosstandartObjectIdentifiers.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Rosstandart
+{
+ public abstract class RosstandartObjectIdentifiers
+ {
+ public static readonly DerObjectIdentifier rosstandart = new DerObjectIdentifier("1.2.643.7");
+
+ public static readonly DerObjectIdentifier id_tc26 = rosstandart.Branch("1");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3411_12_256 = id_tc26.Branch("1.2.2");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3411_12_512 = id_tc26.Branch("1.2.3");
+
+ public static readonly DerObjectIdentifier id_tc26_hmac_gost_3411_12_256 = id_tc26.Branch("1.4.1");
+
+ public static readonly DerObjectIdentifier id_tc26_hmac_gost_3411_12_512 = id_tc26.Branch("1.4.2");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3410_12_256 = id_tc26.Branch("1.1.1");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3410_12_512 = id_tc26.Branch("1.1.2");
+
+ public static readonly DerObjectIdentifier id_tc26_signwithdigest_gost_3410_12_256 = id_tc26.Branch("1.3.2");
+
+ public static readonly DerObjectIdentifier id_tc26_signwithdigest_gost_3410_12_512 = id_tc26.Branch("1.3.3");
+
+ public static readonly DerObjectIdentifier id_tc26_agreement = id_tc26.Branch("1.6");
+
+ public static readonly DerObjectIdentifier id_tc26_agreement_gost_3410_12_256 = id_tc26_agreement.Branch("1");
+
+ public static readonly DerObjectIdentifier id_tc26_agreement_gost_3410_12_512 = id_tc26_agreement.Branch("2");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3410_12_256_paramSet = id_tc26.Branch("2.1.1");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3410_12_256_paramSetA = id_tc26_gost_3410_12_256_paramSet.Branch("1");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3410_12_512_paramSet = id_tc26.Branch("2.1.2");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3410_12_512_paramSetA = id_tc26_gost_3410_12_512_paramSet.Branch("1");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3410_12_512_paramSetB = id_tc26_gost_3410_12_512_paramSet.Branch("2");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_3410_12_512_paramSetC = id_tc26_gost_3410_12_512_paramSet.Branch("3");
+
+ public static readonly DerObjectIdentifier id_tc26_gost_28147_param_Z = id_tc26.Branch("2.5.1.1");
+ }
+}
diff --git a/crypto/src/asn1/x500/DirectoryString.cs b/crypto/src/asn1/x500/DirectoryString.cs
index d907c6456..6585fcdf5 100644
--- a/crypto/src/asn1/x500/DirectoryString.cs
+++ b/crypto/src/asn1/x500/DirectoryString.cs
@@ -9,15 +9,12 @@ namespace Org.BouncyCastle.Asn1.X500
{
private readonly DerStringBase str;
- public static DirectoryString GetInstance(
- object obj)
+ public static DirectoryString GetInstance(object obj)
{
- if (obj is DirectoryString)
- {
+ if (obj == null || obj is DirectoryString)
return (DirectoryString) obj;
- }
- if (obj is DerStringBase)
+ if (obj is DerStringBase)
{
if (obj is DerT61String
|| obj is DerPrintableString
diff --git a/crypto/src/asn1/x9/ECNamedCurveTable.cs b/crypto/src/asn1/x9/ECNamedCurveTable.cs
index 317ef17b4..f91bded18 100644
--- a/crypto/src/asn1/x9/ECNamedCurveTable.cs
+++ b/crypto/src/asn1/x9/ECNamedCurveTable.cs
@@ -2,10 +2,12 @@
using System.Collections;
using Org.BouncyCastle.Asn1.Anssi;
+using Org.BouncyCastle.Asn1.CryptoPro;
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Collections;
@@ -44,6 +46,10 @@ namespace Org.BouncyCastle.Asn1.X9
}
if (ecP == null)
{
+ ecP = FromDomainParameters(ECGost3410NamedCurves.GetByName(name));
+ }
+ if (ecP == null)
+ {
ecP = GMNamedCurves.GetByName(name);
}
return ecP;
@@ -70,6 +76,10 @@ namespace Org.BouncyCastle.Asn1.X9
}
if (name == null)
{
+ name = ECGost3410NamedCurves.GetName(oid);
+ }
+ if (name == null)
+ {
name = GMNamedCurves.GetName(oid);
}
return name;
@@ -102,6 +112,10 @@ namespace Org.BouncyCastle.Asn1.X9
}
if (oid == null)
{
+ oid = ECGost3410NamedCurves.GetOid(name);
+ }
+ if (oid == null)
+ {
oid = GMNamedCurves.GetOid(name);
}
return oid;
@@ -134,6 +148,10 @@ namespace Org.BouncyCastle.Asn1.X9
}
if (ecP == null)
{
+ ecP = FromDomainParameters(ECGost3410NamedCurves.GetByOid(oid));
+ }
+ if (ecP == null)
+ {
ecP = GMNamedCurves.GetByOid(oid);
}
return ecP;
@@ -154,9 +172,15 @@ namespace Org.BouncyCastle.Asn1.X9
CollectionUtilities.AddRange(v, NistNamedCurves.Names);
CollectionUtilities.AddRange(v, TeleTrusTNamedCurves.Names);
CollectionUtilities.AddRange(v, AnssiNamedCurves.Names);
+ CollectionUtilities.AddRange(v, ECGost3410NamedCurves.Names);
CollectionUtilities.AddRange(v, GMNamedCurves.Names);
return v;
}
}
+
+ private static X9ECParameters FromDomainParameters(ECDomainParameters dp)
+ {
+ return dp == null ? null : new X9ECParameters(dp.Curve, dp.G, dp.N, dp.H, dp.GetSeed());
+ }
}
}
diff --git a/crypto/src/crypto/agreement/SM2KeyExchange.cs b/crypto/src/crypto/agreement/SM2KeyExchange.cs
new file mode 100644
index 000000000..1cfcd6a3a
--- /dev/null
+++ b/crypto/src/crypto/agreement/SM2KeyExchange.cs
@@ -0,0 +1,272 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ /// <summary>
+ /// SM2 Key Exchange protocol - based on https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02
+ /// </summary>
+ public class SM2KeyExchange
+ {
+ private readonly IDigest mDigest;
+
+ private byte[] mUserID;
+ private ECPrivateKeyParameters mStaticKey;
+ private ECPoint mStaticPubPoint;
+ private ECPoint mEphemeralPubPoint;
+ private ECDomainParameters mECParams;
+ private int mW;
+ private ECPrivateKeyParameters mEphemeralKey;
+ private bool mInitiator;
+
+ public SM2KeyExchange()
+ : this(new SM3Digest())
+ {
+ }
+
+ public SM2KeyExchange(IDigest digest)
+ {
+ this.mDigest = digest;
+ }
+
+ public virtual void Init(ICipherParameters privParam)
+ {
+ SM2KeyExchangePrivateParameters baseParam;
+
+ if (privParam is ParametersWithID)
+ {
+ baseParam = (SM2KeyExchangePrivateParameters)((ParametersWithID)privParam).Parameters;
+ mUserID = ((ParametersWithID)privParam).GetID();
+ }
+ else
+ {
+ baseParam = (SM2KeyExchangePrivateParameters)privParam;
+ mUserID = new byte[0];
+ }
+
+ mInitiator = baseParam.IsInitiator;
+ mStaticKey = baseParam.StaticPrivateKey;
+ mEphemeralKey = baseParam.EphemeralPrivateKey;
+ mECParams = mStaticKey.Parameters;
+ mStaticPubPoint = baseParam.StaticPublicPoint;
+ mEphemeralPubPoint = baseParam.EphemeralPublicPoint;
+ mW = mECParams.Curve.FieldSize / 2 - 1;
+ }
+
+ public virtual byte[] CalculateKey(int kLen, ICipherParameters pubParam)
+ {
+ SM2KeyExchangePublicParameters otherPub;
+ byte[] otherUserID;
+
+ if (pubParam is ParametersWithID)
+ {
+ otherPub = (SM2KeyExchangePublicParameters)((ParametersWithID)pubParam).Parameters;
+ otherUserID = ((ParametersWithID)pubParam).GetID();
+ }
+ else
+ {
+ otherPub = (SM2KeyExchangePublicParameters)pubParam;
+ otherUserID = new byte[0];
+ }
+
+ byte[] za = GetZ(mDigest, mUserID, mStaticPubPoint);
+ byte[] zb = GetZ(mDigest, otherUserID, otherPub.StaticPublicKey.Q);
+
+ ECPoint U = CalculateU(otherPub);
+
+ byte[] rv;
+ if (mInitiator)
+ {
+ rv = Kdf(U, za, zb, kLen);
+ }
+ else
+ {
+ rv = Kdf(U, zb, za, kLen);
+ }
+
+ return rv;
+ }
+
+ public virtual byte[][] CalculateKeyWithConfirmation(int kLen, byte[] confirmationTag, ICipherParameters pubParam)
+ {
+ SM2KeyExchangePublicParameters otherPub;
+ byte[] otherUserID;
+
+ if (pubParam is ParametersWithID)
+ {
+ otherPub = (SM2KeyExchangePublicParameters)((ParametersWithID)pubParam).Parameters;
+ otherUserID = ((ParametersWithID)pubParam).GetID();
+ }
+ else
+ {
+ otherPub = (SM2KeyExchangePublicParameters)pubParam;
+ otherUserID = new byte[0];
+ }
+
+ if (mInitiator && confirmationTag == null)
+ throw new ArgumentException("if initiating, confirmationTag must be set");
+
+ byte[] za = GetZ(mDigest, mUserID, mStaticPubPoint);
+ byte[] zb = GetZ(mDigest, otherUserID, otherPub.StaticPublicKey.Q);
+
+ ECPoint U = CalculateU(otherPub);
+
+ byte[] rv;
+ if (mInitiator)
+ {
+ rv = Kdf(U, za, zb, kLen);
+
+ byte[] inner = CalculateInnerHash(mDigest, U, za, zb, mEphemeralPubPoint, otherPub.EphemeralPublicKey.Q);
+
+ byte[] s1 = S1(mDigest, U, inner);
+
+ if (!Arrays.ConstantTimeAreEqual(s1, confirmationTag))
+ throw new InvalidOperationException("confirmation tag mismatch");
+
+ return new byte[][] { rv, S2(mDigest, U, inner)};
+ }
+ else
+ {
+ rv = Kdf(U, zb, za, kLen);
+
+ byte[] inner = CalculateInnerHash(mDigest, U, zb, za, otherPub.EphemeralPublicKey.Q, mEphemeralPubPoint);
+
+ return new byte[][] { rv, S1(mDigest, U, inner), S2(mDigest, U, inner) };
+ }
+ }
+
+ protected virtual ECPoint CalculateU(SM2KeyExchangePublicParameters otherPub)
+ {
+ ECPoint p1 = otherPub.StaticPublicKey.Q;
+ ECPoint p2 = otherPub.EphemeralPublicKey.Q;
+
+ BigInteger x1 = Reduce(mEphemeralPubPoint.AffineXCoord.ToBigInteger());
+ BigInteger x2 = Reduce(p2.AffineXCoord.ToBigInteger());
+ BigInteger tA = mStaticKey.D.Add(x1.Multiply(mEphemeralKey.D));
+ BigInteger k1 = mECParams.H.Multiply(tA).Mod(mECParams.N);
+ BigInteger k2 = k1.Multiply(x2).Mod(mECParams.N);
+
+ return ECAlgorithms.SumOfTwoMultiplies(p1, k1, p2, k2).Normalize();
+ }
+
+ protected virtual byte[] Kdf(ECPoint u, byte[] za, byte[] zb, int klen)
+ {
+ int digestSize = mDigest.GetDigestSize();
+ byte[] buf = new byte[System.Math.Max(4, digestSize)];
+ byte[] rv = new byte[(klen + 7) / 8];
+ int off = 0;
+
+ IMemoable memo = mDigest as IMemoable;
+ IMemoable copy = null;
+
+ if (memo != null)
+ {
+ AddFieldElement(mDigest, u.AffineXCoord);
+ AddFieldElement(mDigest, u.AffineYCoord);
+ mDigest.BlockUpdate(za, 0, za.Length);
+ mDigest.BlockUpdate(zb, 0, zb.Length);
+ copy = memo.Copy();
+ }
+
+ uint ct = 0;
+
+ while (off < rv.Length)
+ {
+ if (memo != null)
+ {
+ memo.Reset(copy);
+ }
+ else
+ {
+ AddFieldElement(mDigest, u.AffineXCoord);
+ AddFieldElement(mDigest, u.AffineYCoord);
+ mDigest.BlockUpdate(za, 0, za.Length);
+ mDigest.BlockUpdate(zb, 0, zb.Length);
+ }
+
+ Pack.UInt32_To_BE(++ct, buf, 0);
+ mDigest.BlockUpdate(buf, 0, 4);
+ mDigest.DoFinal(buf, 0);
+
+ int copyLen = System.Math.Min(digestSize, rv.Length - off);
+ Array.Copy(buf, 0, rv, off, copyLen);
+ off += copyLen;
+ }
+
+ return rv;
+ }
+
+ //x1~=2^w+(x1 AND (2^w-1))
+ private BigInteger Reduce(BigInteger x)
+ {
+ return x.And(BigInteger.One.ShiftLeft(mW).Subtract(BigInteger.One)).SetBit(mW);
+ }
+
+ private byte[] S1(IDigest digest, ECPoint u, byte[] inner)
+ {
+ digest.Update((byte)0x02);
+ AddFieldElement(digest, u.AffineYCoord);
+ digest.BlockUpdate(inner, 0, inner.Length);
+
+ return DigestUtilities.DoFinal(digest);
+ }
+
+ private byte[] CalculateInnerHash(IDigest digest, ECPoint u, byte[] za, byte[] zb, ECPoint p1, ECPoint p2)
+ {
+ AddFieldElement(digest, u.AffineXCoord);
+ digest.BlockUpdate(za, 0, za.Length);
+ digest.BlockUpdate(zb, 0, zb.Length);
+ AddFieldElement(digest, p1.AffineXCoord);
+ AddFieldElement(digest, p1.AffineYCoord);
+ AddFieldElement(digest, p2.AffineXCoord);
+ AddFieldElement(digest, p2.AffineYCoord);
+
+ return DigestUtilities.DoFinal(digest);
+ }
+
+ private byte[] S2(IDigest digest, ECPoint u, byte[] inner)
+ {
+ digest.Update((byte)0x03);
+ AddFieldElement(digest, u.AffineYCoord);
+ digest.BlockUpdate(inner, 0, inner.Length);
+
+ return DigestUtilities.DoFinal(digest);
+ }
+
+ private byte[] GetZ(IDigest digest, byte[] userID, ECPoint pubPoint)
+ {
+ AddUserID(digest, userID);
+
+ AddFieldElement(digest, mECParams.Curve.A);
+ AddFieldElement(digest, mECParams.Curve.B);
+ AddFieldElement(digest, mECParams.G.AffineXCoord);
+ AddFieldElement(digest, mECParams.G.AffineYCoord);
+ AddFieldElement(digest, pubPoint.AffineXCoord);
+ AddFieldElement(digest, pubPoint.AffineYCoord);
+
+ return DigestUtilities.DoFinal(digest);
+ }
+
+ private void AddUserID(IDigest digest, byte[] userID)
+ {
+ uint len = (uint)(userID.Length * 8);
+
+ digest.Update((byte)(len >> 8));
+ digest.Update((byte)len);
+ digest.BlockUpdate(userID, 0, userID.Length);
+ }
+
+ private void AddFieldElement(IDigest digest, ECFieldElement v)
+ {
+ byte[] p = v.GetEncoded();
+ digest.BlockUpdate(p, 0, p.Length);
+ }
+ }
+}
diff --git a/crypto/src/crypto/agreement/kdf/ConcatenationKdfGenerator.cs b/crypto/src/crypto/agreement/kdf/ConcatenationKdfGenerator.cs
new file mode 100644
index 000000000..d88f4dfdb
--- /dev/null
+++ b/crypto/src/crypto/agreement/kdf/ConcatenationKdfGenerator.cs
@@ -0,0 +1,100 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Kdf
+{
+ /**
+ * Generator for Concatenation Key Derivation Function defined in NIST SP 800-56A, Sect 5.8.1
+ */
+ public class ConcatenationKdfGenerator
+ : IDerivationFunction
+ {
+ private readonly IDigest mDigest;
+
+ private byte[] mShared;
+ private byte[] mOtherInfo;
+ private int mHLen;
+
+ /**
+ * @param digest the digest to be used as the source of generated bytes
+ */
+ public ConcatenationKdfGenerator(IDigest digest)
+ {
+ this.mDigest = digest;
+ this.mHLen = digest.GetDigestSize();
+ }
+
+ public virtual void Init(IDerivationParameters param)
+ {
+ if (!(param is KdfParameters))
+ throw new ArgumentException("KDF parameters required for ConcatenationKdfGenerator");
+
+ KdfParameters p = (KdfParameters)param;
+
+ mShared = p.GetSharedSecret();
+ mOtherInfo = p.GetIV();
+ }
+
+ /**
+ * return the underlying digest.
+ */
+ public virtual IDigest Digest
+ {
+ get { return mDigest; }
+ }
+
+ /**
+ * fill len bytes of the output buffer with bytes generated from
+ * the derivation function.
+ *
+ * @throws DataLengthException if the out buffer is too small.
+ */
+ public virtual int GenerateBytes(byte[] outBytes, int outOff, int len)
+ {
+ if ((outBytes.Length - len) < outOff)
+ throw new DataLengthException("output buffer too small");
+
+ byte[] hashBuf = new byte[mHLen];
+ byte[] C = new byte[4];
+ uint counter = 1;
+ int outputLen = 0;
+
+ mDigest.Reset();
+
+ if (len > mHLen)
+ {
+ do
+ {
+ Pack.UInt32_To_BE(counter, C);
+
+ mDigest.BlockUpdate(C, 0, C.Length);
+ mDigest.BlockUpdate(mShared, 0, mShared.Length);
+ mDigest.BlockUpdate(mOtherInfo, 0, mOtherInfo.Length);
+
+ mDigest.DoFinal(hashBuf, 0);
+
+ Array.Copy(hashBuf, 0, outBytes, outOff + outputLen, mHLen);
+ outputLen += mHLen;
+ }
+ while ((counter++) < (len / mHLen));
+ }
+
+ if (outputLen < len)
+ {
+ Pack.UInt32_To_BE(counter, C);
+
+ mDigest.BlockUpdate(C, 0, C.Length);
+ mDigest.BlockUpdate(mShared, 0, mShared.Length);
+ mDigest.BlockUpdate(mOtherInfo, 0, mOtherInfo.Length);
+
+ mDigest.DoFinal(hashBuf, 0);
+
+ Array.Copy(hashBuf, 0, outBytes, outOff + outputLen, len - outputLen);
+ }
+
+ return len;
+ }
+ }
+}
diff --git a/crypto/src/crypto/digests/DSTU7564Digest.cs b/crypto/src/crypto/digests/DSTU7564Digest.cs
index 9de41dd6b..e641af6c2 100644
--- a/crypto/src/crypto/digests/DSTU7564Digest.cs
+++ b/crypto/src/crypto/digests/DSTU7564Digest.cs
@@ -11,48 +11,39 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Digests
{
- /**
- * implementation of Ukrainian DSTU 7564 hash function
- */
- public class Dstu7564Digest : IDigest, IMemoable
- {
- private const int ROWS = 8;
- private const int REDUCTION_POLYNOMIAL = 0x011d;
- private const int BITS_IN_BYTE = 8;
-
-
- private const int NB_512 = 8; //Number of 8-byte words in state for <=256-bit hash code.
- private const int NB_1024 = 16; //Number of 8-byte words in state for <=512-bit hash code.
-
- private const int NR_512 = 10; //Number of rounds for 512-bit state.
- private const int NR_1024 = 14; //Number of rounds for 1024-bit state.
-
- private const int STATE_BYTE_SIZE_512 = ROWS * NB_512;
- private const int STATE_BYTE_SIZE_1024 = ROWS * NB_1024;
-
- private int hashSize;
- private int blockSize;
-
-
-
- private int columns;
- private int rounds;
-
-
- private byte[] padded_;
-
- private byte[][] state_;
-
+ /**
+ * implementation of Ukrainian DSTU 7564 hash function
+ */
+ public class Dstu7564Digest : IDigest, IMemoable
+ {
+ private const int ROWS = 8;
+ private const int BITS_IN_BYTE = 8;
+
+ private const int NB_512 = 8; //Number of 8-byte words in state for <=256-bit hash code.
+ private const int NB_1024 = 16; //Number of 8-byte words in state for <=512-bit hash code.
+
+ private const int NR_512 = 10; //Number of rounds for 512-bit state.
+ private const int NR_1024 = 14; //Number of rounds for 1024-bit state.
+
+ private const int STATE_BYTE_SIZE_512 = ROWS * NB_512;
+ private const int STATE_BYTE_SIZE_1024 = ROWS * NB_1024;
+
+ private int hashSize;
+ private int blockSize;
+ private int columns;
+ private int rounds;
+ private byte[] padded_;
+ private byte[][] state_;
private ulong inputLength;
private int bufOff;
private byte[] buf;
public Dstu7564Digest(Dstu7564Digest digest)
{
- copyIn(digest);
+ CopyIn(digest);
}
- private void copyIn(Dstu7564Digest digest)
+ private void CopyIn(Dstu7564Digest digest)
{
this.hashSize = digest.hashSize;
this.blockSize = digest.blockSize;
@@ -62,6 +53,7 @@ namespace Org.BouncyCastle.Crypto.Digests
this.padded_ = Arrays.Clone(digest.padded_);
this.state_ = new byte[digest.state_.Length][];
+
for (int i = 0; i != this.state_.Length; i++)
{
this.state_[i] = Arrays.Clone(digest.state_[i]);
@@ -73,56 +65,51 @@ namespace Org.BouncyCastle.Crypto.Digests
}
public Dstu7564Digest(int hashSizeBits)
- {
- if (hashSizeBits == 256 || hashSizeBits == 384 || hashSizeBits == 512)
- {
- this.hashSize = hashSizeBits / 8;
- }
- else
- {
- throw new ArgumentException("Hash size is not recommended. Use 256 or 384 or 512 size");
- }
-
- if (hashSizeBits > 256)
- {
- this.blockSize = 1024 / 8;
- this.columns = NB_1024;
- this.rounds = NR_1024;
- this.state_ = new byte[STATE_BYTE_SIZE_1024][];
-
- }
- else
- {
- this.blockSize = 512 / 8;
- this.columns = NB_512;
- this.rounds = NR_512;
- this.state_ = new byte[STATE_BYTE_SIZE_512][];
-
- }
-
- //Console.WriteLine("length: " + state_.Length);
-
- for (int i = 0; i < state_.Length; i++)
- {
- this.state_[i] = new byte[columns];
- }
-
- this.state_[0][0] = (byte)state_.Length;
-
- this.hashSize = hashSizeBits / 8;
-
- this.padded_ = null;
- this.buf = new byte[blockSize];
- }
+ {
+ if (hashSizeBits == 256 || hashSizeBits == 384 || hashSizeBits == 512)
+ {
+ this.hashSize = hashSizeBits / 8;
+ }
+ else
+ {
+ throw new ArgumentException("Hash size is not recommended. Use 256 or 384 or 512 size");
+ }
+
+ if (hashSizeBits > 256)
+ {
+ this.blockSize = 1024 / 8;
+ this.columns = NB_1024;
+ this.rounds = NR_1024;
+ this.state_ = new byte[STATE_BYTE_SIZE_1024][];
+ }
+ else
+ {
+ this.blockSize = 512 / 8;
+ this.columns = NB_512;
+ this.rounds = NR_512;
+ this.state_ = new byte[STATE_BYTE_SIZE_512][];
+ }
+
+ for (int i = 0; i < state_.Length; i++)
+ {
+ this.state_[i] = new byte[columns];
+ }
- public string AlgorithmName
- {
- get { return "DSTU7564"; }
- }
+ this.state_[0][0] = (byte)state_.Length;
+ this.hashSize = hashSizeBits / 8;
- public virtual void BlockUpdate(byte[] input, int inOff, int length)
- {
+ this.padded_ = null;
+ this.buf = new byte[blockSize];
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return "DSTU7564"; }
+ }
+
+ public virtual void BlockUpdate(byte[] input, int inOff, int length)
+ {
while (bufOff != 0 && length > 0)
{
Update(input[inOff++]);
@@ -147,10 +134,8 @@ namespace Org.BouncyCastle.Crypto.Digests
}
}
- protected byte[] Pad(byte[] input, int inOff, int length)
- {
- //Console.WriteLine(length);
-
+ protected virtual byte[] Pad(byte[] input, int inOff, int length)
+ {
byte[] padded;
if (blockSize - length < 13) // terminator byte + 96 bits of length
{
@@ -161,55 +146,48 @@ namespace Org.BouncyCastle.Crypto.Digests
padded = new byte[blockSize];
}
+ Array.Copy(input, inOff, padded, 0, length);
+ padded[length] = 0x80;
+ Pack.UInt64_To_LE(inputLength * 8, padded, padded.Length - 12);
- Array.Copy(input, inOff, padded, 0, length);
- padded[length] = 0x80;
- Pack.UInt64_To_LE(inputLength * 8, padded, padded.Length - 12);
-
- return padded;
- }
-
- protected void ProcessBlock(byte[] input, int inOff)
- {
- byte[][] temp1 = new byte[STATE_BYTE_SIZE_1024][];
- byte[][] temp2 = new byte[STATE_BYTE_SIZE_1024][];
-
- for (int i = 0; i < state_.Length; i++)
- {
- temp1[i] = new byte[ROWS];
- temp2[i] = new byte[ROWS];
- }
-
- for (int i = 0; i < ROWS; ++i)
- {
- for (int j = 0; j < columns; ++j)
- {
- //Console.WriteLine("row = {0}, column = {1}", i, j);
-
- temp1[j][i] = (byte)(state_[j][i] ^ input[j * ROWS + i + inOff]);
- temp2[j][i] = input[j * ROWS + i + inOff];
+ return padded;
+ }
- }
-
- }
+ protected virtual void ProcessBlock(byte[] input, int inOff)
+ {
+ byte[][] temp1 = new byte[columns][];
+ byte[][] temp2 = new byte[columns][];
- P(temp1);
+ int pos = inOff;
+ for (int i = 0; i < columns; i++)
+ {
+ byte[] S = state_[i];
+ byte[] T1 = temp1[i] = new byte[ROWS];
+ byte[] T2 = temp2[i] = new byte[ROWS];
- Q(temp2);
+ for (int j = 0; j < ROWS; ++j)
+ {
+ byte inVal = input[pos++];
+ T1[j] = (byte)(S[j] ^ inVal);
+ T2[j] = inVal;
+ }
+ }
- for (int i = 0; i < ROWS; ++i)
- {
- for (int j = 0; j < columns; ++j)
- {
- state_[j][i] ^= (byte)(temp1[j][i] ^ temp2[j][i]);
+ P(temp1);
+ Q(temp2);
- }
-
- }
- }
+ for (int i = 0; i < columns; ++i)
+ {
+ byte[] S = state_[i], T1 = temp1[i], T2 = temp2[i];
+ for (int j = 0; j < ROWS; ++j)
+ {
+ S[j] ^= (byte)(T1[j] ^ T2[j]);
+ }
+ }
+ }
- public int DoFinal(byte[] output, int outOff)
- {
+ public virtual int DoFinal(byte[] output, int outOff)
+ {
padded_ = Pad(buf, 0, bufOff);
int paddedLen = padded_.Length;
@@ -222,55 +200,43 @@ namespace Org.BouncyCastle.Crypto.Digests
paddedLen -= blockSize;
}
-
- //Console.WriteLine(stateLine.Length);
-
byte[][] temp = new byte[STATE_BYTE_SIZE_1024][];
- for (int i = 0; i < state_.Length; i++)
- {
- temp[i] = new byte[ROWS];
- Array.Copy(state_[i], temp[i], ROWS);
- }
+ for (int i = 0; i < state_.Length; i++)
+ {
+ temp[i] = new byte[ROWS];
+ Array.Copy(state_[i], temp[i], ROWS);
+ }
P(temp);
for (int i = 0; i < ROWS; ++i)
{
- for (int j = 0; j < columns; ++j)
- {
- state_[j][i] ^= temp[j][i];
- //Console.Write("{0:x} ", state_[j][i]);
- }
- //Console.WriteLine();
+ for (int j = 0; j < columns; ++j)
+ {
+ state_[j][i] ^= temp[j][i];
+ }
}
byte[] stateLine = new byte[ROWS * columns];
int stateLineIndex = 0;
- for (int j = 0; j < columns; ++j)
- {
- for (int i = 0; i < ROWS; ++i)
- {
-
- stateLine[stateLineIndex] = state_[j][i];
- stateLineIndex++;
-
- //Console.WriteLine("index = {0}, row = {1}, column = {2}", stateLineIndex, i, j);
-
- }
- }
-
- //Console.WriteLine("final: " + Hex.ToHexString(stateLine));
- //Console.WriteLine(stateLine.Length);
-
- Array.Copy(stateLine, stateLine.Length - hashSize, output, outOff, hashSize);
-
- Reset();
-
- return hashSize;
- }
-
- public void Reset()
- {
+ for (int j = 0; j < columns; ++j)
+ {
+ for (int i = 0; i < ROWS; ++i)
+ {
+ stateLine[stateLineIndex] = state_[j][i];
+ stateLineIndex++;
+ }
+ }
+
+ Array.Copy(stateLine, stateLine.Length - hashSize, output, outOff, hashSize);
+
+ Reset();
+
+ return hashSize;
+ }
+
+ public virtual void Reset()
+ {
for (int bufferIndex = 0; bufferIndex < state_.Length; bufferIndex++)
{
state_[bufferIndex] = new byte[columns];
@@ -289,18 +255,18 @@ namespace Org.BouncyCastle.Crypto.Digests
}
}
- public int GetDigestSize()
- {
- return hashSize;
- }
+ public virtual int GetDigestSize()
+ {
+ return hashSize;
+ }
- public int GetByteLength()
- {
- return blockSize;
- }
+ public virtual int GetByteLength()
+ {
+ return blockSize;
+ }
- public void Update(byte input)
- {
+ public virtual void Update(byte input)
+ {
buf[bufOff++] = input;
if (bufOff == blockSize)
{
@@ -308,255 +274,238 @@ namespace Org.BouncyCastle.Crypto.Digests
bufOff = 0;
}
inputLength++;
- }
-
- void SubBytes(byte[][] state)
- {
- int i, j;
- for (i = 0; i < ROWS; ++i)
- {
- for (j = 0; j < columns; ++j)
- {
- state[j][i] = sBoxes[i % 4][state[j][i]];
- }
- }
- }
-
- void ShiftBytes(byte[][] state)
- {
- int i, j;
- byte[] temp = new byte[NB_1024];
- int shift = -1;
- for (i = 0; i < ROWS; ++i)
- {
- if ((i == ROWS - 1) && (columns == NB_1024))
- {
- shift = 11;
- }
- else
- {
- ++shift;
- }
- for (j = 0; j < columns; ++j)
- {
- temp[(j + shift) % columns] = state[j][i];
- }
- for (j = 0; j < columns; ++j)
- {
- state[j][i] = temp[j];
- }
- }
- }
-
- byte MultiplyGF(byte x, byte y)
- {
- int i;
- byte r = 0;
- byte hbit = 0;
- for (i = 0; i < BITS_IN_BYTE; ++i)
- {
- if ((y & 0x1) == 1)
- {
- r ^= x;
- }
-
- hbit = (byte)(x & 0x80);
-
- x <<= 1;
-
- if (hbit == 0x80)
- {
- x = (byte)((int)x ^ REDUCTION_POLYNOMIAL);
- }
-
- y >>= 1;
- }
- return r;
- }
-
- private void MixColumns(byte[][] state)
- {
- int i, row, col, b;
- byte product;
- byte[] result = new byte[ROWS];
-
- for (col = 0; col < columns; ++col)
- {
- Array.Clear(result, 0, ROWS);
- for (row = ROWS - 1; row >= 0; --row)
- {
- product = 0;
- for (b = ROWS - 1; b >= 0; --b)
- {
- product ^= MultiplyGF(state[col][b], mds_matrix[row][b]);
- }
- result[row] = product;
- }
- for (i = 0; i < ROWS; ++i)
- {
- state[col][i] = result[i];
- }
- }
- }
-
- void AddRoundConstantP(byte[][] state, int round)
- {
- int i;
- for (i = 0; i < columns; ++i)
- {
- state[i][0] ^= (byte)((i * 0x10) ^ round);
- }
- }
-
- void AddRoundConstantQ(byte[][] state, int round)
- {
- int j;
- UInt64[] s = new UInt64[columns];
-
- for (j = 0; j < columns; j++)
- {
- s[j] = Pack.LE_To_UInt64(state[j]);
-
- s[j] = s[j] + (0x00F0F0F0F0F0F0F3UL ^ ((((UInt64)(columns - j - 1) * 0x10UL) ^ (UInt64)round) << (7 * 8)));
-
- state[j] = Pack.UInt64_To_LE(s[j]);
- }
- }
-
- void P(byte[][] state)
- {
- int i;
- for (i = 0; i < rounds; ++i)
- {
- AddRoundConstantP(state, i);
- SubBytes(state);
- ShiftBytes(state);
- MixColumns(state);
- }
- }
-
- void Q(byte[][] state)
- {
- int i;
- for (i = 0; i < rounds; ++i)
- {
- AddRoundConstantQ(state, i);
- SubBytes(state);
- ShiftBytes(state);
- MixColumns(state);
- }
- }
-
- public IMemoable Copy()
+ }
+
+ private void SubBytes(byte[][] state)
+ {
+ int i, j;
+ for (i = 0; i < ROWS; ++i)
+ {
+ for (j = 0; j < columns; ++j)
+ {
+ state[j][i] = sBoxes[i % 4][state[j][i]];
+ }
+ }
+ }
+
+ private void ShiftBytes(byte[][] state)
+ {
+ int i, j;
+ byte[] temp = new byte[NB_1024];
+ int shift = -1;
+ for (i = 0; i < ROWS; ++i)
+ {
+ if ((i == ROWS - 1) && (columns == NB_1024))
+ {
+ shift = 11;
+ }
+ else
+ {
+ ++shift;
+ }
+ for (j = 0; j < columns; ++j)
+ {
+ temp[(j + shift) % columns] = state[j][i];
+ }
+ for (j = 0; j < columns; ++j)
+ {
+ state[j][i] = temp[j];
+ }
+ }
+ }
+
+ /* Pair-wise GF multiplication of 4 byte-pairs (at bits 0, 16, 32, 48 within x, y) */
+ private static ulong MultiplyGFx4(ulong u, ulong v)
+ {
+ ulong r = u & ((v & 0x0001000100010001UL) * 0xFFFFUL);
+
+ for (int i = 1; i < 8; ++i)
+ {
+ u <<= 1;
+ v >>= 1;
+ r ^= u & ((v & 0x0001000100010001L) * 0xFFFFL);
+ }
+
+ // REDUCTION_POLYNOMIAL = 0x011d; /* x^8 + x^4 + x^3 + x^2 + 1 */
+
+ ulong hi = r & 0xFF00FF00FF00FF00UL;
+ r ^= hi ^ (hi >> 4) ^ (hi >> 5) ^ (hi >> 6) ^ (hi >> 8);
+ hi = r & 0x0F000F000F000F00UL;
+ r ^= hi ^ (hi >> 4) ^ (hi >> 5) ^ (hi >> 6) ^ (hi >> 8);
+ return r;
+ }
+
+ private void MixColumns(byte[][] state)
+ {
+ for (int col = 0; col < columns; ++col)
+ {
+ ulong colVal = Pack.LE_To_UInt64(state[col]);
+ ulong colEven = colVal & 0x00FF00FF00FF00FFUL;
+ ulong colOdd = (colVal >> 8) & 0x00FF00FF00FF00FFUL;
+
+ //ulong rowMatrix = (mdsMatrix >> 8) | (mdsMatrix << 56);
+ ulong rowMatrix = mdsMatrix;
+
+ ulong result = 0;
+ for (int row = 7; row >= 0; --row)
+ {
+ ulong product = MultiplyGFx4(colEven, rowMatrix & 0x00FF00FF00FF00FFUL);
+
+ rowMatrix = (rowMatrix >> 8) | (rowMatrix << 56);
+
+ product ^= MultiplyGFx4(colOdd, rowMatrix & 0x00FF00FF00FF00FFUL);
+
+ product ^= (product >> 32);
+ product ^= (product >> 16);
+
+ result <<= 8;
+ result |= (product & 0xFFUL);
+ }
+
+ Pack.UInt64_To_LE(result, state[col]);
+ }
+ }
+
+ private void AddRoundConstantP(byte[][] state, int round)
+ {
+ int i;
+ for (i = 0; i < columns; ++i)
+ {
+ state[i][0] ^= (byte)((i * 0x10) ^ round);
+ }
+ }
+
+ private void AddRoundConstantQ(byte[][] state, int round)
+ {
+ int j;
+ UInt64[] s = new UInt64[columns];
+
+ for (j = 0; j < columns; j++)
+ {
+ s[j] = Pack.LE_To_UInt64(state[j]);
+
+ s[j] = s[j] + (0x00F0F0F0F0F0F0F3UL ^ ((((UInt64)(columns - j - 1) * 0x10UL) ^ (UInt64)round) << (7 * 8)));
+
+ state[j] = Pack.UInt64_To_LE(s[j]);
+ }
+ }
+
+ private void P(byte[][] state)
+ {
+ int i;
+ for (i = 0; i < rounds; ++i)
+ {
+ AddRoundConstantP(state, i);
+ SubBytes(state);
+ ShiftBytes(state);
+ MixColumns(state);
+ }
+ }
+
+ private void Q(byte[][] state)
+ {
+ int i;
+ for (i = 0; i < rounds; ++i)
+ {
+ AddRoundConstantQ(state, i);
+ SubBytes(state);
+ ShiftBytes(state);
+ MixColumns(state);
+ }
+ }
+
+ public virtual IMemoable Copy()
{
return new Dstu7564Digest(this);
}
- public void Reset(IMemoable other)
+ public virtual void Reset(IMemoable other)
{
Dstu7564Digest d = (Dstu7564Digest)other;
- copyIn(d);
+ CopyIn(d);
}
- private readonly byte[][] mds_matrix = new byte[][]
- {
- new byte[] {0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04},
- new byte[] {0x04, 0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07},
- new byte[] {0x07, 0x04, 0x01, 0x01, 0x05, 0x01, 0x08, 0x06},
- new byte[] {0x06, 0x07, 0x04, 0x01, 0x01, 0x05, 0x01, 0x08},
- new byte[] {0x08, 0x06, 0x07, 0x04, 0x01, 0x01, 0x05, 0x01},
- new byte[] {0x01, 0x08, 0x06, 0x07, 0x04, 0x01, 0x01, 0x05},
- new byte[] {0x05, 0x01, 0x08, 0x06, 0x07, 0x04, 0x01, 0x01},
- new byte[] {0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04, 0x01}
- };
-
-
-
-
- private readonly byte[][] sBoxes = new byte[][]
- {
- new byte[]
- {
- 0xa8, 0x43, 0x5f, 0x06, 0x6b, 0x75, 0x6c, 0x59, 0x71, 0xdf, 0x87, 0x95, 0x17, 0xf0, 0xd8, 0x09,
- 0x6d, 0xf3, 0x1d, 0xcb, 0xc9, 0x4d, 0x2c, 0xaf, 0x79, 0xe0, 0x97, 0xfd, 0x6f, 0x4b, 0x45, 0x39,
- 0x3e, 0xdd, 0xa3, 0x4f, 0xb4, 0xb6, 0x9a, 0x0e, 0x1f, 0xbf, 0x15, 0xe1, 0x49, 0xd2, 0x93, 0xc6,
- 0x92, 0x72, 0x9e, 0x61, 0xd1, 0x63, 0xfa, 0xee, 0xf4, 0x19, 0xd5, 0xad, 0x58, 0xa4, 0xbb, 0xa1,
- 0xdc, 0xf2, 0x83, 0x37, 0x42, 0xe4, 0x7a, 0x32, 0x9c, 0xcc, 0xab, 0x4a, 0x8f, 0x6e, 0x04, 0x27,
- 0x2e, 0xe7, 0xe2, 0x5a, 0x96, 0x16, 0x23, 0x2b, 0xc2, 0x65, 0x66, 0x0f, 0xbc, 0xa9, 0x47, 0x41,
- 0x34, 0x48, 0xfc, 0xb7, 0x6a, 0x88, 0xa5, 0x53, 0x86, 0xf9, 0x5b, 0xdb, 0x38, 0x7b, 0xc3, 0x1e,
- 0x22, 0x33, 0x24, 0x28, 0x36, 0xc7, 0xb2, 0x3b, 0x8e, 0x77, 0xba, 0xf5, 0x14, 0x9f, 0x08, 0x55,
- 0x9b, 0x4c, 0xfe, 0x60, 0x5c, 0xda, 0x18, 0x46, 0xcd, 0x7d, 0x21, 0xb0, 0x3f, 0x1b, 0x89, 0xff,
- 0xeb, 0x84, 0x69, 0x3a, 0x9d, 0xd7, 0xd3, 0x70, 0x67, 0x40, 0xb5, 0xde, 0x5d, 0x30, 0x91, 0xb1,
- 0x78, 0x11, 0x01, 0xe5, 0x00, 0x68, 0x98, 0xa0, 0xc5, 0x02, 0xa6, 0x74, 0x2d, 0x0b, 0xa2, 0x76,
- 0xb3, 0xbe, 0xce, 0xbd, 0xae, 0xe9, 0x8a, 0x31, 0x1c, 0xec, 0xf1, 0x99, 0x94, 0xaa, 0xf6, 0x26,
- 0x2f, 0xef, 0xe8, 0x8c, 0x35, 0x03, 0xd4, 0x7f, 0xfb, 0x05, 0xc1, 0x5e, 0x90, 0x20, 0x3d, 0x82,
- 0xf7, 0xea, 0x0a, 0x0d, 0x7e, 0xf8, 0x50, 0x1a, 0xc4, 0x07, 0x57, 0xb8, 0x3c, 0x62, 0xe3, 0xc8,
- 0xac, 0x52, 0x64, 0x10, 0xd0, 0xd9, 0x13, 0x0c, 0x12, 0x29, 0x51, 0xb9, 0xcf, 0xd6, 0x73, 0x8d,
- 0x81, 0x54, 0xc0, 0xed, 0x4e, 0x44, 0xa7, 0x2a, 0x85, 0x25, 0xe6, 0xca, 0x7c, 0x8b, 0x56, 0x80
- },
-
- new byte[]
- {
- 0xce, 0xbb, 0xeb, 0x92, 0xea, 0xcb, 0x13, 0xc1, 0xe9, 0x3a, 0xd6, 0xb2, 0xd2, 0x90, 0x17, 0xf8,
- 0x42, 0x15, 0x56, 0xb4, 0x65, 0x1c, 0x88, 0x43, 0xc5, 0x5c, 0x36, 0xba, 0xf5, 0x57, 0x67, 0x8d,
- 0x31, 0xf6, 0x64, 0x58, 0x9e, 0xf4, 0x22, 0xaa, 0x75, 0x0f, 0x02, 0xb1, 0xdf, 0x6d, 0x73, 0x4d,
- 0x7c, 0x26, 0x2e, 0xf7, 0x08, 0x5d, 0x44, 0x3e, 0x9f, 0x14, 0xc8, 0xae, 0x54, 0x10, 0xd8, 0xbc,
- 0x1a, 0x6b, 0x69, 0xf3, 0xbd, 0x33, 0xab, 0xfa, 0xd1, 0x9b, 0x68, 0x4e, 0x16, 0x95, 0x91, 0xee,
- 0x4c, 0x63, 0x8e, 0x5b, 0xcc, 0x3c, 0x19, 0xa1, 0x81, 0x49, 0x7b, 0xd9, 0x6f, 0x37, 0x60, 0xca,
- 0xe7, 0x2b, 0x48, 0xfd, 0x96, 0x45, 0xfc, 0x41, 0x12, 0x0d, 0x79, 0xe5, 0x89, 0x8c, 0xe3, 0x20,
- 0x30, 0xdc, 0xb7, 0x6c, 0x4a, 0xb5, 0x3f, 0x97, 0xd4, 0x62, 0x2d, 0x06, 0xa4, 0xa5, 0x83, 0x5f,
- 0x2a, 0xda, 0xc9, 0x00, 0x7e, 0xa2, 0x55, 0xbf, 0x11, 0xd5, 0x9c, 0xcf, 0x0e, 0x0a, 0x3d, 0x51,
- 0x7d, 0x93, 0x1b, 0xfe, 0xc4, 0x47, 0x09, 0x86, 0x0b, 0x8f, 0x9d, 0x6a, 0x07, 0xb9, 0xb0, 0x98,
- 0x18, 0x32, 0x71, 0x4b, 0xef, 0x3b, 0x70, 0xa0, 0xe4, 0x40, 0xff, 0xc3, 0xa9, 0xe6, 0x78, 0xf9,
- 0x8b, 0x46, 0x80, 0x1e, 0x38, 0xe1, 0xb8, 0xa8, 0xe0, 0x0c, 0x23, 0x76, 0x1d, 0x25, 0x24, 0x05,
- 0xf1, 0x6e, 0x94, 0x28, 0x9a, 0x84, 0xe8, 0xa3, 0x4f, 0x77, 0xd3, 0x85, 0xe2, 0x52, 0xf2, 0x82,
- 0x50, 0x7a, 0x2f, 0x74, 0x53, 0xb3, 0x61, 0xaf, 0x39, 0x35, 0xde, 0xcd, 0x1f, 0x99, 0xac, 0xad,
- 0x72, 0x2c, 0xdd, 0xd0, 0x87, 0xbe, 0x5e, 0xa6, 0xec, 0x04, 0xc6, 0x03, 0x34, 0xfb, 0xdb, 0x59,
- 0xb6, 0xc2, 0x01, 0xf0, 0x5a, 0xed, 0xa7, 0x66, 0x21, 0x7f, 0x8a, 0x27, 0xc7, 0xc0, 0x29, 0xd7
- },
-
- new byte[]
- {
- 0x93, 0xd9, 0x9a, 0xb5, 0x98, 0x22, 0x45, 0xfc, 0xba, 0x6a, 0xdf, 0x02, 0x9f, 0xdc, 0x51, 0x59,
- 0x4a, 0x17, 0x2b, 0xc2, 0x94, 0xf4, 0xbb, 0xa3, 0x62, 0xe4, 0x71, 0xd4, 0xcd, 0x70, 0x16, 0xe1,
- 0x49, 0x3c, 0xc0, 0xd8, 0x5c, 0x9b, 0xad, 0x85, 0x53, 0xa1, 0x7a, 0xc8, 0x2d, 0xe0, 0xd1, 0x72,
- 0xa6, 0x2c, 0xc4, 0xe3, 0x76, 0x78, 0xb7, 0xb4, 0x09, 0x3b, 0x0e, 0x41, 0x4c, 0xde, 0xb2, 0x90,
- 0x25, 0xa5, 0xd7, 0x03, 0x11, 0x00, 0xc3, 0x2e, 0x92, 0xef, 0x4e, 0x12, 0x9d, 0x7d, 0xcb, 0x35,
- 0x10, 0xd5, 0x4f, 0x9e, 0x4d, 0xa9, 0x55, 0xc6, 0xd0, 0x7b, 0x18, 0x97, 0xd3, 0x36, 0xe6, 0x48,
- 0x56, 0x81, 0x8f, 0x77, 0xcc, 0x9c, 0xb9, 0xe2, 0xac, 0xb8, 0x2f, 0x15, 0xa4, 0x7c, 0xda, 0x38,
- 0x1e, 0x0b, 0x05, 0xd6, 0x14, 0x6e, 0x6c, 0x7e, 0x66, 0xfd, 0xb1, 0xe5, 0x60, 0xaf, 0x5e, 0x33,
- 0x87, 0xc9, 0xf0, 0x5d, 0x6d, 0x3f, 0x88, 0x8d, 0xc7, 0xf7, 0x1d, 0xe9, 0xec, 0xed, 0x80, 0x29,
- 0x27, 0xcf, 0x99, 0xa8, 0x50, 0x0f, 0x37, 0x24, 0x28, 0x30, 0x95, 0xd2, 0x3e, 0x5b, 0x40, 0x83,
- 0xb3, 0x69, 0x57, 0x1f, 0x07, 0x1c, 0x8a, 0xbc, 0x20, 0xeb, 0xce, 0x8e, 0xab, 0xee, 0x31, 0xa2,
- 0x73, 0xf9, 0xca, 0x3a, 0x1a, 0xfb, 0x0d, 0xc1, 0xfe, 0xfa, 0xf2, 0x6f, 0xbd, 0x96, 0xdd, 0x43,
- 0x52, 0xb6, 0x08, 0xf3, 0xae, 0xbe, 0x19, 0x89, 0x32, 0x26, 0xb0, 0xea, 0x4b, 0x64, 0x84, 0x82,
- 0x6b, 0xf5, 0x79, 0xbf, 0x01, 0x5f, 0x75, 0x63, 0x1b, 0x23, 0x3d, 0x68, 0x2a, 0x65, 0xe8, 0x91,
- 0xf6, 0xff, 0x13, 0x58, 0xf1, 0x47, 0x0a, 0x7f, 0xc5, 0xa7, 0xe7, 0x61, 0x5a, 0x06, 0x46, 0x44,
- 0x42, 0x04, 0xa0, 0xdb, 0x39, 0x86, 0x54, 0xaa, 0x8c, 0x34, 0x21, 0x8b, 0xf8, 0x0c, 0x74, 0x67
- },
-
- new byte[]
- {
- 0x68, 0x8d, 0xca, 0x4d, 0x73, 0x4b, 0x4e, 0x2a, 0xd4, 0x52, 0x26, 0xb3, 0x54, 0x1e, 0x19, 0x1f,
- 0x22, 0x03, 0x46, 0x3d, 0x2d, 0x4a, 0x53, 0x83, 0x13, 0x8a, 0xb7, 0xd5, 0x25, 0x79, 0xf5, 0xbd,
- 0x58, 0x2f, 0x0d, 0x02, 0xed, 0x51, 0x9e, 0x11, 0xf2, 0x3e, 0x55, 0x5e, 0xd1, 0x16, 0x3c, 0x66,
- 0x70, 0x5d, 0xf3, 0x45, 0x40, 0xcc, 0xe8, 0x94, 0x56, 0x08, 0xce, 0x1a, 0x3a, 0xd2, 0xe1, 0xdf,
- 0xb5, 0x38, 0x6e, 0x0e, 0xe5, 0xf4, 0xf9, 0x86, 0xe9, 0x4f, 0xd6, 0x85, 0x23, 0xcf, 0x32, 0x99,
- 0x31, 0x14, 0xae, 0xee, 0xc8, 0x48, 0xd3, 0x30, 0xa1, 0x92, 0x41, 0xb1, 0x18, 0xc4, 0x2c, 0x71,
- 0x72, 0x44, 0x15, 0xfd, 0x37, 0xbe, 0x5f, 0xaa, 0x9b, 0x88, 0xd8, 0xab, 0x89, 0x9c, 0xfa, 0x60,
- 0xea, 0xbc, 0x62, 0x0c, 0x24, 0xa6, 0xa8, 0xec, 0x67, 0x20, 0xdb, 0x7c, 0x28, 0xdd, 0xac, 0x5b,
- 0x34, 0x7e, 0x10, 0xf1, 0x7b, 0x8f, 0x63, 0xa0, 0x05, 0x9a, 0x43, 0x77, 0x21, 0xbf, 0x27, 0x09,
- 0xc3, 0x9f, 0xb6, 0xd7, 0x29, 0xc2, 0xeb, 0xc0, 0xa4, 0x8b, 0x8c, 0x1d, 0xfb, 0xff, 0xc1, 0xb2,
- 0x97, 0x2e, 0xf8, 0x65, 0xf6, 0x75, 0x07, 0x04, 0x49, 0x33, 0xe4, 0xd9, 0xb9, 0xd0, 0x42, 0xc7,
- 0x6c, 0x90, 0x00, 0x8e, 0x6f, 0x50, 0x01, 0xc5, 0xda, 0x47, 0x3f, 0xcd, 0x69, 0xa2, 0xe2, 0x7a,
- 0xa7, 0xc6, 0x93, 0x0f, 0x0a, 0x06, 0xe6, 0x2b, 0x96, 0xa3, 0x1c, 0xaf, 0x6a, 0x12, 0x84, 0x39,
- 0xe7, 0xb0, 0x82, 0xf7, 0xfe, 0x9d, 0x87, 0x5c, 0x81, 0x35, 0xde, 0xb4, 0xa5, 0xfc, 0x80, 0xef,
- 0xcb, 0xbb, 0x6b, 0x76, 0xba, 0x5a, 0x7d, 0x78, 0x0b, 0x95, 0xe3, 0xad, 0x74, 0x98, 0x3b, 0x36,
- 0x64, 0x6d, 0xdc, 0xf0, 0x59, 0xa9, 0x4c, 0x17, 0x7f, 0x91, 0xb8, 0xc9, 0x57, 0x1b, 0xe0, 0x61
- }
- };
-
-
- }
+ //private const ulong mdsMatrix = 0x0407060801050101UL;
+ private const ulong mdsMatrix = 0x0104070608010501UL;
+
+ private static readonly byte[][] sBoxes = new byte[][]
+ {
+ new byte[] {
+ 0xa8, 0x43, 0x5f, 0x06, 0x6b, 0x75, 0x6c, 0x59, 0x71, 0xdf, 0x87, 0x95, 0x17, 0xf0, 0xd8, 0x09,
+ 0x6d, 0xf3, 0x1d, 0xcb, 0xc9, 0x4d, 0x2c, 0xaf, 0x79, 0xe0, 0x97, 0xfd, 0x6f, 0x4b, 0x45, 0x39,
+ 0x3e, 0xdd, 0xa3, 0x4f, 0xb4, 0xb6, 0x9a, 0x0e, 0x1f, 0xbf, 0x15, 0xe1, 0x49, 0xd2, 0x93, 0xc6,
+ 0x92, 0x72, 0x9e, 0x61, 0xd1, 0x63, 0xfa, 0xee, 0xf4, 0x19, 0xd5, 0xad, 0x58, 0xa4, 0xbb, 0xa1,
+ 0xdc, 0xf2, 0x83, 0x37, 0x42, 0xe4, 0x7a, 0x32, 0x9c, 0xcc, 0xab, 0x4a, 0x8f, 0x6e, 0x04, 0x27,
+ 0x2e, 0xe7, 0xe2, 0x5a, 0x96, 0x16, 0x23, 0x2b, 0xc2, 0x65, 0x66, 0x0f, 0xbc, 0xa9, 0x47, 0x41,
+ 0x34, 0x48, 0xfc, 0xb7, 0x6a, 0x88, 0xa5, 0x53, 0x86, 0xf9, 0x5b, 0xdb, 0x38, 0x7b, 0xc3, 0x1e,
+ 0x22, 0x33, 0x24, 0x28, 0x36, 0xc7, 0xb2, 0x3b, 0x8e, 0x77, 0xba, 0xf5, 0x14, 0x9f, 0x08, 0x55,
+ 0x9b, 0x4c, 0xfe, 0x60, 0x5c, 0xda, 0x18, 0x46, 0xcd, 0x7d, 0x21, 0xb0, 0x3f, 0x1b, 0x89, 0xff,
+ 0xeb, 0x84, 0x69, 0x3a, 0x9d, 0xd7, 0xd3, 0x70, 0x67, 0x40, 0xb5, 0xde, 0x5d, 0x30, 0x91, 0xb1,
+ 0x78, 0x11, 0x01, 0xe5, 0x00, 0x68, 0x98, 0xa0, 0xc5, 0x02, 0xa6, 0x74, 0x2d, 0x0b, 0xa2, 0x76,
+ 0xb3, 0xbe, 0xce, 0xbd, 0xae, 0xe9, 0x8a, 0x31, 0x1c, 0xec, 0xf1, 0x99, 0x94, 0xaa, 0xf6, 0x26,
+ 0x2f, 0xef, 0xe8, 0x8c, 0x35, 0x03, 0xd4, 0x7f, 0xfb, 0x05, 0xc1, 0x5e, 0x90, 0x20, 0x3d, 0x82,
+ 0xf7, 0xea, 0x0a, 0x0d, 0x7e, 0xf8, 0x50, 0x1a, 0xc4, 0x07, 0x57, 0xb8, 0x3c, 0x62, 0xe3, 0xc8,
+ 0xac, 0x52, 0x64, 0x10, 0xd0, 0xd9, 0x13, 0x0c, 0x12, 0x29, 0x51, 0xb9, 0xcf, 0xd6, 0x73, 0x8d,
+ 0x81, 0x54, 0xc0, 0xed, 0x4e, 0x44, 0xa7, 0x2a, 0x85, 0x25, 0xe6, 0xca, 0x7c, 0x8b, 0x56, 0x80
+ },
+
+ new byte[] {
+ 0xce, 0xbb, 0xeb, 0x92, 0xea, 0xcb, 0x13, 0xc1, 0xe9, 0x3a, 0xd6, 0xb2, 0xd2, 0x90, 0x17, 0xf8,
+ 0x42, 0x15, 0x56, 0xb4, 0x65, 0x1c, 0x88, 0x43, 0xc5, 0x5c, 0x36, 0xba, 0xf5, 0x57, 0x67, 0x8d,
+ 0x31, 0xf6, 0x64, 0x58, 0x9e, 0xf4, 0x22, 0xaa, 0x75, 0x0f, 0x02, 0xb1, 0xdf, 0x6d, 0x73, 0x4d,
+ 0x7c, 0x26, 0x2e, 0xf7, 0x08, 0x5d, 0x44, 0x3e, 0x9f, 0x14, 0xc8, 0xae, 0x54, 0x10, 0xd8, 0xbc,
+ 0x1a, 0x6b, 0x69, 0xf3, 0xbd, 0x33, 0xab, 0xfa, 0xd1, 0x9b, 0x68, 0x4e, 0x16, 0x95, 0x91, 0xee,
+ 0x4c, 0x63, 0x8e, 0x5b, 0xcc, 0x3c, 0x19, 0xa1, 0x81, 0x49, 0x7b, 0xd9, 0x6f, 0x37, 0x60, 0xca,
+ 0xe7, 0x2b, 0x48, 0xfd, 0x96, 0x45, 0xfc, 0x41, 0x12, 0x0d, 0x79, 0xe5, 0x89, 0x8c, 0xe3, 0x20,
+ 0x30, 0xdc, 0xb7, 0x6c, 0x4a, 0xb5, 0x3f, 0x97, 0xd4, 0x62, 0x2d, 0x06, 0xa4, 0xa5, 0x83, 0x5f,
+ 0x2a, 0xda, 0xc9, 0x00, 0x7e, 0xa2, 0x55, 0xbf, 0x11, 0xd5, 0x9c, 0xcf, 0x0e, 0x0a, 0x3d, 0x51,
+ 0x7d, 0x93, 0x1b, 0xfe, 0xc4, 0x47, 0x09, 0x86, 0x0b, 0x8f, 0x9d, 0x6a, 0x07, 0xb9, 0xb0, 0x98,
+ 0x18, 0x32, 0x71, 0x4b, 0xef, 0x3b, 0x70, 0xa0, 0xe4, 0x40, 0xff, 0xc3, 0xa9, 0xe6, 0x78, 0xf9,
+ 0x8b, 0x46, 0x80, 0x1e, 0x38, 0xe1, 0xb8, 0xa8, 0xe0, 0x0c, 0x23, 0x76, 0x1d, 0x25, 0x24, 0x05,
+ 0xf1, 0x6e, 0x94, 0x28, 0x9a, 0x84, 0xe8, 0xa3, 0x4f, 0x77, 0xd3, 0x85, 0xe2, 0x52, 0xf2, 0x82,
+ 0x50, 0x7a, 0x2f, 0x74, 0x53, 0xb3, 0x61, 0xaf, 0x39, 0x35, 0xde, 0xcd, 0x1f, 0x99, 0xac, 0xad,
+ 0x72, 0x2c, 0xdd, 0xd0, 0x87, 0xbe, 0x5e, 0xa6, 0xec, 0x04, 0xc6, 0x03, 0x34, 0xfb, 0xdb, 0x59,
+ 0xb6, 0xc2, 0x01, 0xf0, 0x5a, 0xed, 0xa7, 0x66, 0x21, 0x7f, 0x8a, 0x27, 0xc7, 0xc0, 0x29, 0xd7
+ },
+
+ new byte[]{
+ 0x93, 0xd9, 0x9a, 0xb5, 0x98, 0x22, 0x45, 0xfc, 0xba, 0x6a, 0xdf, 0x02, 0x9f, 0xdc, 0x51, 0x59,
+ 0x4a, 0x17, 0x2b, 0xc2, 0x94, 0xf4, 0xbb, 0xa3, 0x62, 0xe4, 0x71, 0xd4, 0xcd, 0x70, 0x16, 0xe1,
+ 0x49, 0x3c, 0xc0, 0xd8, 0x5c, 0x9b, 0xad, 0x85, 0x53, 0xa1, 0x7a, 0xc8, 0x2d, 0xe0, 0xd1, 0x72,
+ 0xa6, 0x2c, 0xc4, 0xe3, 0x76, 0x78, 0xb7, 0xb4, 0x09, 0x3b, 0x0e, 0x41, 0x4c, 0xde, 0xb2, 0x90,
+ 0x25, 0xa5, 0xd7, 0x03, 0x11, 0x00, 0xc3, 0x2e, 0x92, 0xef, 0x4e, 0x12, 0x9d, 0x7d, 0xcb, 0x35,
+ 0x10, 0xd5, 0x4f, 0x9e, 0x4d, 0xa9, 0x55, 0xc6, 0xd0, 0x7b, 0x18, 0x97, 0xd3, 0x36, 0xe6, 0x48,
+ 0x56, 0x81, 0x8f, 0x77, 0xcc, 0x9c, 0xb9, 0xe2, 0xac, 0xb8, 0x2f, 0x15, 0xa4, 0x7c, 0xda, 0x38,
+ 0x1e, 0x0b, 0x05, 0xd6, 0x14, 0x6e, 0x6c, 0x7e, 0x66, 0xfd, 0xb1, 0xe5, 0x60, 0xaf, 0x5e, 0x33,
+ 0x87, 0xc9, 0xf0, 0x5d, 0x6d, 0x3f, 0x88, 0x8d, 0xc7, 0xf7, 0x1d, 0xe9, 0xec, 0xed, 0x80, 0x29,
+ 0x27, 0xcf, 0x99, 0xa8, 0x50, 0x0f, 0x37, 0x24, 0x28, 0x30, 0x95, 0xd2, 0x3e, 0x5b, 0x40, 0x83,
+ 0xb3, 0x69, 0x57, 0x1f, 0x07, 0x1c, 0x8a, 0xbc, 0x20, 0xeb, 0xce, 0x8e, 0xab, 0xee, 0x31, 0xa2,
+ 0x73, 0xf9, 0xca, 0x3a, 0x1a, 0xfb, 0x0d, 0xc1, 0xfe, 0xfa, 0xf2, 0x6f, 0xbd, 0x96, 0xdd, 0x43,
+ 0x52, 0xb6, 0x08, 0xf3, 0xae, 0xbe, 0x19, 0x89, 0x32, 0x26, 0xb0, 0xea, 0x4b, 0x64, 0x84, 0x82,
+ 0x6b, 0xf5, 0x79, 0xbf, 0x01, 0x5f, 0x75, 0x63, 0x1b, 0x23, 0x3d, 0x68, 0x2a, 0x65, 0xe8, 0x91,
+ 0xf6, 0xff, 0x13, 0x58, 0xf1, 0x47, 0x0a, 0x7f, 0xc5, 0xa7, 0xe7, 0x61, 0x5a, 0x06, 0x46, 0x44,
+ 0x42, 0x04, 0xa0, 0xdb, 0x39, 0x86, 0x54, 0xaa, 0x8c, 0x34, 0x21, 0x8b, 0xf8, 0x0c, 0x74, 0x67
+ },
+
+ new byte[]{
+ 0x68, 0x8d, 0xca, 0x4d, 0x73, 0x4b, 0x4e, 0x2a, 0xd4, 0x52, 0x26, 0xb3, 0x54, 0x1e, 0x19, 0x1f,
+ 0x22, 0x03, 0x46, 0x3d, 0x2d, 0x4a, 0x53, 0x83, 0x13, 0x8a, 0xb7, 0xd5, 0x25, 0x79, 0xf5, 0xbd,
+ 0x58, 0x2f, 0x0d, 0x02, 0xed, 0x51, 0x9e, 0x11, 0xf2, 0x3e, 0x55, 0x5e, 0xd1, 0x16, 0x3c, 0x66,
+ 0x70, 0x5d, 0xf3, 0x45, 0x40, 0xcc, 0xe8, 0x94, 0x56, 0x08, 0xce, 0x1a, 0x3a, 0xd2, 0xe1, 0xdf,
+ 0xb5, 0x38, 0x6e, 0x0e, 0xe5, 0xf4, 0xf9, 0x86, 0xe9, 0x4f, 0xd6, 0x85, 0x23, 0xcf, 0x32, 0x99,
+ 0x31, 0x14, 0xae, 0xee, 0xc8, 0x48, 0xd3, 0x30, 0xa1, 0x92, 0x41, 0xb1, 0x18, 0xc4, 0x2c, 0x71,
+ 0x72, 0x44, 0x15, 0xfd, 0x37, 0xbe, 0x5f, 0xaa, 0x9b, 0x88, 0xd8, 0xab, 0x89, 0x9c, 0xfa, 0x60,
+ 0xea, 0xbc, 0x62, 0x0c, 0x24, 0xa6, 0xa8, 0xec, 0x67, 0x20, 0xdb, 0x7c, 0x28, 0xdd, 0xac, 0x5b,
+ 0x34, 0x7e, 0x10, 0xf1, 0x7b, 0x8f, 0x63, 0xa0, 0x05, 0x9a, 0x43, 0x77, 0x21, 0xbf, 0x27, 0x09,
+ 0xc3, 0x9f, 0xb6, 0xd7, 0x29, 0xc2, 0xeb, 0xc0, 0xa4, 0x8b, 0x8c, 0x1d, 0xfb, 0xff, 0xc1, 0xb2,
+ 0x97, 0x2e, 0xf8, 0x65, 0xf6, 0x75, 0x07, 0x04, 0x49, 0x33, 0xe4, 0xd9, 0xb9, 0xd0, 0x42, 0xc7,
+ 0x6c, 0x90, 0x00, 0x8e, 0x6f, 0x50, 0x01, 0xc5, 0xda, 0x47, 0x3f, 0xcd, 0x69, 0xa2, 0xe2, 0x7a,
+ 0xa7, 0xc6, 0x93, 0x0f, 0x0a, 0x06, 0xe6, 0x2b, 0x96, 0xa3, 0x1c, 0xaf, 0x6a, 0x12, 0x84, 0x39,
+ 0xe7, 0xb0, 0x82, 0xf7, 0xfe, 0x9d, 0x87, 0x5c, 0x81, 0x35, 0xde, 0xb4, 0xa5, 0xfc, 0x80, 0xef,
+ 0xcb, 0xbb, 0x6b, 0x76, 0xba, 0x5a, 0x7d, 0x78, 0x0b, 0x95, 0xe3, 0xad, 0x74, 0x98, 0x3b, 0x36,
+ 0x64, 0x6d, 0xdc, 0xf0, 0x59, 0xa9, 0x4c, 0x17, 0x7f, 0x91, 0xb8, 0xc9, 0x57, 0x1b, 0xe0, 0x61
+ }
+ };
+ }
}
diff --git a/crypto/src/crypto/engines/Dstu7624Engine.cs b/crypto/src/crypto/engines/Dstu7624Engine.cs
index cdb0f50e0..534a23bbf 100644
--- a/crypto/src/crypto/engines/Dstu7624Engine.cs
+++ b/crypto/src/crypto/engines/Dstu7624Engine.cs
@@ -16,8 +16,6 @@ namespace Org.BouncyCastle.Crypto.Engines
private static readonly int BITS_IN_WORD = 64;
private static readonly int BITS_IN_BYTE = 8;
- private static readonly int REDUCTION_POLYNOMIAL = 0x011d; /* x^8 + x^4 + x^3 + x^2 + 1 */
-
private ulong[] internalState;
private ulong[] workingKey;
private ulong[][] roundKeys;
@@ -472,51 +470,55 @@ namespace Org.BouncyCastle.Crypto.Engines
MatrixMultiply(mdsInvMatrix);
}
- private void MatrixMultiply(byte[][] matrix)
+ private void MatrixMultiply(ulong matrix)
{
- int col, row, b;
- byte product;
- ulong result;
- byte[] stateBytes = Pack.UInt64_To_LE(internalState);
-
- for (col = 0; col < wordsInBlock; ++col)
+ for (int col = 0; col < wordsInBlock; ++col)
{
- result = 0;
- for (row = 8 - 1; row >= 0; --row)
+ ulong colVal = internalState[col];
+ ulong colEven = colVal & 0x00FF00FF00FF00FFUL;
+ ulong colOdd = (colVal >> 8) & 0x00FF00FF00FF00FFUL;
+
+ //ulong rowMatrix = (matrix >> 8) | (matrix << 56);
+ ulong rowMatrix = matrix;
+
+ ulong result = 0;
+ for (int row = 7; row >= 0; --row)
{
- product = 0;
- for (b = 8 - 1; b >= 0; --b)
- {
- product ^= MultiplyGF(stateBytes[b + col * 8], matrix[row][b]);
- }
- result |= (ulong)product << (row * 8);
+ ulong product = MultiplyGFx4(colEven, rowMatrix & 0x00FF00FF00FF00FFUL);
+
+ rowMatrix = (rowMatrix >> 8) | (rowMatrix << 56);
+
+ product ^= MultiplyGFx4(colOdd, rowMatrix & 0x00FF00FF00FF00FFUL);
+
+ product ^= (product >> 32);
+ product ^= (product >> 16);
+
+ result <<= 8;
+ result |= (product & 0xFFUL);
}
+
internalState[col] = result;
}
}
- private byte MultiplyGF(byte x, byte y)
+ /* Pair-wise GF multiplication of 4 byte-pairs (at bits 0, 16, 32, 48 within x, y) */
+ private static ulong MultiplyGFx4(ulong u, ulong v)
{
- byte r = 0;
- byte hbit = 0;
+ ulong r = u & ((v & 0x0001000100010001UL) * 0xFFFFUL);
- for (int i = 0; i < BITS_IN_BYTE; i++)
+ for (int i = 1; i < 8; ++i)
{
- if ((y & 0x01) == 1)
- {
- r ^= x;
- }
-
- hbit = (byte)(x & 0x80);
+ u <<= 1;
+ v >>= 1;
+ r ^= u & ((v & 0x0001000100010001L) * 0xFFFFL);
+ }
- x <<= 1;
+ // REDUCTION_POLYNOMIAL = 0x011d; /* x^8 + x^4 + x^3 + x^2 + 1 */
- if (hbit == 0x80)
- {
- x = (byte)((int)x ^ REDUCTION_POLYNOMIAL);
- }
- y >>= 1;
- }
+ ulong hi = r & 0xFF00FF00FF00FF00UL;
+ r ^= hi ^ (hi >> 4) ^ (hi >> 5) ^ (hi >> 6) ^ (hi >> 8);
+ hi = r & 0x0F000F000F000F00UL;
+ r ^= hi ^ (hi >> 4) ^ (hi >> 5) ^ (hi >> 6) ^ (hi >> 8);
return r;
}
@@ -552,29 +554,10 @@ namespace Org.BouncyCastle.Crypto.Engines
#region TABLES AND S-BOXES
- private byte[][] mdsMatrix =
- {
- new byte[] { 0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04 },
- new byte[] { 0x04, 0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07 },
- new byte[] { 0x07, 0x04, 0x01, 0x01, 0x05, 0x01, 0x08, 0x06 },
- new byte[] { 0x06, 0x07, 0x04, 0x01, 0x01, 0x05, 0x01, 0x08 },
- new byte[] { 0x08, 0x06, 0x07, 0x04, 0x01, 0x01, 0x05, 0x01 },
- new byte[] { 0x01, 0x08, 0x06, 0x07, 0x04, 0x01, 0x01, 0x05 },
- new byte[] { 0x05, 0x01, 0x08, 0x06, 0x07, 0x04, 0x01, 0x01 },
- new byte[] { 0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04, 0x01 },
- };
-
- private byte[][] mdsInvMatrix =
- {
- new byte[] { 0xAD, 0x95, 0x76, 0xA8, 0x2F, 0x49, 0xD7, 0xCA },
- new byte[] { 0xCA, 0xAD, 0x95, 0x76, 0xA8, 0x2F, 0x49, 0xD7 },
- new byte[] { 0xD7, 0xCA, 0xAD, 0x95, 0x76, 0xA8, 0x2F, 0x49 },
- new byte[] { 0x49, 0xD7, 0xCA, 0xAD, 0x95, 0x76, 0xA8, 0x2F },
- new byte[] { 0x2F, 0x49, 0xD7, 0xCA, 0xAD, 0x95, 0x76, 0xA8 },
- new byte[] { 0xA8, 0x2F, 0x49, 0xD7, 0xCA, 0xAD, 0x95, 0x76 },
- new byte[] { 0x76, 0xA8, 0x2F, 0x49, 0xD7, 0xCA, 0xAD, 0x95 },
- new byte[] { 0x95, 0x76, 0xA8, 0x2F, 0x49, 0xD7, 0xCA, 0xAD },
- };
+ //private const ulong mdsMatrix = 0x0407060801050101UL;
+ //private const ulong mdsInvMatrix = 0xCAD7492FA87695ADUL;
+ private const ulong mdsMatrix = 0x0104070608010501UL;
+ private const ulong mdsInvMatrix = 0xADCAD7492FA87695UL;
private byte[][] sboxesForEncryption =
{
diff --git a/crypto/src/crypto/engines/SM2Engine.cs b/crypto/src/crypto/engines/SM2Engine.cs
new file mode 100644
index 000000000..e5f12bbbb
--- /dev/null
+++ b/crypto/src/crypto/engines/SM2Engine.cs
@@ -0,0 +1,238 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /// <summary>
+ /// SM2 public key encryption engine - based on https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02.
+ /// </summary>
+ public class SM2Engine
+ {
+ private readonly IDigest mDigest;
+
+ private bool mForEncryption;
+ private ECKeyParameters mECKey;
+ private ECDomainParameters mECParams;
+ private int mCurveLength;
+ private SecureRandom mRandom;
+
+ public SM2Engine()
+ : this(new SM3Digest())
+ {
+ }
+
+ public SM2Engine(IDigest digest)
+ {
+ this.mDigest = digest;
+ }
+
+ public virtual void Init(bool forEncryption, ICipherParameters param)
+ {
+ this.mForEncryption = forEncryption;
+
+ if (forEncryption)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ mECKey = (ECKeyParameters)rParam.Parameters;
+ mECParams = mECKey.Parameters;
+
+ ECPoint s = ((ECPublicKeyParameters)mECKey).Q.Multiply(mECParams.H);
+ if (s.IsInfinity)
+ throw new ArgumentException("invalid key: [h]Q at infinity");
+
+ mRandom = rParam.Random;
+ }
+ else
+ {
+ mECKey = (ECKeyParameters)param;
+ mECParams = mECKey.Parameters;
+ }
+
+ mCurveLength = (mECParams.Curve.FieldSize + 7) / 8;
+ }
+
+ public virtual byte[] ProcessBlock(byte[] input, int inOff, int inLen)
+ {
+ if (mForEncryption)
+ {
+ return Encrypt(input, inOff, inLen);
+ }
+ else
+ {
+ return Decrypt(input, inOff, inLen);
+ }
+ }
+
+ protected virtual ECMultiplier CreateBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
+
+ private byte[] Encrypt(byte[] input, int inOff, int inLen)
+ {
+ byte[] c2 = new byte[inLen];
+
+ Array.Copy(input, inOff, c2, 0, c2.Length);
+
+ ECMultiplier multiplier = CreateBasePointMultiplier();
+
+ byte[] c1;
+ ECPoint kPB;
+ do
+ {
+ BigInteger k = NextK();
+
+ ECPoint c1P = multiplier.Multiply(mECParams.G, k).Normalize();
+
+ c1 = c1P.GetEncoded(false);
+
+ kPB = ((ECPublicKeyParameters)mECKey).Q.Multiply(k).Normalize();
+
+ Kdf(mDigest, kPB, c2);
+ }
+ while (NotEncrypted(c2, input, inOff));
+
+ AddFieldElement(mDigest, kPB.AffineXCoord);
+ mDigest.BlockUpdate(input, inOff, inLen);
+ AddFieldElement(mDigest, kPB.AffineYCoord);
+
+ byte[] c3 = DigestUtilities.DoFinal(mDigest);
+
+ return Arrays.ConcatenateAll(c1, c2, c3);
+ }
+
+ private byte[] Decrypt(byte[] input, int inOff, int inLen)
+ {
+ byte[] c1 = new byte[mCurveLength * 2 + 1];
+
+ Array.Copy(input, inOff, c1, 0, c1.Length);
+
+ ECPoint c1P = mECParams.Curve.DecodePoint(c1);
+
+ ECPoint s = c1P.Multiply(mECParams.H);
+ if (s.IsInfinity)
+ throw new InvalidCipherTextException("[h]C1 at infinity");
+
+ c1P = c1P.Multiply(((ECPrivateKeyParameters)mECKey).D).Normalize();
+
+ byte[] c2 = new byte[inLen - c1.Length - mDigest.GetDigestSize()];
+
+ Array.Copy(input, inOff + c1.Length, c2, 0, c2.Length);
+
+ Kdf(mDigest, c1P, c2);
+
+ AddFieldElement(mDigest, c1P.AffineXCoord);
+ mDigest.BlockUpdate(c2, 0, c2.Length);
+ AddFieldElement(mDigest, c1P.AffineYCoord);
+
+ byte[] c3 = DigestUtilities.DoFinal(mDigest);
+
+ int check = 0;
+ for (int i = 0; i != c3.Length; i++)
+ {
+ check |= c3[i] ^ input[c1.Length + c2.Length + i];
+ }
+
+ Arrays.Fill(c1, 0);
+ Arrays.Fill(c3, 0);
+
+ if (check != 0)
+ {
+ Arrays.Fill(c2, 0);
+ throw new InvalidCipherTextException("invalid cipher text");
+ }
+
+ return c2;
+ }
+
+ private bool NotEncrypted(byte[] encData, byte[] input, int inOff)
+ {
+ for (int i = 0; i != encData.Length; i++)
+ {
+ if (encData[i] != input[inOff])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void Kdf(IDigest digest, ECPoint c1, byte[] encData)
+ {
+ int digestSize = digest.GetDigestSize();
+ byte[] buf = new byte[System.Math.Max(4, digestSize)];
+ int off = 0;
+
+ IMemoable memo = digest as IMemoable;
+ IMemoable copy = null;
+
+ if (memo != null)
+ {
+ AddFieldElement(digest, c1.AffineXCoord);
+ AddFieldElement(digest, c1.AffineYCoord);
+ copy = memo.Copy();
+ }
+
+ uint ct = 0;
+
+ while (off < encData.Length)
+ {
+ if (memo != null)
+ {
+ memo.Reset(copy);
+ }
+ else
+ {
+ AddFieldElement(digest, c1.AffineXCoord);
+ AddFieldElement(digest, c1.AffineYCoord);
+ }
+
+ 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);
+ off += xorLen;
+ }
+ }
+
+ private void Xor(byte[] data, byte[] kdfOut, int dOff, int dRemaining)
+ {
+ for (int i = 0; i != dRemaining; i++)
+ {
+ data[dOff + i] ^= kdfOut[i];
+ }
+ }
+
+ private BigInteger NextK()
+ {
+ int qBitLength = mECParams.N.BitLength;
+
+ BigInteger k;
+ do
+ {
+ k = new BigInteger(qBitLength, mRandom);
+ }
+ while (k.SignValue == 0 || k.CompareTo(mECParams.N) >= 0);
+
+ return k;
+ }
+
+ private void AddFieldElement(IDigest digest, ECFieldElement v)
+ {
+ byte[] p = v.GetEncoded();
+ digest.BlockUpdate(p, 0, p.Length);
+ }
+ }
+}
diff --git a/crypto/src/crypto/generators/HKDFBytesGenerator.cs b/crypto/src/crypto/generators/HKDFBytesGenerator.cs
index c2e667c95..6f36a6faa 100644
--- a/crypto/src/crypto/generators/HKDFBytesGenerator.cs
+++ b/crypto/src/crypto/generators/HKDFBytesGenerator.cs
@@ -66,7 +66,6 @@ namespace Org.BouncyCastle.Crypto.Generators
*/
private KeyParameter Extract(byte[] salt, byte[] ikm)
{
- hMacHash.Init(new KeyParameter(ikm));
if (salt == null)
{
// TODO check if hashLen is indeed same as HMAC size
diff --git a/crypto/src/crypto/parameters/ParametersWithID.cs b/crypto/src/crypto/parameters/ParametersWithID.cs
new file mode 100644
index 000000000..37f68705b
--- /dev/null
+++ b/crypto/src/crypto/parameters/ParametersWithID.cs
@@ -0,0 +1,36 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ParametersWithID
+ : ICipherParameters
+ {
+ private readonly ICipherParameters parameters;
+ private readonly byte[] id;
+
+ public ParametersWithID(ICipherParameters parameters,
+ byte[] id)
+ : this(parameters, id, 0, id.Length)
+ {
+ }
+
+ public ParametersWithID(ICipherParameters parameters,
+ byte[] id, int idOff, int idLen)
+ {
+ this.parameters = parameters;
+ this.id = Arrays.CopyOfRange(id, idOff, idOff + idLen);
+ }
+
+ public byte[] GetID()
+ {
+ return id;
+ }
+
+ public ICipherParameters Parameters
+ {
+ get { return parameters; }
+ }
+ }
+}
diff --git a/crypto/src/crypto/parameters/ParametersWithIV.cs b/crypto/src/crypto/parameters/ParametersWithIV.cs
index 11a8b77a0..4b2eb930f 100644
--- a/crypto/src/crypto/parameters/ParametersWithIV.cs
+++ b/crypto/src/crypto/parameters/ParametersWithIV.cs
@@ -1,41 +1,38 @@
using System;
+using Org.BouncyCastle.Utilities;
+
namespace Org.BouncyCastle.Crypto.Parameters
{
public class ParametersWithIV
- : ICipherParameters
+ : ICipherParameters
{
- private readonly ICipherParameters parameters;
- private readonly byte[] iv;
+ private readonly ICipherParameters parameters;
+ private readonly byte[] iv;
- public ParametersWithIV(
- ICipherParameters parameters,
- byte[] iv)
- : this(parameters, iv, 0, iv.Length)
- {
- }
+ public ParametersWithIV(ICipherParameters parameters,
+ byte[] iv)
+ : this(parameters, iv, 0, iv.Length)
+ {
+ }
- public ParametersWithIV(
- ICipherParameters parameters,
- byte[] iv,
- int ivOff,
- int ivLen)
+ public ParametersWithIV(ICipherParameters parameters,
+ byte[] iv, int ivOff, int ivLen)
{
// NOTE: 'parameters' may be null to imply key re-use
- if (iv == null)
- throw new ArgumentNullException("iv");
+ if (iv == null)
+ throw new ArgumentNullException("iv");
- this.parameters = parameters;
- this.iv = new byte[ivLen];
- Array.Copy(iv, ivOff, this.iv, 0, ivLen);
+ this.parameters = parameters;
+ this.iv = Arrays.CopyOfRange(iv, ivOff, ivOff + ivLen);
}
- public byte[] GetIV()
+ public byte[] GetIV()
{
- return (byte[]) iv.Clone();
+ return (byte[])iv.Clone();
}
- public ICipherParameters Parameters
+ public ICipherParameters Parameters
{
get { return parameters; }
}
diff --git a/crypto/src/crypto/parameters/SM2KeyExchangePrivateParameters.cs b/crypto/src/crypto/parameters/SM2KeyExchangePrivateParameters.cs
new file mode 100644
index 000000000..8afb61544
--- /dev/null
+++ b/crypto/src/crypto/parameters/SM2KeyExchangePrivateParameters.cs
@@ -0,0 +1,64 @@
+using System;
+
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ /// <summary>Private parameters for an SM2 key exchange.</summary>
+ /// <remarks>The ephemeralPrivateKey is used to calculate the random point used in the algorithm.</remarks>
+ public class SM2KeyExchangePrivateParameters
+ : ICipherParameters
+ {
+ private readonly bool mInitiator;
+ private readonly ECPrivateKeyParameters mStaticPrivateKey;
+ private readonly ECPoint mStaticPublicPoint;
+ private readonly ECPrivateKeyParameters mEphemeralPrivateKey;
+ private readonly ECPoint mEphemeralPublicPoint;
+
+ public SM2KeyExchangePrivateParameters(
+ bool initiator,
+ ECPrivateKeyParameters staticPrivateKey,
+ ECPrivateKeyParameters ephemeralPrivateKey)
+ {
+ if (staticPrivateKey == null)
+ throw new ArgumentNullException("staticPrivateKey");
+ if (ephemeralPrivateKey == null)
+ throw new ArgumentNullException("ephemeralPrivateKey");
+
+ ECDomainParameters parameters = staticPrivateKey.Parameters;
+ if (!parameters.Equals(ephemeralPrivateKey.Parameters))
+ throw new ArgumentException("Static and ephemeral private keys have different domain parameters");
+
+ this.mInitiator = initiator;
+ this.mStaticPrivateKey = staticPrivateKey;
+ this.mStaticPublicPoint = parameters.G.Multiply(staticPrivateKey.D).Normalize();
+ this.mEphemeralPrivateKey = ephemeralPrivateKey;
+ this.mEphemeralPublicPoint = parameters.G.Multiply(ephemeralPrivateKey.D).Normalize();
+ }
+
+ public virtual bool IsInitiator
+ {
+ get { return mInitiator; }
+ }
+
+ public virtual ECPrivateKeyParameters StaticPrivateKey
+ {
+ get { return mStaticPrivateKey; }
+ }
+
+ public virtual ECPoint StaticPublicPoint
+ {
+ get { return mStaticPublicPoint; }
+ }
+
+ public virtual ECPrivateKeyParameters EphemeralPrivateKey
+ {
+ get { return mEphemeralPrivateKey; }
+ }
+
+ public virtual ECPoint EphemeralPublicPoint
+ {
+ get { return mEphemeralPublicPoint; }
+ }
+ }
+}
diff --git a/crypto/src/crypto/parameters/SM2KeyExchangePublicParameters.cs b/crypto/src/crypto/parameters/SM2KeyExchangePublicParameters.cs
new file mode 100644
index 000000000..5c213159e
--- /dev/null
+++ b/crypto/src/crypto/parameters/SM2KeyExchangePublicParameters.cs
@@ -0,0 +1,40 @@
+using System;
+
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ /// <summary>Public parameters for an SM2 key exchange.</summary>
+ /// <remarks>In this case the ephemeralPublicKey provides the random point used in the algorithm.</remarks>
+ public class SM2KeyExchangePublicParameters
+ : ICipherParameters
+ {
+ private readonly ECPublicKeyParameters mStaticPublicKey;
+ private readonly ECPublicKeyParameters mEphemeralPublicKey;
+
+ public SM2KeyExchangePublicParameters(
+ ECPublicKeyParameters staticPublicKey,
+ ECPublicKeyParameters ephemeralPublicKey)
+ {
+ if (staticPublicKey == null)
+ throw new ArgumentNullException("staticPublicKey");
+ if (ephemeralPublicKey == null)
+ throw new ArgumentNullException("ephemeralPublicKey");
+ if (!staticPublicKey.Parameters.Equals(ephemeralPublicKey.Parameters))
+ throw new ArgumentException("Static and ephemeral public keys have different domain parameters");
+
+ this.mStaticPublicKey = staticPublicKey;
+ this.mEphemeralPublicKey = ephemeralPublicKey;
+ }
+
+ public virtual ECPublicKeyParameters StaticPublicKey
+ {
+ get { return mStaticPublicKey; }
+ }
+
+ public virtual ECPublicKeyParameters EphemeralPublicKey
+ {
+ get { return mEphemeralPublicKey; }
+ }
+ }
+}
diff --git a/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs b/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs
index 68579aaf4..459d3a740 100644
--- a/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs
+++ b/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs
@@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Prng
private readonly bool mPredictionResistant;
public CryptoApiEntropySourceProvider()
- : this(new RNGCryptoServiceProvider(), true)
+ : this(RandomNumberGenerator.Create(), true)
{
}
diff --git a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
index fa5f523d3..0b6592025 100644
--- a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
+++ b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
@@ -6,7 +6,7 @@ using System.Security.Cryptography;
namespace Org.BouncyCastle.Crypto.Prng
{
/// <summary>
- /// Uses Microsoft's RNGCryptoServiceProvider
+ /// Uses RandomNumberGenerator.Create() to get randomness generator
/// </summary>
public class CryptoApiRandomGenerator
: IRandomGenerator
@@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Crypto.Prng
private readonly RandomNumberGenerator rndProv;
public CryptoApiRandomGenerator()
- : this(new RNGCryptoServiceProvider())
+ : this(RandomNumberGenerator.Create())
{
}
diff --git a/crypto/src/crypto/signers/SM2Signer.cs b/crypto/src/crypto/signers/SM2Signer.cs
new file mode 100644
index 000000000..8151e6be0
--- /dev/null
+++ b/crypto/src/crypto/signers/SM2Signer.cs
@@ -0,0 +1,258 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /// <summary>The SM2 Digital Signature algorithm.</summary>
+ public class SM2Signer
+ : ISigner
+ {
+ private readonly IDsaKCalculator kCalculator = new RandomDsaKCalculator();
+ private readonly SM3Digest digest = new SM3Digest();
+
+ private ECDomainParameters ecParams;
+ private ECPoint pubPoint;
+ private ECKeyParameters ecKey;
+ private byte[] z;
+
+ public virtual string AlgorithmName
+ {
+ get { return "SM2Sign"; }
+ }
+
+ public virtual void Init(bool forSigning, ICipherParameters parameters)
+ {
+ ICipherParameters baseParam;
+ byte[] userID;
+
+ if (parameters is ParametersWithID)
+ {
+ baseParam = ((ParametersWithID)parameters).Parameters;
+ userID = ((ParametersWithID)parameters).GetID();
+ }
+ else
+ {
+ baseParam = parameters;
+ userID = Hex.Decode("31323334353637383132333435363738"); // the default value (ASCII "1234567812345678")
+ }
+
+ if (forSigning)
+ {
+ if (baseParam is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)baseParam;
+
+ ecKey = (ECKeyParameters)rParam.Parameters;
+ ecParams = ecKey.Parameters;
+ kCalculator.Init(ecParams.N, rParam.Random);
+ }
+ else
+ {
+ ecKey = (ECKeyParameters)baseParam;
+ ecParams = ecKey.Parameters;
+ kCalculator.Init(ecParams.N, new SecureRandom());
+ }
+ pubPoint = CreateBasePointMultiplier().Multiply(ecParams.G, ((ECPrivateKeyParameters)ecKey).D).Normalize();
+ }
+ else
+ {
+ ecKey = (ECKeyParameters)baseParam;
+ ecParams = ecKey.Parameters;
+ pubPoint = ((ECPublicKeyParameters)ecKey).Q;
+ }
+
+ digest.Reset();
+ z = GetZ(userID);
+
+ digest.BlockUpdate(z, 0, z.Length);
+ }
+
+ public virtual void Update(byte b)
+ {
+ digest.Update(b);
+ }
+
+ public virtual void BlockUpdate(byte[] buf, int off, int len)
+ {
+ digest.BlockUpdate(buf, off, len);
+ }
+
+ public virtual bool VerifySignature(byte[] signature)
+ {
+ try
+ {
+ BigInteger[] rs = DerDecode(signature);
+ if (rs != null)
+ {
+ return VerifySignature(rs[0], rs[1]);
+ }
+ }
+ catch (IOException e)
+ {
+ }
+
+ return false;
+ }
+
+ public virtual void Reset()
+ {
+ if (z != null)
+ {
+ digest.Reset();
+ digest.BlockUpdate(z, 0, z.Length);
+ }
+ }
+
+ public virtual byte[] GenerateSignature()
+ {
+ byte[] eHash = DigestUtilities.DoFinal(digest);
+
+ BigInteger n = ecParams.N;
+ BigInteger e = CalculateE(eHash);
+ BigInteger d = ((ECPrivateKeyParameters)ecKey).D;
+
+ BigInteger r, s;
+
+ ECMultiplier basePointMultiplier = CreateBasePointMultiplier();
+
+ // 5.2.1 Draft RFC: SM2 Public Key Algorithms
+ do // generate s
+ {
+ BigInteger k;
+ do // generate r
+ {
+ // A3
+ k = kCalculator.NextK();
+
+ // A4
+ ECPoint p = basePointMultiplier.Multiply(ecParams.G, k).Normalize();
+
+ // A5
+ r = e.Add(p.AffineXCoord.ToBigInteger()).Mod(n);
+ }
+ while (r.SignValue == 0 || r.Add(k).Equals(n));
+
+ // A6
+ BigInteger dPlus1ModN = d.Add(BigInteger.One).ModInverse(n);
+
+ s = k.Subtract(r.Multiply(d)).Mod(n);
+ s = dPlus1ModN.Multiply(s).Mod(n);
+ }
+ while (s.SignValue == 0);
+
+ // A7
+ try
+ {
+ return DerEncode(r, s);
+ }
+ catch (IOException ex)
+ {
+ throw new CryptoException("unable to encode signature: " + ex.Message, ex);
+ }
+ }
+
+ private bool VerifySignature(BigInteger r, BigInteger s)
+ {
+ BigInteger n = ecParams.N;
+
+ // 5.3.1 Draft RFC: SM2 Public Key Algorithms
+ // B1
+ if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
+ return false;
+
+ // B2
+ if (s.CompareTo(BigInteger.One) < 0 || s.CompareTo(n) >= 0)
+ return false;
+
+ // B3
+ byte[] eHash = DigestUtilities.DoFinal(digest);
+
+ // B4
+ BigInteger e = CalculateE(eHash);
+
+ // B5
+ BigInteger t = r.Add(s).Mod(n);
+ if (t.SignValue == 0)
+ return false;
+
+ // B6
+ ECPoint q = ((ECPublicKeyParameters)ecKey).Q;
+ ECPoint x1y1 = ECAlgorithms.SumOfTwoMultiplies(ecParams.G, s, q, t).Normalize();
+ if (x1y1.IsInfinity)
+ return false;
+
+ // B7
+ return r.Equals(e.Add(x1y1.AffineXCoord.ToBigInteger()).Mod(n));
+ }
+
+ private byte[] GetZ(byte[] userID)
+ {
+ AddUserID(digest, userID);
+
+ AddFieldElement(digest, ecParams.Curve.A);
+ AddFieldElement(digest, ecParams.Curve.B);
+ AddFieldElement(digest, ecParams.G.AffineXCoord);
+ AddFieldElement(digest, ecParams.G.AffineYCoord);
+ AddFieldElement(digest, pubPoint.AffineXCoord);
+ AddFieldElement(digest, pubPoint.AffineYCoord);
+
+ return DigestUtilities.DoFinal(digest);
+ }
+
+ private void AddUserID(IDigest digest, byte[] userID)
+ {
+ int len = userID.Length * 8;
+ digest.Update((byte)(len >> 8));
+ digest.Update((byte)len);
+ digest.BlockUpdate(userID, 0, userID.Length);
+ }
+
+ private void AddFieldElement(IDigest digest, ECFieldElement v)
+ {
+ byte[] p = v.GetEncoded();
+ digest.BlockUpdate(p, 0, p.Length);
+ }
+
+ protected virtual BigInteger CalculateE(byte[] message)
+ {
+ return new BigInteger(1, message);
+ }
+
+ protected virtual ECMultiplier CreateBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
+
+ protected virtual BigInteger[] DerDecode(byte[] encoding)
+ {
+ Asn1Sequence seq = Asn1Sequence.GetInstance(Asn1Object.FromByteArray(encoding));
+ if (seq.Count != 2)
+ return null;
+
+ BigInteger r = DerInteger.GetInstance(seq[0]).Value;
+ BigInteger s = DerInteger.GetInstance(seq[1]).Value;
+
+ byte[] expectedEncoding = DerEncode(r, s);
+ if (!Arrays.ConstantTimeAreEqual(expectedEncoding, encoding))
+ return null;
+
+ return new BigInteger[]{ r, s };
+ }
+
+ protected virtual byte[] DerEncode(BigInteger r, BigInteger s)
+ {
+ return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded(Asn1Encodable.Der);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/CertificateStatus.cs b/crypto/src/crypto/tls/CertificateStatus.cs
index 0f95475b9..bc4128722 100644
--- a/crypto/src/crypto/tls/CertificateStatus.cs
+++ b/crypto/src/crypto/tls/CertificateStatus.cs
@@ -95,7 +95,7 @@ namespace Org.BouncyCastle.Crypto.Tls
case CertificateStatusType.ocsp:
return response is OcspResponse;
default:
- throw new ArgumentException("unsupported value", "statusType");
+ throw new ArgumentException("unsupported CertificateStatusType", "statusType");
}
}
}
diff --git a/crypto/src/crypto/tls/CertificateStatusRequest.cs b/crypto/src/crypto/tls/CertificateStatusRequest.cs
index 9587d7df8..24ce37655 100644
--- a/crypto/src/crypto/tls/CertificateStatusRequest.cs
+++ b/crypto/src/crypto/tls/CertificateStatusRequest.cs
@@ -88,7 +88,7 @@ namespace Org.BouncyCastle.Crypto.Tls
case CertificateStatusType.ocsp:
return request is OcspStatusRequest;
default:
- throw new ArgumentException("unsupported value", "statusType");
+ throw new ArgumentException("unsupported CertificateStatusType", "statusType");
}
}
}
diff --git a/crypto/src/crypto/tls/ServerName.cs b/crypto/src/crypto/tls/ServerName.cs
index 508c2ddbc..45d18b79b 100644
--- a/crypto/src/crypto/tls/ServerName.cs
+++ b/crypto/src/crypto/tls/ServerName.cs
@@ -98,7 +98,7 @@ namespace Org.BouncyCastle.Crypto.Tls
case Tls.NameType.host_name:
return name is string;
default:
- throw new ArgumentException("unsupported value", "name");
+ throw new ArgumentException("unsupported NameType", "nameType");
}
}
}
diff --git a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
index a8ef5a77a..05bb4000b 100644
--- a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
+++ b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
@@ -48,7 +48,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
R = R.TwicePlus(lookupTable[index]);
}
- return R;
+ return R.Add(info.Offset);
}
protected virtual int GetWidthForCombSize(int combSize)
diff --git a/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs b/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs
index 56a6326a1..11bdadc6f 100644
--- a/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs
+++ b/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs
@@ -6,11 +6,13 @@
public class FixedPointPreCompInfo
: PreCompInfo
{
+ protected ECPoint m_offset = null;
+
/**
* Array holding the precomputed <code>ECPoint</code>s used for a fixed
* point multiplication.
*/
- protected ECPoint[] m_preComp = null;
+ protected ECPoint[] m_preComp = null;
/**
* The width used for the precomputation. If a larger width precomputation
@@ -19,6 +21,12 @@
*/
protected int m_width = -1;
+ public virtual ECPoint Offset
+ {
+ get { return m_offset; }
+ set { this.m_offset = value; }
+ }
+
public virtual ECPoint[] PreComp
{
get { return m_preComp; }
diff --git a/crypto/src/math/ec/multiplier/FixedPointUtilities.cs b/crypto/src/math/ec/multiplier/FixedPointUtilities.cs
index d927d010b..8e129a8f3 100644
--- a/crypto/src/math/ec/multiplier/FixedPointUtilities.cs
+++ b/crypto/src/math/ec/multiplier/FixedPointUtilities.cs
@@ -35,17 +35,20 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
int bits = GetCombSize(c);
int d = (bits + minWidth - 1) / minWidth;
- ECPoint[] pow2Table = new ECPoint[minWidth];
+ ECPoint[] pow2Table = new ECPoint[minWidth + 1];
pow2Table[0] = p;
for (int i = 1; i < minWidth; ++i)
{
pow2Table[i] = pow2Table[i - 1].TimesPow2(d);
}
-
+
+ // This will be the 'offset' value
+ pow2Table[minWidth] = pow2Table[0].Subtract(pow2Table[1]);
+
c.NormalizeAll(pow2Table);
lookupTable = new ECPoint[n];
- lookupTable[0] = c.Infinity;
+ lookupTable[0] = pow2Table[0];
for (int bit = minWidth - 1; bit >= 0; --bit)
{
@@ -60,6 +63,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
c.NormalizeAll(lookupTable);
+ info.Offset = pow2Table[minWidth];
info.PreComp = lookupTable;
info.Width = minWidth;
diff --git a/crypto/src/util/encoders/Base64Encoder.cs b/crypto/src/util/encoders/Base64Encoder.cs
index 7b53df25a..07294c9a6 100644
--- a/crypto/src/util/encoders/Base64Encoder.cs
+++ b/crypto/src/util/encoders/Base64Encoder.cs
@@ -269,6 +269,9 @@ namespace Org.BouncyCastle.Utilities.Encoders
{
if (c3 == padding)
{
+ if (c4 != padding)
+ throw new IOException("invalid characters encountered at end of base64 data");
+
byte b1 = decodingTable[c1];
byte b2 = decodingTable[c2];
|