summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/asn1/DerApplicationSpecific.cs33
-rw-r--r--crypto/src/asn1/edec/EdECObjectIdentifiers.cs17
-rw-r--r--crypto/src/asn1/pkcs/EncryptionScheme.cs8
-rw-r--r--crypto/src/asn1/pkcs/PrivateKeyInfo.cs193
-rw-r--r--crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs50
-rw-r--r--crypto/src/asn1/x509/TBSCertificateStructure.cs8
-rw-r--r--crypto/src/bcpg/ECPublicBCPGKey.cs2
-rw-r--r--crypto/src/cms/SignerInformation.cs24
-rw-r--r--crypto/src/crypto/IRawAgreement.cs13
-rw-r--r--crypto/src/crypto/agreement/X25519Agreement.cs27
-rw-r--r--crypto/src/crypto/agreement/X448Agreement.cs27
-rw-r--r--crypto/src/crypto/digests/Blake2bDigest.cs4
-rw-r--r--crypto/src/crypto/digests/Blake2sDigest.cs7
-rw-r--r--crypto/src/crypto/digests/NullDigest.cs24
-rw-r--r--crypto/src/crypto/engines/RFC3211WrapEngine.cs59
-rw-r--r--crypto/src/crypto/engines/SM4Engine.cs189
-rw-r--r--crypto/src/crypto/generators/Ed25519KeyPairGenerator.cs25
-rw-r--r--crypto/src/crypto/generators/Ed448KeyPairGenerator.cs25
-rw-r--r--crypto/src/crypto/generators/X25519KeyPairGenerator.cs25
-rw-r--r--crypto/src/crypto/generators/X448KeyPairGenerator.cs25
-rw-r--r--crypto/src/crypto/parameters/Ed25519KeyGenerationParameters.cs15
-rw-r--r--crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs98
-rw-r--r--crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs40
-rw-r--r--crypto/src/crypto/parameters/Ed448KeyGenerationParameters.cs15
-rw-r--r--crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs90
-rw-r--r--crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs40
-rw-r--r--crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs178
-rw-r--r--crypto/src/crypto/parameters/X25519KeyGenerationParameters.cs15
-rw-r--r--crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs63
-rw-r--r--crypto/src/crypto/parameters/X25519PublicKeyParameters.cs40
-rw-r--r--crypto/src/crypto/parameters/X448KeyGenerationParameters.cs15
-rw-r--r--crypto/src/crypto/parameters/X448PrivateKeyParameters.cs63
-rw-r--r--crypto/src/crypto/parameters/X448PublicKeyParameters.cs40
-rw-r--r--crypto/src/crypto/signers/Ed25519Signer.cs129
-rw-r--r--crypto/src/crypto/signers/Ed25519ctxSigner.cs131
-rw-r--r--crypto/src/crypto/signers/Ed25519phSigner.cs89
-rw-r--r--crypto/src/crypto/signers/Ed448Signer.cs131
-rw-r--r--crypto/src/crypto/signers/Ed448phSigner.cs89
-rw-r--r--crypto/src/crypto/tls/TlsProtocol.cs29
-rw-r--r--crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs8
-rw-r--r--crypto/src/math/ec/multiplier/WNafUtilities.cs12
-rw-r--r--crypto/src/math/ec/rfc7748/X25519.cs11
-rw-r--r--crypto/src/math/ec/rfc7748/X448.cs11
-rw-r--r--crypto/src/math/ec/rfc8032/Ed25519.cs271
-rw-r--r--crypto/src/math/ec/rfc8032/Ed448.cs250
-rw-r--r--crypto/src/math/raw/Nat.cs18
-rw-r--r--crypto/src/openpgp/PgpSignatureSubpacketVector.cs8
-rw-r--r--crypto/src/pkcs/PrivateKeyInfoFactory.cs116
-rw-r--r--crypto/src/security/AgreementUtilities.cs55
-rw-r--r--crypto/src/security/CipherUtilities.cs5
-rw-r--r--crypto/src/security/GeneratorUtilities.cs37
-rw-r--r--crypto/src/security/ParameterUtilities.cs4
-rw-r--r--crypto/src/security/PrivateKeyFactory.cs28
-rw-r--r--crypto/src/security/PublicKeyFactory.cs32
-rw-r--r--crypto/src/security/SignerUtilities.cs36
-rw-r--r--crypto/src/util/Arrays.cs13
-rw-r--r--crypto/src/util/Integers.cs12
-rw-r--r--crypto/src/util/collections/CollectionUtilities.cs8
-rw-r--r--crypto/src/util/io/Streams.cs25
-rw-r--r--crypto/src/x509/SubjectPublicKeyInfoFactory.cs67
60 files changed, 2618 insertions, 504 deletions
diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs
index 52467fabe..a2d57bf9d 100644
--- a/crypto/src/asn1/DerApplicationSpecific.cs
+++ b/crypto/src/asn1/DerApplicationSpecific.cs
@@ -199,38 +199,27 @@ namespace Org.BouncyCastle.Asn1
 		{
 			int tagNo = input[0] & 0x1f;
 			int index = 1;
-			//
-			// with tagged object tag number is bottom 5 bits, or stored at the start of the content
-			//
+
+            // with tagged object tag number is bottom 5 bits, or stored at the start of the content
 			if (tagNo == 0x1f)
 			{
-				tagNo = 0;
-
-				int b = input[index++] & 0xff;
+				int b = input[index++];
 
-				// X.690-0207 8.1.2.4.2
+                // X.690-0207 8.1.2.4.2
 				// "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
 				if ((b & 0x7f) == 0) // Note: -1 will pass
-				{
-					throw new InvalidOperationException("corrupted stream - invalid high tag number found");
-				}
+					throw new IOException("corrupted stream - invalid high tag number found");
 
-				while ((b >= 0) && ((b & 0x80) != 0))
+                while ((b & 0x80) != 0)
 				{
-					tagNo |= (b & 0x7f);
-					tagNo <<= 7;
-					b = input[index++] & 0xff;
+					b = input[index++];
 				}
-
-				tagNo |= (b & 0x7f);
 			}
 
-			byte[] tmp = new byte[input.Length - index + 1];
-
-			Array.Copy(input, index, tmp, 1, tmp.Length - 1);
-
-			tmp[0] = (byte)newTag;
-
+            int remaining = input.Length - index;
+            byte[] tmp = new byte[1 + remaining];
+            tmp[0] = (byte)newTag;
+			Array.Copy(input, index, tmp, 1, remaining);
 			return tmp;
 		}
     }
diff --git a/crypto/src/asn1/edec/EdECObjectIdentifiers.cs b/crypto/src/asn1/edec/EdECObjectIdentifiers.cs
new file mode 100644
index 000000000..f8c5713d8
--- /dev/null
+++ b/crypto/src/asn1/edec/EdECObjectIdentifiers.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.EdEC
+{
+    /**
+     * Edwards Elliptic Curve Object Identifiers (RFC 8410)
+     */
+    public abstract class EdECObjectIdentifiers
+    {
+        public static readonly DerObjectIdentifier id_edwards_curve_algs = new DerObjectIdentifier("1.3.101");
+
+        public static readonly DerObjectIdentifier id_X25519 = id_edwards_curve_algs.Branch("110");
+        public static readonly DerObjectIdentifier id_X448 = id_edwards_curve_algs.Branch("111");
+        public static readonly DerObjectIdentifier id_Ed25519 = id_edwards_curve_algs.Branch("112");
+        public static readonly DerObjectIdentifier id_Ed448 = id_edwards_curve_algs.Branch("113");
+    }
+}
diff --git a/crypto/src/asn1/pkcs/EncryptionScheme.cs b/crypto/src/asn1/pkcs/EncryptionScheme.cs
index 7b90ece53..34d26e172 100644
--- a/crypto/src/asn1/pkcs/EncryptionScheme.cs
+++ b/crypto/src/asn1/pkcs/EncryptionScheme.cs
@@ -8,7 +8,13 @@ namespace Org.BouncyCastle.Asn1.Pkcs
     public class EncryptionScheme
         : AlgorithmIdentifier
     {
-		public EncryptionScheme(
+        public EncryptionScheme(
+            DerObjectIdentifier	objectID)
+            : base(objectID)
+        {
+        }
+
+        public EncryptionScheme(
             DerObjectIdentifier	objectID,
             Asn1Encodable		parameters)
 			: base(objectID, parameters)
diff --git a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
index c5be7a315..dfb332fdd 100644
--- a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
+++ b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
@@ -4,15 +4,55 @@ using System.IO;
 
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Asn1.Pkcs
 {
+    /**
+     *  RFC 5958
+     *
+     *  <pre>
+     *  [IMPLICIT TAGS]
+     *
+     *  OneAsymmetricKey ::= SEQUENCE {
+     *      version                   Version,
+     *      privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
+     *      privateKey                PrivateKey,
+     *      attributes            [0] Attributes OPTIONAL,
+     *      ...,
+     *      [[2: publicKey        [1] PublicKey OPTIONAL ]],
+     *      ...
+     *  }
+     *
+     *  PrivateKeyInfo ::= OneAsymmetricKey
+     *
+     *  Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2)
+     *
+     *  PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+     *                                     { PUBLIC-KEY,
+     *                                       { PrivateKeyAlgorithms } }
+     *
+     *  PrivateKey ::= OCTET STRING
+     *                     -- Content varies based on type of key.  The
+     *                     -- algorithm identifier dictates the format of
+     *                     -- the key.
+     *
+     *  PublicKey ::= BIT STRING
+     *                     -- Content varies based on type of key.  The
+     *                     -- algorithm identifier dictates the format of
+     *                     -- the key.
+     *
+     *  Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
+     *  </pre>
+     */
     public class PrivateKeyInfo
         : Asn1Encodable
     {
-        private readonly Asn1OctetString        privKey;
-        private readonly AlgorithmIdentifier	algID;
-        private readonly Asn1Set				attributes;
+        private readonly DerInteger version;
+        private readonly AlgorithmIdentifier privateKeyAlgorithm;
+        private readonly Asn1OctetString privateKey;
+        private readonly Asn1Set attributes;
+        private readonly DerBitString publicKey;
 
         public static PrivateKeyInfo GetInstance(Asn1TaggedObject obj, bool explicitly)
         {
@@ -25,110 +65,139 @@ namespace Org.BouncyCastle.Asn1.Pkcs
             if (obj == null)
                 return null;
             if (obj is PrivateKeyInfo)
-                return (PrivateKeyInfo) obj;
+                return (PrivateKeyInfo)obj;
             return new PrivateKeyInfo(Asn1Sequence.GetInstance(obj));
         }
 
-        public PrivateKeyInfo(AlgorithmIdentifier algID, Asn1Encodable privateKey)
-            : this(algID, privateKey, null)
+        private static int GetVersionValue(DerInteger version)
+        {
+            BigInteger bigValue = version.Value;
+            if (bigValue.CompareTo(BigInteger.Zero) < 0 || bigValue.CompareTo(BigInteger.One) > 0)
+                throw new ArgumentException("invalid version for private key info", "version");
+
+            return bigValue.IntValue;
+        }
+
+        public PrivateKeyInfo(
+            AlgorithmIdentifier privateKeyAlgorithm,
+            Asn1Encodable privateKey)
+            : this(privateKeyAlgorithm, privateKey, null, null)
         {
         }
 
         public PrivateKeyInfo(
-            AlgorithmIdentifier	algID,
-            Asn1Encodable       privateKey,
-            Asn1Set				attributes)
+            AlgorithmIdentifier privateKeyAlgorithm,
+            Asn1Encodable privateKey,
+            Asn1Set attributes)
+            : this(privateKeyAlgorithm, privateKey, attributes, null)
         {
-            this.algID = algID;
-            this.privKey = new DerOctetString(privateKey.GetEncoded(Asn1Encodable.Der));
+        }
+
+        public PrivateKeyInfo(
+            AlgorithmIdentifier privateKeyAlgorithm,
+            Asn1Encodable privateKey,
+            Asn1Set attributes,
+            byte[] publicKey)
+        {
+            this.version = new DerInteger(publicKey != null ? BigInteger.One : BigInteger.Zero);
+            this.privateKeyAlgorithm = privateKeyAlgorithm;
+            this.privateKey = new DerOctetString(privateKey);
             this.attributes = attributes;
+            this.publicKey = publicKey == null ? null : new DerBitString(publicKey);
         }
 
         private PrivateKeyInfo(Asn1Sequence seq)
         {
             IEnumerator e = seq.GetEnumerator();
 
-            e.MoveNext();
-            BigInteger version = ((DerInteger)e.Current).Value;
-            if (version.IntValue != 0)
-            {
-                throw new ArgumentException("wrong version for private key info: " + version.IntValue);
-            }
+            this.version = DerInteger.GetInstance(CollectionUtilities.RequireNext(e));
 
-            e.MoveNext();
-            algID = AlgorithmIdentifier.GetInstance(e.Current);
-            e.MoveNext();
-            privKey = Asn1OctetString.GetInstance(e.Current);
+            int versionValue = GetVersionValue(version);
 
-            if (e.MoveNext())
+            this.privateKeyAlgorithm = AlgorithmIdentifier.GetInstance(CollectionUtilities.RequireNext(e));
+            this.privateKey = Asn1OctetString.GetInstance(CollectionUtilities.RequireNext(e));
+
+            int lastTag = -1;
+            while (e.MoveNext())
             {
-                attributes = Asn1Set.GetInstance((Asn1TaggedObject)e.Current, false);
+                Asn1TaggedObject tagged = (Asn1TaggedObject)e.Current;
+
+                int tag = tagged.TagNo;
+                if (tag <= lastTag)
+                    throw new ArgumentException("invalid optional field in private key info", "seq");
+
+                lastTag = tag;
+
+                switch (tag)
+                {
+                case 0:
+                {
+                    this.attributes = Asn1Set.GetInstance(tagged, false);
+                    break;
+                }
+                case 1:
+                {
+                    if (versionValue < 1)
+                        throw new ArgumentException("'publicKey' requires version v2(1) or later", "seq");
+
+                    this.publicKey = DerBitString.GetInstance(tagged, false);
+                    break;
+                }
+                default:
+                {
+                    throw new ArgumentException("unknown optional field in private key info", "seq");
+                }
+                }
             }
         }
 
-        public virtual AlgorithmIdentifier PrivateKeyAlgorithm
+        public virtual Asn1Set Attributes
         {
-            get { return algID; }
+            get { return attributes; }
         }
 
-        [Obsolete("Use 'PrivateKeyAlgorithm' property instead")]
-        public virtual AlgorithmIdentifier AlgorithmID
+        /// <summary>Return true if a public key is present, false otherwise.</summary>
+        public virtual bool HasPublicKey
         {
-            get { return algID; }
+            get { return publicKey != null; }
+        }
+
+        public virtual AlgorithmIdentifier PrivateKeyAlgorithm
+        {
+            get { return privateKeyAlgorithm; }
         }
 
         public virtual Asn1Object ParsePrivateKey()
         {
-            return Asn1Object.FromByteArray(privKey.GetOctets());
+            return Asn1Object.FromByteArray(privateKey.GetOctets());
         }
 
-        [Obsolete("Use 'ParsePrivateKey' instead")]
-        public virtual Asn1Object PrivateKey
+        /// <summary>For when the public key is an ASN.1 encoding.</summary>
+        public virtual Asn1Object ParsePublicKey()
         {
-            get
-            {
-                try
-                {
-                    return ParsePrivateKey();
-                }
-                catch (IOException)
-                {
-                    throw new InvalidOperationException("unable to parse private key");
-                }
-            }
+            return publicKey == null ? null : Asn1Object.FromByteArray(publicKey.GetOctets());
         }
 
-        public virtual Asn1Set Attributes
+        /// <summary>Return the public key as a raw bit string.</summary>
+        public virtual DerBitString PublicKeyData
         {
-            get { return attributes; }
+            get { return publicKey; }
         }
 
-        /**
-         * write out an RSA private key with its associated information
-         * as described in Pkcs8.
-         * <pre>
-         *      PrivateKeyInfo ::= Sequence {
-         *                              version Version,
-         *                              privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}},
-         *                              privateKey PrivateKey,
-         *                              attributes [0] IMPLICIT Attributes OPTIONAL
-         *                          }
-         *      Version ::= Integer {v1(0)} (v1,...)
-         *
-         *      PrivateKey ::= OCTET STRING
-         *
-         *      Attributes ::= Set OF Attr
-         * </pre>
-         */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(new DerInteger(0), algID, privKey);
+            Asn1EncodableVector v = new Asn1EncodableVector(version, privateKeyAlgorithm, privateKey);
 
             if (attributes != null)
             {
                 v.Add(new DerTaggedObject(false, 0, attributes));
             }
 
+            if (publicKey != null)
+            {
+                v.Add(new DerTaggedObject(false, 1, publicKey));
+            }
+
             return new DerSequence(v);
         }
     }
diff --git a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
index 721299105..738a97eb4 100644
--- a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
+++ b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
@@ -9,14 +9,14 @@ namespace Org.BouncyCastle.Asn1.Pkcs
     public class RsaPrivateKeyStructure
         : Asn1Encodable
     {
-        private readonly BigInteger	modulus;
-        private readonly BigInteger	publicExponent;
-        private readonly BigInteger	privateExponent;
-        private readonly BigInteger	prime1;
-        private readonly BigInteger	prime2;
-        private readonly BigInteger	exponent1;
-        private readonly BigInteger	exponent2;
-        private readonly BigInteger	coefficient;
+        private readonly BigInteger modulus;
+        private readonly BigInteger publicExponent;
+        private readonly BigInteger privateExponent;
+        private readonly BigInteger prime1;
+        private readonly BigInteger prime2;
+        private readonly BigInteger exponent1;
+        private readonly BigInteger exponent2;
+        private readonly BigInteger coefficient;
 
         public static RsaPrivateKeyStructure GetInstance(Asn1TaggedObject obj, bool isExplicit)
         {
@@ -33,14 +33,14 @@ namespace Org.BouncyCastle.Asn1.Pkcs
         }
 
         public RsaPrivateKeyStructure(
-            BigInteger	modulus,
-            BigInteger	publicExponent,
-            BigInteger	privateExponent,
-            BigInteger	prime1,
-            BigInteger	prime2,
-            BigInteger	exponent1,
-            BigInteger	exponent2,
-            BigInteger	coefficient)
+            BigInteger modulus,
+            BigInteger publicExponent,
+            BigInteger privateExponent,
+            BigInteger prime1,
+            BigInteger prime2,
+            BigInteger exponent1,
+            BigInteger exponent2,
+            BigInteger coefficient)
         {
             this.modulus = modulus;
             this.publicExponent = publicExponent;
@@ -56,18 +56,18 @@ namespace Org.BouncyCastle.Asn1.Pkcs
         public RsaPrivateKeyStructure(
             Asn1Sequence seq)
         {
-            BigInteger version = ((DerInteger) seq[0]).Value;
+            BigInteger version = ((DerInteger)seq[0]).Value;
             if (version.IntValue != 0)
                 throw new ArgumentException("wrong version for RSA private key");
 
-            modulus = ((DerInteger) seq[1]).Value;
-            publicExponent = ((DerInteger) seq[2]).Value;
-            privateExponent = ((DerInteger) seq[3]).Value;
-            prime1 = ((DerInteger) seq[4]).Value;
-            prime2 = ((DerInteger) seq[5]).Value;
-            exponent1 = ((DerInteger) seq[6]).Value;
-            exponent2 = ((DerInteger) seq[7]).Value;
-            coefficient = ((DerInteger) seq[8]).Value;
+            modulus = ((DerInteger)seq[1]).Value;
+            publicExponent = ((DerInteger)seq[2]).Value;
+            privateExponent = ((DerInteger)seq[3]).Value;
+            prime1 = ((DerInteger)seq[4]).Value;
+            prime2 = ((DerInteger)seq[5]).Value;
+            exponent1 = ((DerInteger)seq[6]).Value;
+            exponent2 = ((DerInteger)seq[7]).Value;
+            coefficient = ((DerInteger)seq[8]).Value;
         }
 
         public BigInteger Modulus
diff --git a/crypto/src/asn1/x509/TBSCertificateStructure.cs b/crypto/src/asn1/x509/TBSCertificateStructure.cs
index 9df078539..e69e985f5 100644
--- a/crypto/src/asn1/x509/TBSCertificateStructure.cs
+++ b/crypto/src/asn1/x509/TBSCertificateStructure.cs
@@ -121,7 +121,7 @@ namespace Org.BouncyCastle.Asn1.X509
 
             while (extras > 0)
 			{
-				DerTaggedObject extra = (DerTaggedObject) seq[seqStart + 6 + extras];
+				DerTaggedObject extra = (DerTaggedObject)seq[seqStart + 6 + extras];
 
 				switch (extra.TagNo)
 				{
@@ -140,9 +140,13 @@ namespace Org.BouncyCastle.Asn1.X509
                     if (isV2)
                         throw new ArgumentException("version 2 certificate cannot contain extensions");
 
-                    extensions = X509Extensions.GetInstance(extra);
+                    extensions = X509Extensions.GetInstance(Asn1Sequence.GetInstance(extra, true));
 					break;
                 }
+                default:
+                {
+                    throw new ArgumentException("Unknown tag encountered in structure: " + extra.TagNo);
+                }
                 }
                 extras--;
 			}
diff --git a/crypto/src/bcpg/ECPublicBCPGKey.cs b/crypto/src/bcpg/ECPublicBCPGKey.cs
index f328f9dc3..c473139e7 100644
--- a/crypto/src/bcpg/ECPublicBCPGKey.cs
+++ b/crypto/src/bcpg/ECPublicBCPGKey.cs
@@ -26,7 +26,7 @@ namespace Org.BouncyCastle.Bcpg
             DerObjectIdentifier oid,
             ECPoint point)
         {
-            this.point = new BigInteger(1, point.GetEncoded());
+            this.point = new BigInteger(1, point.GetEncoded(false));
             this.oid = oid;
         }
 
