summary refs log tree commit diff
path: root/crypto/src/crypto/agreement
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/crypto/agreement')
-rw-r--r--crypto/src/crypto/agreement/DHAgreement.cs93
-rw-r--r--crypto/src/crypto/agreement/DHBasicAgreement.cs64
-rw-r--r--crypto/src/crypto/agreement/ECDHBasicAgreement.cs54
-rw-r--r--crypto/src/crypto/agreement/ECDHCBasicAgreement.cs62
-rw-r--r--crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs64
-rw-r--r--crypto/src/crypto/agreement/ECMqvBasicAgreement.cs90
-rw-r--r--crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs64
-rw-r--r--crypto/src/crypto/agreement/kdf/DHKdfParameters.cs57
-rw-r--r--crypto/src/crypto/agreement/kdf/DHKekGenerator.cs112
-rw-r--r--crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs55
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6Client.cs93
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6Server.cs90
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6Utilities.cs85
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs49
14 files changed, 1032 insertions, 0 deletions
diff --git a/crypto/src/crypto/agreement/DHAgreement.cs b/crypto/src/crypto/agreement/DHAgreement.cs
new file mode 100644
index 000000000..d214caafe
--- /dev/null
+++ b/crypto/src/crypto/agreement/DHAgreement.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+	/**
+	 * a Diffie-Hellman key exchange engine.
+	 * <p>
+	 * note: This uses MTI/A0 key agreement in order to make the key agreement
+	 * secure against passive attacks. If you're doing Diffie-Hellman and both
+	 * parties have long term public keys you should look at using this. For
+	 * further information have a look at RFC 2631.</p>
+	 * <p>
+	 * It's possible to extend this to more than two parties as well, for the moment
+	 * that is left as an exercise for the reader.</p>
+	 */
+	public class DHAgreement
+	{
+		private DHPrivateKeyParameters  key;
+		private DHParameters			dhParams;
+		private BigInteger				privateValue;
+		private SecureRandom			random;
+
+		public void Init(
+			ICipherParameters parameters)
+		{
+			AsymmetricKeyParameter kParam;
+			if (parameters is ParametersWithRandom)
+			{
+				ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+
+				this.random = rParam.Random;
+				kParam = (AsymmetricKeyParameter)rParam.Parameters;
+			}
+			else
+			{
+				this.random = new SecureRandom();
+				kParam = (AsymmetricKeyParameter)parameters;
+			}
+
+			if (!(kParam is DHPrivateKeyParameters))
+			{
+				throw new ArgumentException("DHEngine expects DHPrivateKeyParameters");
+			}
+
+			this.key = (DHPrivateKeyParameters)kParam;
+			this.dhParams = key.Parameters;
+		}
+
+		/**
+		 * calculate our initial message.
+		 */
+		public BigInteger CalculateMessage()
+		{
+			DHKeyPairGenerator dhGen = new DHKeyPairGenerator();
+			dhGen.Init(new DHKeyGenerationParameters(random, dhParams));
+			AsymmetricCipherKeyPair dhPair = dhGen.GenerateKeyPair();
+
+			this.privateValue = ((DHPrivateKeyParameters)dhPair.Private).X;
+
+			return ((DHPublicKeyParameters)dhPair.Public).Y;
+		}
+
+		/**
+		 * given a message from a given party and the corresponding public key
+		 * calculate the next message in the agreement sequence. In this case
+		 * this will represent the shared secret.
+		 */
+		public BigInteger CalculateAgreement(
+			DHPublicKeyParameters	pub,
+			BigInteger				message)
+		{
+			if (pub == null)
+				throw new ArgumentNullException("pub");
+			if (message == null)
+				throw new ArgumentNullException("message");
+
+			if (!pub.Parameters.Equals(dhParams))
+			{
+				throw new ArgumentException("Diffie-Hellman public key has wrong parameters.");
+			}
+
+			BigInteger p = dhParams.P;
+
+			return message.ModPow(key.X, p).Multiply(pub.Y.ModPow(privateValue, p)).Mod(p);
+		}
+	}
+}
diff --git a/crypto/src/crypto/agreement/DHBasicAgreement.cs b/crypto/src/crypto/agreement/DHBasicAgreement.cs
new file mode 100644
index 000000000..75b5e9db5
--- /dev/null
+++ b/crypto/src/crypto/agreement/DHBasicAgreement.cs
@@ -0,0 +1,64 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+    /**
+     * a Diffie-Hellman key agreement class.
+     * <p>
+     * note: This is only the basic algorithm, it doesn't take advantage of
+     * long term public keys if they are available. See the DHAgreement class
+     * for a "better" implementation.</p>
+     */
+    public class DHBasicAgreement
+        : IBasicAgreement
+    {
+        private DHPrivateKeyParameters	key;
+        private DHParameters			dhParams;
+
+        public virtual void Init(
+            ICipherParameters parameters)
+        {
+            if (parameters is ParametersWithRandom)
+            {
+                parameters = ((ParametersWithRandom) parameters).Parameters;
+            }
+
+            if (!(parameters is DHPrivateKeyParameters))
+            {
+                throw new ArgumentException("DHEngine expects DHPrivateKeyParameters");
+            }
+
+            this.key = (DHPrivateKeyParameters) parameters;
+            this.dhParams = key.Parameters;
+        }
+
+        public virtual int GetFieldSize()
+        {
+            return (key.Parameters.P.BitLength + 7) / 8;
+        }
+
+        /**
+         * given a short term public key from a given party calculate the next
+         * message in the agreement sequence.
+         */
+        public virtual BigInteger CalculateAgreement(
+            ICipherParameters pubKey)
+        {
+            if (this.key == null)
+                throw new InvalidOperationException("Agreement algorithm not initialised");
+
+            DHPublicKeyParameters pub = (DHPublicKeyParameters)pubKey;
+
+            if (!pub.Parameters.Equals(dhParams))
+            {
+                throw new ArgumentException("Diffie-Hellman public key has wrong parameters.");
+            }
+
+            return pub.Y.ModPow(key.X, dhParams.P);
+        }
+    }
+}
diff --git a/crypto/src/crypto/agreement/ECDHBasicAgreement.cs b/crypto/src/crypto/agreement/ECDHBasicAgreement.cs
new file mode 100644
index 000000000..fa587b234
--- /dev/null
+++ b/crypto/src/crypto/agreement/ECDHBasicAgreement.cs
@@ -0,0 +1,54 @@
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+    /**
+     * P1363 7.2.1 ECSVDP-DH
+     *
+     * ECSVDP-DH is Elliptic Curve Secret Value Derivation Primitive,
+     * Diffie-Hellman version. It is based on the work of [DH76], [Mil86],
+     * and [Kob87]. This primitive derives a shared secret value from one
+     * party's private key and another party's public key, where both have
+     * the same set of EC domain parameters. If two parties correctly
+     * execute this primitive, they will produce the same output. This
+     * primitive can be invoked by a scheme to derive a shared secret key;
+     * specifically, it may be used with the schemes ECKAS-DH1 and
+     * DL/ECKAS-DH2. It assumes that the input keys are valid (see also
+     * Section 7.2.2).
+     */
+    public class ECDHBasicAgreement
+		: IBasicAgreement
+    {
+        protected internal ECPrivateKeyParameters privKey;
+
+        public virtual void Init(
+			ICipherParameters parameters)
+        {
+			if (parameters is ParametersWithRandom)
+			{
+				parameters = ((ParametersWithRandom)parameters).Parameters;
+			}
+
+			this.privKey = (ECPrivateKeyParameters)parameters;
+        }
+
+        public virtual int GetFieldSize()
+        {
+            return (privKey.Parameters.Curve.FieldSize + 7) / 8;
+        }
+
+        public virtual BigInteger CalculateAgreement(
+            ICipherParameters pubKey)
+        {
+            ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey;
+            ECPoint P = pub.Q.Multiply(privKey.D);
+
+            // if ( p.IsInfinity ) throw new Exception("d*Q == infinity");
+
+            return P.X.ToBigInteger();
+        }
+    }
+}
diff --git a/crypto/src/crypto/agreement/ECDHCBasicAgreement.cs b/crypto/src/crypto/agreement/ECDHCBasicAgreement.cs
new file mode 100644
index 000000000..e1c572373
--- /dev/null
+++ b/crypto/src/crypto/agreement/ECDHCBasicAgreement.cs
@@ -0,0 +1,62 @@
+using System;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+    /**
+     * P1363 7.2.2 ECSVDP-DHC
+     *
+     * ECSVDP-DHC is Elliptic Curve Secret Value Derivation Primitive,
+     * Diffie-Hellman version with cofactor multiplication. It is based on
+     * the work of [DH76], [Mil86], [Kob87], [LMQ98] and [Kal98a]. This
+     * primitive derives a shared secret value from one party's private key
+     * and another party's public key, where both have the same set of EC
+     * domain parameters. If two parties correctly execute this primitive,
+     * they will produce the same output. This primitive can be invoked by a
+     * scheme to derive a shared secret key; specifically, it may be used
+     * with the schemes ECKAS-DH1 and DL/ECKAS-DH2. It does not assume the
+     * validity of the input public key (see also Section 7.2.1).
+     * <p>
+     * Note: As stated P1363 compatibility mode with ECDH can be preset, and
+     * in this case the implementation doesn't have a ECDH compatibility mode
+     * (if you want that just use ECDHBasicAgreement and note they both implement
+     * BasicAgreement!).</p>
+     */
+    public class ECDHCBasicAgreement
+        : IBasicAgreement
+    {
+        private ECPrivateKeyParameters key;
+
+        public virtual void Init(
+            ICipherParameters parameters)
+        {
+            if (parameters is ParametersWithRandom)
+            {
+                parameters = ((ParametersWithRandom) parameters).Parameters;
+            }
+
+            this.key = (ECPrivateKeyParameters)parameters;
+        }
+
+        public virtual int GetFieldSize()
+        {
+            return (key.Parameters.Curve.FieldSize + 7) / 8;
+        }
+
+        public virtual BigInteger CalculateAgreement(
+            ICipherParameters pubKey)
+        {
+            ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey;
+            ECDomainParameters parameters = pub.Parameters;
+            ECPoint P = pub.Q.Multiply(parameters.H.Multiply(key.D));
+
+            // if ( p.IsInfinity ) throw new Exception("Invalid public key");
+
+            return P.X.ToBigInteger();
+        }
+    }
+}
diff --git a/crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs b/crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs
new file mode 100644
index 000000000..28437a268
--- /dev/null
+++ b/crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.Agreement.Kdf;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+	public class ECDHWithKdfBasicAgreement
+		: ECDHBasicAgreement
+	{
+		private readonly string algorithm;
+		private readonly IDerivationFunction kdf;
+
+		public ECDHWithKdfBasicAgreement(
+			string				algorithm,
+			IDerivationFunction	kdf)
+		{
+			if (algorithm == null)
+				throw new ArgumentNullException("algorithm");
+			if (kdf == null)
+				throw new ArgumentNullException("kdf");
+
+			this.algorithm = algorithm;
+			this.kdf = kdf;
+		}
+
+		public override BigInteger CalculateAgreement(
+			ICipherParameters pubKey)
+		{
+			// Note that the ec.KeyAgreement class in JCE only uses kdf in one
+			// of the engineGenerateSecret methods.
+
+			BigInteger result = base.CalculateAgreement(pubKey);
+
+			int keySize = GeneratorUtilities.GetDefaultKeySize(algorithm);
+
+			DHKdfParameters dhKdfParams = new DHKdfParameters(
+				new DerObjectIdentifier(algorithm),
+				keySize,
+				bigIntToBytes(result));
+
+			kdf.Init(dhKdfParams);
+
+			byte[] keyBytes = new byte[keySize / 8];
+			kdf.GenerateBytes(keyBytes, 0, keyBytes.Length);
+
+			return new BigInteger(1, keyBytes);
+		}
+
+		private byte[] bigIntToBytes(
+			BigInteger r)
+		{
+			int byteLength = X9IntegerConverter.GetByteLength(privKey.Parameters.G.X);
+			return X9IntegerConverter.IntegerToBytes(r, byteLength);
+		}
+	}
+}
diff --git a/crypto/src/crypto/agreement/ECMqvBasicAgreement.cs b/crypto/src/crypto/agreement/ECMqvBasicAgreement.cs
new file mode 100644
index 000000000..3559d3e81
--- /dev/null
+++ b/crypto/src/crypto/agreement/ECMqvBasicAgreement.cs
@@ -0,0 +1,90 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+    public class ECMqvBasicAgreement
+        : IBasicAgreement
+    {
+        protected internal MqvPrivateParameters privParams;
+
+        public virtual void Init(
+            ICipherParameters parameters)
+        {
+            if (parameters is ParametersWithRandom)
+            {
+                parameters = ((ParametersWithRandom)parameters).Parameters;
+            }
+
+            this.privParams = (MqvPrivateParameters)parameters;
+        }
+
+        public virtual int GetFieldSize()
+        {
+            return (privParams.StaticPrivateKey.Parameters.Curve.FieldSize + 7) / 8;
+        }
+
+        public virtual BigInteger CalculateAgreement(
+            ICipherParameters pubKey)
+        {
+            MqvPublicParameters pubParams = (MqvPublicParameters)pubKey;
+
+            ECPrivateKeyParameters staticPrivateKey = privParams.StaticPrivateKey;
+
+            ECPoint agreement = calculateMqvAgreement(staticPrivateKey.Parameters, staticPrivateKey,
+                privParams.EphemeralPrivateKey, privParams.EphemeralPublicKey,
+                pubParams.StaticPublicKey, pubParams.EphemeralPublicKey);
+
+            return agreement.X.ToBigInteger();
+        }
+        
+        // The ECMQV Primitive as described in SEC-1, 3.4
+        private static ECPoint calculateMqvAgreement(
+            ECDomainParameters		parameters,
+            ECPrivateKeyParameters	d1U,
+            ECPrivateKeyParameters	d2U,
+            ECPublicKeyParameters	Q2U,
+            ECPublicKeyParameters	Q1V,
+            ECPublicKeyParameters	Q2V)
+        {
+            BigInteger n = parameters.N;
+            int e = (n.BitLength + 1) / 2;
+            BigInteger powE = BigInteger.One.ShiftLeft(e);
+
+            // The Q2U public key is optional
+            ECPoint q;
+            if (Q2U == null)
+            {
+                q = parameters.G.Multiply(d2U.D);
+            }
+            else
+            {
+                q = Q2U.Q;
+            }
+
+            BigInteger x = q.X.ToBigInteger();
+            BigInteger xBar = x.Mod(powE);
+            BigInteger Q2UBar = xBar.SetBit(e);
+            BigInteger s = d1U.D.Multiply(Q2UBar).Mod(n).Add(d2U.D).Mod(n);
+
+            BigInteger xPrime = Q2V.Q.X.ToBigInteger();
+            BigInteger xPrimeBar = xPrime.Mod(powE);
+            BigInteger Q2VBar = xPrimeBar.SetBit(e);
+
+            BigInteger hs = parameters.H.Multiply(s).Mod(n);
+
+            //ECPoint p = Q1V.Q.Multiply(Q2VBar).Add(Q2V.Q).Multiply(hs);
+            ECPoint p = ECAlgorithms.SumOfTwoMultiplies(
+                Q1V.Q, Q2VBar.Multiply(hs).Mod(n), Q2V.Q, hs);
+
+            if (p.IsInfinity)
+                throw new InvalidOperationException("Infinity is not a valid agreement value for MQV");
+
+            return p;
+        }
+    }
+}
diff --git a/crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs b/crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs
new file mode 100644
index 000000000..093ce4056
--- /dev/null
+++ b/crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.Agreement.Kdf;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+	public class ECMqvWithKdfBasicAgreement
+		: ECMqvBasicAgreement
+	{
+		private readonly string algorithm;
+		private readonly IDerivationFunction kdf;
+
+		public ECMqvWithKdfBasicAgreement(
+			string				algorithm,
+			IDerivationFunction	kdf)
+		{
+			if (algorithm == null)
+				throw new ArgumentNullException("algorithm");
+			if (kdf == null)
+				throw new ArgumentNullException("kdf");
+
+			this.algorithm = algorithm;
+			this.kdf = kdf;
+		}
+
+		public override BigInteger CalculateAgreement(
+			ICipherParameters pubKey)
+		{
+			// Note that the ec.KeyAgreement class in JCE only uses kdf in one
+			// of the engineGenerateSecret methods.
+
+			BigInteger result = base.CalculateAgreement(pubKey);
+
+			int keySize = GeneratorUtilities.GetDefaultKeySize(algorithm);
+
+			DHKdfParameters dhKdfParams = new DHKdfParameters(
+				new DerObjectIdentifier(algorithm),
+				keySize,
+				bigIntToBytes(result));
+
+			kdf.Init(dhKdfParams);
+
+			byte[] keyBytes = new byte[keySize / 8];
+			kdf.GenerateBytes(keyBytes, 0, keyBytes.Length);
+
+			return new BigInteger(1, keyBytes);
+		}
+
+		private byte[] bigIntToBytes(
+			BigInteger r)
+		{
+			int byteLength = X9IntegerConverter.GetByteLength(privParams.StaticPrivateKey.Parameters.G.X);
+			return X9IntegerConverter.IntegerToBytes(r, byteLength);
+		}
+	}
+}
diff --git a/crypto/src/crypto/agreement/kdf/DHKdfParameters.cs b/crypto/src/crypto/agreement/kdf/DHKdfParameters.cs
new file mode 100644
index 000000000..f6c9e6079
--- /dev/null
+++ b/crypto/src/crypto/agreement/kdf/DHKdfParameters.cs
@@ -0,0 +1,57 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Kdf
+{
+	public class DHKdfParameters
+		: IDerivationParameters
+	{
+		private readonly DerObjectIdentifier algorithm;
+		private readonly int keySize;
+		private readonly byte[] z;
+		private readonly byte[] extraInfo;
+
+		public DHKdfParameters(
+			DerObjectIdentifier	algorithm,
+			int					keySize,
+			byte[]				z)
+			: this(algorithm, keySize, z, null)
+		{
+		}
+
+		public DHKdfParameters(
+			DerObjectIdentifier algorithm,
+			int keySize,
+			byte[] z,
+			byte[] extraInfo)
+		{
+			this.algorithm = algorithm;
+			this.keySize = keySize;
+			this.z = z; // TODO Clone?
+			this.extraInfo = extraInfo;
+		}
+
+		public DerObjectIdentifier Algorithm
+		{
+			get { return algorithm; }
+		}
+
+		public int KeySize
+		{
+			get { return keySize; }
+		}
+
+		public byte[] GetZ()
+		{
+			// TODO Clone?
+			return z;
+		}
+
+		public byte[] GetExtraInfo()
+		{
+			// TODO Clone?
+			return extraInfo;
+		}
+	}
+}
diff --git a/crypto/src/crypto/agreement/kdf/DHKekGenerator.cs b/crypto/src/crypto/agreement/kdf/DHKekGenerator.cs
new file mode 100644
index 000000000..259e21e69
--- /dev/null
+++ b/crypto/src/crypto/agreement/kdf/DHKekGenerator.cs
@@ -0,0 +1,112 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Kdf
+{
+    /**
+    * RFC 2631 Diffie-hellman KEK derivation function.
+    */
+    public class DHKekGenerator
+        : IDerivationFunction
+    {
+        private readonly IDigest digest;
+
+        private DerObjectIdentifier	algorithm;
+        private int					keySize;
+        private byte[]				z;
+        private byte[]				partyAInfo;
+
+        public DHKekGenerator(IDigest digest)
+        {
+            this.digest = digest;
+        }
+
+        public virtual void Init(IDerivationParameters param)
+        {
+            DHKdfParameters parameters = (DHKdfParameters)param;
+
+            this.algorithm = parameters.Algorithm;
+            this.keySize = parameters.KeySize;
+            this.z = parameters.GetZ(); // TODO Clone?
+            this.partyAInfo = parameters.GetExtraInfo(); // TODO Clone?
+        }
+
+        public virtual IDigest Digest
+        {
+            get { return digest; }
+        }
+
+        public virtual int GenerateBytes(byte[]	outBytes, int outOff, int len)
+        {
+            if ((outBytes.Length - len) < outOff)
+            {
+                throw new DataLengthException("output buffer too small");
+            }
+
+            long oBytes = len;
+            int outLen = digest.GetDigestSize();
+
+            //
+            // this is at odds with the standard implementation, the
+            // maximum value should be hBits * (2^32 - 1) where hBits
+            // is the digest output size in bits. We can't have an
+            // array with a long index at the moment...
+            //
+            if (oBytes > ((2L << 32) - 1))
+            {
+                throw new ArgumentException("Output length too large");
+            }
+
+            int cThreshold = (int)((oBytes + outLen - 1) / outLen);
+
+            byte[] dig = new byte[digest.GetDigestSize()];
+
+            uint counter = 1;
+
+            for (int i = 0; i < cThreshold; i++)
+            {
+                digest.BlockUpdate(z, 0, z.Length);
+
+                // KeySpecificInfo
+                DerSequence keyInfo = new DerSequence(
+                    algorithm,
+                    new DerOctetString(Pack.UInt32_To_BE(counter)));
+
+                // OtherInfo
+                Asn1EncodableVector v1 = new Asn1EncodableVector(keyInfo);
+
+                if (partyAInfo != null)
+                {
+                    v1.Add(new DerTaggedObject(true, 0, new DerOctetString(partyAInfo)));
+                }
+
+                v1.Add(new DerTaggedObject(true, 2, new DerOctetString(Pack.UInt32_To_BE((uint)keySize))));
+
+                byte[] other = new DerSequence(v1).GetDerEncoded();
+
+                digest.BlockUpdate(other, 0, other.Length);
+
+                digest.DoFinal(dig, 0);
+
+                if (len > outLen)
+                {
+                    Array.Copy(dig, 0, outBytes, outOff, outLen);
+                    outOff += outLen;
+                    len -= outLen;
+                }
+                else
+                {
+                    Array.Copy(dig, 0, outBytes, outOff, len);
+                }
+
+                counter++;
+            }
+
+            digest.Reset();
+
+            return (int)oBytes;
+        }
+    }
+}
diff --git a/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs b/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
new file mode 100644
index 000000000..74464574c
--- /dev/null
+++ b/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
@@ -0,0 +1,55 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Kdf
+{
+    /**
+    * X9.63 based key derivation function for ECDH CMS.
+    */
+    public class ECDHKekGenerator
+        : IDerivationFunction
+    {
+        private readonly IDerivationFunction kdf;
+
+        private DerObjectIdentifier	algorithm;
+        private int					keySize;
+        private byte[]				z;
+
+        public ECDHKekGenerator(IDigest digest)
+        {
+            this.kdf = new Kdf2BytesGenerator(digest);
+        }
+
+        public virtual void Init(IDerivationParameters param)
+        {
+            DHKdfParameters parameters = (DHKdfParameters)param;
+
+            this.algorithm = parameters.Algorithm;
+            this.keySize = parameters.KeySize;
+            this.z = parameters.GetZ(); // TODO Clone?
+        }
+
+        public virtual IDigest Digest
+        {
+            get { return kdf.Digest; }
+        }
+
+        public virtual int GenerateBytes(byte[]	outBytes, int outOff, int len)
+        {
+            // TODO Create an ASN.1 class for this (RFC3278)
+            // ECC-CMS-SharedInfo
+            DerSequence s = new DerSequence(
+                new AlgorithmIdentifier(algorithm, DerNull.Instance),
+                new DerTaggedObject(true, 2, new DerOctetString(Pack.UInt32_To_BE((uint)keySize))));
+
+            kdf.Init(new KdfParameters(z, s.GetDerEncoded()));
+
+            return kdf.GenerateBytes(outBytes, outOff, len);
+        }
+    }
+}
diff --git a/crypto/src/crypto/agreement/srp/SRP6Client.cs b/crypto/src/crypto/agreement/srp/SRP6Client.cs
new file mode 100644
index 000000000..309736564
--- /dev/null
+++ b/crypto/src/crypto/agreement/srp/SRP6Client.cs
@@ -0,0 +1,93 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Srp
+{
+	/**
+	 * Implements the client side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
+	 * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper
+	 * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
+	 */
+	public class Srp6Client
+	{
+	    protected BigInteger N;
+	    protected BigInteger g;
+
+	    protected BigInteger privA;
+	    protected BigInteger pubA;
+
+	    protected BigInteger B;
+
+	    protected BigInteger x;
+	    protected BigInteger u;
+	    protected BigInteger S;
+
+	    protected IDigest digest;
+	    protected SecureRandom random;
+
+	    public Srp6Client()
+	    {
+	    }
+
+	    /**
+	     * Initialises the client to begin new authentication attempt
+	     * @param N The safe prime associated with the client's verifier
+	     * @param g The group parameter associated with the client's verifier
+	     * @param digest The digest algorithm associated with the client's verifier
+	     * @param random For key generation
+	     */
+	    public virtual void Init(BigInteger N, BigInteger g, IDigest digest, SecureRandom random)
+	    {
+	        this.N = N;
+	        this.g = g;
+	        this.digest = digest;
+	        this.random = random;
+	    }
+
+	    /**
+	     * Generates client's credentials given the client's salt, identity and password
+	     * @param salt The salt used in the client's verifier.
+	     * @param identity The user's identity (eg. username)
+	     * @param password The user's password
+	     * @return Client's public value to send to server
+	     */
+	    public virtual BigInteger GenerateClientCredentials(byte[] salt, byte[] identity, byte[] password)
+	    {
+	        this.x = Srp6Utilities.CalculateX(digest, N, salt, identity, password);
+	        this.privA = SelectPrivateValue();
+	        this.pubA = g.ModPow(privA, N);
+
+	        return pubA;
+	    }
+
+	    /**
+	     * Generates client's verification message given the server's credentials
+	     * @param serverB The server's credentials
+	     * @return Client's verification message for the server
+	     * @throws CryptoException If server's credentials are invalid
+	     */
+	    public virtual BigInteger CalculateSecret(BigInteger serverB)
+	    {
+	        this.B = Srp6Utilities.ValidatePublicValue(N, serverB);
+	        this.u = Srp6Utilities.CalculateU(digest, N, pubA, B);
+	        this.S = CalculateS();
+
+	        return S;
+	    }
+
+	    protected virtual BigInteger SelectPrivateValue()
+	    {
+	    	return Srp6Utilities.GeneratePrivateValue(digest, N, g, random);    	
+	    }
+
+	    private BigInteger CalculateS()
+	    {
+	        BigInteger k = Srp6Utilities.CalculateK(digest, N, g);
+	        BigInteger exp = u.Multiply(x).Add(privA);
+	        BigInteger tmp = g.ModPow(x, N).Multiply(k).Mod(N);
+	        return B.Subtract(tmp).Mod(N).ModPow(exp, N);
+	    }
+	}
+}
diff --git a/crypto/src/crypto/agreement/srp/SRP6Server.cs b/crypto/src/crypto/agreement/srp/SRP6Server.cs
new file mode 100644
index 000000000..35b96d488
--- /dev/null
+++ b/crypto/src/crypto/agreement/srp/SRP6Server.cs
@@ -0,0 +1,90 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Srp
+{
+	/**
+	 * Implements the server side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
+	 * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper
+	 * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
+	 */
+	public class Srp6Server
+	{
+	    protected BigInteger N;
+	    protected BigInteger g;
+	    protected BigInteger v;
+
+	    protected SecureRandom random;
+	    protected IDigest digest;
+
+	    protected BigInteger A;
+
+	    protected BigInteger privB;
+	    protected BigInteger pubB;
+
+	    protected BigInteger u;
+	    protected BigInteger S;
+
+	    public Srp6Server()
+	    {
+	    }
+
+	    /**
+	     * Initialises the server to accept a new client authentication attempt
+	     * @param N The safe prime associated with the client's verifier
+	     * @param g The group parameter associated with the client's verifier
+	     * @param v The client's verifier
+	     * @param digest The digest algorithm associated with the client's verifier
+	     * @param random For key generation
+	     */
+	    public virtual void Init(BigInteger N, BigInteger g, BigInteger v, IDigest digest, SecureRandom random)
+	    {
+	        this.N = N;
+	        this.g = g;
+	        this.v = v;
+
+	        this.random = random;
+	        this.digest = digest;
+	    }
+
+	    /**
+	     * Generates the server's credentials that are to be sent to the client.
+	     * @return The server's public value to the client
+	     */
+	    public virtual BigInteger GenerateServerCredentials()
+	    {
+	        BigInteger k = Srp6Utilities.CalculateK(digest, N, g);
+	        this.privB = SelectPrivateValue();
+	    	this.pubB = k.Multiply(v).Mod(N).Add(g.ModPow(privB, N)).Mod(N);
+
+	        return pubB;
+	    }
+
+	    /**
+	     * Processes the client's credentials. If valid the shared secret is generated and returned.
+	     * @param clientA The client's credentials
+	     * @return A shared secret BigInteger
+	     * @throws CryptoException If client's credentials are invalid
+	     */
+	    public virtual BigInteger CalculateSecret(BigInteger clientA)
+	    {
+	        this.A = Srp6Utilities.ValidatePublicValue(N, clientA);
+	        this.u = Srp6Utilities.CalculateU(digest, N, A, pubB);
+	        this.S = CalculateS();
+
+	        return S;
+	    }
+
+	    protected virtual BigInteger SelectPrivateValue()
+	    {
+	    	return Srp6Utilities.GeneratePrivateValue(digest, N, g, random);    	
+	    }
+
+		private BigInteger CalculateS()
+	    {
+			return v.ModPow(u, N).Multiply(A).Mod(N).ModPow(privB, N);
+	    }
+	}
+}
diff --git a/crypto/src/crypto/agreement/srp/SRP6Utilities.cs b/crypto/src/crypto/agreement/srp/SRP6Utilities.cs
new file mode 100644
index 000000000..4e790f572
--- /dev/null
+++ b/crypto/src/crypto/agreement/srp/SRP6Utilities.cs
@@ -0,0 +1,85 @@
+using System;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Srp
+{
+	public class Srp6Utilities
+	{
+		public static BigInteger CalculateK(IDigest digest, BigInteger N, BigInteger g)
+		{
+			return HashPaddedPair(digest, N, N, g);
+		}
+
+	    public static BigInteger CalculateU(IDigest digest, BigInteger N, BigInteger A, BigInteger B)
+	    {
+	    	return HashPaddedPair(digest, N, A, B);
+	    }
+
+		public static BigInteger CalculateX(IDigest digest, BigInteger N, byte[] salt, byte[] identity, byte[] password)
+	    {
+	        byte[] output = new byte[digest.GetDigestSize()];
+
+	        digest.BlockUpdate(identity, 0, identity.Length);
+	        digest.Update((byte)':');
+	        digest.BlockUpdate(password, 0, password.Length);
+	        digest.DoFinal(output, 0);
+
+	        digest.BlockUpdate(salt, 0, salt.Length);
+	        digest.BlockUpdate(output, 0, output.Length);
+	        digest.DoFinal(output, 0);
+
+	        return new BigInteger(1, output);
+	    }
+
+		public static BigInteger GeneratePrivateValue(IDigest digest, BigInteger N, BigInteger g, SecureRandom random)
+	    {
+			int minBits = System.Math.Min(256, N.BitLength / 2);
+	        BigInteger min = BigInteger.One.ShiftLeft(minBits - 1);
+	        BigInteger max = N.Subtract(BigInteger.One);
+
+	        return BigIntegers.CreateRandomInRange(min, max, random);
+	    }
+
+		public static BigInteger ValidatePublicValue(BigInteger N, BigInteger val)
+		{
+		    val = val.Mod(N);
+
+	        // Check that val % N != 0
+	        if (val.Equals(BigInteger.Zero))
+	            throw new CryptoException("Invalid public value: 0");
+
+		    return val;
+		}
+
+		private static BigInteger HashPaddedPair(IDigest digest, BigInteger N, BigInteger n1, BigInteger n2)
+		{
+	    	int padLength = (N.BitLength + 7) / 8;
+
+	    	byte[] n1_bytes = GetPadded(n1, padLength);
+	    	byte[] n2_bytes = GetPadded(n2, padLength);
+
+	        digest.BlockUpdate(n1_bytes, 0, n1_bytes.Length);
+	        digest.BlockUpdate(n2_bytes, 0, n2_bytes.Length);
+
+	        byte[] output = new byte[digest.GetDigestSize()];
+	        digest.DoFinal(output, 0);
+
+	        return new BigInteger(1, output);
+		}
+
+		private static byte[] GetPadded(BigInteger n, int length)
+		{
+			byte[] bs = BigIntegers.AsUnsignedByteArray(n);
+			if (bs.Length < length)
+			{
+				byte[] tmp = new byte[length];
+				Array.Copy(bs, 0, tmp, length - bs.Length, bs.Length);
+				bs = tmp;
+			}
+			return bs;
+		}
+	}
+}
diff --git a/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs b/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
new file mode 100644
index 000000000..264833b4d
--- /dev/null
+++ b/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
@@ -0,0 +1,49 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Srp
+{
+	/**
+	 * Generates new SRP verifier for user
+	 */
+	public class Srp6VerifierGenerator
+	{
+	    protected BigInteger N;
+	    protected BigInteger g;
+	    protected IDigest digest;
+
+	    public Srp6VerifierGenerator()
+	    {
+	    }
+
+	    /**
+	     * Initialises generator to create new verifiers
+	     * @param N The safe prime to use (see DHParametersGenerator)
+	     * @param g The group parameter to use (see DHParametersGenerator)
+	     * @param digest The digest to use. The same digest type will need to be used later for the actual authentication
+	     * attempt. Also note that the final session key size is dependent on the chosen digest.
+	     */
+	    public virtual void Init(BigInteger N, BigInteger g, IDigest digest)
+	    {
+	        this.N = N;
+	        this.g = g;
+	        this.digest = digest;
+	    }
+
+	    /**
+	     * Creates a new SRP verifier
+	     * @param salt The salt to use, generally should be large and random
+	     * @param identity The user's identifying information (eg. username)
+	     * @param password The user's password
+	     * @return A new verifier for use in future SRP authentication
+	     */
+	    public virtual BigInteger GenerateVerifier(byte[] salt, byte[] identity, byte[] password)
+	    {
+	    	BigInteger x = Srp6Utilities.CalculateX(digest, N, salt, identity, password);
+
+	        return g.ModPow(x, N);
+	    }
+	}
+}
+