diff --git a/crypto/src/cms/SignerInformation.cs b/crypto/src/cms/SignerInformation.cs
index dad128263..39ecfa6d3 100644
--- a/crypto/src/cms/SignerInformation.cs
+++ b/crypto/src/cms/SignerInformation.cs
@@ -84,6 +84,30 @@ namespace Org.BouncyCastle.Cms
 			this.digestCalculator = digestCalculator;
 		}
 
+        /**
+         * Protected constructor. In some cases clients have their own idea about how to encode
+         * the signed attributes and calculate the signature. This constructor is to allow developers
+         * to deal with that by extending off the class and overridng methods like getSignedAttributes().
+         *
+         * @param baseInfo the SignerInformation to base this one on.
+         */
+        protected SignerInformation(SignerInformation baseInfo)
+        {
+            this.info = baseInfo.info;
+            this.contentType = baseInfo.contentType;
+            this.isCounterSignature = baseInfo.IsCounterSignature;
+            this.sid = baseInfo.SignerID;
+            this.digestAlgorithm = info.DigestAlgorithm;
+            this.signedAttributeSet = info.AuthenticatedAttributes;
+            this.unsignedAttributeSet = info.UnauthenticatedAttributes;
+            this.encryptionAlgorithm = info.DigestEncryptionAlgorithm;
+            this.signature = info.EncryptedDigest.GetOctets();
+            this.content = baseInfo.content;
+            this.resultDigest = baseInfo.resultDigest;
+            this.signedAttributeTable = baseInfo.signedAttributeTable;
+            this.unsignedAttributeTable = baseInfo.unsignedAttributeTable;
+        }
+
 		public bool IsCounterSignature
 		{
 			get { return isCounterSignature; }
diff --git a/crypto/src/crypto/IRawAgreement.cs b/crypto/src/crypto/IRawAgreement.cs
new file mode 100644
index 000000000..63e664888
--- /dev/null
+++ b/crypto/src/crypto/IRawAgreement.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+    public interface IRawAgreement
+    {
+        void Init(ICipherParameters parameters);
+
+        int AgreementSize { get; }
+
+        void CalculateAgreement(ICipherParameters publicKey, byte[] buf, int off);
+    }
+}
diff --git a/crypto/src/crypto/agreement/X25519Agreement.cs b/crypto/src/crypto/agreement/X25519Agreement.cs
new file mode 100644
index 000000000..7e5890c16
--- /dev/null
+++ b/crypto/src/crypto/agreement/X25519Agreement.cs
@@ -0,0 +1,27 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+    public sealed class X25519Agreement
+        : IRawAgreement
+    {
+        private X25519PrivateKeyParameters privateKey;
+
+        public void Init(ICipherParameters parameters)
+        {
+            this.privateKey = (X25519PrivateKeyParameters)parameters;
+        }
+
+        public int AgreementSize
+        {
+            get { return X25519PrivateKeyParameters.SecretSize; }
+        }
+
+        public void CalculateAgreement(ICipherParameters publicKey, byte[] buf, int off)
+        {
+            privateKey.GenerateSecret((X25519PublicKeyParameters)publicKey, buf, off);
+        }
+    }
+}
diff --git a/crypto/src/crypto/agreement/X448Agreement.cs b/crypto/src/crypto/agreement/X448Agreement.cs
new file mode 100644
index 000000000..26f608c26
--- /dev/null
+++ b/crypto/src/crypto/agreement/X448Agreement.cs
@@ -0,0 +1,27 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+    public sealed class X448Agreement
+        : IRawAgreement
+    {
+        private X448PrivateKeyParameters privateKey;
+
+        public void Init(ICipherParameters parameters)
+        {
+            this.privateKey = (X448PrivateKeyParameters)parameters;
+        }
+
+        public int AgreementSize
+        {
+            get { return X448PrivateKeyParameters.SecretSize; }
+        }
+
+        public void CalculateAgreement(ICipherParameters publicKey, byte[] buf, int off)
+        {
+            privateKey.GenerateSecret((X448PublicKeyParameters)publicKey, buf, off);
+        }
+    }
+}
diff --git a/crypto/src/crypto/digests/Blake2bDigest.cs b/crypto/src/crypto/digests/Blake2bDigest.cs
index b8e4f272e..770e35caf 100644
--- a/crypto/src/crypto/digests/Blake2bDigest.cs
+++ b/crypto/src/crypto/digests/Blake2bDigest.cs
@@ -136,8 +136,8 @@ namespace Org.BouncyCastle.Crypto.Digests
          */
         public Blake2bDigest(int digestSize)
         {
-            if (digestSize != 160 && digestSize != 256 && digestSize != 384 && digestSize != 512)
-                throw new ArgumentException("BLAKE2b digest restricted to one of [160, 256, 384, 512]");
+            if (digestSize < 8 || digestSize > 512 || digestSize % 8 != 0)
+                throw new ArgumentException("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512");
 
             buffer = new byte[BLOCK_LENGTH_BYTES];
             keyLength = 0;
diff --git a/crypto/src/crypto/digests/Blake2sDigest.cs b/crypto/src/crypto/digests/Blake2sDigest.cs
index f31032874..432b0f4d2 100644
--- a/crypto/src/crypto/digests/Blake2sDigest.cs
+++ b/crypto/src/crypto/digests/Blake2sDigest.cs
@@ -152,13 +152,12 @@ namespace Org.BouncyCastle.Crypto.Digests
         /**
          * BLAKE2s for hashing.
          *
-         * @param digestBits the desired digest length in bits. Must be one of
-         *                   [128, 160, 224, 256].
+         * @param digestBits the desired digest length in bits. Must be a multiple of 8 and less than 256.
          */
         public Blake2sDigest(int digestBits)
         {
-            if (digestBits != 128 && digestBits != 160 && digestBits != 224 && digestBits != 256)
-                throw new ArgumentException("BLAKE2s digest restricted to one of [128, 160, 224, 256]");
+            if (digestBits < 8 || digestBits > 256 || digestBits % 8 != 0)
+                throw new ArgumentException("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256");
 
             buffer = new byte[BLOCK_LENGTH_BYTES];
             keyLength = 0;
diff --git a/crypto/src/crypto/digests/NullDigest.cs b/crypto/src/crypto/digests/NullDigest.cs
index e598cb145..76b69afbf 100644
--- a/crypto/src/crypto/digests/NullDigest.cs
+++ b/crypto/src/crypto/digests/NullDigest.cs
@@ -1,6 +1,8 @@
 using System;
 using System.IO;
 
+using Org.BouncyCastle.Utilities.IO;
+
 namespace Org.BouncyCastle.Crypto.Digests
 {
 	public class NullDigest : IDigest
@@ -20,7 +22,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
 		public int GetDigestSize()
 		{
-			return (int) bOut.Length;
+			return (int)bOut.Length;
 		}
 
 		public void Update(byte b)
@@ -33,15 +35,19 @@ namespace Org.BouncyCastle.Crypto.Digests
 			bOut.Write(inBytes, inOff, len);
 		}
 
-		public int DoFinal(byte[] outBytes, int outOff)
+        public int DoFinal(byte[] outBytes, int outOff)
 		{
-			byte[] res = bOut.ToArray();
-			res.CopyTo(outBytes, outOff);
-			Reset();
-			return res.Length;
-		}
-
-		public void Reset()
+            try
+            {
+                return Streams.WriteBufTo(bOut, outBytes, outOff);
+            }
+            finally
+            {
+                Reset();
+            }
+        }
+
+        public void Reset()
 		{
 			bOut.SetLength(0);
 		}
diff --git a/crypto/src/crypto/engines/RFC3211WrapEngine.cs b/crypto/src/crypto/engines/RFC3211WrapEngine.cs
index 4e3af5227..86480145c 100644
--- a/crypto/src/crypto/engines/RFC3211WrapEngine.cs
+++ b/crypto/src/crypto/engines/RFC3211WrapEngine.cs
@@ -32,10 +32,10 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 			if (param is ParametersWithRandom)
 			{
-				ParametersWithRandom p = (ParametersWithRandom) param;
+				ParametersWithRandom p = (ParametersWithRandom)param;
 
-				this.rand = p.Random;
-				this.param = (ParametersWithIV) p.Parameters;
+                this.rand = p.Random;
+                this.param = p.Parameters as ParametersWithIV;
 			}
 			else
 			{
@@ -44,9 +44,12 @@ namespace Org.BouncyCastle.Crypto.Engines
 					rand = new SecureRandom();
 				}
 
-				this.param = (ParametersWithIV) param;
-			}
-		}
+                this.param = param as ParametersWithIV;
+            }
+
+            if (null == this.param)
+                throw new ArgumentException("RFC3211Wrap requires an IV", "param");
+        }
 
         public virtual string AlgorithmName
 		{
@@ -59,11 +62,11 @@ namespace Org.BouncyCastle.Crypto.Engines
 			int		inLen)
 		{
 			if (!forWrapping)
-			{
 				throw new InvalidOperationException("not set for wrapping");
-			}
+            if (inLen > 255 || inLen < 0)
+                throw new ArgumentException("input must be from 0 to 255 bytes", "inLen");
 
-			engine.Init(true, param);
+            engine.Init(true, param);
 
 			int blockSize = engine.GetBlockSize();
 			byte[] cekBlock;
@@ -78,15 +81,16 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 
 			cekBlock[0] = (byte)inLen;
-			cekBlock[1] = (byte)~inBytes[inOff];
-			cekBlock[2] = (byte)~inBytes[inOff + 1];
-			cekBlock[3] = (byte)~inBytes[inOff + 2];
 
 			Array.Copy(inBytes, inOff, cekBlock, 4, inLen);
 
 			rand.NextBytes(cekBlock, inLen + 4, cekBlock.Length - inLen - 4);
 
-			for (int i = 0; i < cekBlock.Length; i += blockSize)
+            cekBlock[1] = (byte)~cekBlock[4];
+            cekBlock[2] = (byte)~cekBlock[4 + 1];
+            cekBlock[3] = (byte)~cekBlock[4 + 2];
+
+            for (int i = 0; i < cekBlock.Length; i += blockSize)
 			{
 				engine.ProcessBlock(cekBlock, i, cekBlock, i);
 			}
@@ -142,27 +146,34 @@ namespace Org.BouncyCastle.Crypto.Engines
 				engine.ProcessBlock(cekBlock, i, cekBlock, i);
 			}
 
-			if ((cekBlock[0] & 0xff) > cekBlock.Length - 4)
-			{
-				throw new InvalidCipherTextException("wrapped key corrupted");
-			}
+            bool invalidLength = (int)cekBlock[0] > (cekBlock.Length - 4);
 
-			byte[] key = new byte[cekBlock[0] & 0xff];
+            byte[] key;
+            if (invalidLength)
+            {
+                key = new byte[cekBlock.Length - 4];
+            }
+            else
+            {
+                key = new byte[cekBlock[0]];
+            }
 
-			Array.Copy(cekBlock, 4, key, 0, cekBlock[0]);
+            Array.Copy(cekBlock, 4, key, 0, key.Length);
 
 			// Note: Using constant time comparison
 			int nonEqual = 0;
 			for (int i = 0; i != 3; i++)
 			{
 				byte check = (byte)~cekBlock[1 + i];
-				nonEqual |= (check ^ key[i]);
-			}
+                nonEqual |= (check ^ cekBlock[4 + i]);
+            }
+
+            Array.Clear(cekBlock, 0, cekBlock.Length);
 
-			if (nonEqual != 0)
-				throw new InvalidCipherTextException("wrapped key fails checksum");
+            if (nonEqual != 0 | invalidLength)
+                throw new InvalidCipherTextException("wrapped key corrupted");
 
-			return key;
+            return key;
 		}
 	}
 }
diff --git a/crypto/src/crypto/engines/SM4Engine.cs b/crypto/src/crypto/engines/SM4Engine.cs
new file mode 100644
index 000000000..7477b070e
--- /dev/null
+++ b/crypto/src/crypto/engines/SM4Engine.cs
@@ -0,0 +1,189 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+    /// <summary>SM4 Block Cipher - SM4 is a 128 bit block cipher with a 128 bit key.</summary>
+    /// <remarks>
+    /// The implementation here is based on the document <a href="http://eprint.iacr.org/2008/329.pdf">http://eprint.iacr.org/2008/329.pdf</a>
+    /// by Whitfield Diffie and George Ledin, which is a translation of Prof. LU Shu-wang's original standard.
+    /// </remarks>
+    public class SM4Engine
+        : IBlockCipher
+    {
+        private const int BlockSize = 16;
+
+        private static readonly byte[] Sbox =
+        {
+            0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
+            0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
+            0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
+            0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
+            0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
+            0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
+            0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
+            0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
+            0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
+            0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
+            0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
+            0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
+            0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
+            0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
+            0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
+            0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
+        };
+
+        private static readonly uint[] CK =
+        {
+            0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
+            0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
+            0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
+            0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
+            0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
+            0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
+            0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
+            0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
+        };
+
+        private static readonly uint[] FK =
+        {
+            0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc
+        };
+
+        private uint[] rk;
+
+        // non-linear substitution tau.
+        private static uint tau(uint A)
+        {
+            uint b0 = Sbox[A >> 24];
+            uint b1 = Sbox[(A >> 16) & 0xFF];
+            uint b2 = Sbox[(A >> 8) & 0xFF];
+            uint b3 = Sbox[A & 0xFF];
+
+            return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
+        }
+
+        private static uint L_ap(uint B)
+        {
+            return B ^ Integers.RotateLeft(B, 13) ^ Integers.RotateLeft(B, 23);
+        }
+
+        private uint T_ap(uint Z)
+        {
+            return L_ap(tau(Z));
+        }
+
+        // Key expansion
+        private void ExpandKey(bool forEncryption, byte[] key)
+        {
+            uint K0 = Pack.BE_To_UInt32(key,  0) ^ FK[0];
+            uint K1 = Pack.BE_To_UInt32(key,  4) ^ FK[1];
+            uint K2 = Pack.BE_To_UInt32(key,  8) ^ FK[2];
+            uint K3 = Pack.BE_To_UInt32(key, 12) ^ FK[3];
+
+            if (forEncryption)
+            {
+                rk[0] = K0 ^ T_ap(K1    ^ K2    ^ K3    ^ CK[0]);
+                rk[1] = K1 ^ T_ap(K2    ^ K3    ^ rk[0] ^ CK[1]);
+                rk[2] = K2 ^ T_ap(K3    ^ rk[0] ^ rk[1] ^ CK[2]);
+                rk[3] = K3 ^ T_ap(rk[0] ^ rk[1] ^ rk[2] ^ CK[3]);
+                for (int i = 4; i < 32; ++i)
+                {
+                    rk[i] = rk[i - 4] ^ T_ap(rk[i - 3] ^ rk[i - 2] ^ rk[i - 1] ^ CK[i]);
+                }
+            }
+            else
+            {
+                rk[31] = K0 ^ T_ap(K1     ^ K2     ^ K3     ^ CK[0]);
+                rk[30] = K1 ^ T_ap(K2     ^ K3     ^ rk[31] ^ CK[1]);
+                rk[29] = K2 ^ T_ap(K3     ^ rk[31] ^ rk[30] ^ CK[2]);
+                rk[28] = K3 ^ T_ap(rk[31] ^ rk[30] ^ rk[29] ^ CK[3]);
+                for (int i = 27; i >= 0; --i)
+                {
+                    rk[i] = rk[i + 4] ^ T_ap(rk[i + 3] ^ rk[i + 2] ^ rk[i + 1] ^ CK[31 - i]);
+                }
+            }
+        }
+
+        // Linear substitution L
+        private static uint L(uint B)
+        {
+            return B ^ Integers.RotateLeft(B, 2) ^ Integers.RotateLeft(B, 10) ^ Integers.RotateLeft(B, 18) ^ Integers.RotateLeft(B, 24);
+        }
+
+        // Mixer-substitution T
+        private static uint T(uint Z)
+        {
+            return L(tau(Z));
+        }
+
+        public virtual void Init(bool forEncryption, ICipherParameters parameters)
+        {
+            KeyParameter keyParameter = parameters as KeyParameter;
+            if (null == keyParameter)
+                throw new ArgumentException("invalid parameter passed to SM4 init - " + Platform.GetTypeName(parameters), "parameters");
+
+            byte[] key = keyParameter.GetKey();
+            if (key.Length != 16)
+                throw new ArgumentException("SM4 requires a 128 bit key", "parameters");
+
+            if (null == rk)
+            {
+                rk = new uint[32];
+            }
+
+            ExpandKey(forEncryption, key);
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return "SM4"; }
+        }
+
+        public virtual bool IsPartialBlockOkay
+        {
+            get { return false; }
+        }
+
+        public virtual int GetBlockSize()
+        {
+            return BlockSize;
+        }
+
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
+        {
+            if (null == rk)
+                throw new InvalidOperationException("SM4 not initialised");
+
+            Check.DataLength(input, inOff, BlockSize, "input buffer too short");
+            Check.OutputLength(output, outOff, BlockSize, "output buffer too short");
+
+            uint X0 = Pack.BE_To_UInt32(input, inOff);
+            uint X1 = Pack.BE_To_UInt32(input, inOff + 4);
+            uint X2 = Pack.BE_To_UInt32(input, inOff + 8);
+            uint X3 = Pack.BE_To_UInt32(input, inOff + 12);
+
+            for (int i = 0; i < 32; i += 4)
+            {
+                X0 ^= T(X1 ^ X2 ^ X3 ^ rk[i    ]);  // F0
+                X1 ^= T(X2 ^ X3 ^ X0 ^ rk[i + 1]);  // F1
+                X2 ^= T(X3 ^ X0 ^ X1 ^ rk[i + 2]);  // F2
+                X3 ^= T(X0 ^ X1 ^ X2 ^ rk[i + 3]);  // F3
+            }
+
+            Pack.UInt32_To_BE(X3, output, outOff);
+            Pack.UInt32_To_BE(X2, output, outOff + 4);
+            Pack.UInt32_To_BE(X1, output, outOff + 8);
+            Pack.UInt32_To_BE(X0, output, outOff + 12);
+
+            return BlockSize;
+        }
+
+        public virtual void Reset()
+        {
+        }
+    }
+}
diff --git a/crypto/src/crypto/generators/Ed25519KeyPairGenerator.cs b/crypto/src/crypto/generators/Ed25519KeyPairGenerator.cs
new file mode 100644
index 000000000..266d111cf
--- /dev/null
+++ b/crypto/src/crypto/generators/Ed25519KeyPairGenerator.cs
@@ -0,0 +1,25 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+    public class Ed25519KeyPairGenerator
+        : IAsymmetricCipherKeyPairGenerator
+    {
+        private SecureRandom random;
+
+        public virtual void Init(KeyGenerationParameters parameters)
+        {
+            this.random = parameters.Random;
+        }
+
+        public virtual AsymmetricCipherKeyPair GenerateKeyPair()
+        {
+            Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(random);
+            Ed25519PublicKeyParameters publicKey = privateKey.GeneratePublicKey();
+            return new AsymmetricCipherKeyPair(publicKey, privateKey);
+        }
+    }
+}
diff --git a/crypto/src/crypto/generators/Ed448KeyPairGenerator.cs b/crypto/src/crypto/generators/Ed448KeyPairGenerator.cs
new file mode 100644
index 000000000..50aee631e
--- /dev/null
+++ b/crypto/src/crypto/generators/Ed448KeyPairGenerator.cs
@@ -0,0 +1,25 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+    public class Ed448KeyPairGenerator
+        : IAsymmetricCipherKeyPairGenerator
+    {
+        private SecureRandom random;
+
+        public virtual void Init(KeyGenerationParameters parameters)
+        {
+            this.random = parameters.Random;
+        }
+
+        public virtual AsymmetricCipherKeyPair GenerateKeyPair()
+        {
+            Ed448PrivateKeyParameters privateKey = new Ed448PrivateKeyParameters(random);
+            Ed448PublicKeyParameters publicKey = privateKey.GeneratePublicKey();
+            return new AsymmetricCipherKeyPair(publicKey, privateKey);
+        }
+    }
+}
diff --git a/crypto/src/crypto/generators/X25519KeyPairGenerator.cs b/crypto/src/crypto/generators/X25519KeyPairGenerator.cs
new file mode 100644
index 000000000..94378448b
--- /dev/null
+++ b/crypto/src/crypto/generators/X25519KeyPairGenerator.cs
@@ -0,0 +1,25 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+    public class X25519KeyPairGenerator
+        : IAsymmetricCipherKeyPairGenerator
+    {
+        private SecureRandom random;
+
+        public virtual void Init(KeyGenerationParameters parameters)
+        {
+            this.random = parameters.Random;
+        }
+
+        public virtual AsymmetricCipherKeyPair GenerateKeyPair()
+        {
+            X25519PrivateKeyParameters privateKey = new X25519PrivateKeyParameters(random);
+            X25519PublicKeyParameters publicKey = privateKey.GeneratePublicKey();
+            return new AsymmetricCipherKeyPair(publicKey, privateKey);
+        }
+    }
+}
diff --git a/crypto/src/crypto/generators/X448KeyPairGenerator.cs b/crypto/src/crypto/generators/X448KeyPairGenerator.cs
new file mode 100644
index 000000000..4a203e4f1
--- /dev/null
+++ b/crypto/src/crypto/generators/X448KeyPairGenerator.cs
@@ -0,0 +1,25 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+    public class X448KeyPairGenerator
+        : IAsymmetricCipherKeyPairGenerator
+    {
+        private SecureRandom random;
+
+        public virtual void Init(KeyGenerationParameters parameters)
+        {
+            this.random = parameters.Random;
+        }
+
+        public virtual AsymmetricCipherKeyPair GenerateKeyPair()
+        {
+            X448PrivateKeyParameters privateKey = new X448PrivateKeyParameters(random);
+            X448PublicKeyParameters publicKey = privateKey.GeneratePublicKey();
+            return new AsymmetricCipherKeyPair(publicKey, privateKey);
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/Ed25519KeyGenerationParameters.cs b/crypto/src/crypto/parameters/Ed25519KeyGenerationParameters.cs
new file mode 100644
index 000000000..daf3856c3
--- /dev/null
+++ b/crypto/src/crypto/parameters/Ed25519KeyGenerationParameters.cs
@@ -0,0 +1,15 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public class Ed25519KeyGenerationParameters
+        : KeyGenerationParameters
+    {
+        public Ed25519KeyGenerationParameters(SecureRandom random)
+            : base(random, 256)
+        {
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs
new file mode 100644
index 000000000..97902e093
--- /dev/null
+++ b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs
@@ -0,0 +1,98 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public sealed class Ed25519PrivateKeyParameters
+        : AsymmetricKeyParameter
+    {
+        public static readonly int KeySize = Ed25519.SecretKeySize;
+        public static readonly int SignatureSize = Ed25519.SignatureSize;
+
+        private readonly byte[] data = new byte[KeySize];
+
+        public Ed25519PrivateKeyParameters(SecureRandom random)
+            : base(true)
+        {
+            random.NextBytes(data);
+        }
+
+        public Ed25519PrivateKeyParameters(byte[] buf, int off)
+            : base(true)
+        {
+            Array.Copy(buf, off, data, 0, KeySize);
+        }
+
+        public Ed25519PrivateKeyParameters(Stream input)
+            : base(true)
+        {
+            if (KeySize != Streams.ReadFully(input, data))
+                throw new EndOfStreamException("EOF encountered in middle of Ed25519 private key");
+        }
+
+        public void Encode(byte[] buf, int off)
+        {
+            Array.Copy(data, 0, buf, off, KeySize);
+        }
+
+        public byte[] GetEncoded()
+        {
+            return Arrays.Clone(data);
+        }
+
+        public Ed25519PublicKeyParameters GeneratePublicKey()
+        {
+            byte[] publicKey = new byte[Ed25519.PublicKeySize];
+            Ed25519.GeneratePublicKey(data, 0, publicKey, 0);
+            return new Ed25519PublicKeyParameters(publicKey, 0);
+        }
+
+        public void Sign(Ed25519.Algorithm algorithm, Ed25519PublicKeyParameters publicKey, byte[] ctx, byte[] msg, int msgOff, int msgLen,
+            byte[] sig, int sigOff)
+        {
+            byte[] pk = new byte[Ed25519.PublicKeySize];
+            if (null == publicKey)
+            {
+                Ed25519.GeneratePublicKey(data, 0, pk, 0);
+            }
+            else
+            {
+                publicKey.Encode(pk, 0);
+            }
+
+            switch (algorithm)
+            {
+            case Ed25519.Algorithm.Ed25519:
+            {
+                if (null != ctx)
+                    throw new ArgumentException("ctx");
+
+                Ed25519.Sign(data, 0, pk, 0, msg, msgOff, msgLen, sig, sigOff);
+                break;
+            }
+            case Ed25519.Algorithm.Ed25519ctx:
+            {
+                Ed25519.Sign(data, 0, pk, 0, ctx, msg, msgOff, msgLen, sig, sigOff);
+                break;
+            }
+            case Ed25519.Algorithm.Ed25519ph:
+            {
+                if (Ed25519.PrehashSize != msgLen)
+                    throw new ArgumentException("msgLen");
+
+                Ed25519.SignPrehash(data, 0, pk, 0, ctx, msg, msgOff, sig, sigOff);
+                break;
+            }
+            default:
+            {
+                throw new ArgumentException("algorithm");
+            }
+            }
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs
new file mode 100644
index 000000000..96e9ec21f
--- /dev/null
+++ b/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs
@@ -0,0 +1,40 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public sealed class Ed25519PublicKeyParameters
+        : AsymmetricKeyParameter
+    {
+        public static readonly int KeySize = Ed25519.PublicKeySize;
+
+        private readonly byte[] data = new byte[KeySize];
+
+        public Ed25519PublicKeyParameters(byte[] buf, int off)
+            : base(false)
+        {
+            Array.Copy(buf, off, data, 0, KeySize);
+        }
+
+        public Ed25519PublicKeyParameters(Stream input)
+            : base(false)
+        {
+            if (KeySize != Streams.ReadFully(input, data))
+                throw new EndOfStreamException("EOF encountered in middle of Ed25519 public key");
+        }
+
+        public void Encode(byte[] buf, int off)
+        {
+            Array.Copy(data, 0, buf, off, KeySize);
+        }
+
+        public byte[] GetEncoded()
+        {
+            return Arrays.Clone(data);
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/Ed448KeyGenerationParameters.cs b/crypto/src/crypto/parameters/Ed448KeyGenerationParameters.cs
new file mode 100644
index 000000000..830d15a04
--- /dev/null
+++ b/crypto/src/crypto/parameters/Ed448KeyGenerationParameters.cs
@@ -0,0 +1,15 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public class Ed448KeyGenerationParameters
+        : KeyGenerationParameters
+    {
+        public Ed448KeyGenerationParameters(SecureRandom random)
+            : base(random, 448)
+        {
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs
new file mode 100644
index 000000000..74b5d63f3
--- /dev/null
+++ b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs
@@ -0,0 +1,90 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public sealed class Ed448PrivateKeyParameters
+        : AsymmetricKeyParameter
+    {
+        public static readonly int KeySize = Ed448.SecretKeySize;
+        public static readonly int SignatureSize = Ed448.SignatureSize;
+
+        private readonly byte[] data = new byte[KeySize];
+
+        public Ed448PrivateKeyParameters(SecureRandom random)
+            : base(true)
+        {
+            random.NextBytes(data);
+        }
+
+        public Ed448PrivateKeyParameters(byte[] buf, int off)
+            : base(true)
+        {
+            Array.Copy(buf, off, data, 0, KeySize);
+        }
+
+        public Ed448PrivateKeyParameters(Stream input)
+            : base(true)
+        {
+            if (KeySize != Streams.ReadFully(input, data))
+                throw new EndOfStreamException("EOF encountered in middle of Ed448 private key");
+        }
+
+        public void Encode(byte[] buf, int off)
+        {
+            Array.Copy(data, 0, buf, off, KeySize);
+        }
+
+        public byte[] GetEncoded()
+        {
+            return Arrays.Clone(data);
+        }
+
+        public Ed448PublicKeyParameters GeneratePublicKey()
+        {
+            byte[] publicKey = new byte[Ed448.PublicKeySize];
+            Ed448.GeneratePublicKey(data, 0, publicKey, 0);
+            return new Ed448PublicKeyParameters(publicKey, 0);
+        }
+
+        public void Sign(Ed448.Algorithm algorithm, Ed448PublicKeyParameters publicKey, byte[] ctx, byte[] msg, int msgOff, int msgLen,
+            byte[] sig, int sigOff)
+        {
+            byte[] pk = new byte[Ed448.PublicKeySize];
+            if (null == publicKey)
+            {
+                Ed448.GeneratePublicKey(data, 0, pk, 0);
+            }
+            else
+            {
+                publicKey.Encode(pk, 0);
+            }
+
+            switch (algorithm)
+            {
+            case Ed448.Algorithm.Ed448:
+            {
+                Ed448.Sign(data, 0, pk, 0, ctx, msg, msgOff, msgLen, sig, sigOff);
+                break;
+            }
+            case Ed448.Algorithm.Ed448ph:
+            {
+                if (Ed448.PrehashSize != msgLen)
+                    throw new ArgumentException("msgLen");
+
+                Ed448.SignPrehash(data, 0, pk, 0, ctx, msg, msgOff, sig, sigOff);
+                break;
+            }
+            default:
+            {
+                throw new ArgumentException("algorithm");
+            }
+            }
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs
new file mode 100644
index 000000000..d7faac246
--- /dev/null
+++ b/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs
@@ -0,0 +1,40 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public sealed class Ed448PublicKeyParameters
+        : AsymmetricKeyParameter
+    {
+        public static readonly int KeySize = Ed448.PublicKeySize;
+
+        private readonly byte[] data = new byte[KeySize];
+
+        public Ed448PublicKeyParameters(byte[] buf, int off)
+            : base(false)
+        {
+            Array.Copy(buf, off, data, 0, KeySize);
+        }
+
+        public Ed448PublicKeyParameters(Stream input)
+            : base(false)
+        {
+            if (KeySize != Streams.ReadFully(input, data))
+                throw new EndOfStreamException("EOF encountered in middle of Ed448 public key");
+        }
+
+        public void Encode(byte[] buf, int off)
+        {
+            Array.Copy(data, 0, buf, off, KeySize);
+        }
+
+        public byte[] GetEncoded()
+        {
+            return Arrays.Clone(data);
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs b/crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs
index 7bd8abd76..557ee94e2 100644
--- a/crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs
+++ b/crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs
@@ -2,33 +2,34 @@ using System;
 
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Asn1.Pkcs;
 
 namespace Org.BouncyCastle.Crypto.Parameters
 {
     public class RsaPrivateCrtKeyParameters
-		: RsaKeyParameters
+        : RsaKeyParameters
     {
         private readonly BigInteger e, p, q, dP, dQ, qInv;
 
-		public RsaPrivateCrtKeyParameters(
-            BigInteger	modulus,
-            BigInteger	publicExponent,
-            BigInteger	privateExponent,
-            BigInteger	p,
-            BigInteger	q,
-            BigInteger	dP,
-            BigInteger	dQ,
-            BigInteger	qInv)
-			: base(true, modulus, privateExponent)
+        public RsaPrivateCrtKeyParameters(
+            BigInteger modulus,
+            BigInteger publicExponent,
+            BigInteger privateExponent,
+            BigInteger p,
+            BigInteger q,
+            BigInteger dP,
+            BigInteger dQ,
+            BigInteger qInv)
+            : base(true, modulus, privateExponent)
         {
-			ValidateValue(publicExponent, "publicExponent", "exponent");
-			ValidateValue(p, "p", "P value");
-			ValidateValue(q, "q", "Q value");
-			ValidateValue(dP, "dP", "DP value");
-			ValidateValue(dQ, "dQ", "DQ value");
-			ValidateValue(qInv, "qInv", "InverseQ value");
-
-			this.e = publicExponent;
+            ValidateValue(publicExponent, "publicExponent", "exponent");
+            ValidateValue(p, "p", "P value");
+            ValidateValue(q, "q", "Q value");
+            ValidateValue(dP, "dP", "DP value");
+            ValidateValue(dQ, "dQ", "DQ value");
+            ValidateValue(qInv, "qInv", "InverseQ value");
+
+            this.e = publicExponent;
             this.p = p;
             this.q = q;
             this.dP = dP;
@@ -36,69 +37,82 @@ namespace Org.BouncyCastle.Crypto.Parameters
             this.qInv = qInv;
         }
 
-		public BigInteger PublicExponent
+        public RsaPrivateCrtKeyParameters(RsaPrivateKeyStructure rsaPrivateKey)
+            : this(
+                rsaPrivateKey.Modulus,
+                rsaPrivateKey.PublicExponent,
+                rsaPrivateKey.PrivateExponent,
+                rsaPrivateKey.Prime1,
+                rsaPrivateKey.Prime2,
+                rsaPrivateKey.Exponent1,
+                rsaPrivateKey.Exponent2,
+                rsaPrivateKey.Coefficient)
+        {
+        }
+
+        public BigInteger PublicExponent
         {
             get { return e; }
-		}
-
-		public BigInteger P
-		{
-			get { return p; }
-		}
-
-		public BigInteger Q
-		{
-			get { return q; }
-		}
-
-		public BigInteger DP
-		{
-			get { return dP; }
-		}
-
-		public BigInteger DQ
-		{
-			get { return dQ; }
-		}
-
-		public BigInteger QInv
-		{
-			get { return qInv; }
-		}
-
-		public override bool Equals(
-			object obj)
-		{
-			if (obj == this)
-				return true;
-
-			RsaPrivateCrtKeyParameters kp = obj as RsaPrivateCrtKeyParameters;
-
-			if (kp == null)
-				return false;
-
-			return kp.DP.Equals(dP)
-				&& kp.DQ.Equals(dQ)
-				&& kp.Exponent.Equals(this.Exponent)
-				&& kp.Modulus.Equals(this.Modulus)
-				&& kp.P.Equals(p)
-				&& kp.Q.Equals(q)
-				&& kp.PublicExponent.Equals(e)
-				&& kp.QInv.Equals(qInv);
-		}
-
-		public override int GetHashCode()
-		{
-			return DP.GetHashCode() ^ DQ.GetHashCode() ^ Exponent.GetHashCode() ^ Modulus.GetHashCode()
-				^ P.GetHashCode() ^ Q.GetHashCode() ^ PublicExponent.GetHashCode() ^ QInv.GetHashCode();
-		}
-
-		private static void ValidateValue(BigInteger x, string name, string desc)
-		{
-			if (x == null)
-				throw new ArgumentNullException(name);
-			if (x.SignValue <= 0)
-				throw new ArgumentException("Not a valid RSA " + desc, name);
-		}
-	}
+        }
+
+        public BigInteger P
+        {
+            get { return p; }
+        }
+
+        public BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public BigInteger DP
+        {
+            get { return dP; }
+        }
+
+        public BigInteger DQ
+        {
+            get { return dQ; }
+        }
+
+        public BigInteger QInv
+        {
+            get { return qInv; }
+        }
+
+        public override bool Equals(
+            object obj)
+        {
+            if (obj == this)
+                return true;
+
+            RsaPrivateCrtKeyParameters kp = obj as RsaPrivateCrtKeyParameters;
+
+            if (kp == null)
+                return false;
+
+            return kp.DP.Equals(dP)
+                && kp.DQ.Equals(dQ)
+                && kp.Exponent.Equals(this.Exponent)
+                && kp.Modulus.Equals(this.Modulus)
+                && kp.P.Equals(p)
+                && kp.Q.Equals(q)
+                && kp.PublicExponent.Equals(e)
+                && kp.QInv.Equals(qInv);
+        }
+
+        public override int GetHashCode()
+        {
+            return DP.GetHashCode() ^ DQ.GetHashCode() ^ Exponent.GetHashCode() ^ Modulus.GetHashCode()
+                ^ P.GetHashCode() ^ Q.GetHashCode() ^ PublicExponent.GetHashCode() ^ QInv.GetHashCode();
+        }
+
+        private static void ValidateValue(BigInteger x, string name, string desc)
+        {
+            if (x == null)
+                throw new ArgumentNullException(name);
+            if (x.SignValue <= 0)
+                throw new ArgumentException("Not a valid RSA " + desc, name);
+        }
+    }
 }
diff --git a/crypto/src/crypto/parameters/X25519KeyGenerationParameters.cs b/crypto/src/crypto/parameters/X25519KeyGenerationParameters.cs
new file mode 100644
index 000000000..09972c7a2
--- /dev/null
+++ b/crypto/src/crypto/parameters/X25519KeyGenerationParameters.cs
@@ -0,0 +1,15 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public class X25519KeyGenerationParameters
+        : KeyGenerationParameters
+    {
+        public X25519KeyGenerationParameters(SecureRandom random)
+            : base(random, 256)
+        {
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs
new file mode 100644
index 000000000..fb49a02b3
--- /dev/null
+++ b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math.EC.Rfc7748;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public sealed class X25519PrivateKeyParameters
+        : AsymmetricKeyParameter
+    {
+        public static readonly int KeySize = X25519.ScalarSize;
+        public static readonly int SecretSize = X25519.PointSize;
+
+        private readonly byte[] data = new byte[KeySize];
+
+        public X25519PrivateKeyParameters(SecureRandom random)
+            : base(true)
+        {
+            random.NextBytes(data);
+        }
+
+        public X25519PrivateKeyParameters(byte[] buf, int off)
+            : base(true)
+        {
+            Array.Copy(buf, off, data, 0, KeySize);
+        }
+
+        public X25519PrivateKeyParameters(Stream input)
+            : base(true)
+        {
+            if (KeySize != Streams.ReadFully(input, data))
+                throw new EndOfStreamException("EOF encountered in middle of X25519 private key");
+        }
+
+        public void Encode(byte[] buf, int off)
+        {
+            Array.Copy(data, 0, buf, off, KeySize);
+        }
+
+        public byte[] GetEncoded()
+        {
+            return Arrays.Clone(data);
+        }
+
+        public X25519PublicKeyParameters GeneratePublicKey()
+        {
+            byte[] publicKey = new byte[X25519.PointSize];
+            X25519.ScalarMultBase(data, 0, publicKey, 0);
+            return new X25519PublicKeyParameters(publicKey, 0);
+        }
+
+        public void GenerateSecret(X25519PublicKeyParameters publicKey, byte[] buf, int off)
+        {
+            byte[] encoded = new byte[X25519.PointSize];
+            publicKey.Encode(encoded, 0);
+            if (!X25519.CalculateAgreement(data, 0, encoded, 0, buf, off))
+                throw new InvalidOperationException("X25519 agreement failed");
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs
new file mode 100644
index 000000000..7df5f624d
--- /dev/null
+++ b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs
@@ -0,0 +1,40 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math.EC.Rfc7748;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public sealed class X25519PublicKeyParameters
+        : AsymmetricKeyParameter
+    {
+        public static readonly int KeySize = X25519.PointSize;
+
+        private readonly byte[] data = new byte[KeySize];
+
+        public X25519PublicKeyParameters(byte[] buf, int off)
+            : base(false)
+        {
+            Array.Copy(buf, off, data, 0, KeySize);
+        }
+
+        public X25519PublicKeyParameters(Stream input)
+            : base(false)
+        {
+            if (KeySize != Streams.ReadFully(input, data))
+                throw new EndOfStreamException("EOF encountered in middle of X25519 public key");
+        }
+
+        public void Encode(byte[] buf, int off)
+        {
+            Array.Copy(data, 0, buf, off, KeySize);
+        }
+
+        public byte[] GetEncoded()
+        {
+            return Arrays.Clone(data);
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/X448KeyGenerationParameters.cs b/crypto/src/crypto/parameters/X448KeyGenerationParameters.cs
new file mode 100644
index 000000000..a7cb55844
--- /dev/null
+++ b/crypto/src/crypto/parameters/X448KeyGenerationParameters.cs
@@ -0,0 +1,15 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public class X448KeyGenerationParameters
+        : KeyGenerationParameters
+    {
+        public X448KeyGenerationParameters(SecureRandom random)
+            : base(random, 448)
+        {
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs
new file mode 100644
index 000000000..d17aa7947
--- /dev/null
+++ b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math.EC.Rfc7748;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public sealed class X448PrivateKeyParameters
+        : AsymmetricKeyParameter
+    {
+        public static readonly int KeySize = X448.ScalarSize;
+        public static readonly int SecretSize = X448.PointSize;
+
+        private readonly byte[] data = new byte[KeySize];
+
+        public X448PrivateKeyParameters(SecureRandom random)
+            : base(true)
+        {
+            random.NextBytes(data);
+        }
+
+        public X448PrivateKeyParameters(byte[] buf, int off)
+            : base(true)
+        {
+            Array.Copy(buf, off, data, 0, KeySize);
+        }
+
+        public X448PrivateKeyParameters(Stream input)
+            : base(true)
+        {
+            if (KeySize != Streams.ReadFully(input, data))
+                throw new EndOfStreamException("EOF encountered in middle of X448 private key");
+        }
+
+        public void Encode(byte[] buf, int off)
+        {
+            Array.Copy(data, 0, buf, off, KeySize);
+        }
+
+        public byte[] GetEncoded()
+        {
+            return Arrays.Clone(data);
+        }
+
+        public X448PublicKeyParameters GeneratePublicKey()
+        {
+            byte[] publicKey = new byte[X448.PointSize];
+            X448.ScalarMultBase(data, 0, publicKey, 0);
+            return new X448PublicKeyParameters(publicKey, 0);
+        }
+
+        public void GenerateSecret(X448PublicKeyParameters publicKey, byte[] buf, int off)
+        {
+            byte[] encoded = new byte[X448.PointSize];
+            publicKey.Encode(encoded, 0);
+            if (!X448.CalculateAgreement(data, 0, encoded, 0, buf, off))
+                throw new InvalidOperationException("X448 agreement failed");
+        }
+    }
+}
diff --git a/crypto/src/crypto/parameters/X448PublicKeyParameters.cs b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs
new file mode 100644
index 000000000..6c566ddf2
--- /dev/null
+++ b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs
@@ -0,0 +1,40 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math.EC.Rfc7748;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public sealed class X448PublicKeyParameters
+        : AsymmetricKeyParameter
+    {
+        public static readonly int KeySize = X448.PointSize;
+
+        private readonly byte[] data = new byte[KeySize];
+
+        public X448PublicKeyParameters(byte[] buf, int off)
+            : base(false)
+        {
+            Array.Copy(buf, off, data, 0, KeySize);
+        }
+
+        public X448PublicKeyParameters(Stream input)
+            : base(false)
+        {
+            if (KeySize != Streams.ReadFully(input, data))
+                throw new EndOfStreamException("EOF encountered in middle of X448 public key");
+        }
+
+        public void Encode(byte[] buf, int off)
+        {
+            Array.Copy(data, 0, buf, off, KeySize);
+        }
+
+        public byte[] GetEncoded()
+        {
+            return Arrays.Clone(data);
+        }
+    }
+}
diff --git a/crypto/src/crypto/signers/Ed25519Signer.cs b/crypto/src/crypto/signers/Ed25519Signer.cs
new file mode 100644
index 000000000..1b3142c7b
--- /dev/null
+++ b/crypto/src/crypto/signers/Ed25519Signer.cs
@@ -0,0 +1,129 @@
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+    public class Ed25519Signer
+        : ISigner
+    {
+        private readonly Buffer buffer = new Buffer();
+
+        private bool forSigning;
+        private Ed25519PrivateKeyParameters privateKey;
+        private Ed25519PublicKeyParameters publicKey;
+
+        public Ed25519Signer()
+        {
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return "Ed25519"; }
+        }
+
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
+        {
+            this.forSigning = forSigning;
+
+            if (forSigning)
+            {
+                // TODO Allow IAsymmetricCipherKeyPair to be an ICipherParameters?
+
+                this.privateKey = (Ed25519PrivateKeyParameters)parameters;
+                this.publicKey = privateKey.GeneratePublicKey();
+            }
+            else
+            {
+                this.privateKey = null;
+                this.publicKey = (Ed25519PublicKeyParameters)parameters;
+            }
+
+            Reset();
+        }
+
+        public virtual void Update(byte b)
+        {
+            buffer.WriteByte(b);
+        }
+
+        public virtual void BlockUpdate(byte[] buf, int off, int len)
+        {
+            buffer.Write(buf, off, len);
+        }
+
+        public virtual byte[] GenerateSignature()
+        {
+            if (!forSigning || null == privateKey)
+                throw new InvalidOperationException("Ed25519Signer not initialised for signature generation.");
+
+            return buffer.GenerateSignature(privateKey, publicKey);
+        }
+
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            if (forSigning || null == publicKey)
+                throw new InvalidOperationException("Ed25519Signer not initialised for verification");
+
+            return buffer.VerifySignature(publicKey, signature);
+        }
+
+        public virtual void Reset()
+        {
+            buffer.Reset();
+        }
+
+        private class Buffer : MemoryStream
+        {
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            internal byte[] GenerateSignature(Ed25519PrivateKeyParameters privateKey, Ed25519PublicKeyParameters publicKey)
+            {
+#if PORTABLE
+                byte[] buf = ToArray();
+                int count = buf.Length;
+#else
+                byte[] buf = GetBuffer();
+                int count = (int)Position;
+#endif
+                byte[] signature = new byte[Ed25519PrivateKeyParameters.SignatureSize];
+                privateKey.Sign(Ed25519.Algorithm.Ed25519, publicKey, null, buf, 0, count, signature, 0);
+                Reset();
+                return signature;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            internal bool VerifySignature(Ed25519PublicKeyParameters publicKey, byte[] signature)
+            {
+#if PORTABLE
+                byte[] buf = ToArray();
+                int count = buf.Length;
+#else
+                byte[] buf = GetBuffer();
+                int count = (int)Position;
+#endif
+                byte[] pk = publicKey.GetEncoded();
+                bool result = Ed25519.Verify(signature, 0, pk, 0, buf, 0, count);
+                Reset();
+                return result;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            internal void Reset()
+            {
+                long count = Position;
+#if PORTABLE
+                this.Position = 0L;
+                Streams.WriteZeroes(this, count);
+#else
+                Array.Clear(GetBuffer(), 0, (int)count);
+#endif
+                this.Position = 0L;
+            }
+        }
+    }
+}
diff --git a/crypto/src/crypto/signers/Ed25519ctxSigner.cs b/crypto/src/crypto/signers/Ed25519ctxSigner.cs
new file mode 100644
index 000000000..965453011
--- /dev/null
+++ b/crypto/src/crypto/signers/Ed25519ctxSigner.cs
@@ -0,0 +1,131 @@
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+    public class Ed25519ctxSigner
+        : ISigner
+    {
+        private readonly Buffer buffer = new Buffer();
+        private readonly byte[] context;
+
+        private bool forSigning;
+        private Ed25519PrivateKeyParameters privateKey;
+        private Ed25519PublicKeyParameters publicKey;
+
+        public Ed25519ctxSigner(byte[] context)
+        {
+            this.context = Arrays.Clone(context);
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return "Ed25519ctx"; }
+        }
+
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
+        {
+            this.forSigning = forSigning;
+
+            if (forSigning)
+            {
+                // TODO Allow IAsymmetricCipherKeyPair to be an ICipherParameters?
+
+                this.privateKey = (Ed25519PrivateKeyParameters)parameters;
+                this.publicKey = privateKey.GeneratePublicKey();
+            }
+            else
+            {
+                this.privateKey = null;
+                this.publicKey = (Ed25519PublicKeyParameters)parameters;
+            }
+
+            Reset();
+        }
+
+        public virtual void Update(byte b)
+        {
+            buffer.WriteByte(b);
+        }
+
+        public virtual void BlockUpdate(byte[] buf, int off, int len)
+        {
+            buffer.Write(buf, off, len);
+        }
+
+        public virtual byte[] GenerateSignature()
+        {
+            if (!forSigning || null == privateKey)
+                throw new InvalidOperationException("Ed25519ctxSigner not initialised for signature generation.");
+
+            return buffer.GenerateSignature(privateKey, publicKey, context);
+        }
+
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            if (forSigning || null == publicKey)
+                throw new InvalidOperationException("Ed25519ctxSigner not initialised for verification");
+
+            return buffer.VerifySignature(publicKey, context, signature);
+        }
+
+        public virtual void Reset()
+        {
+            buffer.Reset();
+        }
+
+        private class Buffer : MemoryStream
+        {
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            internal byte[] GenerateSignature(Ed25519PrivateKeyParameters privateKey, Ed25519PublicKeyParameters publicKey, byte[] ctx)
+            {
+#if PORTABLE
+                byte[] buf = ToArray();
+                int count = buf.Length;
+#else
+                byte[] buf = GetBuffer();
+                int count = (int)Position;
+#endif
+                byte[] signature = new byte[Ed25519PrivateKeyParameters.SignatureSize];
+                privateKey.Sign(Ed25519.Algorithm.Ed25519ctx, publicKey, ctx, buf, 0, count, signature, 0);
+                Reset();
+                return signature;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            internal bool VerifySignature(Ed25519PublicKeyParameters publicKey, byte[] ctx, byte[] signature)
+            {
+#if PORTABLE
+                byte[] buf = ToArray();
+                int count = buf.Length;
+#else
+                byte[] buf = GetBuffer();
+                int count = (int)Position;
+#endif
+                byte[] pk = publicKey.GetEncoded();
+                bool result = Ed25519.Verify(signature, 0, pk, 0, ctx, buf, 0, count);
+                Reset();
+                return result;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            internal void Reset()
+            {
+                long count = Position;
+#if PORTABLE
+                this.Position = 0L;
+                Streams.WriteZeroes(this, count);
+#else
+                Array.Clear(GetBuffer(), 0, (int)count);
+#endif
+                this.Position = 0L;
+            }
+        }
+    }
+}
diff --git a/crypto/src/crypto/signers/Ed25519phSigner.cs b/crypto/src/crypto/signers/Ed25519phSigner.cs
new file mode 100644
index 000000000..3318f6438
--- /dev/null
+++ b/crypto/src/crypto/signers/Ed25519phSigner.cs
@@ -0,0 +1,89 @@
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+    public class Ed25519phSigner
+        : ISigner
+    {
+        private readonly IDigest prehash = Ed25519.CreatePrehash();
+        private readonly byte[] context;
+
+        private bool forSigning;
+        private Ed25519PrivateKeyParameters privateKey;
+        private Ed25519PublicKeyParameters publicKey;
+
+        public Ed25519phSigner(byte[] context)
+        {
+            this.context = Arrays.Clone(context);
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return "Ed25519ph"; }
+        }
+
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
+        {
+            this.forSigning = forSigning;
+
+            if (forSigning)
+            {
+                // TODO Allow AsymmetricCipherKeyPair to be a CipherParameters?
+
+                this.privateKey = (Ed25519PrivateKeyParameters)parameters;
+                this.publicKey = privateKey.GeneratePublicKey();
+            }
+            else
+            {
+                this.privateKey = null;
+                this.publicKey = (Ed25519PublicKeyParameters)parameters;
+            }
+
+            Reset();
+        }
+
+        public virtual void Update(byte b)
+        {
+            prehash.Update(b);
+        }
+
+        public virtual void BlockUpdate(byte[] buf, int off, int len)
+        {
+            prehash.BlockUpdate(buf, off, len);
+        }
+
+        public virtual byte[] GenerateSignature()
+        {
+            if (!forSigning || null == privateKey)
+                throw new InvalidOperationException("Ed25519phSigner not initialised for signature generation.");
+
+            byte[] msg = new byte[Ed25519.PrehashSize];
+            if (Ed25519.PrehashSize != prehash.DoFinal(msg, 0))
+                throw new InvalidOperationException("Prehash digest failed");
+
+            byte[] signature = new byte[Ed25519PrivateKeyParameters.SignatureSize];
+            privateKey.Sign(Ed25519.Algorithm.Ed25519ph, publicKey, context, msg, 0, Ed25519.PrehashSize, signature, 0);
+            return signature;
+        }
+
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            if (forSigning || null == publicKey)
+                throw new InvalidOperationException("Ed25519phSigner not initialised for verification");
+
+            byte[] pk = publicKey.GetEncoded();
+            return Ed25519.VerifyPrehash(signature, 0, pk, 0, context, prehash);
+        }
+
+        public void Reset()
+        {
+            prehash.Reset();
+        }
+    }
+}
diff --git a/crypto/src/crypto/signers/Ed448Signer.cs b/crypto/src/crypto/signers/Ed448Signer.cs
new file mode 100644
index 000000000..d18f956a8
--- /dev/null
+++ b/crypto/src/crypto/signers/Ed448Signer.cs
@@ -0,0 +1,131 @@
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+    public class Ed448Signer
+        : ISigner
+    {
+        private readonly Buffer buffer = new Buffer();
+        private readonly byte[] context;
+
+        private bool forSigning;
+        private Ed448PrivateKeyParameters privateKey;
+        private Ed448PublicKeyParameters publicKey;
+
+        public Ed448Signer(byte[] context)
+        {
+            this.context = Arrays.Clone(context);
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return "Ed448"; }
+        }
+
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
+        {
+            this.forSigning = forSigning;
+
+            if (forSigning)
+            {
+                // TODO Allow IAsymmetricCipherKeyPair to be an ICipherParameters?
+
+                this.privateKey = (Ed448PrivateKeyParameters)parameters;
+                this.publicKey = privateKey.GeneratePublicKey();
+            }
+            else
+            {
+                this.privateKey = null;
+                this.publicKey = (Ed448PublicKeyParameters)parameters;
+            }
+
+            Reset();
+        }
+
+        public virtual void Update(byte b)
+        {
+            buffer.WriteByte(b);
+        }
+
+        public virtual void BlockUpdate(byte[] buf, int off, int len)
+        {
+            buffer.Write(buf, off, len);
+        }
+
+        public virtual byte[] GenerateSignature()
+        {
+            if (!forSigning || null == privateKey)
+                throw new InvalidOperationException("Ed448Signer not initialised for signature generation.");
+
+            return buffer.GenerateSignature(privateKey, publicKey, context);
+        }
+
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            if (forSigning || null == publicKey)
+                throw new InvalidOperationException("Ed448Signer not initialised for verification");
+
+            return buffer.VerifySignature(publicKey, context, signature);
+        }
+
+        public virtual void Reset()
+        {
+            buffer.Reset();
+        }
+
+        private class Buffer : MemoryStream
+        {
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            internal byte[] GenerateSignature(Ed448PrivateKeyParameters privateKey, Ed448PublicKeyParameters publicKey, byte[] ctx)
+            {
+#if PORTABLE
+                byte[] buf = ToArray();
+                int count = buf.Length;
+#else
+                byte[] buf = GetBuffer();
+                int count = (int)Position;
+#endif
+                byte[] signature = new byte[Ed448PrivateKeyParameters.SignatureSize];
+                privateKey.Sign(Ed448.Algorithm.Ed448, publicKey, ctx, buf, 0, count, signature, 0);
+                Reset();
+                return signature;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            internal bool VerifySignature(Ed448PublicKeyParameters publicKey, byte[] ctx, byte[] signature)
+            {
+#if PORTABLE
+                byte[] buf = ToArray();
+                int count = buf.Length;
+#else
+                byte[] buf = GetBuffer();
+                int count = (int)Position;
+#endif
+                byte[] pk = publicKey.GetEncoded();
+                bool result = Ed448.Verify(signature, 0, pk, 0, ctx, buf, 0, count);
+                Reset();
+                return result;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            internal void Reset()
+            {
+                long count = Position;
+#if PORTABLE
+                this.Position = 0L;
+                Streams.WriteZeroes(this, count);
+#else
+                Array.Clear(GetBuffer(), 0, (int)count);
+#endif
+                this.Position = 0L;
+            }
+        }
+    }
+}
diff --git a/crypto/src/crypto/signers/Ed448phSigner.cs b/crypto/src/crypto/signers/Ed448phSigner.cs
new file mode 100644
index 000000000..b86d0855c
--- /dev/null
+++ b/crypto/src/crypto/signers/Ed448phSigner.cs
@@ -0,0 +1,89 @@
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+    public class Ed448phSigner
+        : ISigner
+    {
+        private readonly IXof prehash = Ed448.CreatePrehash();
+        private readonly byte[] context;
+
+        private bool forSigning;
+        private Ed448PrivateKeyParameters privateKey;
+        private Ed448PublicKeyParameters publicKey;
+
+        public Ed448phSigner(byte[] context)
+        {
+            this.context = Arrays.Clone(context);
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return "Ed448ph"; }
+        }
+
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
+        {
+            this.forSigning = forSigning;
+
+            if (forSigning)
+            {
+                // TODO Allow AsymmetricCipherKeyPair to be a CipherParameters?
+
+                this.privateKey = (Ed448PrivateKeyParameters)parameters;
+                this.publicKey = privateKey.GeneratePublicKey();
+            }
+            else
+            {
+                this.privateKey = null;
+                this.publicKey = (Ed448PublicKeyParameters)parameters;
+            }
+
+            Reset();
+        }
+
+        public virtual void Update(byte b)
+        {
+            prehash.Update(b);
+        }
+
+        public virtual void BlockUpdate(byte[] buf, int off, int len)
+        {
+            prehash.BlockUpdate(buf, off, len);
+        }
+
+        public virtual byte[] GenerateSignature()
+        {
+            if (!forSigning || null == privateKey)
+                throw new InvalidOperationException("Ed448phSigner not initialised for signature generation.");
+
+            byte[] msg = new byte[Ed448.PrehashSize];
+            if (Ed448.PrehashSize != prehash.DoFinal(msg, 0, Ed448.PrehashSize))
+                throw new InvalidOperationException("Prehash digest failed");
+
+            byte[] signature = new byte[Ed448PrivateKeyParameters.SignatureSize];
+            privateKey.Sign(Ed448.Algorithm.Ed448ph, publicKey, context, msg, 0, Ed448.PrehashSize, signature, 0);
+            return signature;
+        }
+
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            if (forSigning || null == publicKey)
+                throw new InvalidOperationException("Ed448phSigner not initialised for verification");
+
+            byte[] pk = publicKey.GetEncoded();
+            return Ed448.VerifyPrehash(signature, 0, pk, 0, context, prehash);
+        }
+
+        public void Reset()
+        {
+            prehash.Reset();
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs
index 72151d414..bbb76d53c 100644
--- a/crypto/src/crypto/tls/TlsProtocol.cs
+++ b/crypto/src/crypto/tls/TlsProtocol.cs
@@ -391,31 +391,30 @@ namespace Org.BouncyCastle.Crypto.Tls
                 if (queue.Available < totalLength)
                     break;
 
-                CheckReceivedChangeCipherSpec(mConnectionState == CS_END || type == HandshakeType.finished);
-
                 /*
                  * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages
                  * starting at client hello up to, but not including, this finished message.
                  * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes.
                  */
-                switch (type)
-                {
-                case HandshakeType.hello_request:
-                    break;
-                case HandshakeType.finished:
-                default:
+                if (HandshakeType.hello_request != type)
                 {
-                    TlsContext ctx = Context;
-                    if (type == HandshakeType.finished
-                        && this.mExpectedVerifyData == null
-                        && ctx.SecurityParameters.MasterSecret != null)
+                    if (HandshakeType.finished == type)
                     {
-                        this.mExpectedVerifyData = CreateVerifyData(!ctx.IsServer);
+                        CheckReceivedChangeCipherSpec(true);
+
+                        TlsContext ctx = Context;
+                        if (this.mExpectedVerifyData == null
+                            && ctx.SecurityParameters.MasterSecret != null)
+                        {
+                            this.mExpectedVerifyData = CreateVerifyData(!ctx.IsServer);
+                        }
+                    }
+                    else
+                    {
+                        CheckReceivedChangeCipherSpec(mConnectionState == CS_END);
                     }
 
                     queue.CopyTo(mRecordStream.HandshakeHashUpdater, totalLength);
-                    break;
-                }
                 }
 
                 queue.RemoveData(4);
diff --git a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
index 505832442..37e5b5c29 100644
--- a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
+++ b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
@@ -37,15 +37,17 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
             int top = fullComb - 1;
             for (int i = 0; i < d; ++i)
             {
-                int secretIndex = 0;
+                uint secretIndex = 0;
 
                 for (int j = top - i; j >= 0; j -= d)
                 {
+                    uint secretBit = K[j >> 5] >> (j & 0x1F);
+                    secretIndex ^= secretBit >> 1;
                     secretIndex <<= 1;
-                    secretIndex |= (int)Nat.GetBit(K, j);
+                    secretIndex ^= secretBit;
                 }
 
-                ECPoint add = lookupTable.Lookup(secretIndex);
+                ECPoint add = lookupTable.Lookup((int)secretIndex);
 
                 R = R.TwicePlus(add);
             }
diff --git a/crypto/src/math/ec/multiplier/WNafUtilities.cs b/crypto/src/math/ec/multiplier/WNafUtilities.cs
index e893abd49..24646deb2 100644
--- a/crypto/src/math/ec/multiplier/WNafUtilities.cs
+++ b/crypto/src/math/ec/multiplier/WNafUtilities.cs
@@ -1,5 +1,7 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Math.EC.Multiplier
 {
     public abstract class WNafUtilities
@@ -8,8 +10,6 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
 
         private static readonly int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 };
 
-        private static readonly byte[] EMPTY_BYTES = new byte[0];
-        private static readonly int[] EMPTY_INTS = new int[0];
         private static readonly ECPoint[] EMPTY_POINTS = new ECPoint[0];
 
         public static int[] GenerateCompactNaf(BigInteger k)
@@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
             if ((k.BitLength >> 16) != 0)
                 throw new ArgumentException("must have bitlength < 2^16", "k");
             if (k.SignValue == 0)
-                return EMPTY_INTS;
+                return Arrays.EmptyInts;
 
             BigInteger _3k = k.ShiftLeft(1).Add(k);
 
@@ -63,7 +63,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
             if ((k.BitLength >> 16) != 0)
                 throw new ArgumentException("must have bitlength < 2^16", "k");
             if (k.SignValue == 0)
-                return EMPTY_INTS;
+                return Arrays.EmptyInts;
 
             int[] wnaf = new int[k.BitLength / width + 1];
 
@@ -176,7 +176,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
         public static byte[] GenerateNaf(BigInteger k)
         {
             if (k.SignValue == 0)
-                return EMPTY_BYTES;
+                return Arrays.EmptyBytes;
 
             BigInteger _3k = k.ShiftLeft(1).Add(k);
 
@@ -221,7 +221,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
             if (width < 2 || width > 8)
                 throw new ArgumentException("must be in the range [2, 8]", "width");
             if (k.SignValue == 0)
-                return EMPTY_BYTES;
+                return Arrays.EmptyBytes;
 
             byte[] wnaf = new byte[k.BitLength + 1];
 
diff --git a/crypto/src/math/ec/rfc7748/X25519.cs b/crypto/src/math/ec/rfc7748/X25519.cs
index a10d53da5..6b6acdecd 100644
--- a/crypto/src/math/ec/rfc7748/X25519.cs
+++ b/crypto/src/math/ec/rfc7748/X25519.cs
@@ -2,10 +2,15 @@
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Math.EC.Rfc7748
 {
     public abstract class X25519
     {
+        public const int PointSize = 32;
+        public const int ScalarSize = 32;
+
         private const int C_A = 486662;
         private const int C_A24 = (C_A + 2)/4;
 
@@ -18,6 +23,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
 
         private static int[] precompBase = null;
 
+        public static bool CalculateAgreement(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff)
+        {
+            ScalarMult(k, kOff, u, uOff, r, rOff);
+            return !Arrays.AreAllZeroes(r, rOff, PointSize);
+        }
+
         private static uint Decode32(byte[] bs, int off)
         {
             uint n = bs[off];
diff --git a/crypto/src/math/ec/rfc7748/X448.cs b/crypto/src/math/ec/rfc7748/X448.cs
index 88e8a5d76..b93cb24c5 100644
--- a/crypto/src/math/ec/rfc7748/X448.cs
+++ b/crypto/src/math/ec/rfc7748/X448.cs
@@ -2,10 +2,15 @@
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Math.EC.Rfc7748
 {
     public abstract class X448
     {
+        public const int PointSize = 56;
+        public const int ScalarSize = 56;
+
         private const uint C_A = 156326;
         private const uint C_A24 = (C_A + 2)/4;
 
@@ -21,6 +26,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
 
         private static uint[] precompBase = null;
 
+        public static bool CalculateAgreement(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff)
+        {
+            ScalarMult(k, kOff, u, uOff, r, rOff);
+            return !Arrays.AreAllZeroes(r, rOff, PointSize);
+        }
+
         private static uint Decode32(byte[] bs, int off)
         {
             uint n = bs[off];
diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs
index ff4587cb2..f9ba1ff97 100644
--- a/crypto/src/math/ec/rfc8032/Ed25519.cs
+++ b/crypto/src/math/ec/rfc8032/Ed25519.cs
@@ -2,6 +2,7 @@
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 
+using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Digests;
 using Org.BouncyCastle.Math.EC.Rfc7748;
 using Org.BouncyCastle.Math.Raw;
@@ -11,6 +12,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
 {
     public abstract class Ed25519
     {
+        public enum Algorithm
+        {
+            Ed25519 = 0,
+            Ed25519ctx = 1,
+            Ed25519ph = 2,
+        }
+
         private const long M28L = 0x0FFFFFFFL;
         private const long M32L = 0xFFFFFFFFL;
 
@@ -18,11 +26,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
         private const int ScalarUints = 8;
         private const int ScalarBytes = ScalarUints * 4;
 
+        public static readonly int PrehashSize = 64;
         public static readonly int PublicKeySize = PointBytes;
         public static readonly int SecretKeySize = 32;
         public static readonly int SignatureSize = PointBytes + ScalarBytes;
 
-        //private static readonly byte[] Dom2Prefix = Strings.ToByteArray("SigEd25519 no Ed25519 collisions");
+        private static readonly byte[] Dom2Prefix = Strings.ToByteArray("SigEd25519 no Ed25519 collisions");
 
         private static readonly uint[] P = { 0xFFFFFFEDU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0x7FFFFFFFU };
         private static readonly uint[] L = { 0x5CF5D3EDU, 0x5812631AU, 0xA2F79CD6U, 0x14DEF9DEU, 0x00000000U, 0x00000000U, 0x00000000U, 0x10000000U };
@@ -96,6 +105,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             return ReduceScalar(result);
         }
 
+        private static bool CheckContextVar(byte[] ctx, byte phflag)
+        {
+            return ctx == null && phflag == 0x00
+                || ctx != null && ctx.Length < 256;
+        }
+
         private static bool CheckPointVar(byte[] p)
         {
             uint[] t = new uint[8];
@@ -111,6 +126,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             return !Nat256.Gte(n, L);
         }
 
+        private static IDigest CreateDigest()
+        {
+            return new Sha512Digest();
+        }
+
+        public static IDigest CreatePrehash()
+        {
+            return CreateDigest();
+        }
+
         private static uint Decode24(byte[] bs, int off)
         {
             uint n = bs[off];
@@ -140,9 +165,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
         {
             byte[] py = Arrays.CopyOfRange(p, pOff, pOff + PointBytes);
             if (!CheckPointVar(py))
-            {
                 return false;
-            }
 
             int x_0 = (py[PointBytes - 1] & 0x80) >> 7;
             py[PointBytes - 1] &= 0x7F;
@@ -158,15 +181,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             X25519Field.AddOne(v);
 
             if (!X25519Field.SqrtRatioVar(u, v, r.x))
-            {
                 return false;
-            }
 
             X25519Field.Normalize(r.x);
             if (x_0 == 1 && X25519Field.IsZeroVar(r.x))
-            {
                 return false;
-            }
 
             if (negate ^ (x_0 != (r.x[0] & 1)))
             {
@@ -182,6 +201,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             Decode32(k, kOff, n, 0, ScalarUints);
         }
 
+        private static void Dom2(IDigest d, byte phflag, byte[] ctx)
+        {
+            if (ctx != null)
+            {
+                d.BlockUpdate(Dom2Prefix, 0, Dom2Prefix.Length);
+                d.Update(phflag);
+                d.Update((byte)ctx.Length);
+                d.BlockUpdate(ctx, 0, ctx.Length);
+            }
+        }
+
         private static void Encode24(uint n, byte[] bs, int off)
         {
             bs[off] = (byte)(n);
@@ -220,7 +250,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
 
         public static void GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff)
         {
-            Sha512Digest d = new Sha512Digest();
+            IDigest d = CreateDigest();
             byte[] h = new byte[d.GetDigestSize()];
 
             d.BlockUpdate(sk, skOff, SecretKeySize);
@@ -286,8 +316,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             return ws;
         }
 
-        private static void ImplSign(Sha512Digest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+        private static void ImplSign(IDigest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+            byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
         {
+            Dom2(d, phflag, ctx);
             d.BlockUpdate(h, ScalarBytes, ScalarBytes);
             d.BlockUpdate(m, mOff, mLen);
             d.DoFinal(h, 0);
@@ -296,8 +328,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             byte[] R = new byte[PointBytes];
             ScalarMultBaseEncoded(r, R, 0);
 
+            Dom2(d, phflag, ctx);
             d.BlockUpdate(R, 0, PointBytes);
-            d.BlockUpdate(pk, 0, PointBytes);
+            d.BlockUpdate(pk, pkOff, PointBytes);
             d.BlockUpdate(m, mOff, mLen);
             d.DoFinal(h, 0);
 
@@ -308,6 +341,90 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             Array.Copy(S, 0, sig, sigOff + PointBytes, ScalarBytes);
         }
 
+        private static void ImplSign(byte[] sk, int skOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen,
+            byte[] sig, int sigOff)
+        {
+            if (!CheckContextVar(ctx, phflag))
+                throw new ArgumentException("ctx");
+
+            IDigest d = CreateDigest();
+            byte[] h = new byte[d.GetDigestSize()];
+
+            d.BlockUpdate(sk, skOff, SecretKeySize);
+            d.DoFinal(h, 0);
+
+            byte[] s = new byte[ScalarBytes];
+            PruneScalar(h, 0, s);
+
+            byte[] pk = new byte[PointBytes];
+            ScalarMultBaseEncoded(s, pk, 0);
+
+            ImplSign(d, h, s, pk, 0, ctx, phflag, m, mOff, mLen, sig, sigOff);
+        }
+
+        private static void ImplSign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+            byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+        {
+            if (!CheckContextVar(ctx, phflag))
+                throw new ArgumentException("ctx");
+
+            IDigest d = CreateDigest();
+            byte[] h = new byte[d.GetDigestSize()];
+
+            d.BlockUpdate(sk, skOff, SecretKeySize);
+            d.DoFinal(h, 0);
+
+            byte[] s = new byte[ScalarBytes];
+            PruneScalar(h, 0, s);
+
+            ImplSign(d, h, s, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+        }
+
+        private static bool ImplVerify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+            byte[] m, int mOff, int mLen)
+        {
+            if (!CheckContextVar(ctx, phflag))
+                throw new ArgumentException("ctx");
+
+            byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes);
+            byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize);
+
+            if (!CheckPointVar(R))
+                return false;
+
+            if (!CheckScalarVar(S))
+                return false;
+
+            PointExt pA = new PointExt();
+            if (!DecodePointVar(pk, pkOff, true, pA))
+                return false;
+
+            IDigest d = CreateDigest();
+            byte[] h = new byte[d.GetDigestSize()];
+
+            Dom2(d, phflag, ctx);
+            d.BlockUpdate(R, 0, PointBytes);
+            d.BlockUpdate(pk, pkOff, PointBytes);
+            d.BlockUpdate(m, mOff, mLen);
+            d.DoFinal(h, 0);
+
+            byte[] k = ReduceScalar(h);
+
+            uint[] nS = new uint[ScalarUints];
+            DecodeScalar(S, 0, nS);
+
+            uint[] nA = new uint[ScalarUints];
+            DecodeScalar(k, 0, nA);
+
+            PointAccum pR = new PointAccum();
+            ScalarMultStraussVar(nS, nA, pA, pR);
+
+            byte[] check = new byte[PointBytes];
+            EncodePoint(pR, check, 0);
+
+            return Arrays.AreEqual(check, R);
+        }
+
         private static void PointAddVar(bool negate, PointExt p, PointAccum r)
         {
             int[] A = X25519Field.Create();
@@ -518,9 +635,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
         public static void Precompute()
         {
             if (precompBase != null)
-            {
                 return;
-            }
 
             // Precomputed table for the base point in verification ladder
             {
@@ -555,9 +670,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
 
                     ds[t] = PointCopy(p);
 
-                    for (int s = 1; s < PrecompSpacing; ++s)
+                    if (b + t != PrecompBlocks + PrecompTeeth - 2)
                     {
-                        PointDouble(p);
+                        for (int s = 1; s < PrecompSpacing; ++s)
+                        {
+                            PointDouble(p);
+                        }
                     }
                 }
 
@@ -795,9 +913,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                 }
 
                 if ((cOff -= PrecompTeeth) < 0)
-                {
                     break;
-                }
 
                 PointDouble(r);
             }
@@ -850,9 +966,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                 }
 
                 if (--bit < 0)
-                {
                     break;
-                }
 
                 PointDouble(r);
             }
@@ -860,78 +974,101 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
 
         public static void Sign(byte[] sk, int skOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
         {
-            Sha512Digest d = new Sha512Digest();
-            byte[] h = new byte[d.GetDigestSize()];
+            byte[] ctx = null;
+            byte phflag = 0x00;
 
-            d.BlockUpdate(sk, skOff, SecretKeySize);
-            d.DoFinal(h, 0);
+            ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+        }
 
-            byte[] s = new byte[ScalarBytes];
-            PruneScalar(h, 0, s);
+        public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+        {
+            byte[] ctx = null;
+            byte phflag = 0x00;
 
-            byte[] pk = new byte[PointBytes];
-            ScalarMultBaseEncoded(s, pk, 0);
+            ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+        }
+
+        public static void Sign(byte[] sk, int skOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+        {
+            byte phflag = 0x00;
 
-            ImplSign(d, h, s, pk, 0, m, mOff, mLen, sig, sigOff);
+            ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
         }
 
-        public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+        public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
         {
-            Sha512Digest d = new Sha512Digest();
-            byte[] h = new byte[d.GetDigestSize()];
+            byte phflag = 0x00;
 
-            d.BlockUpdate(sk, skOff, SecretKeySize);
-            d.DoFinal(h, 0);
+            ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+        }
 
-            byte[] s = new byte[ScalarBytes];
-            PruneScalar(h, 0, s);
+        public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff)
+        {
+            byte phflag = 0x01;
 
-            ImplSign(d, h, s, pk, pkOff, m, mOff, mLen, sig, sigOff);
+            ImplSign(sk, skOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff);
         }
 
-        public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen)
+        public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff)
         {
-            byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes);
-            byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize);
+            byte phflag = 0x01;
 
-            if (!CheckPointVar(R))
-            {
-                return false;
-            }
-            if (!CheckScalarVar(S))
-            {
-                return false;
-            }
+            ImplSign(sk, skOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff);
+        }
 
-            PointExt pA = new PointExt();
-            if (!DecodePointVar(pk, pkOff, true, pA))
-            {
-                return false;
-            }
+        public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, IDigest ph, byte[] sig, int sigOff)
+        {
+            byte[] m = new byte[PrehashSize];
+            if (PrehashSize != ph.DoFinal(m, 0))
+                throw new ArgumentException("ph");
 
-            Sha512Digest d = new Sha512Digest();
-            byte[] h = new byte[d.GetDigestSize()];
+            byte phflag = 0x01;
 
-            d.BlockUpdate(R, 0, PointBytes);
-            d.BlockUpdate(pk, pkOff, PointBytes);
-            d.BlockUpdate(m, mOff, mLen);
-            d.DoFinal(h, 0);
+            ImplSign(sk, skOff, ctx, phflag, m, 0, m.Length, sig, sigOff);
+        }
 
-            byte[] k = ReduceScalar(h);
+        public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, IDigest ph, byte[] sig, int sigOff)
+        {
+            byte[] m = new byte[PrehashSize];
+            if (PrehashSize != ph.DoFinal(m, 0))
+                throw new ArgumentException("ph");
 
-            uint[] nS = new uint[ScalarUints];
-            DecodeScalar(S, 0, nS);
+            byte phflag = 0x01;
 
-            uint[] nA = new uint[ScalarUints];
-            DecodeScalar(k, 0, nA);
+            ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, 0, m.Length, sig, sigOff);
+        }
 
-            PointAccum pR = new PointAccum();
-            ScalarMultStraussVar(nS, nA, pA, pR);
+        public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen)
+        {
+            byte[] ctx = null;
+            byte phflag = 0x00;
 
-            byte[] check = new byte[PointBytes];
-            EncodePoint(pR, check, 0);
+            return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen);
+        }
 
-            return Arrays.AreEqual(check, R);
+        public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen)
+        {
+            byte phflag = 0x00;
+
+            return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen);
+        }
+
+        public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff)
+        {
+            byte phflag = 0x01;
+
+            return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize);
+        }
+
+        public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IDigest ph)
+        {
+            byte[] m = new byte[PrehashSize];
+            if (PrehashSize != ph.DoFinal(m, 0))
+                throw new ArgumentException("ph");
+
+            byte phflag = 0x01;
+
+            return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length);
         }
     }
 }
diff --git a/crypto/src/math/ec/rfc8032/Ed448.cs b/crypto/src/math/ec/rfc8032/Ed448.cs
index 52c215160..a1f0e93b0 100644
--- a/crypto/src/math/ec/rfc8032/Ed448.cs
+++ b/crypto/src/math/ec/rfc8032/Ed448.cs
@@ -2,6 +2,7 @@
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 
+using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Digests;
 using Org.BouncyCastle.Math.EC.Rfc7748;
 using Org.BouncyCastle.Math.Raw;
@@ -11,6 +12,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
 {
     public abstract class Ed448
     {
+        public enum Algorithm
+        {
+            Ed448 = 0,
+            Ed448ph = 1,
+        }
+
         private const ulong M26UL = 0x03FFFFFFUL;
         private const ulong M28UL = 0x0FFFFFFFUL;
 
@@ -18,6 +25,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
         private const int ScalarUints = 14;
         private const int ScalarBytes = ScalarUints * 4 + 1;
 
+        public static readonly int PrehashSize = 64;
         public static readonly int PublicKeySize = PointBytes;
         public static readonly int SecretKeySize = 57;
         public static readonly int SignatureSize = PointBytes + ScalarBytes;
@@ -103,9 +111,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
         private static bool CheckPointVar(byte[] p)
         {
             if ((p[PointBytes - 1] & 0x7F) != 0x00)
-            {
                 return false;
-            }
 
             uint[] t = new uint[14];
             Decode32(p, 0, t, 0, 14);
@@ -115,15 +121,23 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
         private static bool CheckScalarVar(byte[] s)
         {
             if (s[ScalarBytes - 1] != 0x00)
-            {
                 return false;
-            }
 
             uint[] n = new uint[ScalarUints];
             DecodeScalar(s, 0, n);
             return !Nat.Gte(ScalarUints, n, L);
         }
 
+        public static IXof CreatePrehash()
+        {
+            return CreateXof();
+        }
+
+        private static IXof CreateXof()
+        {
+            return new ShakeDigest(256);
+        }
+
         private static uint Decode16(byte[] bs, int off)
         {
             uint n = bs[off];
@@ -160,9 +174,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
         {
             byte[] py = Arrays.CopyOfRange(p, pOff, pOff + PointBytes);
             if (!CheckPointVar(py))
-            {
                 return false;
-            }
 
             int x_0 = (py[PointBytes - 1] & 0x80) >> 7;
             py[PointBytes - 1] &= 0x7F;
@@ -179,15 +191,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             X448Field.AddOne(v);
 
             if (!X448Field.SqrtRatioVar(u, v, r.x))
-            {
                 return false;
-            }
 
             X448Field.Normalize(r.x);
             if (x_0 == 1 && X448Field.IsZeroVar(r.x))
-            {
                 return false;
-            }
 
             if (negate ^ (x_0 != (r.x[0] & 1)))
             {
@@ -205,7 +213,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             Decode32(k, kOff, n, 0, ScalarUints);
         }
 
-        private static void Dom4(ShakeDigest d, byte x, byte[] y)
+        private static void Dom4(IXof d, byte x, byte[] y)
         {
             d.BlockUpdate(Dom4Prefix, 0, Dom4Prefix.Length);
             d.Update(x);
@@ -251,7 +259,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
 
         public static void GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff)
         {
-            ShakeDigest d = new ShakeDigest(256);
+            IXof d = CreateXof();
             byte[] h = new byte[ScalarBytes * 2];
 
             d.BlockUpdate(sk, skOff, SecretKeySize);
@@ -317,10 +325,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             return ws;
         }
 
-        private static void ImplSign(ShakeDigest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+        private static void ImplSign(IXof d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+            byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
         {
-            byte phflag = 0x00;
-
             Dom4(d, phflag, ctx);
             d.BlockUpdate(h, ScalarBytes, ScalarBytes);
             d.BlockUpdate(m, mOff, mLen);
@@ -343,6 +350,90 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             Array.Copy(S, 0, sig, sigOff + PointBytes, ScalarBytes);
         }
 
+        private static void ImplSign(byte[] sk, int skOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen,
+            byte[] sig, int sigOff)
+        {
+            if (!CheckContextVar(ctx))
+                throw new ArgumentException("ctx");
+
+            IXof d = CreateXof();
+            byte[] h = new byte[ScalarBytes * 2];
+
+            d.BlockUpdate(sk, skOff, SecretKeySize);
+            d.DoFinal(h, 0, h.Length);
+
+            byte[] s = new byte[ScalarBytes];
+            PruneScalar(h, 0, s);
+
+            byte[] pk = new byte[PointBytes];
+            ScalarMultBaseEncoded(s, pk, 0);
+
+            ImplSign(d, h, s, pk, 0, ctx, phflag, m, mOff, mLen, sig, sigOff);
+        }
+
+        private static void ImplSign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+            byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+        {
+            if (!CheckContextVar(ctx))
+                throw new ArgumentException("ctx");
+
+            IXof d = CreateXof();
+            byte[] h = new byte[ScalarBytes * 2];
+
+            d.BlockUpdate(sk, skOff, SecretKeySize);
+            d.DoFinal(h, 0, h.Length);
+
+            byte[] s = new byte[ScalarBytes];
+            PruneScalar(h, 0, s);
+
+            ImplSign(d, h, s, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+        }
+
+        private static bool ImplVerify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte phflag,
+            byte[] m, int mOff, int mLen)
+        {
+            if (!CheckContextVar(ctx))
+                throw new ArgumentException("ctx");
+
+            byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes);
+            byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize);
+
+            if (!CheckPointVar(R))
+                return false;
+
+            if (!CheckScalarVar(S))
+                return false;
+
+            PointExt pA = new PointExt();
+            if (!DecodePointVar(pk, pkOff, true, pA))
+                return false;
+
+            IXof d = CreateXof();
+            byte[] h = new byte[ScalarBytes * 2];
+
+            Dom4(d, phflag, ctx);
+            d.BlockUpdate(R, 0, PointBytes);
+            d.BlockUpdate(pk, pkOff, PointBytes);
+            d.BlockUpdate(m, mOff, mLen);
+            d.DoFinal(h, 0, h.Length);
+
+            byte[] k = ReduceScalar(h);
+
+            uint[] nS = new uint[ScalarUints];
+            DecodeScalar(S, 0, nS);
+
+            uint[] nA = new uint[ScalarUints];
+            DecodeScalar(k, 0, nA);
+
+            PointExt pR = new PointExt();
+            ScalarMultStraussVar(nS, nA, pA, pR);
+
+            byte[] check = new byte[PointBytes];
+            EncodePoint(pR, check, 0);
+
+            return Arrays.AreEqual(check, R);
+        }
+
         private static void PointAddVar(bool negate, PointExt p, PointExt r)
         {
             uint[] A = X448Field.Create();
@@ -505,9 +596,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
         public static void Precompute()
         {
             if (precompBase != null)
-            {
                 return;
-            }
 
             PointExt p = new PointExt();
             X448Field.Copy(B_x, 0, p.x, 0);
@@ -533,9 +622,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
 
                     ds[t] = PointCopy(p);
 
-                    for (int s = 1; s < PrecompSpacing; ++s)
+                    if (b + t != PrecompBlocks + PrecompTeeth - 2)
                     {
-                        PointDouble(p);
+                        for (int s = 1; s < PrecompSpacing; ++s)
+                        {
+                            PointDouble(p);
+                        }
                     }
                 }
 
@@ -888,8 +980,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                     uint w = 0;
                     for (int t = 0; t < PrecompTeeth; ++t)
                     {
-                        uint tBit = (n[tPos >> 5] >> (tPos & 0x1F)) & 1U;
-                        w |= tBit << t;
+                        uint tBit = n[tPos >> 5] >> (tPos & 0x1F);
+                        w &= ~(1U << t);
+                        w ^= (tBit << t);
                         tPos += PrecompSpacing;
                     }
 
@@ -907,9 +1000,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                 }
 
                 if (--cOff < 0)
-                {
                     break;
-                }
 
                 PointDouble(r);
             }
@@ -962,9 +1053,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                 }
 
                 if (--bit < 0)
-                {
                     break;
-                }
 
                 PointDouble(r);
             }
@@ -972,96 +1061,77 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
 
         public static void Sign(byte[] sk, int skOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
         {
-            if (!CheckContextVar(ctx))
-            {
-                throw new ArgumentException("ctx");
-            }
+            byte phflag = 0x00;
 
-            ShakeDigest d = new ShakeDigest(256);
-            byte[] h = new byte[ScalarBytes * 2];
+            ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+        }
 
-            d.BlockUpdate(sk, skOff, SecretKeySize);
-            d.DoFinal(h, 0, h.Length);
+        public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+        {
+            byte phflag = 0x00;
 
-            byte[] s = new byte[ScalarBytes];
-            PruneScalar(h, 0, s);
+            ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff);
+        }
 
-            byte[] pk = new byte[PointBytes];
-            ScalarMultBaseEncoded(s, pk, 0);
+        public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff)
+        {
+            byte phflag = 0x01;
 
-            ImplSign(d, h, s, pk, 0, ctx, m, mOff, mLen, sig, sigOff);
+            ImplSign(sk, skOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff);
         }
 
-        public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff)
+        public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff)
         {
-            if (!CheckContextVar(ctx))
-            {
-                throw new ArgumentException("ctx");
-            }
+            byte phflag = 0x01;
 
-            ShakeDigest d = new ShakeDigest(256);
-            byte[] h = new byte[ScalarBytes * 2];
+            ImplSign(sk, skOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff);
+        }
 
-            d.BlockUpdate(sk, skOff, SecretKeySize);
-            d.DoFinal(h, 0, h.Length);
+        public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, IXof ph, byte[] sig, int sigOff)
+        {
+            byte[] m = new byte[PrehashSize];
+            if (PrehashSize != ph.DoFinal(m, 0, PrehashSize))
+                throw new ArgumentException("ph");
 
-            byte[] s = new byte[ScalarBytes];
-            PruneScalar(h, 0, s);
+            byte phflag = 0x01;
 
-            ImplSign(d, h, s, pk, pkOff, ctx, m, mOff, mLen, sig, sigOff);
+            ImplSign(sk, skOff, ctx, phflag, m, 0, m.Length, sig, sigOff);
         }
 
-        public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen)
+        public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, IXof ph, byte[] sig, int sigOff)
         {
-            if (!CheckContextVar(ctx))
-            {
-                throw new ArgumentException("ctx");
-            }
-
-            byte[] R = Arrays.CopyOfRange(sig, sigOff, sigOff + PointBytes);
-            byte[] S = Arrays.CopyOfRange(sig, sigOff + PointBytes, sigOff + SignatureSize);
+            byte[] m = new byte[PrehashSize];
+            if (PrehashSize != ph.DoFinal(m, 0, PrehashSize))
+                throw new ArgumentException("ph");
 
-            if (!CheckPointVar(R))
-            {
-                return false;
-            }
-            if (!CheckScalarVar(S))
-            {
-                return false;
-            }
+            byte phflag = 0x01;
 
-            PointExt pA = new PointExt();
-            if (!DecodePointVar(pk, pkOff, true, pA))
-            {
-                return false;
-            }
+            ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, 0, m.Length, sig, sigOff);
+        }
 
+        public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen)
+        {
             byte phflag = 0x00;
 
-            ShakeDigest d = new ShakeDigest(256);
-            byte[] h = new byte[ScalarBytes * 2];
-
-            Dom4(d, phflag, ctx);
-            d.BlockUpdate(R, 0, PointBytes);
-            d.BlockUpdate(pk, pkOff, PointBytes);
-            d.BlockUpdate(m, mOff, mLen);
-            d.DoFinal(h, 0, h.Length);
-
-            byte[] k = ReduceScalar(h);
+            return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen);
+        }
 
-            uint[] nS = new uint[ScalarUints];
-            DecodeScalar(S, 0, nS);
+        public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff)
+        {
+            byte phflag = 0x01;
 
-            uint[] nA = new uint[ScalarUints];
-            DecodeScalar(k, 0, nA);
+            return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize);
+        }
 
-            PointExt pR = new PointExt();
-            ScalarMultStraussVar(nS, nA, pA, pR);
+        public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IXof ph)
+        {
+            byte[] m = new byte[PrehashSize];
+            if (PrehashSize != ph.DoFinal(m, 0, PrehashSize))
+                throw new ArgumentException("ph");
 
-            byte[] check = new byte[PointBytes];
-            EncodePoint(pR, check, 0);
+            byte phflag = 0x01;
 
-            return Arrays.AreEqual(check, R);
+            return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length);
         }
     }
 }
diff --git a/crypto/src/math/raw/Nat.cs b/crypto/src/math/raw/Nat.cs
index 7ca60278a..040ade74f 100644
--- a/crypto/src/math/raw/Nat.cs
+++ b/crypto/src/math/raw/Nat.cs
@@ -488,21 +488,31 @@ namespace Org.BouncyCastle.Math.Raw
 
         public static void Mul(int len, uint[] x, uint[] y, uint[] zz)
         {
-            zz[len] = (uint)MulWord(len, x[0], y, zz);
+            zz[len] = MulWord(len, x[0], y, zz);
 
             for (int i = 1; i < len; ++i)
             {
-                zz[i + len] = (uint)MulWordAddTo(len, x[i], y, 0, zz, i);
+                zz[i + len] = MulWordAddTo(len, x[i], y, 0, zz, i);
             }
         }
 
         public static void Mul(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff)
         {
-            zz[zzOff + len] = (uint)MulWord(len, x[xOff], y, yOff, zz, zzOff);
+            zz[zzOff + len] = MulWord(len, x[xOff], y, yOff, zz, zzOff);
 
             for (int i = 1; i < len; ++i)
             {
-                zz[zzOff + i + len] = (uint)MulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff + i);
+                zz[zzOff + i + len] = MulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff + i);
+            }
+        }
+
+        public static void Mul(uint[] x, int xOff, int xLen, uint[] y, int yOff, int yLen, uint[] zz, int zzOff)
+        {
+            zz[zzOff + yLen] = MulWord(yLen, x[xOff], y, yOff, zz, zzOff);
+
+            for (int i = 1; i < xLen; ++i)
+            {
+                zz[zzOff + i + yLen] = MulWordAddTo(yLen, x[xOff + i], y, yOff, zz, zzOff + i);
             }
         }
 
diff --git a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
index 156243f4e..1d3d75941 100644
--- a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
+++ b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
@@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return result;
         }
 
-        public NotationData[] GetNotationDataOccurences()
+        public NotationData[] GetNotationDataOccurrences()
 		{
 			SignatureSubpacket[] notations = GetSubpackets(SignatureSubpacketTag.NotationData);
 			NotationData[] vals = new NotationData[notations.Length];
@@ -87,6 +87,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			return vals;
 		}
 
+        [Obsolete("Use 'GetNotationDataOccurrences' instead")]
+        public NotationData[] GetNotationDataOccurences()
+        {
+            return GetNotationDataOccurrences();
+        }
+
 		public long GetIssuerKeyId()
         {
             SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IssuerKeyId);
diff --git a/crypto/src/pkcs/PrivateKeyInfoFactory.cs b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
index a349a11d2..69eb3fa67 100644
--- a/crypto/src/pkcs/PrivateKeyInfoFactory.cs
+++ b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
@@ -2,6 +2,7 @@ using System;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
 using Org.BouncyCastle.Asn1.Sec;
@@ -22,59 +23,68 @@ namespace Org.BouncyCastle.Pkcs
         }
 
         public static PrivateKeyInfo CreatePrivateKeyInfo(
-            AsymmetricKeyParameter key)
+            AsymmetricKeyParameter privateKey)
         {
-            if (key == null)
-                throw new ArgumentNullException("key");
-            if (!key.IsPrivate)
-                throw new ArgumentException("Public key passed - private key expected", "key");
+            return CreatePrivateKeyInfo(privateKey, null);
+        }
+
+        /**
+         * Create a PrivateKeyInfo representation of a private key with attributes.
+         *
+         * @param privateKey the key to be encoded into the info object.
+         * @param attributes the set of attributes to be included.
+         * @return the appropriate PrivateKeyInfo
+         * @throws java.io.IOException on an error encoding the key
+         */
+        public static PrivateKeyInfo CreatePrivateKeyInfo(AsymmetricKeyParameter privateKey, Asn1Set attributes)
+        {
+            if (privateKey == null)
+                throw new ArgumentNullException("privateKey");
+            if (!privateKey.IsPrivate)
+                throw new ArgumentException("Public key passed - private key expected", "privateKey");
 
-            if (key is ElGamalPrivateKeyParameters)
+            if (privateKey is ElGamalPrivateKeyParameters)
             {
-                ElGamalPrivateKeyParameters _key = (ElGamalPrivateKeyParameters)key;
+                ElGamalPrivateKeyParameters _key = (ElGamalPrivateKeyParameters)privateKey;
+                ElGamalParameters egp = _key.Parameters;
                 return new PrivateKeyInfo(
-                    new AlgorithmIdentifier(
-                    OiwObjectIdentifiers.ElGamalAlgorithm,
-                    new ElGamalParameter(
-                    _key.Parameters.P,
-                    _key.Parameters.G).ToAsn1Object()),
-                    new DerInteger(_key.X));
+                    new AlgorithmIdentifier(OiwObjectIdentifiers.ElGamalAlgorithm, new ElGamalParameter(egp.P, egp.G).ToAsn1Object()),
+                    new DerInteger(_key.X),
+                    attributes);
             }
 
-            if (key is DsaPrivateKeyParameters)
+            if (privateKey is DsaPrivateKeyParameters)
             {
-                DsaPrivateKeyParameters _key = (DsaPrivateKeyParameters)key;
+                DsaPrivateKeyParameters _key = (DsaPrivateKeyParameters)privateKey;
+                DsaParameters dp = _key.Parameters;
                 return new PrivateKeyInfo(
-                    new AlgorithmIdentifier(
-                    X9ObjectIdentifiers.IdDsa,
-                    new DsaParameter(
-                    _key.Parameters.P,
-                    _key.Parameters.Q,
-                    _key.Parameters.G).ToAsn1Object()),
-                    new DerInteger(_key.X));
+                    new AlgorithmIdentifier(X9ObjectIdentifiers.IdDsa, new DsaParameter(dp.P, dp.Q, dp.G).ToAsn1Object()),
+                    new DerInteger(_key.X),
+                    attributes);
             }
 
-            if (key is DHPrivateKeyParameters)
+            if (privateKey is DHPrivateKeyParameters)
             {
-                DHPrivateKeyParameters _key = (DHPrivateKeyParameters)key;
+                DHPrivateKeyParameters _key = (DHPrivateKeyParameters)privateKey;
 
                 DHParameter p = new DHParameter(
                     _key.Parameters.P, _key.Parameters.G, _key.Parameters.L);
 
                 return new PrivateKeyInfo(
                     new AlgorithmIdentifier(_key.AlgorithmOid, p.ToAsn1Object()),
-                    new DerInteger(_key.X));
+                    new DerInteger(_key.X),
+                    attributes);
             }
 
-            if (key is RsaKeyParameters)
+            if (privateKey is RsaKeyParameters)
             {
                 AlgorithmIdentifier algID = new AlgorithmIdentifier(
                     PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance);
 
                 RsaPrivateKeyStructure keyStruct;
-                if (key is RsaPrivateCrtKeyParameters)
+                if (privateKey is RsaPrivateCrtKeyParameters)
                 {
-                    RsaPrivateCrtKeyParameters _key = (RsaPrivateCrtKeyParameters)key;
+                    RsaPrivateCrtKeyParameters _key = (RsaPrivateCrtKeyParameters)privateKey;
 
                     keyStruct = new RsaPrivateKeyStructure(
                         _key.Modulus,
@@ -88,7 +98,7 @@ namespace Org.BouncyCastle.Pkcs
                 }
                 else
                 {
-                    RsaKeyParameters _key = (RsaKeyParameters) key;
+                    RsaKeyParameters _key = (RsaKeyParameters) privateKey;
 
                     keyStruct = new RsaPrivateKeyStructure(
                         _key.Modulus,
@@ -101,12 +111,12 @@ namespace Org.BouncyCastle.Pkcs
                         BigInteger.Zero);
                 }
 
-                return new PrivateKeyInfo(algID, keyStruct.ToAsn1Object());
+                return new PrivateKeyInfo(algID, keyStruct.ToAsn1Object(), attributes);
             }
 
-            if (key is ECPrivateKeyParameters)
+            if (privateKey is ECPrivateKeyParameters)
             {
-                ECPrivateKeyParameters priv = (ECPrivateKeyParameters)key;
+                ECPrivateKeyParameters priv = (ECPrivateKeyParameters)privateKey;
                 ECDomainParameters dp = priv.Parameters;
                 int orderBitLength = dp.N.BitLength;
 
@@ -145,12 +155,12 @@ namespace Org.BouncyCastle.Pkcs
                     algID = new AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey, x962);
                 }
 
-                return new PrivateKeyInfo(algID, ec);
+                return new PrivateKeyInfo(algID, ec, attributes);
             }
 
-            if (key is Gost3410PrivateKeyParameters)
+            if (privateKey is Gost3410PrivateKeyParameters)
             {
-                Gost3410PrivateKeyParameters _key = (Gost3410PrivateKeyParameters)key;
+                Gost3410PrivateKeyParameters _key = (Gost3410PrivateKeyParameters)privateKey;
 
                 if (_key.PublicKeyParamSet == null)
                     throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set");
@@ -170,10 +180,42 @@ namespace Org.BouncyCastle.Pkcs
                     CryptoProObjectIdentifiers.GostR3410x94,
                     algParams.ToAsn1Object());
 
-                return new PrivateKeyInfo(algID, new DerOctetString(keyBytes));
+                return new PrivateKeyInfo(algID, new DerOctetString(keyBytes), attributes);
+            }
+
+            if (privateKey is X448PrivateKeyParameters)
+            {
+                X448PrivateKeyParameters key = (X448PrivateKeyParameters)privateKey;
+
+                return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448),
+                    new DerOctetString(key.GetEncoded()), attributes, key.GeneratePublicKey().GetEncoded());
+            }
+
+            if (privateKey is X25519PrivateKeyParameters)
+            {
+                X25519PrivateKeyParameters key = (X25519PrivateKeyParameters)privateKey;
+
+                return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519),
+                    new DerOctetString(key.GetEncoded()), attributes, key.GeneratePublicKey().GetEncoded());
+            }
+
+            if (privateKey is Ed448PrivateKeyParameters)
+            {
+                Ed448PrivateKeyParameters key = (Ed448PrivateKeyParameters)privateKey;
+
+                return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448),
+                    new DerOctetString(key.GetEncoded()), attributes, key.GeneratePublicKey().GetEncoded());
+            }
+
+            if (privateKey is Ed25519PrivateKeyParameters)
+            {
+                Ed25519PrivateKeyParameters key = (Ed25519PrivateKeyParameters)privateKey;
+
+                return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519),
+                    new DerOctetString(key.GetEncoded()), attributes, key.GeneratePublicKey().GetEncoded());
             }
 
-            throw new ArgumentException("Class provided is not convertible: " + Platform.GetTypeName(key));
+            throw new ArgumentException("Class provided is not convertible: " + Platform.GetTypeName(privateKey));
         }
 
         public static PrivateKeyInfo CreatePrivateKeyInfo(
diff --git a/crypto/src/security/AgreementUtilities.cs b/crypto/src/security/AgreementUtilities.cs
index 12d427c8c..26d1628cc 100644
--- a/crypto/src/security/AgreementUtilities.cs
+++ b/crypto/src/security/AgreementUtilities.cs
@@ -1,6 +1,7 @@
 using System.Collections;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Agreement;
@@ -27,7 +28,10 @@ namespace Org.BouncyCastle.Security
             algorithms[X9ObjectIdentifiers.DHSinglePassCofactorDHSha1KdfScheme.Id] = "ECCDHWITHSHA1KDF";
 			algorithms[X9ObjectIdentifiers.DHSinglePassStdDHSha1KdfScheme.Id] = "ECDHWITHSHA1KDF";
 			algorithms[X9ObjectIdentifiers.MqvSinglePassSha1KdfScheme.Id] = "ECMQVWITHSHA1KDF";
-		}
+
+            algorithms[EdECObjectIdentifiers.id_X25519.Id] = "X25519";
+            algorithms[EdECObjectIdentifiers.id_X448.Id] = "X448";
+        }
 
         public static IBasicAgreement GetBasicAgreement(
 			DerObjectIdentifier oid)
@@ -38,15 +42,9 @@ namespace Org.BouncyCastle.Security
 		public static IBasicAgreement GetBasicAgreement(
 			string algorithm)
 		{
-			string upper = Platform.ToUpperInvariant(algorithm);
-			string mechanism = (string) algorithms[upper];
-
-			if (mechanism == null)
-			{
-				mechanism = upper;
-			}
+            string mechanism = GetMechanism(algorithm);
 
-			if (mechanism == "DH" || mechanism == "DIFFIEHELLMAN")
+            if (mechanism == "DH" || mechanism == "DIFFIEHELLMAN")
 				return new DHBasicAgreement();
 
 			if (mechanism == "ECDH")
@@ -72,15 +70,9 @@ namespace Org.BouncyCastle.Security
 			string agreeAlgorithm,
 			string wrapAlgorithm)
 		{
-			string upper = Platform.ToUpperInvariant(agreeAlgorithm);
-			string mechanism = (string) algorithms[upper];
+            string mechanism = GetMechanism(agreeAlgorithm);
 
-			if (mechanism == null)
-			{
-				mechanism = upper;
-			}
-
-			// 'DHWITHSHA1KDF' retained for backward compatibility
+            // 'DHWITHSHA1KDF' retained for backward compatibility
 			if (mechanism == "DHWITHSHA1KDF" || mechanism == "ECDHWITHSHA1KDF")
 				return new ECDHWithKdfBasicAgreement(
 					wrapAlgorithm,
@@ -96,10 +88,37 @@ namespace Org.BouncyCastle.Security
 			throw new SecurityUtilityException("Basic Agreement (with KDF) " + agreeAlgorithm + " not recognised.");
 		}
 
+        public static IRawAgreement GetRawAgreement(
+            DerObjectIdentifier oid)
+        {
+            return GetRawAgreement(oid.Id);
+        }
+
+        public static IRawAgreement GetRawAgreement(
+            string algorithm)
+        {
+            string mechanism = GetMechanism(algorithm);
+
+            if (mechanism == "X25519")
+                return new X25519Agreement();
+
+            if (mechanism == "X448")
+                return new X448Agreement();
+
+            throw new SecurityUtilityException("Raw Agreement " + algorithm + " not recognised.");
+        }
+
 		public static string GetAlgorithmName(
 			DerObjectIdentifier oid)
 		{
-			return (string) algorithms[oid.Id];
+			return (string)algorithms[oid.Id];
 		}
+
+        private static string GetMechanism(string algorithm)
+        {
+            string upper = Platform.ToUpperInvariant(algorithm);
+            string mechanism = (string)algorithms[upper];
+            return mechanism == null ? upper : mechanism;
+        }
 	}
 }
diff --git a/crypto/src/security/CipherUtilities.cs b/crypto/src/security/CipherUtilities.cs
index de05bc9ef..eb10baec8 100644
--- a/crypto/src/security/CipherUtilities.cs
+++ b/crypto/src/security/CipherUtilities.cs
@@ -53,6 +53,7 @@ namespace Org.BouncyCastle.Security
             SEED,
             SERPENT,
             SKIPJACK,
+            SM4,
             TEA,
             THREEFISH_256,
             THREEFISH_512,
@@ -433,6 +434,9 @@ namespace Org.BouncyCastle.Security
                 case CipherAlgorithm.SKIPJACK:
                     blockCipher = new SkipjackEngine();
                     break;
+                case CipherAlgorithm.SM4:
+                    blockCipher = new SM4Engine();
+                    break;
                 case CipherAlgorithm.TEA:
                     blockCipher = new TeaEngine();
                     break;
@@ -740,6 +744,7 @@ namespace Org.BouncyCastle.Security
                 case CipherAlgorithm.SEED: return new SeedEngine();
                 case CipherAlgorithm.SERPENT: return new SerpentEngine();
                 case CipherAlgorithm.SKIPJACK: return new SkipjackEngine();
+                case CipherAlgorithm.SM4: return new SM4Engine();
                 case CipherAlgorithm.TEA: return new TeaEngine();
                 case CipherAlgorithm.THREEFISH_256: return new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256);
                 case CipherAlgorithm.THREEFISH_512: return new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512);
diff --git a/crypto/src/security/GeneratorUtilities.cs b/crypto/src/security/GeneratorUtilities.cs
index db1929c16..08281493a 100644
--- a/crypto/src/security/GeneratorUtilities.cs
+++ b/crypto/src/security/GeneratorUtilities.cs
@@ -2,6 +2,7 @@ using System.Collections;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Asn1.Iana;
 using Org.BouncyCastle.Asn1.Kisa;
 using Org.BouncyCastle.Asn1.Nist;
@@ -109,6 +110,7 @@ namespace Org.BouncyCastle.Security
                 KisaObjectIdentifiers.IdSeedCbc);
             AddKgAlgorithm("SERPENT");
             AddKgAlgorithm("SKIPJACK");
+            AddKgAlgorithm("SM4");
             AddKgAlgorithm("TEA");
             AddKgAlgorithm("THREEFISH-256");
             AddKgAlgorithm("THREEFISH-512");
@@ -180,18 +182,29 @@ namespace Org.BouncyCastle.Security
             AddKpgAlgorithm("ECGOST3410",
                 "ECGOST-3410",
                 "GOST-3410-2001");
+            AddKpgAlgorithm("Ed25519",
+                "Ed25519ctx",
+                "Ed25519ph",
+                EdECObjectIdentifiers.id_Ed25519);
+            AddKpgAlgorithm("Ed448",
+                "Ed448ph",
+                EdECObjectIdentifiers.id_Ed448);
             AddKpgAlgorithm("ELGAMAL");
             AddKpgAlgorithm("GOST3410",
                 "GOST-3410",
                 "GOST-3410-94");
             AddKpgAlgorithm("RSA",
                 "1.2.840.113549.1.1.1");
+            AddKpgAlgorithm("X25519",
+                EdECObjectIdentifiers.id_X25519);
+            AddKpgAlgorithm("X448",
+                EdECObjectIdentifiers.id_X448);
 
             AddDefaultKeySizeEntries(64, "DES");
             AddDefaultKeySizeEntries(80, "SKIPJACK");
             AddDefaultKeySizeEntries(128, "AES128", "BLOWFISH", "CAMELLIA128", "CAST5", "DESEDE",
                 "HC128", "HMACMD2", "HMACMD4", "HMACMD5", "HMACRIPEMD128", "IDEA", "NOEKEON",
-                "RC2", "RC4", "RC5", "SALSA20", "SEED", "TEA", "XTEA", "VMPC", "VMPC-KSA3");
+                "RC2", "RC4", "RC5", "SALSA20", "SEED", "SM4", "TEA", "XTEA", "VMPC", "VMPC-KSA3");
             AddDefaultKeySizeEntries(160, "HMACRIPEMD160", "HMACSHA1");
             AddDefaultKeySizeEntries(192, "AES", "AES192", "CAMELLIA192", "DESEDE3", "HMACTIGER",
                 "RIJNDAEL", "SERPENT", "TNEPRES");
@@ -216,11 +229,11 @@ namespace Org.BouncyCastle.Security
             string			canonicalName,
             params object[] aliases)
         {
-            kgAlgorithms[canonicalName] = canonicalName;
+            kgAlgorithms[Platform.ToUpperInvariant(canonicalName)] = canonicalName;
 
             foreach (object alias in aliases)
             {
-                kgAlgorithms[alias.ToString()] = canonicalName;
+                kgAlgorithms[Platform.ToUpperInvariant(alias.ToString())] = canonicalName;
             }
         }
 
@@ -228,11 +241,11 @@ namespace Org.BouncyCastle.Security
             string			canonicalName,
             params object[] aliases)
         {
-            kpgAlgorithms[canonicalName] = canonicalName;
+            kpgAlgorithms[Platform.ToUpperInvariant(canonicalName)] = canonicalName;
 
             foreach (object alias in aliases)
             {
-                kpgAlgorithms[alias.ToString()] = canonicalName;
+                kpgAlgorithms[Platform.ToUpperInvariant(alias.ToString())] = canonicalName;
             }
         }
 
@@ -248,7 +261,7 @@ namespace Org.BouncyCastle.Security
 
             foreach (object alias in aliases)
             {
-                kgAlgorithms[alias.ToString()] = mainName;
+                kgAlgorithms[Platform.ToUpperInvariant(alias.ToString())] = mainName;
             }
         }
 
@@ -318,6 +331,12 @@ namespace Org.BouncyCastle.Security
             if (Platform.StartsWith(canonicalName, "EC"))
                 return new ECKeyPairGenerator(canonicalName);
 
+            if (canonicalName == "Ed25519")
+                return new Ed25519KeyPairGenerator();
+
+            if (canonicalName == "Ed448")
+                return new Ed448KeyPairGenerator();
+
             if (canonicalName == "ELGAMAL")
                 return new ElGamalKeyPairGenerator();
 
@@ -327,6 +346,12 @@ namespace Org.BouncyCastle.Security
             if (canonicalName == "RSA")
                 return new RsaKeyPairGenerator();
 
+            if (canonicalName == "X25519")
+                return new X25519KeyPairGenerator();
+
+            if (canonicalName == "X448")
+                return new X448KeyPairGenerator();
+
             throw new SecurityUtilityException("KeyPairGenerator " + algorithm
                 + " (" + canonicalName + ") not supported.");
         }
diff --git a/crypto/src/security/ParameterUtilities.cs b/crypto/src/security/ParameterUtilities.cs
index c12155878..792067bba 100644
--- a/crypto/src/security/ParameterUtilities.cs
+++ b/crypto/src/security/ParameterUtilities.cs
@@ -103,6 +103,7 @@ namespace Org.BouncyCastle.Security
                 KisaObjectIdentifiers.IdSeedCbc);
             AddAlgorithm("SERPENT");
             AddAlgorithm("SKIPJACK");
+            AddAlgorithm("SM4");
             AddAlgorithm("TEA");
             AddAlgorithm("THREEFISH-256");
             AddAlgorithm("THREEFISH-512");
@@ -115,7 +116,8 @@ namespace Org.BouncyCastle.Security
 
             AddBasicIVSizeEntries(8, "BLOWFISH", "DES", "DESEDE", "DESEDE3");
             AddBasicIVSizeEntries(16, "AES", "AES128", "AES192", "AES256",
-                "CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "NOEKEON", "SEED");
+                "CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256",
+                "NOEKEON", "SEED", "SM4");
 
             // TODO These algorithms support an IV
             // but JCE doesn't seem to provide an AlgorithmParametersGenerator for them
diff --git a/crypto/src/security/PrivateKeyFactory.cs b/crypto/src/security/PrivateKeyFactory.cs
index c9e19cc7d..0b07d0659 100644
--- a/crypto/src/security/PrivateKeyFactory.cs
+++ b/crypto/src/security/PrivateKeyFactory.cs
@@ -5,6 +5,7 @@ using System.Text;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
 using Org.BouncyCastle.Asn1.Sec;
@@ -170,12 +171,37 @@ namespace Org.BouncyCastle.Security
 
 				return new Gost3410PrivateKeyParameters(x, gostParams.PublicKeyParamSet);
 			}
+            else if (algOid.Equals(EdECObjectIdentifiers.id_X25519))
+            {
+                return new X25519PrivateKeyParameters(GetRawKey(keyInfo, X25519PrivateKeyParameters.KeySize), 0);
+            }
+            else if (algOid.Equals(EdECObjectIdentifiers.id_X448))
+            {
+                return new X448PrivateKeyParameters(GetRawKey(keyInfo, X448PrivateKeyParameters.KeySize), 0);
+            }
+            else if (algOid.Equals(EdECObjectIdentifiers.id_Ed25519))
+            {
+                return new Ed25519PrivateKeyParameters(GetRawKey(keyInfo, Ed25519PrivateKeyParameters.KeySize), 0);
+            }
+            else if (algOid.Equals(EdECObjectIdentifiers.id_Ed448))
+            {
+                return new Ed448PrivateKeyParameters(GetRawKey(keyInfo, Ed448PrivateKeyParameters.KeySize), 0);
+            }
             else
             {
-                throw new SecurityUtilityException("algorithm identifier in key not recognised");
+                throw new SecurityUtilityException("algorithm identifier in private key not recognised");
             }
         }
 
+        private static byte[] GetRawKey(PrivateKeyInfo keyInfo, int expectedSize)
+        {
+            byte[] result = Asn1OctetString.GetInstance(keyInfo.ParsePrivateKey()).GetOctets();
+            if (expectedSize != result.Length)
+                throw new SecurityUtilityException("private key encoding has incorrect length");
+
+            return result;
+        }
+
         public static AsymmetricKeyParameter DecryptKey(
             char[]					passPhrase,
             EncryptedPrivateKeyInfo	encInfo)
diff --git a/crypto/src/security/PublicKeyFactory.cs b/crypto/src/security/PublicKeyFactory.cs
index f1b28b774..e39748e45 100644
--- a/crypto/src/security/PublicKeyFactory.cs
+++ b/crypto/src/security/PublicKeyFactory.cs
@@ -5,6 +5,7 @@ using System.Text;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
 using Org.BouncyCastle.Asn1.Sec;
@@ -218,12 +219,41 @@ namespace Org.BouncyCastle.Security
 
                 return new Gost3410PublicKeyParameters(y, algParams.PublicKeyParamSet);
             }
+            else if (algOid.Equals(EdECObjectIdentifiers.id_X25519))
+            {
+                return new X25519PublicKeyParameters(GetRawKey(keyInfo, X25519PublicKeyParameters.KeySize), 0);
+            }
+            else if (algOid.Equals(EdECObjectIdentifiers.id_X448))
+            {
+                return new X448PublicKeyParameters(GetRawKey(keyInfo, X448PublicKeyParameters.KeySize), 0);
+            }
+            else if (algOid.Equals(EdECObjectIdentifiers.id_Ed25519))
+            {
+                return new Ed25519PublicKeyParameters(GetRawKey(keyInfo, Ed25519PublicKeyParameters.KeySize), 0);
+            }
+            else if (algOid.Equals(EdECObjectIdentifiers.id_Ed448))
+            {
+                return new Ed448PublicKeyParameters(GetRawKey(keyInfo, Ed448PublicKeyParameters.KeySize), 0);
+            }
             else
             {
-                throw new SecurityUtilityException("algorithm identifier in key not recognised: " + algOid);
+                throw new SecurityUtilityException("algorithm identifier in public key not recognised: " + algOid);
             }
         }
 
+        private static byte[] GetRawKey(SubjectPublicKeyInfo keyInfo, int expectedSize)
+        {
+            /*
+             * TODO[RFC 8422]
+             * - Require keyInfo.Algorithm.Parameters == null?
+             */
+            byte[] result = keyInfo.PublicKeyData.GetOctets();
+            if (expectedSize != result.Length)
+                throw new SecurityUtilityException("public key encoding has incorrect length");
+
+            return result;
+        }
+
         private static bool IsPkcsDHParam(Asn1Sequence seq)
         {
             if (seq.Count == 2)
diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs
index 44281503a..a9045ae6e 100644
--- a/crypto/src/security/SignerUtilities.cs
+++ b/crypto/src/security/SignerUtilities.cs
@@ -4,6 +4,7 @@ using System.IO;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Asn1.Nist;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
@@ -231,7 +232,13 @@ namespace Org.BouncyCastle.Security
             algorithms["GOST3411WITHECGOST3410"] = "ECGOST3410";
             algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001.Id] = "ECGOST3410";
 
-
+            algorithms["ED25519"] = "Ed25519";
+            algorithms[EdECObjectIdentifiers.id_Ed25519.Id] = "Ed25519";
+            algorithms["ED25519CTX"] = "Ed25519ctx";
+            algorithms["ED25519PH"] = "Ed25519ph";
+            algorithms["ED448"] = "Ed448";
+            algorithms[EdECObjectIdentifiers.id_Ed448.Id] = "Ed448";
+            algorithms["ED448PH"] = "Ed448ph";
 
             oids["MD2withRSA"] = PkcsObjectIdentifiers.MD2WithRsaEncryption;
             oids["MD4withRSA"] = PkcsObjectIdentifiers.MD4WithRsaEncryption;
@@ -264,6 +271,9 @@ namespace Org.BouncyCastle.Security
 
             oids["GOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94;
             oids["ECGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001;
+
+            oids["Ed25519"] = EdECObjectIdentifiers.id_Ed25519;
+            oids["Ed448"] = EdECObjectIdentifiers.id_Ed448;
         }
 
         /// <summary>
@@ -361,6 +371,30 @@ namespace Org.BouncyCastle.Security
             if (mechanism == null)
                 mechanism = algorithm;
 
+            if (Platform.StartsWith(mechanism, "Ed"))
+            {
+                if (mechanism.Equals("Ed25519"))
+                {
+                    return new Ed25519Signer();
+                }
+                if (mechanism.Equals("Ed25519ctx"))
+                {
+                    return new Ed25519ctxSigner(Arrays.EmptyBytes);
+                }
+                if (mechanism.Equals("Ed25519ph"))
+                {
+                    return new Ed25519phSigner(Arrays.EmptyBytes);
+                }
+                if (mechanism.Equals("Ed448"))
+                {
+                    return new Ed448Signer(Arrays.EmptyBytes);
+                }
+                if (mechanism.Equals("Ed448ph"))
+                {
+                    return new Ed448phSigner(Arrays.EmptyBytes);
+                }
+            }
+
             if (mechanism.Equals("RSA"))
             {
                 return (new RsaDigestSigner(new NullDigest(), (AlgorithmIdentifier)null));
diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index df9b4e7ee..a9a574dbf 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -8,6 +8,19 @@ namespace Org.BouncyCastle.Utilities
     /// <summary> General array utilities.</summary>
     public abstract class Arrays
     {
+        public static readonly byte[] EmptyBytes = new byte[0];
+        public static readonly int[] EmptyInts = new int[0];
+
+        public static bool AreAllZeroes(byte[] buf, int off, int len)
+        {
+            uint bits = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                bits |= buf[off + i];
+            }
+            return bits == 0;
+        }
+
         public static bool AreEqual(
             bool[]  a,
             bool[]  b)
diff --git a/crypto/src/util/Integers.cs b/crypto/src/util/Integers.cs
index ccbf872c4..e746b0ef4 100644
--- a/crypto/src/util/Integers.cs
+++ b/crypto/src/util/Integers.cs
@@ -9,9 +9,21 @@ namespace Org.BouncyCastle.Utilities
             return (i << distance) ^ (int)((uint)i >> -distance);
         }
 
+        [CLSCompliantAttribute(false)]
+        public static uint RotateLeft(uint i, int distance)
+        {
+            return (i << distance) ^ (i >> -distance);
+        }
+
         public static int RotateRight(int i, int distance)
         {
             return (int)((uint)i >> distance) ^ (i << -distance);
         }
+
+        [CLSCompliantAttribute(false)]
+        public static uint RotateRight(uint i, int distance)
+        {
+            return (i >> distance) ^ (i << -distance);
+        }
     }
 }
diff --git a/crypto/src/util/collections/CollectionUtilities.cs b/crypto/src/util/collections/CollectionUtilities.cs
index 18fcb6774..cac158226 100644
--- a/crypto/src/util/collections/CollectionUtilities.cs
+++ b/crypto/src/util/collections/CollectionUtilities.cs
@@ -39,6 +39,14 @@ namespace Org.BouncyCastle.Utilities.Collections
             return new UnmodifiableSetProxy(s);
         }
 
+        public static object RequireNext(IEnumerator e)
+        {
+            if (!e.MoveNext())
+                throw new InvalidOperationException();
+
+            return e.Current;
+        }
+
         public static string ToString(IEnumerable c)
         {
             StringBuilder sb = new StringBuilder("[");
diff --git a/crypto/src/util/io/Streams.cs b/crypto/src/util/io/Streams.cs
index cc7fa924c..503a1b4f1 100644
--- a/crypto/src/util/io/Streams.cs
+++ b/crypto/src/util/io/Streams.cs
@@ -96,5 +96,30 @@ namespace Org.BouncyCastle.Utilities.IO
         {
             buf.WriteTo(output);
         }
+
+        /// <exception cref="IOException"></exception>
+        public static int WriteBufTo(MemoryStream buf, byte[] output, int offset)
+        {
+#if PORTABLE
+            byte[] bytes = buf.ToArray();
+            bytes.CopyTo(output, offset);
+            return bytes.Length;
+#else
+            int size = (int)buf.Length;
+            buf.WriteTo(new MemoryStream(output, offset, size, true));
+            return size;
+#endif
+        }
+
+        public static void WriteZeroes(Stream outStr, long count)
+        {
+            byte[] zeroes = new byte[BufferSize];
+            while (count > BufferSize)
+            {
+                outStr.Write(zeroes, 0, BufferSize);
+                count -= BufferSize;
+            }
+            outStr.Write(zeroes, 0, (int)count);
+        }
     }
 }
diff --git a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs
index 7614321d4..fca5da3f5 100644
--- a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs
+++ b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs
@@ -3,6 +3,7 @@ using System;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
 using Org.BouncyCastle.Asn1.X509;
@@ -26,20 +27,20 @@ namespace Org.BouncyCastle.X509
         /// <summary>
         /// Create a Subject Public Key Info object for a given public key.
         /// </summary>
-        /// <param name="key">One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters</param>
+        /// <param name="publicKey">One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters</param>
         /// <returns>A subject public key info object.</returns>
         /// <exception cref="Exception">Throw exception if object provided is not one of the above.</exception>
         public static SubjectPublicKeyInfo CreateSubjectPublicKeyInfo(
-            AsymmetricKeyParameter key)
+            AsymmetricKeyParameter publicKey)
         {
-            if (key == null)
-                throw new ArgumentNullException("key");
-            if (key.IsPrivate)
-                throw new ArgumentException("Private key passed - public key expected.", "key");
+            if (publicKey == null)
+                throw new ArgumentNullException("publicKey");
+            if (publicKey.IsPrivate)
+                throw new ArgumentException("Private key passed - public key expected.", "publicKey");
 
-            if (key is ElGamalPublicKeyParameters)
+            if (publicKey is ElGamalPublicKeyParameters)
             {
-                ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)key;
+                ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)publicKey;
                 ElGamalParameters kp = _key.Parameters;
 
                 SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
@@ -51,9 +52,9 @@ namespace Org.BouncyCastle.X509
                 return info;
             }
 
-            if (key is DsaPublicKeyParameters)
+            if (publicKey is DsaPublicKeyParameters)
             {
-                DsaPublicKeyParameters _key = (DsaPublicKeyParameters) key;
+                DsaPublicKeyParameters _key = (DsaPublicKeyParameters) publicKey;
                 DsaParameters kp = _key.Parameters;
                 Asn1Encodable ae = kp == null
                     ?	null
@@ -64,9 +65,9 @@ namespace Org.BouncyCastle.X509
                     new DerInteger(_key.Y));
             }
 
-            if (key is DHPublicKeyParameters)
+            if (publicKey is DHPublicKeyParameters)
             {
-                DHPublicKeyParameters _key = (DHPublicKeyParameters) key;
+                DHPublicKeyParameters _key = (DHPublicKeyParameters) publicKey;
                 DHParameters kp = _key.Parameters;
 
                 SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
@@ -78,9 +79,9 @@ namespace Org.BouncyCastle.X509
                 return info;
             } // End of DH
 
-            if (key is RsaKeyParameters)
+            if (publicKey is RsaKeyParameters)
             {
-                RsaKeyParameters _key = (RsaKeyParameters) key;
+                RsaKeyParameters _key = (RsaKeyParameters) publicKey;
 
                 SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
                     new AlgorithmIdentifier(PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance),
@@ -89,9 +90,9 @@ namespace Org.BouncyCastle.X509
                 return info;
             } // End of RSA.
 
-            if (key is ECPublicKeyParameters)
+            if (publicKey is ECPublicKeyParameters)
             {
-                ECPublicKeyParameters _key = (ECPublicKeyParameters) key;
+                ECPublicKeyParameters _key = (ECPublicKeyParameters) publicKey;
 
                 if (_key.AlgorithmName == "ECGOST3410")
                 {
@@ -139,9 +140,9 @@ namespace Org.BouncyCastle.X509
                 }
             } // End of EC
 
-            if (key is Gost3410PublicKeyParameters)
+            if (publicKey is Gost3410PublicKeyParameters)
             {
-                Gost3410PublicKeyParameters _key = (Gost3410PublicKeyParameters) key;
+                Gost3410PublicKeyParameters _key = (Gost3410PublicKeyParameters) publicKey;
 
                 if (_key.PublicKeyParamSet == null)
                     throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set");
@@ -164,7 +165,35 @@ namespace Org.BouncyCastle.X509
                 return new SubjectPublicKeyInfo(algID, new DerOctetString(keyBytes));
             }
 
-            throw new ArgumentException("Class provided no convertible: " + Platform.GetTypeName(key));
+            if (publicKey is X448PublicKeyParameters)
+            {
+                X448PublicKeyParameters key = (X448PublicKeyParameters)publicKey;
+
+                return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), key.GetEncoded());
+            }
+
+            if (publicKey is X25519PublicKeyParameters)
+            {
+                X25519PublicKeyParameters key = (X25519PublicKeyParameters)publicKey;
+
+                return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), key.GetEncoded());
+            }
+
+            if (publicKey is Ed448PublicKeyParameters)
+            {
+                Ed448PublicKeyParameters key = (Ed448PublicKeyParameters)publicKey;
+
+                return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448), key.GetEncoded());
+            }
+
+            if (publicKey is Ed25519PublicKeyParameters)
+            {
+                Ed25519PublicKeyParameters key = (Ed25519PublicKeyParameters)publicKey;
+
+                return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), key.GetEncoded());
+            }
+
+            throw new ArgumentException("Class provided no convertible: " + Platform.GetTypeName(publicKey));
         }
 
         private static void ExtractBytes(