summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'crypto')
-rw-r--r--crypto/Readme.html16
-rw-r--r--crypto/crypto.csproj165
-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/x509/TBSCertificateStructure.cs8
-rw-r--r--crypto/src/bcpg/ECPublicBCPGKey.cs2
-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/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/X25519KeyGenerationParameters.cs15
-rw-r--r--crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs62
-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.cs62
-rw-r--r--crypto/src/crypto/parameters/X448PublicKeyParameters.cs40
-rw-r--r--crypto/src/crypto/signers/Ed25519Signer.cs128
-rw-r--r--crypto/src/crypto/signers/Ed25519ctxSigner.cs130
-rw-r--r--crypto/src/crypto/signers/Ed25519phSigner.cs89
-rw-r--r--crypto/src/crypto/signers/Ed448Signer.cs130
-rw-r--r--crypto/src/crypto/signers/Ed448phSigner.cs89
-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.cs3
-rw-r--r--crypto/src/math/ec/rfc7748/X448.cs3
-rw-r--r--crypto/src/math/ec/rfc8032/Ed25519.cs273
-rw-r--r--crypto/src/math/ec/rfc8032/Ed448.cs252
-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.cs3
-rw-r--r--crypto/src/util/Integers.cs12
-rw-r--r--crypto/src/util/collections/CollectionUtilities.cs8
-rw-r--r--crypto/src/x509/SubjectPublicKeyInfoFactory.cs67
-rw-r--r--crypto/test/src/asn1/test/PrivateKeyInfoTest.cs60
-rw-r--r--crypto/test/src/asn1/test/RegressionTest.cs1
-rw-r--r--crypto/test/src/crypto/test/Blake2bDigestTest.cs496
-rw-r--r--crypto/test/src/crypto/test/Blake2sDigestTest.cs120
-rw-r--r--crypto/test/src/crypto/test/Ed25519Test.cs111
-rw-r--r--crypto/test/src/crypto/test/Ed448Test.cs107
-rw-r--r--crypto/test/src/crypto/test/RFC3211WrapTest.cs2
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs5
-rw-r--r--crypto/test/src/crypto/test/SM4Test.cs93
-rw-r--r--crypto/test/src/crypto/test/X25519Test.cs69
-rw-r--r--crypto/test/src/crypto/test/X448Test.cs69
-rw-r--r--crypto/test/src/math/ec/rfc7748/test/X25519Test.cs49
-rw-r--r--crypto/test/src/math/ec/rfc7748/test/X448Test.cs53
-rw-r--r--crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs283
-rw-r--r--crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs182
-rw-r--r--crypto/test/src/openpgp/examples/DirectKeySignature.cs4
-rw-r--r--crypto/test/src/security/test/TestSignerUtil.cs18
-rw-r--r--crypto/test/src/test/RegressionTest.cs1
-rw-r--r--crypto/test/src/test/SM4Test.cs149
-rw-r--r--crypto/test/src/util/test/SimpleTest.cs57
76 files changed, 4191 insertions, 684 deletions
diff --git a/crypto/Readme.html b/crypto/Readme.html
index 72e97516f..a89e7e535 100644
--- a/crypto/Readme.html
+++ b/crypto/Readme.html
@@ -31,6 +31,8 @@
 				<a href="#mozTocId3413">Notes:</a>
 		<ol>
             <li>
+                <a href="#mozTocId85318">Release 1.8.4</a>
+            <li>
                 <a href="#mozTocId85317">Release 1.8.3</a>
             <li>
                 <a href="#mozTocId85316">Release 1.8.2</a>
@@ -294,6 +296,17 @@ We state, where EC MQV has not otherwise been disabled or removed:
 		<hr style="WIDTH: 100%; HEIGHT: 2px">
 		<h3><a class="mozTocH3" name="mozTocId3413"></a>Notes:</h3>
 
+        <h4><a class="mozTocH4" name="mozTocId85318"></a>Release 1.8.4, TBD</h4>
+
+        <h5>Defects Fixed</h5>
+        <ul>
+            <li>Rfc3211WrapEngine would not properly handle messages longer than 127 bytes. This has been fixed.</li>
+        </ul>
+        <h5>Additional Features and Functionality</h5>
+        <ul>
+            <li>Restrictions on the output sizes of the Blake2b/s digests have been removed.</li>
+        </ul>
+
         <h4><a class="mozTocH4" name="mozTocId85317"></a>Release 1.8.3, Saturday August 11, 2018</h4>
 
         <h5>IMPORTANT</h5>
@@ -302,7 +315,8 @@ We state, where EC MQV has not otherwise been disabled or removed:
                 In this release, the TLS library has moved to a whitelisting approach for client-side validation of server-presented
                 Diffie-Hellman (DH) parameters. In the default configuration, if a ciphersuite using ephemeral DH is selected by the
                 server, the client will abort the handshake if the proposed DH group is not one of those specified in RFC 3526 or RFC 7919,
-                or if the DH prime is < 2048 bits. The client therefore no longer offers DH ciphersuites by default.
+                or if the DH prime is &lt; 2048 bits. The client therefore no longer offers DH ciphersuites by default. See also the paper
+                <a href="https://eprint.iacr.org/2018/749">"Prime and Prejudice: Primality Testing Under Adversarial Conditions"</a>.
             </li>
         </ul>
 
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index b6dfb3963..d0f672e8e 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -1149,6 +1149,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\edec\EdECObjectIdentifiers.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\esf\CertificateValues.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3129,6 +3134,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\IRawAgreement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\ISignatureFactory.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3249,6 +3259,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\agreement\X25519Agreement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\agreement\X448Agreement.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\agreement\jpake\JPakeParticipant.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3749,6 +3769,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\engines\SM4Engine.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\engines\TEAEngine.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3849,6 +3874,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\generators\Ed25519KeyPairGenerator.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\generators\Ed448KeyPairGenerator.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\generators\ElGamalKeyPairGenerator.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3939,6 +3974,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\generators\X25519KeyPairGenerator.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\generators\X448KeyPairGenerator.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\io\CipherStream.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4289,6 +4334,36 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\parameters\Ed25519KeyGenerationParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\Ed25519PrivateKeyParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\Ed25519PublicKeyParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\Ed448KeyGenerationParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\Ed448PrivateKeyParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\Ed448PublicKeyParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\parameters\ElGamalKeyGenerationParameters.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4484,6 +4559,36 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\parameters\X25519KeyGenerationParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\X25519PrivateKeyParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\X25519PublicKeyParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\X448KeyGenerationParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\X448PrivateKeyParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\parameters\X448PublicKeyParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\prng\BasicEntropySourceProvider.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4609,6 +4714,31 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\signers\Ed25519ctxSigner.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\signers\Ed25519phSigner.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\signers\Ed25519Signer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\signers\Ed448phSigner.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\signers\Ed448Signer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\signers\GenericSigner.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11230,6 +11360,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\asn1\test\PrivateKeyInfoTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\asn1\test\PKCS10Test.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11670,6 +11805,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\Ed25519Test.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\test\Ed448Test.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\ElGamalTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -12090,6 +12235,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\SM4Test.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\SRP6Test.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -12160,6 +12310,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\X25519Test.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\test\X448Test.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\X931SignerTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -12890,6 +13050,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\test\SM4Test.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\test\TestUtilities.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
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/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/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/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/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..c25ab9364
--- /dev/null
+++ b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs
@@ -0,0 +1,62 @@
+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);
+            X25519.ScalarMult(data, 0, encoded, 0, buf, off);
+        }
+    }
+}
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..291eac10f
--- /dev/null
+++ b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs
@@ -0,0 +1,62 @@
+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);
+            X448.ScalarMult(data, 0, encoded, 0, buf, off);
+        }
+    }
+}
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..904450ed1
--- /dev/null
+++ b/crypto/src/crypto/signers/Ed25519Signer.cs
@@ -0,0 +1,128 @@
+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 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)
+                throw new InvalidOperationException("Ed25519Signer not initialised for signature generation.");
+
+            return buffer.GenerateSignature(privateKey, publicKey);
+        }
+
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            if (forSigning)
+                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()
+            {
+#if PORTABLE
+                this.Position = 0L;
+
+                // TODO Clear using Write method
+#else
+                Array.Clear(GetBuffer(), 0, (int)Position);
+#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..e9c2eca44
--- /dev/null
+++ b/crypto/src/crypto/signers/Ed25519ctxSigner.cs
@@ -0,0 +1,130 @@
+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 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)
+                throw new InvalidOperationException("Ed25519ctxSigner not initialised for signature generation.");
+
+            return buffer.GenerateSignature(privateKey, publicKey, context);
+        }
+
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            if (forSigning)
+                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()
+            {
+#if PORTABLE
+                this.Position = 0L;
+
+                // TODO Clear using Write method
+#else
+                Array.Clear(GetBuffer(), 0, (int)Position);
+#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..0d3de96f3
--- /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)
+                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)
+                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..c01d84b4d
--- /dev/null
+++ b/crypto/src/crypto/signers/Ed448Signer.cs
@@ -0,0 +1,130 @@
+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 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)
+                throw new InvalidOperationException("Ed448Signer not initialised for signature generation.");
+
+            return buffer.GenerateSignature(privateKey, publicKey, context);
+        }
+
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            if (forSigning)
+                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()
+            {
+#if PORTABLE
+                this.Position = 0L;
+
+                // TODO Clear using Write method
+#else
+                Array.Clear(GetBuffer(), 0, (int)Position);
+#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..50d0a0154
--- /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)
+                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)
+                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/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 38bc9c1f1..9db3a24c6 100644
--- a/crypto/src/math/ec/rfc7748/X25519.cs
+++ b/crypto/src/math/ec/rfc7748/X25519.cs
@@ -6,6 +6,9 @@ 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;
 
diff --git a/crypto/src/math/ec/rfc7748/X448.cs b/crypto/src/math/ec/rfc7748/X448.cs
index 4e6e32879..cf581e9fc 100644
--- a/crypto/src/math/ec/rfc7748/X448.cs
+++ b/crypto/src/math/ec/rfc7748/X448.cs
@@ -6,6 +6,9 @@ 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;
 
diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs
index 747b5b724..1154a1021 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();
@@ -520,9 +637,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             lock (typeof(Ed25519))
             {
                 if (precompBase != null)
-                {
                     return;
-                }
 
                 // Precomputed table for the base point in verification ladder
                 {
@@ -557,11 +672,14 @@ 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);
+                            }
                         }
-                    }
+                }
 
                     PointExt[] points = new PointExt[PrecompPoints];
                     int k = 0;
@@ -798,9 +916,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                 }
 
                 if ((cOff -= PrecompTeeth) < 0)
-                {
                     break;
-                }
 
                 PointDouble(r);
             }
@@ -853,9 +969,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                 }
 
                 if (--bit < 0)
-                {
                     break;
-                }
 
                 PointDouble(r);
             }
@@ -863,78 +977,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);
+        }
 
-            ImplSign(d, h, s, pk, 0, 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(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 58f583ddf..12f69b676 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();
@@ -507,9 +598,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
             lock (typeof(Ed448))
             {
                 if (precompBase != null)
-                {
                     return;
-                }
 
                 PointExt p = new PointExt();
                 X448Field.Copy(B_x, 0, p.x, 0);
@@ -535,11 +624,14 @@ 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);
+                            }
                         }
-                    }
+                }
 
                     PointExt[] points = new PointExt[PrecompPoints];
                     int k = 0;
@@ -891,8 +983,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;
                     }
 
@@ -910,9 +1003,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                 }
 
                 if (--cOff < 0)
-                {
                     break;
-                }
 
                 PointDouble(r);
             }
@@ -965,9 +1056,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                 }
 
                 if (--bit < 0)
-                {
                     break;
-                }
 
                 PointDouble(r);
             }
@@ -975,96 +1064,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..3df908240 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -8,6 +8,9 @@ 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 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/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(
diff --git a/crypto/test/src/asn1/test/PrivateKeyInfoTest.cs b/crypto/test/src/asn1/test/PrivateKeyInfoTest.cs
new file mode 100644
index 000000000..eb17a54c3
--- /dev/null
+++ b/crypto/test/src/asn1/test/PrivateKeyInfoTest.cs
@@ -0,0 +1,60 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Asn1.Tests
+{
+    [TestFixture]
+    public class PrivateKeyInfoTest
+        : SimpleTest
+    {
+        private static readonly byte[] priv = Base64.Decode(
+            "MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC");
+
+        private static readonly byte[] privWithPub = Base64.Decode(
+            "MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC" +
+            "oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB" +
+            "Z9w7lshQhqowtrbLDFw4rXAxZuE=");
+
+        public override string Name
+        {
+            get { return "PrivateKeyInfoTest"; }
+        }
+
+        public override void PerformTest()
+        {
+            PrivateKeyInfo privInfo1 = PrivateKeyInfo.GetInstance(priv);
+
+            IsTrue(!privInfo1.HasPublicKey);
+
+            PrivateKeyInfo privInfo2 = new PrivateKeyInfo(privInfo1.PrivateKeyAlgorithm, privInfo1.ParsePrivateKey());
+
+            IsTrue("enc 1 failed", AreEqual(priv, privInfo2.GetEncoded()));
+
+            privInfo1 = PrivateKeyInfo.GetInstance(privWithPub);
+
+            IsTrue(privInfo1.HasPublicKey);
+
+            privInfo2 = new PrivateKeyInfo(privInfo1.PrivateKeyAlgorithm, privInfo1.ParsePrivateKey(), privInfo1.Attributes, privInfo1.PublicKeyData.GetOctets());
+
+            IsTrue("enc 2 failed", AreEqual(privWithPub, privInfo2.GetEncoded()));
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new PrivateKeyInfoTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/asn1/test/RegressionTest.cs b/crypto/test/src/asn1/test/RegressionTest.cs
index f78c0dd72..b62c66b5f 100644
--- a/crypto/test/src/asn1/test/RegressionTest.cs
+++ b/crypto/test/src/asn1/test/RegressionTest.cs
@@ -51,6 +51,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 			new Pkcs10Test(),
             new Pkcs12Test(),
 			new PkiFailureInfoTest(),
+            new PrivateKeyInfoTest(),
 			new ProcurationSyntaxUnitTest(),
 			new ProfessionInfoUnitTest(),
 			new QCStatementUnitTest(),
diff --git a/crypto/test/src/crypto/test/Blake2bDigestTest.cs b/crypto/test/src/crypto/test/Blake2bDigestTest.cs
index fe2101fee..877c8bc16 100644
--- a/crypto/test/src/crypto/test/Blake2bDigestTest.cs
+++ b/crypto/test/src/crypto/test/Blake2bDigestTest.cs
@@ -14,226 +14,300 @@ namespace Org.BouncyCastle.Crypto.Tests
     public class Blake2bDigestTest 
 	    : SimpleTest
     {
-	    private static readonly string[][] keyedTestVectors =
-	    { // input/message, key, hash
-
-			    // Vectors from BLAKE2 web site: https://blake2.net/blake2b-test.txt
-			    new string[]{
-					    "",
-					    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					    "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568" },
-
-			    new string[]{
-					    "00",
-					    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					    "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd" },
-
-			    new string[]{
-					    "0001",
-					    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					    "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965" },
-
-			    new string[]{
-					    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
-					    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					    "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd" },
-
-			    new string[]{
-					    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
-					    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					    "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f" },
-
-			    new string[]{
-					    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
-					    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
-					    "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461" }
+        private static readonly string[,] keyedTestVectors = { // input/message, key, hash
+            // Vectors from BLAKE2 web site: https://blake2.net/blake2b-test.txt
+            {
+                "",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"
+            },
+            {
+                "00",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd"
+            },
+            {
+                "0001",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965"
+            },
+            {
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd"
+            },
+            {
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f"
+            },
+            {
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
+                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+                "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461"
+            }
         };
 
-	    private static readonly string[][] unkeyedTestVectors =
-	    { // from: http://fossies.org/linux/john/src/rawBLAKE2_512_fmt_plug.c
-			    // hash, input/message
-			    // digests without leading $BLAKE2$
-			    new string[]{
-					    "4245af08b46fbb290222ab8a68613621d92ce78577152d712467742417ebc1153668f1c9e1ec1e152a32a9c242dc686d175e087906377f0c483c5be2cb68953e",
-					    "blake2" },
-			    new string[]{
-					    "021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0",
-					    "hello world" },
-			    new string[]{
-					    "1f7d9b7c9a90f7bfc66e52b69f3b6c3befbd6aee11aac860e99347a495526f30c9e51f6b0db01c24825092a09dd1a15740f0ade8def87e60c15da487571bcef7",
-					    "verystrongandlongpassword" },
-			    new string[]{
-					    "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918",
-					    "The quick brown fox jumps over the lazy dog" },
-			    new string[]{
-					    "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
-					    "" },
-		        new string[]{
-				        "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
-				        "abc" },
-	    };
-
-	    public override string Name
-	    {
-		    get { return "BLAKE2b"; }
-	    }
-
-	    private void offsetTest(
-		    IDigest digest,
-		    byte[] input,
-		    byte[] expected)
-	    {
-		    byte[] resBuf = new byte[expected.Length + 11];
-
-		    digest.BlockUpdate(input, 0, input.Length);
+        // from: http://fossies.org/linux/john/src/rawBLAKE2_512_fmt_plug.c
+        private static readonly string[,] unkeyedTestVectors = { // hash, input/message
+            // digests without leading $BLAKE2$
+            {
+                "4245af08b46fbb290222ab8a68613621d92ce78577152d712467742417ebc1153668f1c9e1ec1e152a32a9c242dc686d175e087906377f0c483c5be2cb68953e",
+                "blake2"
+            },
+            {
+                "021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0",
+                "hello world"
+            },
+            {
+                "1f7d9b7c9a90f7bfc66e52b69f3b6c3befbd6aee11aac860e99347a495526f30c9e51f6b0db01c24825092a09dd1a15740f0ade8def87e60c15da487571bcef7",
+                "verystrongandlongpassword"
+            },
+            {
+                "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918",
+                "The quick brown fox jumps over the lazy dog"
+            },
+            {
+                "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
+                ""
+            },
+            {
+                "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
+                "abc"
+            },
+        };
+
+        public override string Name
+        {
+            get { return "BLAKE2b"; }
+        }
+
+        private void offsetTest(
+            IDigest digest,
+            byte[] input,
+            byte[] expected)
+        {
+            byte[] resBuf = new byte[expected.Length + 11];
+
+            digest.BlockUpdate(input, 0, input.Length);
 
             digest.DoFinal(resBuf, 11);
 
-		    if (!AreEqual(Arrays.CopyOfRange(resBuf, 11, resBuf.Length), expected))
-		    {
-			    Fail("Offset failed got " + Hex.ToHexString(resBuf));
-		    }
-	    }
-
-	    public override void PerformTest()
-	    {
-		    // test keyed test vectors:
-		
-		    Blake2bDigest blake2bkeyed = new Blake2bDigest(Hex.Decode(keyedTestVectors[0][1]));
-		    for (int tv = 0; tv < keyedTestVectors.Length; tv++)
-		    {						
-
-			    byte[] input = Hex.Decode(keyedTestVectors[tv][0]);
-			    blake2bkeyed.Reset();
-
-			    blake2bkeyed.BlockUpdate(input, 0, input.Length);
-			    byte[] keyedHash = new byte[64];
-			    blake2bkeyed.DoFinal(keyedHash, 0);
-
-			    if (!Arrays.AreEqual(Hex.Decode(keyedTestVectors[tv][2]), keyedHash))
-			    {
-                    Fail("BLAKE2b mismatch on test vector ",
-						    keyedTestVectors[tv][2],
-                            Hex.ToHexString(keyedHash));
-			    }
-
-			    offsetTest(blake2bkeyed, input, keyedHash);
-		    }
-
-		    Blake2bDigest blake2bunkeyed = new Blake2bDigest();
-		    // test unkeyed test vectors:
-		    for (int i = 0; i < unkeyedTestVectors.Length; i++)
-		    {
-				// blake2bunkeyed.update(
-				// unkeyedTestVectors[i][1].getBytes("UTF-8"));
-				// test update(byte b)
-				byte[] unkeyedInput = Encoding.UTF8.GetBytes(unkeyedTestVectors[i][1]);
-				for (int j = 0; j < unkeyedInput.Length; j++)
-				{
-					blake2bunkeyed.Update(unkeyedInput[j]);
-				}
+            if (!AreEqual(Arrays.CopyOfRange(resBuf, 11, resBuf.Length), expected))
+            {
+                Fail("Offset failed got " + Hex.ToHexString(resBuf));
+            }
+        }
+
+        public override void PerformTest()
+        {
+            // test keyed test vectors:
+
+            Blake2bDigest blake2bkeyed = new Blake2bDigest(Hex.Decode(keyedTestVectors[0, 1]));
+            for (int tv = 0; tv < keyedTestVectors.GetLength(0); tv++)
+            {
+                byte[] input = Hex.Decode(keyedTestVectors[tv, 0]);
+                blake2bkeyed.Reset();
+
+                blake2bkeyed.BlockUpdate(input, 0, input.Length);
+                byte[] keyedHash = new byte[64];
+                blake2bkeyed.DoFinal(keyedHash, 0);
+
+                if (!Arrays.AreEqual(Hex.Decode(keyedTestVectors[tv, 2]), keyedHash))
+                {
+                    Fail("BLAKE2b mismatch on test vector ", keyedTestVectors[tv, 2], Hex.ToHexString(keyedHash));
+                }
+
+                offsetTest(blake2bkeyed, input, keyedHash);
+            }
+
+            Blake2bDigest blake2bunkeyed = new Blake2bDigest();
+            // test unkeyed test vectors:
+            for (int i = 0; i < unkeyedTestVectors.GetLength(0); i++)
+            {
+                // test update(byte b)
+                byte[] unkeyedInput = Encoding.UTF8.GetBytes(unkeyedTestVectors[i, 1]);
+                for (int j = 0; j < unkeyedInput.Length; j++)
+                {
+                    blake2bunkeyed.Update(unkeyedInput[j]);
+                }
 
                 byte[] unkeyedHash = new byte[64];
-			    blake2bunkeyed.DoFinal(unkeyedHash, 0);
-			    blake2bunkeyed.Reset();
-
-			    if (!Arrays.AreEqual(Hex.Decode(unkeyedTestVectors[i][0]),
-					    unkeyedHash))
-			    {
-                    Fail("BLAKE2b mismatch on test vector ",
-						    unkeyedTestVectors[i][0],
-						    Hex.ToHexString(unkeyedHash));
-			    }
-		    }
-
-		    cloneTest();
-		    resetTest();
-	    }
-
-	    private void cloneTest()
-	    {
-		    Blake2bDigest blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3][1]), 16, Hex.Decode("000102030405060708090a0b0c0d0e0f"), Hex.Decode("101112131415161718191a1b1c1d1e1f"));
-		    byte[] expected = Hex.Decode("b6d48ed5771b17414c4e08bd8d8a3bc4");
-
-		    checkClone(blake2bCloneSource, expected);
-
-		    // just digest size
-		    blake2bCloneSource = new Blake2bDigest(160);
-		    expected = Hex.Decode("64202454e538279b21cea0f5a7688be656f8f484");
-		    checkClone(blake2bCloneSource, expected);
-
-		    // null salt and personalisation
-		    blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3][1]), 16, null, null);
-		    expected = Hex.Decode("2b4a081fae2d7b488f5eed7e83e42a20");
-		    checkClone(blake2bCloneSource, expected);
-
-		    // null personalisation
-		    blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3][1]), 16, Hex.Decode("000102030405060708090a0b0c0d0e0f"), null);
-		    expected = Hex.Decode("00c3a2a02fcb9f389857626e19d706f6");
-		    checkClone(blake2bCloneSource, expected);
-
-		    // null salt
-		    blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3][1]), 16, null, Hex.Decode("101112131415161718191a1b1c1d1e1f"));
-		    expected = Hex.Decode("f445ec9c062a3c724f8fdef824417abb");
-		    checkClone(blake2bCloneSource, expected);
-	    }
-
-	    private void checkClone(Blake2bDigest blake2bCloneSource, byte[] expected)
-	    {
-		    byte[] message = Hex.Decode(keyedTestVectors[3][0]);
-
-		    blake2bCloneSource.BlockUpdate(message, 0, message.Length);
-
-		    byte[] hash = new byte[blake2bCloneSource.GetDigestSize()];
-
-		    Blake2bDigest digClone = new Blake2bDigest(blake2bCloneSource);
-
-		    blake2bCloneSource.DoFinal(hash, 0);
-		    if (!AreEqual(expected, hash))
-		    {
-			    Fail("clone source not correct");
-		    }
-
-		    digClone.DoFinal(hash, 0);
-		    if (!AreEqual(expected, hash))
-		    {
-			    Fail("clone not correct");
-		    }
-	    }
-
-	    private void resetTest()
-	    {
-		    // Generate a non-zero key
-		    byte[] key = new byte[32];
-		    for (byte i = 0; i < key.Length; i++)
-		    {
-			    key[i] = i;
-		    }
-		    // Generate some non-zero input longer than the key
-		    byte[] input = new byte[key.Length + 1];
-		    for (byte i = 0; i < input.Length; i++)
-		    {
-			    input[i] = i;
-		    }
-		    // Hash the input
-		    Blake2bDigest digest = new Blake2bDigest(key);
+                blake2bunkeyed.DoFinal(unkeyedHash, 0);
+                blake2bunkeyed.Reset();
+
+                if (!Arrays.AreEqual(Hex.Decode(unkeyedTestVectors[i, 0]), unkeyedHash))
+                {
+                    Fail("BLAKE2b mismatch on test vector ", unkeyedTestVectors[i, 0], Hex.ToHexString(unkeyedHash));
+                }
+            }
+
+            CloneTest();
+            ResetTest();
+            DoTestNullKeyVsUnkeyed();
+            DoTestLengthConstruction();
+        }
+
+        private void CloneTest()
+        {
+            Blake2bDigest blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3, 1]), 16,
+                Hex.Decode("000102030405060708090a0b0c0d0e0f"), Hex.Decode("101112131415161718191a1b1c1d1e1f"));
+            byte[] expected = Hex.Decode("b6d48ed5771b17414c4e08bd8d8a3bc4");
+
+            CheckClone(blake2bCloneSource, expected);
+
+            // just digest size
+            blake2bCloneSource = new Blake2bDigest(160);
+            expected = Hex.Decode("64202454e538279b21cea0f5a7688be656f8f484");
+            CheckClone(blake2bCloneSource, expected);
+
+            // null salt and personalisation
+            blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3, 1]), 16, null, null);
+            expected = Hex.Decode("2b4a081fae2d7b488f5eed7e83e42a20");
+            CheckClone(blake2bCloneSource, expected);
+
+            // null personalisation
+            blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3, 1]), 16, Hex.Decode("000102030405060708090a0b0c0d0e0f"), null);
+            expected = Hex.Decode("00c3a2a02fcb9f389857626e19d706f6");
+            CheckClone(blake2bCloneSource, expected);
+
+            // null salt
+            blake2bCloneSource = new Blake2bDigest(Hex.Decode(keyedTestVectors[3, 1]), 16, null, Hex.Decode("101112131415161718191a1b1c1d1e1f"));
+            expected = Hex.Decode("f445ec9c062a3c724f8fdef824417abb");
+            CheckClone(blake2bCloneSource, expected);
+        }
+
+        private void CheckClone(Blake2bDigest blake2bCloneSource, byte[] expected)
+        {
+            byte[] message = Hex.Decode(keyedTestVectors[3, 0]);
+
+            blake2bCloneSource.BlockUpdate(message, 0, message.Length);
+
+            byte[] hash = new byte[blake2bCloneSource.GetDigestSize()];
+
+            Blake2bDigest digClone = new Blake2bDigest(blake2bCloneSource);
+
+            blake2bCloneSource.DoFinal(hash, 0);
+            if (!AreEqual(expected, hash))
+            {
+                Fail("clone source not correct");
+            }
+
+            digClone.DoFinal(hash, 0);
+            if (!AreEqual(expected, hash))
+            {
+                Fail("clone not correct");
+            }
+        }
+
+        private void DoTestLengthConstruction()
+        {
+            try
+            {
+                new Blake2bDigest(-1);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512", e.Message);
+            }
+
+            try
+            {
+                new Blake2bDigest(9);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512", e.Message);
+            }
+
+            try
+            {
+                new Blake2bDigest(520);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512", e.Message);
+            }
+
+            try
+            {
+                new Blake2bDigest(null, -1, null, null);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("Invalid digest length (required: 1 - 64)", e.Message);
+            }
+
+            try
+            {
+                new Blake2bDigest(null, 65, null, null);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("Invalid digest length (required: 1 - 64)", e.Message);
+            }
+        }
+
+        private void DoTestNullKeyVsUnkeyed()
+        {
+            byte[] abc = Strings.ToByteArray("abc");
+
+            for (int i = 1; i != 64; i++)
+            {
+                Blake2bDigest dig1 = new Blake2bDigest(i * 8);
+                Blake2bDigest dig2 = new Blake2bDigest(null, i, null, null);
+
+                byte[] out1 = new byte[i];
+                byte[] out2 = new byte[i];
+
+                dig1.BlockUpdate(abc, 0, abc.Length);
+                dig2.BlockUpdate(abc, 0, abc.Length);
+
+                dig1.DoFinal(out1, 0);
+                dig2.DoFinal(out2, 0);
+
+                IsTrue(Arrays.AreEqual(out1, out2));
+            }
+        }
+
+        private void ResetTest()
+        {
+            // Generate a non-zero key
+            byte[] key = new byte[32];
+            for (byte i = 0; i < key.Length; i++)
+            {
+                key[i] = i;
+            }
+            // Generate some non-zero input longer than the key
+            byte[] input = new byte[key.Length + 1];
+            for (byte i = 0; i < input.Length; i++)
+            {
+                input[i] = i;
+            }
+            // Hash the input
+            Blake2bDigest digest = new Blake2bDigest(key);
             digest.BlockUpdate(input, 0, input.Length);
-		    byte[] hash = new byte[digest.GetDigestSize()];
-		    digest.DoFinal(hash, 0);
-		    // Using a second instance, hash the input without calling doFinal()
-		    Blake2bDigest digest1 = new Blake2bDigest(key);
+            byte[] hash = new byte[digest.GetDigestSize()];
+            digest.DoFinal(hash, 0);
+            // Using a second instance, hash the input without calling doFinal()
+            Blake2bDigest digest1 = new Blake2bDigest(key);
+            digest1.BlockUpdate(input, 0, input.Length);
+            // Reset the second instance and hash the input again
+            digest1.Reset();
             digest1.BlockUpdate(input, 0, input.Length);
-		    // Reset the second instance and hash the input again
-		    digest1.Reset();
-		    digest1.BlockUpdate(input, 0, input.Length);
-		    byte[] hash1 = new byte[digest.GetDigestSize()];
-		    digest1.DoFinal(hash1, 0);
-		    // The hashes should be identical
-		    if (!Arrays.AreEqual(hash, hash1))
-		    {
-			    Fail("state was not reset");
-		    }
-	    }
+            byte[] hash1 = new byte[digest.GetDigestSize()];
+            digest1.DoFinal(hash1, 0);
+            // The hashes should be identical
+            if (!Arrays.AreEqual(hash, hash1))
+            {
+                Fail("state was not reset");
+            }
+        }
 
         public static void MainOld(string[] args)
 		{
diff --git a/crypto/test/src/crypto/test/Blake2sDigestTest.cs b/crypto/test/src/crypto/test/Blake2sDigestTest.cs
index f7c2c44fc..36ca7bce3 100644
--- a/crypto/test/src/crypto/test/Blake2sDigestTest.cs
+++ b/crypto/test/src/crypto/test/Blake2sDigestTest.cs
@@ -15,34 +15,33 @@ namespace Org.BouncyCastle.Crypto.Tests
         : SimpleTest
     {
         // Vectors from BLAKE2 web site: https://blake2.net/blake2s-test.txt
-        private static readonly string[][] keyedTestVectors = {
-            // input/message, key, hash
-            new string[]{
+        private static readonly string[,] keyedTestVectors = { // input/message, key, hash
+            {
                 "",
                 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
                 "48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49",
             },
-            new string[]{
+            {
                 "00",
                 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
                 "40d15fee7c328830166ac3f918650f807e7e01e177258cdc0a39b11f598066f1",
             },
-            new string[]{
+            {
                 "0001",
                 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
                 "6bb71300644cd3991b26ccd4d274acd1adeab8b1d7914546c1198bbe9fc9d803",
             },
-            new string[]{
+            {
                 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
                 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
                 "172ffc67153d12e0ca76a8b6cd5d4731885b39ce0cac93a8972a18006c8b8baf",
             },
-            new string[]{
+            {
                 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
                 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
                 "4f8ce1e51d2fe7f24043a904d898ebfc91975418753413aa099b795ecb35cedb",
             },
-            new string[]{
+            {
                 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
                 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
                 "3fb735061abc519dfe979e54c1ee5bfad0a9d858b3315bad34bde999efd724dd",
@@ -56,38 +55,32 @@ namespace Org.BouncyCastle.Crypto.Tests
 
         public void DoTestDigestWithKeyedTestVectors()
         {
-            Blake2sDigest digest = new Blake2sDigest(Hex.Decode(
-                keyedTestVectors[0][1]));
-            for (int i = 0; i != keyedTestVectors.Length; i++)
+            Blake2sDigest digest = new Blake2sDigest(Hex.Decode(keyedTestVectors[0, 1]));
+            for (int i = 0; i != keyedTestVectors.GetLength(0); i++)
             {
-                String[] keyedTestVector = keyedTestVectors[i];
-                byte[] input = Hex.Decode(keyedTestVector[0]);
+                byte[] input = Hex.Decode(keyedTestVectors[i, 0]);
                 digest.Reset();
 
                 digest.BlockUpdate(input, 0, input.Length);
                 byte[] hash = new byte[32];
                 digest.DoFinal(hash, 0);
 
-                if (!AreEqual(Hex.Decode(keyedTestVector[2]), hash))
+                if (!AreEqual(Hex.Decode(keyedTestVectors[i, 2]), hash))
                 {
-                    Fail("BLAKE2s mismatch on test vector ",
-                        keyedTestVector[2],
-                        Hex.ToHexString(hash));
+                    Fail("BLAKE2s mismatch on test vector ", keyedTestVectors[i, 2], Hex.ToHexString(hash));
                 }
             }
         }
 
         public void DoTestDigestWithKeyedTestVectorsAndRandomUpdate()
         {
-            Blake2sDigest digest = new Blake2sDigest(Hex.Decode(
-                keyedTestVectors[0][1]));
+            Blake2sDigest digest = new Blake2sDigest(Hex.Decode(keyedTestVectors[0, 1]));
             Random random = new Random();
             for (int i = 0; i < 100; i++)
             {
-                for (int j = 0; j != keyedTestVectors.Length; j++)
+                for (int j = 0; j != keyedTestVectors.GetLength(0); j++)
                 {
-                    String[] keyedTestVector = keyedTestVectors[j];
-                    byte[] input = Hex.Decode(keyedTestVector[0]);
+                    byte[] input = Hex.Decode(keyedTestVectors[j, 0]);
                     if (input.Length < 3)
                     {
                         continue;
@@ -108,16 +101,89 @@ namespace Org.BouncyCastle.Crypto.Tests
                     byte[] hash = new byte[32];
                     digest.DoFinal(hash, 0);
 
-                    if (!AreEqual(Hex.Decode(keyedTestVector[2]), hash))
+                    if (!AreEqual(Hex.Decode(keyedTestVectors[j, 2]), hash))
                     {
-                        Fail("BLAKE2s mismatch on test vector ",
-                            keyedTestVector[2],
-                            Hex.ToHexString(hash));
+                        Fail("BLAKE2s mismatch on test vector ", keyedTestVectors[j, 2], Hex.ToHexString(hash));
                     }
                 }
             }
         }
 
+        private void DoTestLengthConstruction()
+        {
+            try
+            {
+                new Blake2sDigest(-1);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256", e.Message);
+            }
+
+            try
+            {
+                new Blake2sDigest(9);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256", e.Message);
+            }
+
+            try
+            {
+                new Blake2sDigest(512);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256", e.Message);
+            }
+
+            try
+            {
+                new Blake2sDigest(null, -1, null, null);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("Invalid digest length (required: 1 - 32)", e.Message);
+            }
+
+            try
+            {
+                new Blake2sDigest(null, 33, null, null);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsEquals("Invalid digest length (required: 1 - 32)", e.Message);
+            }
+        }
+
+        private void DoTestNullKeyVsUnkeyed()
+        {
+            byte[] abc = Strings.ToByteArray("abc");
+
+            for (int i = 1; i != 32; i++)
+            {
+                Blake2sDigest dig1 = new Blake2sDigest(i * 8);
+                Blake2sDigest dig2 = new Blake2sDigest(null, i, null, null);
+
+                byte[] out1 = new byte[i];
+                byte[] out2 = new byte[i];
+
+                dig1.BlockUpdate(abc, 0, abc.Length);
+                dig2.BlockUpdate(abc, 0, abc.Length);
+
+                dig1.DoFinal(out1, 0);
+                dig2.DoFinal(out2, 0);
+
+                IsTrue(Arrays.AreEqual(out1, out2));
+            }
+        }
+
         public void DoTestReset()
         {
             // Generate a non-zero key
@@ -225,6 +291,8 @@ namespace Org.BouncyCastle.Crypto.Tests
             DoTestDigestWithKeyedTestVectorsAndRandomUpdate();
             DoTestReset();
             RunSelfTest();
+            DoTestNullKeyVsUnkeyed();
+            DoTestLengthConstruction();
         }
 
         public static void MainOld(string[] args)
diff --git a/crypto/test/src/crypto/test/Ed25519Test.cs b/crypto/test/src/crypto/test/Ed25519Test.cs
new file mode 100644
index 000000000..82e36d991
--- /dev/null
+++ b/crypto/test/src/crypto/test/Ed25519Test.cs
@@ -0,0 +1,111 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+	[TestFixture]
+    public class Ed25519Test
+        : SimpleTest
+    {
+        private static readonly SecureRandom Random = new SecureRandom();
+
+        public override string Name
+        {
+            get { return "Ed25519"; }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new Ed25519Test());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+
+        public override void PerformTest()
+        {
+            for (int i = 0; i < 10; ++i)
+            {
+                DoTestConsistency(Ed25519.Algorithm.Ed25519, null);
+
+                byte[] context = RandomContext(Random.NextInt() & 255);
+                DoTestConsistency(Ed25519.Algorithm.Ed25519ctx, context);
+                DoTestConsistency(Ed25519.Algorithm.Ed25519ph, context);
+            }
+        }
+
+        private ISigner CreateSigner(Ed25519.Algorithm algorithm, byte[] context)
+        {
+            switch (algorithm)
+            {
+            case Ed25519.Algorithm.Ed25519:
+                return new Ed25519Signer();
+            case Ed25519.Algorithm.Ed25519ctx:
+                return new Ed25519ctxSigner(context);
+            case Ed25519.Algorithm.Ed25519ph:
+                return new Ed25519phSigner(context);
+            default:
+                throw new ArgumentException("algorithm");
+            }
+        }
+
+        private byte[] RandomContext(int length)
+        {
+            byte[] context = new byte[length];
+            Random.NextBytes(context);
+            return context;
+        }
+
+        private void DoTestConsistency(Ed25519.Algorithm algorithm, byte[] context)
+        {
+            Ed25519KeyPairGenerator kpg = new Ed25519KeyPairGenerator();
+            kpg.Init(new Ed25519KeyGenerationParameters(Random));
+
+            AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair();
+            Ed25519PrivateKeyParameters privateKey = (Ed25519PrivateKeyParameters)kp.Private;
+            Ed25519PublicKeyParameters publicKey = (Ed25519PublicKeyParameters)kp.Public;
+
+            byte[] msg = new byte[Random.NextInt() & 255];
+            Random.NextBytes(msg);
+
+            ISigner signer = CreateSigner(algorithm, context);
+            signer.Init(true, privateKey);
+            signer.BlockUpdate(msg, 0, msg.Length);
+            byte[] signature = signer.GenerateSignature();
+
+            ISigner verifier = CreateSigner(algorithm, context);
+            verifier.Init(false, publicKey);
+            verifier.BlockUpdate(msg, 0, msg.Length);
+            bool shouldVerify = verifier.VerifySignature(signature);
+
+            if (!shouldVerify)
+            {
+                Fail("Ed25519(" + algorithm + ") signature failed to verify");
+            }
+
+            signature[Random.Next() % signature.Length] ^= (byte)(1 << (Random.NextInt() & 7));
+
+            verifier.Init(false, publicKey);
+            verifier.BlockUpdate(msg, 0, msg.Length);
+            bool shouldNotVerify = verifier.VerifySignature(signature);
+
+            if (shouldNotVerify)
+            {
+                Fail("Ed25519(" + algorithm + ") bad signature incorrectly verified");
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/Ed448Test.cs b/crypto/test/src/crypto/test/Ed448Test.cs
new file mode 100644
index 000000000..b035f554e
--- /dev/null
+++ b/crypto/test/src/crypto/test/Ed448Test.cs
@@ -0,0 +1,107 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Math.EC.Rfc8032;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    [TestFixture]
+    public class Ed448Test
+        : SimpleTest
+    {
+        private static readonly SecureRandom Random = new SecureRandom();
+
+        public override string Name
+        {
+            get { return "Ed448"; }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new Ed448Test());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+
+        public override void PerformTest()
+        {
+            for (int i = 0; i < 10; ++i)
+            {
+                byte[] context = RandomContext(Random.NextInt() & 255);
+                DoTestConsistency(Ed448.Algorithm.Ed448, context);
+                DoTestConsistency(Ed448.Algorithm.Ed448ph, context);
+            }
+        }
+
+        private ISigner CreateSigner(Ed448.Algorithm algorithm, byte[] context)
+        {
+            switch (algorithm)
+            {
+                case Ed448.Algorithm.Ed448:
+                    return new Ed448Signer(context);
+                case Ed448.Algorithm.Ed448ph:
+                    return new Ed448phSigner(context);
+                default:
+                    throw new ArgumentException("algorithm");
+            }
+        }
+
+        private byte[] RandomContext(int length)
+        {
+            byte[] context = new byte[length];
+            Random.NextBytes(context);
+            return context;
+        }
+
+        private void DoTestConsistency(Ed448.Algorithm algorithm, byte[] context)
+        {
+            Ed448KeyPairGenerator kpg = new Ed448KeyPairGenerator();
+            kpg.Init(new Ed448KeyGenerationParameters(Random));
+
+            AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair();
+            Ed448PrivateKeyParameters privateKey = (Ed448PrivateKeyParameters)kp.Private;
+            Ed448PublicKeyParameters publicKey = (Ed448PublicKeyParameters)kp.Public;
+
+            byte[] msg = new byte[Random.NextInt() & 255];
+            Random.NextBytes(msg);
+
+            ISigner signer = CreateSigner(algorithm, context);
+            signer.Init(true, privateKey);
+            signer.BlockUpdate(msg, 0, msg.Length);
+            byte[] signature = signer.GenerateSignature();
+
+            ISigner verifier = CreateSigner(algorithm, context);
+            verifier.Init(false, publicKey);
+            verifier.BlockUpdate(msg, 0, msg.Length);
+            bool shouldVerify = verifier.VerifySignature(signature);
+
+            if (!shouldVerify)
+            {
+                Fail("Ed448(" + algorithm + ") signature failed to verify");
+            }
+
+            signature[Random.Next() % signature.Length] ^= (byte)(1 << (Random.NextInt() & 7));
+
+            verifier.Init(false, publicKey);
+            verifier.BlockUpdate(msg, 0, msg.Length);
+            bool shouldNotVerify = verifier.VerifySignature(signature);
+
+            if (shouldNotVerify)
+            {
+                Fail("Ed448(" + algorithm + ") bad signature incorrectly verified");
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/RFC3211WrapTest.cs b/crypto/test/src/crypto/test/RFC3211WrapTest.cs
index c129bc896..bb9304851 100644
--- a/crypto/test/src/crypto/test/RFC3211WrapTest.cs
+++ b/crypto/test/src/crypto/test/RFC3211WrapTest.cs
@@ -115,7 +115,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 			}
 			catch (InvalidCipherTextException e)
 			{
-				if (!e.Message.Equals("wrapped key fails checksum"))
+                if (!e.Message.Equals("wrapped key corrupted"))
 				{
 					Fail("wrong exception");
 				}
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index 1e2255147..d4bd8a628 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -133,6 +133,11 @@ namespace Org.BouncyCastle.Crypto.Tests
             new SM2EngineTest(),
             new SM2KeyExchangeTest(),
             new SM2SignerTest(),
+            new SM4Test(),
+            new X25519Test(),
+            new X448Test(),
+            new Ed25519Test(),
+            new Ed448Test(),
         };
 
         public static void MainOld(string[] args)
diff --git a/crypto/test/src/crypto/test/SM4Test.cs b/crypto/test/src/crypto/test/SM4Test.cs
new file mode 100644
index 000000000..ae2f18b00
--- /dev/null
+++ b/crypto/test/src/crypto/test/SM4Test.cs
@@ -0,0 +1,93 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    /**
+     * SM4 tester, vectors from <a href="http://eprint.iacr.org/2008/329.pdf">http://eprint.iacr.org/2008/329.pdf</a>
+     */
+	[TestFixture]
+    public class SM4Test
+        : CipherTest
+    {
+        static SimpleTest[] tests =
+        {
+            new BlockCipherVectorTest(0, new SM4Engine(),
+                new KeyParameter(Hex.Decode("0123456789abcdeffedcba9876543210")),
+                "0123456789abcdeffedcba9876543210",
+                "681edf34d206965e86b3e94f536e4246")
+        };
+
+        public SM4Test()
+            : base(tests, new SM4Engine(), new KeyParameter(new byte[16]))
+        {
+        }
+
+        public override void PerformTest()
+        {
+            base.PerformTest();
+
+            DoTest1000000();
+        }
+
+        private void DoTest1000000()
+        {
+            byte[] plain = Hex.Decode("0123456789abcdeffedcba9876543210");
+            byte[] key = Hex.Decode("0123456789abcdeffedcba9876543210");
+            byte[] cipher = Hex.Decode("595298c7c6fd271f0402f804c33d3f66");
+            byte[] buf = new byte[16];
+
+            IBlockCipher engine = new SM4Engine();
+
+            engine.Init(true, new KeyParameter(key));
+
+            Array.Copy(plain, 0, buf, 0, buf.Length);
+
+            for (int i = 0; i != 1000000; i++)
+            {
+                engine.ProcessBlock(buf, 0, buf, 0);
+            }
+
+            if (!AreEqual(cipher, buf))
+            {
+                Fail("1000000 encryption test failed");
+            }
+
+            engine.Init(false, new KeyParameter(key));
+
+            for (int i = 0; i != 1000000; i++)
+            {
+                engine.ProcessBlock(buf, 0, buf, 0);
+            }
+
+            if (!AreEqual(plain, buf))
+            {
+                Fail("1000000 decryption test failed");
+            }
+        }
+
+        public override string Name
+        {
+            get { return "SM4"; }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new SM4Test());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/X25519Test.cs b/crypto/test/src/crypto/test/X25519Test.cs
new file mode 100644
index 000000000..29466e0c6
--- /dev/null
+++ b/crypto/test/src/crypto/test/X25519Test.cs
@@ -0,0 +1,69 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+	[TestFixture]
+	public class X25519Test
+		: SimpleTest
+    {
+        private static readonly SecureRandom Random = new SecureRandom();
+
+        public override string Name
+        {
+            get { return "X25519"; }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new X25519Test());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+
+        public override void PerformTest()
+        {
+            for (int i = 0; i < 10; ++i)
+            {
+                DoTestAgreement();
+            }
+        }
+
+        private void DoTestAgreement()
+        {
+            IAsymmetricCipherKeyPairGenerator kpGen = new X25519KeyPairGenerator();
+            kpGen.Init(new X25519KeyGenerationParameters(Random));
+
+            AsymmetricCipherKeyPair kpA = kpGen.GenerateKeyPair();
+            AsymmetricCipherKeyPair kpB = kpGen.GenerateKeyPair();
+
+            X25519Agreement agreeA = new X25519Agreement();
+            agreeA.Init(kpA.Private);
+            byte[] secretA = new byte[agreeA.AgreementSize];
+            agreeA.CalculateAgreement(kpB.Public, secretA, 0);
+
+            X25519Agreement agreeB = new X25519Agreement();
+            agreeB.Init(kpB.Private);
+            byte[] secretB = new byte[agreeB.AgreementSize];
+            agreeB.CalculateAgreement(kpA.Public, secretB, 0);
+
+            if (!AreEqual(secretA, secretB))
+            {
+                Fail("X25519 agreement failed");
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/X448Test.cs b/crypto/test/src/crypto/test/X448Test.cs
new file mode 100644
index 000000000..5d4b14b63
--- /dev/null
+++ b/crypto/test/src/crypto/test/X448Test.cs
@@ -0,0 +1,69 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    [TestFixture]
+    public class X448Test
+        : SimpleTest
+    {
+        private static readonly SecureRandom Random = new SecureRandom();
+
+        public override string Name
+        {
+            get { return "X448"; }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new X448Test());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+
+        public override void PerformTest()
+        {
+            for (int i = 0; i < 10; ++i)
+            {
+                DoTestAgreement();
+            }
+        }
+
+        private void DoTestAgreement()
+        {
+            IAsymmetricCipherKeyPairGenerator kpGen = new X448KeyPairGenerator();
+            kpGen.Init(new X448KeyGenerationParameters(Random));
+
+            AsymmetricCipherKeyPair kpA = kpGen.GenerateKeyPair();
+            AsymmetricCipherKeyPair kpB = kpGen.GenerateKeyPair();
+
+            X448Agreement agreeA = new X448Agreement();
+            agreeA.Init(kpA.Private);
+            byte[] secretA = new byte[agreeA.AgreementSize];
+            agreeA.CalculateAgreement(kpB.Public, secretA, 0);
+
+            X448Agreement agreeB = new X448Agreement();
+            agreeB.Init(kpB.Private);
+            byte[] secretB = new byte[agreeB.AgreementSize];
+            agreeB.CalculateAgreement(kpA.Public, secretB, 0);
+
+            if (!AreEqual(secretA, secretB))
+            {
+                Fail("X448 agreement failed");
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs b/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs
index 89c325fd5..562e0e423 100644
--- a/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs
+++ b/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs
@@ -22,10 +22,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
         [Test]
         public void TestConsistency()
         {
-            byte[] u = new byte[32];    u[0] = 9;
-            byte[] k = new byte[32];
-            byte[] rF = new byte[32];
-            byte[] rV = new byte[32];
+            byte[] u = new byte[X25519.PointSize];      u[0] = 9;
+            byte[] k = new byte[X25519.ScalarSize];
+            byte[] rF = new byte[X25519.PointSize];
+            byte[] rV = new byte[X25519.PointSize];
 
             for (int i = 1; i <= 100; ++i)
             {
@@ -39,12 +39,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
         [Test]
         public void TestECDH()
         {
-            byte[] kA = new byte[32];
-            byte[] kB = new byte[32];
-            byte[] qA = new byte[32];
-            byte[] qB = new byte[32];
-            byte[] sA = new byte[32];
-            byte[] sB = new byte[32];
+            byte[] kA = new byte[X25519.ScalarSize];
+            byte[] kB = new byte[X25519.ScalarSize];
+            byte[] qA = new byte[X25519.PointSize];
+            byte[] qB = new byte[X25519.PointSize];
+            byte[] sA = new byte[X25519.PointSize];
+            byte[] sB = new byte[X25519.PointSize];
 
             for (int i = 1; i <= 100; ++i)
             {
@@ -116,38 +116,43 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
         private static void CheckECDHVector(string sA, string sAPub, string sB, string sBPub, string sK, string text)
         {
             byte[] a = Hex.Decode(sA);
+            Assert.AreEqual(X25519.ScalarSize, a.Length);
+
             byte[] b = Hex.Decode(sB);
+            Assert.AreEqual(X25519.ScalarSize, b.Length);
 
-            byte[] aPub = new byte[32];
+            byte[] aPub = new byte[X25519.PointSize];
             X25519.ScalarMultBase(a, 0, aPub, 0);
             CheckValue(aPub, text, sAPub);
 
-            byte[] bPub = new byte[32];
+            byte[] bPub = new byte[X25519.PointSize];
             X25519.ScalarMultBase(b, 0, bPub, 0);
             CheckValue(bPub, text, sBPub);
 
-            byte[] aK = new byte[32];
+            byte[] aK = new byte[X25519.PointSize];
             X25519.ScalarMult(a, 0, bPub, 0, aK, 0);
             CheckValue(aK, text, sK);
 
-            byte[] bK = new byte[32];
+            byte[] bK = new byte[X25519.PointSize];
             X25519.ScalarMult(b, 0, aPub, 0, bK, 0);
             CheckValue(bK, text, sK);
         }
 
         private static void CheckIterated(int count)
         {
-            byte[] k = new byte[32]; k[0] = 9;
-            byte[] u = new byte[32]; u[0] = 9;
-            byte[] r = new byte[32];
+            Assert.AreEqual(X25519.PointSize, X25519.ScalarSize);
+
+            byte[] k = new byte[X25519.PointSize];     k[0] = 9;
+            byte[] u = new byte[X25519.PointSize];     u[0] = 9;
+            byte[] r = new byte[X25519.PointSize];
 
             int iterations = 0;
             while (iterations < count)
             {
                 X25519.ScalarMult(k, 0, u, 0, r, 0);
 
-                Array.Copy(k, 0, u, 0, 32);
-                Array.Copy(r, 0, k, 0, 32);
+                Array.Copy(k, 0, u, 0, X25519.PointSize);
+                Array.Copy(r, 0, k, 0, X25519.PointSize);
 
                 switch (++iterations)
                 {
@@ -175,8 +180,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
         private static void CheckX25519Vector(string sk, string su, string se, string text)
         {
             byte[] k = Hex.Decode(sk);
+            Assert.AreEqual(X25519.ScalarSize, k.Length);
+
             byte[] u = Hex.Decode(su);
-            byte[] r = new byte[32];
+            Assert.AreEqual(X25519.PointSize, u.Length);
+
+            byte[] r = new byte[X25519.PointSize];
             X25519.ScalarMult(k, 0, u, 0, r, 0);
             CheckValue(r, text, se);
         }
diff --git a/crypto/test/src/math/ec/rfc7748/test/X448Test.cs b/crypto/test/src/math/ec/rfc7748/test/X448Test.cs
index b095eade0..df0158b96 100644
--- a/crypto/test/src/math/ec/rfc7748/test/X448Test.cs
+++ b/crypto/test/src/math/ec/rfc7748/test/X448Test.cs
@@ -22,10 +22,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
         [Test]
         public void TestConsistency()
         {
-            byte[] u = new byte[56];    u[0] = 5;
-            byte[] k = new byte[56];
-            byte[] rF = new byte[56];
-            byte[] rV = new byte[56];
+            byte[] u = new byte[X448.PointSize];       u[0] = 5;
+            byte[] k = new byte[X448.ScalarSize];
+            byte[] rF = new byte[X448.PointSize];
+            byte[] rV = new byte[X448.PointSize];
 
             for (int i = 1; i <= 100; ++i)
             {
@@ -39,12 +39,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
         [Test]
         public void TestECDH()
         {
-            byte[] kA = new byte[56];
-            byte[] kB = new byte[56];
-            byte[] qA = new byte[56];
-            byte[] qB = new byte[56];
-            byte[] sA = new byte[56];
-            byte[] sB = new byte[56];
+            byte[] kA = new byte[X448.ScalarSize];
+            byte[] kB = new byte[X448.ScalarSize];
+            byte[] qA = new byte[X448.PointSize];
+            byte[] qB = new byte[X448.PointSize];
+            byte[] sA = new byte[X448.PointSize];
+            byte[] sB = new byte[X448.PointSize];
 
             for (int i = 1; i <= 100; ++i)
             {
@@ -112,38 +112,43 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
         private static void CheckECDHVector(string sA, string sAPub, string sB, string sBPub, string sK, string text)
         {
             byte[] a = Hex.Decode(sA);
+            Assert.AreEqual(X448.ScalarSize, a.Length);
+
             byte[] b = Hex.Decode(sB);
+            Assert.AreEqual(X448.ScalarSize, b.Length);
 
-            byte[] aPub = new byte[56];
+            byte[] aPub = new byte[X448.PointSize];
             X448.ScalarMultBase(a, 0, aPub, 0);
             CheckValue(aPub, text, sAPub);
 
-            byte[] bPub = new byte[56];
+            byte[] bPub = new byte[X448.PointSize];
             X448.ScalarMultBase(b, 0, bPub, 0);
             CheckValue(bPub, text, sBPub);
 
-            byte[] aK = new byte[56];
+            byte[] aK = new byte[X448.PointSize];
             X448.ScalarMult(a, 0, bPub, 0, aK, 0);
             CheckValue(aK, text, sK);
 
-            byte[] bK = new byte[56];
+            byte[] bK = new byte[X448.PointSize];
             X448.ScalarMult(b, 0, aPub, 0, bK, 0);
             CheckValue(bK, text, sK);
         }
 
         private static void CheckIterated(int count)
         {
-            byte[] k = new byte[56]; k[0] = 5;
-            byte[] u = new byte[56]; u[0] = 5;
-            byte[] r = new byte[56];
+            Assert.AreEqual(X448.PointSize, X448.ScalarSize);
+
+            byte[] k = new byte[X448.PointSize];    k[0] = 5;
+            byte[] u = new byte[X448.PointSize];    u[0] = 5;
+            byte[] r = new byte[X448.PointSize];
 
             int iterations = 0;
             while (iterations < count)
             {
                 X448.ScalarMult(k, 0, u, 0, r, 0);
 
-                Array.Copy(k, 0, u, 0, 56);
-                Array.Copy(r, 0, k, 0, 56);
+                Array.Copy(k, 0, u, 0, X448.PointSize);
+                Array.Copy(r, 0, k, 0, X448.PointSize);
 
                 switch (++iterations)
                 {
@@ -165,17 +170,21 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
             }
         }
 
-        private static void CheckValue(byte[] n, String text, String se)
+        private static void CheckValue(byte[] n, string text, string se)
         {
             byte[] e = Hex.Decode(se);
             Assert.IsTrue(Arrays.AreEqual(e, n), text);
         }
 
-        private static void CheckX448Vector(String sk, String su, String se, String text)
+        private static void CheckX448Vector(string sk, string su, string se, string text)
         {
             byte[] k = Hex.Decode(sk);
+            Assert.AreEqual(X448.ScalarSize, k.Length);
+
             byte[] u = Hex.Decode(su);
-            byte[] r = new byte[56];
+            Assert.AreEqual(X448.PointSize, u.Length);
+
+            byte[] r = new byte[X448.PointSize];
             X448.ScalarMult(k, 0, u, 0, r, 0);
             CheckValue(r, text, se);
         }
diff --git a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs
index 43ea23988..8a61609af 100644
--- a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs
+++ b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs
@@ -2,6 +2,7 @@
 
 using NUnit.Framework;
 
+using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
@@ -35,21 +36,98 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
 				Random.NextBytes(sk);
                 Ed25519.GeneratePublicKey(sk, 0, pk, 0);
 
-                int mLen = Random.Next() & 255;
+                int mLen = Random.NextInt() & 255;
 
                 Ed25519.Sign(sk, 0, m, 0, mLen, sig1, 0);
                 Ed25519.Sign(sk, 0, pk, 0, m, 0, mLen, sig2, 0);
 
-                Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Consistent signatures #" + i);
+                Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed25519 consistent signatures #" + i);
 
                 bool shouldVerify = Ed25519.Verify(sig1, 0, pk, 0, m, 0, mLen);
 
-                Assert.IsTrue(shouldVerify, "Consistent sign/verify #" + i);
+                Assert.IsTrue(shouldVerify, "Ed25519 consistent sign/verify #" + i);
 
                 sig1[Ed25519.PublicKeySize - 1] ^= 0x80;
                 bool shouldNotVerify = Ed25519.Verify(sig1, 0, pk, 0, m, 0, mLen);
 
-                Assert.IsFalse(shouldNotVerify, "Consistent verification failure #" + i);
+                Assert.IsFalse(shouldNotVerify, "Ed25519 consistent verification failure #" + i);
+            }
+        }
+
+        [Test]
+        public void TestEd25519ctxConsistency()
+        {
+            byte[] sk = new byte[Ed25519.SecretKeySize];
+            byte[] pk = new byte[Ed25519.PublicKeySize];
+            byte[] ctx = new byte[Random.NextInt() & 7];
+            byte[] m = new byte[255];
+            byte[] sig1 = new byte[Ed25519.SignatureSize];
+            byte[] sig2 = new byte[Ed25519.SignatureSize];
+
+            Random.NextBytes(ctx);
+            Random.NextBytes(m);
+
+            for (int i = 0; i < 10; ++i)
+            {
+                Random.NextBytes(sk);
+                Ed25519.GeneratePublicKey(sk, 0, pk, 0);
+
+                int mLen = Random.NextInt() & 255;
+
+                Ed25519.Sign(sk, 0, ctx, m, 0, mLen, sig1, 0);
+                Ed25519.Sign(sk, 0, pk, 0, ctx, m, 0, mLen, sig2, 0);
+
+                Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed25519ctx consistent signatures #" + i);
+
+                bool shouldVerify = Ed25519.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen);
+
+                Assert.IsTrue(shouldVerify, "Ed25519ctx consistent sign/verify #" + i);
+
+                sig1[Ed25519.PublicKeySize - 1] ^= 0x80;
+                bool shouldNotVerify = Ed25519.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen);
+
+                Assert.IsFalse(shouldNotVerify, "Ed25519ctx consistent verification failure #" + i);
+            }
+        }
+
+        [Test]
+        public void TestEd25519phConsistency()
+        {
+            byte[] sk = new byte[Ed25519.SecretKeySize];
+            byte[] pk = new byte[Ed25519.PublicKeySize];
+            byte[] ctx = new byte[Random.NextInt() & 7];
+            byte[] m = new byte[255];
+            byte[] ph = new byte[Ed25519.PrehashSize];
+            byte[] sig1 = new byte[Ed25519.SignatureSize];
+            byte[] sig2 = new byte[Ed25519.SignatureSize];
+
+            Random.NextBytes(ctx);
+            Random.NextBytes(m);
+
+            for (int i = 0; i < 10; ++i)
+            {
+                Random.NextBytes(sk);
+                Ed25519.GeneratePublicKey(sk, 0, pk, 0);
+
+                int mLen = Random.NextInt() & 255;
+
+                IDigest prehash = Ed25519.CreatePrehash();
+                prehash.BlockUpdate(m, 0, mLen);
+                prehash.DoFinal(ph, 0);
+
+                Ed25519.SignPrehash(sk, 0, ctx, ph, 0, sig1, 0);
+                Ed25519.SignPrehash(sk, 0, pk, 0, ctx, ph, 0, sig2, 0);
+
+                Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed25519ph consistent signatures #" + i);
+
+                bool shouldVerify = Ed25519.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0);
+
+                Assert.IsTrue(shouldVerify, "Ed25519ph consistent sign/verify #" + i);
+
+                sig1[Ed25519.PublicKeySize - 1] ^= 0x80;
+                bool shouldNotVerify = Ed25519.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0);
+
+                Assert.IsFalse(shouldNotVerify, "Ed25519ph consistent verification failure #" + i);
             }
         }
 
@@ -201,18 +279,107 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
                 + "3dca179c138ac17ad9bef1177331a704"),
                 "Ed25519 Vector SHA(abc)");
         }
-      
+
+        [Test]
+        public void TestEd25519ctxVector1()
+        {
+            CheckEd25519ctxVector(
+                ("0305334e381af78f141cb666f6199f57"
+                + "bc3495335a256a95bd2a55bf546663f6"),
+                ("dfc9425e4f968f7f0c29f0259cf5f9ae"
+                + "d6851c2bb4ad8bfb860cfee0ab248292"),
+                "f726936d19c800494e3fdaff20b276a8",
+                "666f6f",
+                ("55a4cc2f70a54e04288c5f4cd1e45a7b"
+                + "b520b36292911876cada7323198dd87a"
+                + "8b36950b95130022907a7fb7c4e9b2d5"
+                + "f6cca685a587b4b21f4b888e4e7edb0d"),
+                "Ed25519ctx Vector #1");
+        }
+
+        [Test]
+        public void TestEd25519ctxVector2()
+        {
+            CheckEd25519ctxVector(
+                ("0305334e381af78f141cb666f6199f57"
+                + "bc3495335a256a95bd2a55bf546663f6"),
+                ("dfc9425e4f968f7f0c29f0259cf5f9ae"
+                + "d6851c2bb4ad8bfb860cfee0ab248292"),
+                "f726936d19c800494e3fdaff20b276a8",
+                "626172",
+                ("fc60d5872fc46b3aa69f8b5b4351d580"
+                + "8f92bcc044606db097abab6dbcb1aee3"
+                + "216c48e8b3b66431b5b186d1d28f8ee1"
+                + "5a5ca2df6668346291c2043d4eb3e90d"),
+                "Ed25519ctx Vector #2");
+        }
+
+        [Test]
+        public void TestEd25519ctxVector3()
+        {
+            CheckEd25519ctxVector(
+                ("0305334e381af78f141cb666f6199f57"
+                + "bc3495335a256a95bd2a55bf546663f6"),
+                ("dfc9425e4f968f7f0c29f0259cf5f9ae"
+                + "d6851c2bb4ad8bfb860cfee0ab248292"),
+                "508e9e6882b979fea900f62adceaca35",
+                "666f6f",
+                ("8b70c1cc8310e1de20ac53ce28ae6e72"
+                + "07f33c3295e03bb5c0732a1d20dc6490"
+                + "8922a8b052cf99b7c4fe107a5abb5b2c"
+                + "4085ae75890d02df26269d8945f84b0b"),
+                "Ed25519ctx Vector #3");
+        }
+
+        [Test]
+        public void TestEd25519ctxVector4()
+        {
+            CheckEd25519ctxVector(
+                ("ab9c2853ce297ddab85c993b3ae14bca"
+                + "d39b2c682beabc27d6d4eb20711d6560"),
+                ("0f1d1274943b91415889152e893d80e9"
+                + "3275a1fc0b65fd71b4b0dda10ad7d772"),
+                "f726936d19c800494e3fdaff20b276a8",
+                "666f6f",
+                ("21655b5f1aa965996b3f97b3c849eafb"
+                + "a922a0a62992f73b3d1b73106a84ad85"
+                + "e9b86a7b6005ea868337ff2d20a7f5fb"
+                + "d4cd10b0be49a68da2b2e0dc0ad8960f"),
+                "Ed25519ctx Vector #4");
+        }
+
+        [Test]
+        public void TestEd25519phVector1()
+        {
+            CheckEd25519phVector(
+                ("833fe62409237b9d62ec77587520911e"
+                + "9a759cec1d19755b7da901b96dca3d42"),
+                ("ec172b93ad5e563bf4932c70e1245034"
+                + "c35467ef2efd4d64ebf819683467e2bf"),
+                "616263",
+                "",
+                ("98a70222f0b8121aa9d30f813d683f80"
+                + "9e462b469c7ff87639499bb94e6dae41"
+                + "31f85042463c2a355a2003d062adf5aa"
+                + "a10b8c61e636062aaad11c2a26083406"),
+                "Ed25519ph Vector #1");
+        }
+
         private static void CheckEd25519Vector(string sSK, string sPK, string sM, string sSig, string text)
         {
             byte[] sk = Hex.Decode(sSK);
-
             byte[] pk = Hex.Decode(sPK);
+
             byte[] pkGen = new byte[Ed25519.PublicKeySize];
             Ed25519.GeneratePublicKey(sk, 0, pkGen, 0);
             Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text);
          
 			byte[] m = Hex.Decode(sM);
             byte[] sig = Hex.Decode(sSig);
+
+            byte[] badsig = Arrays.Clone(sig);
+            badsig[Ed25519.SignatureSize - 1] ^= 0x80;
+
             byte[] sigGen = new byte[Ed25519.SignatureSize];
             Ed25519.Sign(sk, 0, m, 0, m.Length, sigGen, 0);
             Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
@@ -223,9 +390,109 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
             bool shouldVerify = Ed25519.Verify(sig, 0, pk, 0, m, 0, m.Length);
             Assert.IsTrue(shouldVerify, text);
 
-			sig[Ed25519.SignatureSize - 1] ^= 0x80;
-            bool shouldNotVerify = Ed25519.Verify(sig, 0, pk, 0, m, 0, m.Length);
+            bool shouldNotVerify = Ed25519.Verify(badsig, 0, pk, 0, m, 0, m.Length);
+            Assert.IsFalse(shouldNotVerify, text);
+        }
+
+        private static void CheckEd25519ctxVector(string sSK, string sPK, string sM, string sCTX, string sSig, string text)
+        {
+            byte[] sk = Hex.Decode(sSK);
+            byte[] pk = Hex.Decode(sPK);
+
+            byte[] pkGen = new byte[Ed25519.PublicKeySize];
+            Ed25519.GeneratePublicKey(sk, 0, pkGen, 0);
+            Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text);
+
+            byte[] m = Hex.Decode(sM);
+            byte[] ctx = Hex.Decode(sCTX);
+            byte[] sig = Hex.Decode(sSig);
+
+            byte[] badsig = Arrays.Clone(sig);
+            badsig[Ed25519.SignatureSize - 1] ^= 0x80;
+
+            byte[] sigGen = new byte[Ed25519.SignatureSize];
+            Ed25519.Sign(sk, 0, ctx, m, 0, m.Length, sigGen, 0);
+            Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+
+            Ed25519.Sign(sk, 0, pk, 0, ctx, m, 0, m.Length, sigGen, 0);
+            Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+
+            bool shouldVerify = Ed25519.Verify(sig, 0, pk, 0, ctx, m, 0, m.Length);
+            Assert.IsTrue(shouldVerify, text);
+
+            bool shouldNotVerify = Ed25519.Verify(badsig, 0, pk, 0, ctx, m, 0, m.Length);
             Assert.IsFalse(shouldNotVerify, text);
         }
+
+        private static void CheckEd25519phVector(string sSK, string sPK, string sM, string sCTX, string sSig, string text)
+        {
+            byte[] sk = Hex.Decode(sSK);
+            byte[] pk = Hex.Decode(sPK);
+
+            byte[] pkGen = new byte[Ed25519.PublicKeySize];
+            Ed25519.GeneratePublicKey(sk, 0, pkGen, 0);
+            Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text);
+
+            byte[] m = Hex.Decode(sM);
+            byte[] ctx = Hex.Decode(sCTX);
+            byte[] sig = Hex.Decode(sSig);
+
+            byte[] badsig = Arrays.Clone(sig);
+            badsig[Ed25519.SignatureSize - 1] ^= 0x80;
+
+            byte[] sigGen = new byte[Ed25519.SignatureSize];
+
+            {
+                IDigest prehash = Ed25519.CreatePrehash();
+                prehash.BlockUpdate(m, 0, m.Length);
+
+                byte[] ph = new byte[Ed25519.PrehashSize];
+                prehash.DoFinal(ph, 0);
+
+                Ed25519.SignPrehash(sk, 0, ctx, ph, 0, sigGen, 0);
+                Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+
+                Ed25519.SignPrehash(sk, 0, pk, 0, ctx, ph, 0, sigGen, 0);
+                Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+
+                bool shouldVerify = Ed25519.VerifyPrehash(sig, 0, pk, 0, ctx, ph, 0);
+                Assert.IsTrue(shouldVerify, text);
+
+                bool shouldNotVerify = Ed25519.VerifyPrehash(badsig, 0, pk, 0, ctx, ph, 0);
+                Assert.IsFalse(shouldNotVerify, text);
+            }
+
+            {
+                IDigest ph = Ed25519.CreatePrehash();
+                ph.BlockUpdate(m, 0, m.Length);
+
+                Ed25519.SignPrehash(sk, 0, ctx, ph, sigGen, 0);
+                Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+            }
+
+            {
+                IDigest ph = Ed25519.CreatePrehash();
+                ph.BlockUpdate(m, 0, m.Length);
+
+                Ed25519.SignPrehash(sk, 0, pk, 0, ctx, ph, sigGen, 0);
+                Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+            }
+
+            {
+                IDigest ph = Ed25519.CreatePrehash();
+                ph.BlockUpdate(m, 0, m.Length);
+
+                bool shouldVerify = Ed25519.VerifyPrehash(sig, 0, pk, 0, ctx, ph);
+                Assert.IsTrue(shouldVerify, text);
+            }
+
+            {
+                IDigest ph = Ed25519.CreatePrehash();
+                ph.BlockUpdate(m, 0, m.Length);
+
+                bool shouldNotVerify = Ed25519.VerifyPrehash(badsig, 0, pk, 0, ctx, ph);
+                Assert.IsFalse(shouldNotVerify, text);
+            }
+        }
     }
 }
diff --git a/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs b/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs
index 98c487c09..cc8e82de0 100644
--- a/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs
+++ b/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs
@@ -2,6 +2,7 @@
 
 using NUnit.Framework;
 
+using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
@@ -24,7 +25,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
         {
             byte[] sk = new byte[Ed448.SecretKeySize];
             byte[] pk = new byte[Ed448.PublicKeySize];
-            byte[] ctx = new byte[Random.Next() & 7];
+            byte[] ctx = new byte[Random.NextInt() & 7];
             byte[] m = new byte[255];
             byte[] sig1 = new byte[Ed448.SignatureSize];
             byte[] sig2 = new byte[Ed448.SignatureSize];
@@ -37,21 +38,62 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
                 Random.NextBytes(sk);
                 Ed448.GeneratePublicKey(sk, 0, pk, 0);
 
-                int mLen = Random.Next() & 255;
+                int mLen = Random.NextInt() & 255;
 
                 Ed448.Sign(sk, 0, ctx, m, 0, mLen, sig1, 0);
                 Ed448.Sign(sk, 0, pk, 0, ctx, m, 0, mLen, sig2, 0);
 
-                Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Consistent signatures #" + i);
+                Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed448 consistent signatures #" + i);
 
                 bool shouldVerify = Ed448.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen);
 
-                Assert.IsTrue(shouldVerify, "Consistent sign/verify #" + i);
+                Assert.IsTrue(shouldVerify, "Ed448 consistent sign/verify #" + i);
 
                 sig1[Ed448.PublicKeySize - 1] ^= 0x80;
                 bool shouldNotVerify = Ed448.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen);
 
-                Assert.IsFalse(shouldNotVerify, "Consistent verification failure #" + i);
+                Assert.IsFalse(shouldNotVerify, "Ed448 consistent verification failure #" + i);
+            }
+        }
+
+        [Test]
+        public void TestEd448phConsistency()
+        {
+            byte[] sk = new byte[Ed448.SecretKeySize];
+            byte[] pk = new byte[Ed448.PublicKeySize];
+            byte[] ctx = new byte[Random.NextInt() & 7];
+            byte[] m = new byte[255];
+            byte[] ph = new byte[Ed448.PrehashSize];
+            byte[] sig1 = new byte[Ed448.SignatureSize];
+            byte[] sig2 = new byte[Ed448.SignatureSize];
+
+            Random.NextBytes(ctx);
+            Random.NextBytes(m);
+
+            for (int i = 0; i < 10; ++i)
+            {
+                Random.NextBytes(sk);
+                Ed448.GeneratePublicKey(sk, 0, pk, 0);
+
+                int mLen = Random.NextInt() & 255;
+
+                IXof prehash = Ed448.CreatePrehash();
+                prehash.BlockUpdate(m, 0, mLen);
+                prehash.DoFinal(ph, 0, ph.Length);
+
+                Ed448.SignPrehash(sk, 0, ctx, ph, 0, sig1, 0);
+                Ed448.SignPrehash(sk, 0, pk, 0, ctx, ph, 0, sig2, 0);
+
+                Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed448ph consistent signatures #" + i);
+
+                bool shouldVerify = Ed448.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0);
+
+                Assert.IsTrue(shouldVerify, "Ed448ph consistent sign/verify #" + i);
+
+                sig1[Ed448.PublicKeySize - 1] ^= 0x80;
+                bool shouldNotVerify = Ed448.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0);
+
+                Assert.IsFalse(shouldNotVerify, "Ed448ph consistent verification failure #" + i);
             }
         }
 
@@ -370,11 +412,61 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
                 "Ed448 Vector #1023");
         }
 
+        [Test]
+        public void TestEd448phVector1()
+        {
+            CheckEd448phVector(
+                ("833fe62409237b9d62ec77587520911e"
+                + "9a759cec1d19755b7da901b96dca3d42"
+                + "ef7822e0d5104127dc05d6dbefde69e3"
+                + "ab2cec7c867c6e2c49"),
+                ("259b71c19f83ef77a7abd26524cbdb31"
+                + "61b590a48f7d17de3ee0ba9c52beb743"
+                + "c09428a131d6b1b57303d90d8132c276"
+                + "d5ed3d5d01c0f53880"),
+                "616263",
+                "",
+                ("822f6901f7480f3d5f562c592994d969"
+                + "3602875614483256505600bbc281ae38"
+                + "1f54d6bce2ea911574932f52a4e6cadd"
+                + "78769375ec3ffd1b801a0d9b3f4030cd"
+                + "433964b6457ea39476511214f97469b5"
+                + "7dd32dbc560a9a94d00bff07620464a3"
+                + "ad203df7dc7ce360c3cd3696d9d9fab9"
+                + "0f00"),
+                "Ed448ph Vector #1");
+        }
+
+        [Test]
+        public void TestEd448phVector2()
+        {
+            CheckEd448phVector(
+                ("833fe62409237b9d62ec77587520911e"
+                + "9a759cec1d19755b7da901b96dca3d42"
+                + "ef7822e0d5104127dc05d6dbefde69e3"
+                + "ab2cec7c867c6e2c49"),
+                ("259b71c19f83ef77a7abd26524cbdb31"
+                + "61b590a48f7d17de3ee0ba9c52beb743"
+                + "c09428a131d6b1b57303d90d8132c276"
+                + "d5ed3d5d01c0f53880"),
+                "616263",
+                "666f6f",
+                ("c32299d46ec8ff02b54540982814dce9"
+                + "a05812f81962b649d528095916a2aa48"
+                + "1065b1580423ef927ecf0af5888f90da"
+                + "0f6a9a85ad5dc3f280d91224ba9911a3"
+                + "653d00e484e2ce232521481c8658df30"
+                + "4bb7745a73514cdb9bf3e15784ab7128"
+                + "4f8d0704a608c54a6b62d97beb511d13"
+                + "2100"),
+                "Ed448ph Vector #2");
+        }
+
         private static void CheckEd448Vector(string sSK, string sPK, string sM, string sCTX, string sSig, string text)
         {
             byte[] sk = Hex.Decode(sSK);
-
             byte[] pk = Hex.Decode(sPK);
+
             byte[] pkGen = new byte[Ed448.PublicKeySize];
             Ed448.GeneratePublicKey(sk, 0, pkGen, 0);
             Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text);
@@ -383,6 +475,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
             byte[] ctx = Hex.Decode(sCTX);
             byte[] sig = Hex.Decode(sSig);
             byte[] sigGen = new byte[Ed448.SignatureSize];
+
+            byte[] badsig = Arrays.Clone(sig);
+            badsig[Ed448.SignatureSize - 1] ^= 0x80;
+
             Ed448.Sign(sk, 0, ctx, m, 0, m.Length, sigGen, 0);
             Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
 
@@ -392,9 +488,79 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests
             bool shouldVerify = Ed448.Verify(sig, 0, pk, 0, ctx, m, 0, m.Length);
             Assert.IsTrue(shouldVerify, text);
 
-            sig[Ed448.SignatureSize - 1] ^= 0x80;
-            bool shouldNotVerify = Ed448.Verify(sig, 0, pk, 0, ctx, m, 0, m.Length);
+            bool shouldNotVerify = Ed448.Verify(badsig, 0, pk, 0, ctx, m, 0, m.Length);
             Assert.IsFalse(shouldNotVerify, text);
         }
+
+        private static void CheckEd448phVector(string sSK, string sPK, string sM, string sCTX, string sSig, string text)
+        {
+            byte[] sk = Hex.Decode(sSK);
+            byte[] pk = Hex.Decode(sPK);
+
+            byte[] pkGen = new byte[Ed448.PublicKeySize];
+            Ed448.GeneratePublicKey(sk, 0, pkGen, 0);
+            Assert.IsTrue(Arrays.AreEqual(pk, pkGen), text);
+
+            byte[] m = Hex.Decode(sM);
+            byte[] ctx = Hex.Decode(sCTX);
+            byte[] sig = Hex.Decode(sSig);
+
+            byte[] badsig = Arrays.Clone(sig);
+            badsig[Ed448.SignatureSize - 1] ^= 0x80;
+
+            byte[] sigGen = new byte[Ed448.SignatureSize];
+
+            {
+                IXof prehash = Ed448.CreatePrehash();
+                prehash.BlockUpdate(m, 0, m.Length);
+
+                byte[] ph = new byte[Ed448.PrehashSize];
+                prehash.DoFinal(ph, 0, ph.Length);
+
+                Ed448.SignPrehash(sk, 0, ctx, ph, 0, sigGen, 0);
+                Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+
+                Ed448.SignPrehash(sk, 0, pk, 0, ctx, ph, 0, sigGen, 0);
+                Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+
+                bool shouldVerify = Ed448.VerifyPrehash(sig, 0, pk, 0, ctx, ph, 0);
+                Assert.IsTrue(shouldVerify, text);
+
+                bool shouldNotVerify = Ed448.VerifyPrehash(badsig, 0, pk, 0, ctx, ph, 0);
+                Assert.IsFalse(shouldNotVerify, text);
+            }
+
+            {
+                IXof ph = Ed448.CreatePrehash();
+                ph.BlockUpdate(m, 0, m.Length);
+
+                Ed448.SignPrehash(sk, 0, ctx, ph, sigGen, 0);
+                Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+            }
+
+            {
+                IXof ph = Ed448.CreatePrehash();
+                ph.BlockUpdate(m, 0, m.Length);
+
+                Ed448.SignPrehash(sk, 0, pk, 0, ctx, ph, sigGen, 0);
+                Assert.IsTrue(Arrays.AreEqual(sig, sigGen), text);
+            }
+
+            {
+                IXof ph = Ed448.CreatePrehash();
+                ph.BlockUpdate(m, 0, m.Length);
+
+                bool shouldVerify = Ed448.VerifyPrehash(sig, 0, pk, 0, ctx, ph);
+                Assert.IsTrue(shouldVerify, text);
+            }
+
+            {
+                IXof ph = Ed448.CreatePrehash();
+                ph.BlockUpdate(m, 0, m.Length);
+
+                bool shouldNotVerify = Ed448.VerifyPrehash(badsig, 0, pk, 0, ctx, ph);
+                Assert.IsFalse(shouldNotVerify, text);
+            }
+        }
     }
 }
diff --git a/crypto/test/src/openpgp/examples/DirectKeySignature.cs b/crypto/test/src/openpgp/examples/DirectKeySignature.cs
index a6bf8e755..3926a787b 100644
--- a/crypto/test/src/openpgp/examples/DirectKeySignature.cs
+++ b/crypto/test/src/openpgp/examples/DirectKeySignature.cs
@@ -38,11 +38,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples
 					Console.WriteLine("Signature date is: "
 						+ sig.GetHashedSubPackets().GetSignatureCreationTime());
 
-					NotationData[] data = sig.GetHashedSubPackets().GetNotationDataOccurences();
+					NotationData[] data = sig.GetHashedSubPackets().GetNotationDataOccurrences();
 
 					for (int i = 0; i < data.Length; i++)
 					{
-						Console.WriteLine("Found Notaion named '" + data[i].GetNotationName()
+						Console.WriteLine("Found Notation named '" + data[i].GetNotationName()
 							+"' with content '" + data[i].GetNotationValue() + "'.");
 					}
 				}
diff --git a/crypto/test/src/security/test/TestSignerUtil.cs b/crypto/test/src/security/test/TestSignerUtil.cs
index 144ab67b1..afd85ff4d 100644
--- a/crypto/test/src/security/test/TestSignerUtil.cs
+++ b/crypto/test/src/security/test/TestSignerUtil.cs
@@ -94,6 +94,14 @@ namespace Org.BouncyCastle.Security.Tests
 
             AsymmetricCipherKeyPair ecGostPair = ecGostKpg.GenerateKeyPair();
 
+            IAsymmetricCipherKeyPairGenerator ed25519Kpg = GeneratorUtilities.GetKeyPairGenerator("Ed25519");
+            ed25519Kpg.Init(new Ed25519KeyGenerationParameters(new SecureRandom()));
+            AsymmetricCipherKeyPair ed25519Pair = ed25519Kpg.GenerateKeyPair();
+
+            IAsymmetricCipherKeyPairGenerator ed448Kpg = GeneratorUtilities.GetKeyPairGenerator("Ed448");
+            ed448Kpg.Init(new Ed448KeyGenerationParameters(new SecureRandom()));
+            AsymmetricCipherKeyPair ed448Pair = ed448Kpg.GenerateKeyPair();
+
             //
             // GOST3410 parameters
             //
@@ -147,6 +155,16 @@ namespace Org.BouncyCastle.Security.Tests
                     signParams = ecGostPair.Private;
                     verifyParams = ecGostPair.Public;
                 }
+                else if (cipherName == "ED25519")
+                {
+                    signParams = ed25519Pair.Private;
+                    verifyParams = ed25519Pair.Public;
+                }
+                else if (cipherName == "ED448")
+                {
+                    signParams = ed448Pair.Private;
+                    verifyParams = ed448Pair.Public;
+                }
                 else if (cipherName == "GOST3410")
                 {
                     signParams = gostPair.Private;
diff --git a/crypto/test/src/test/RegressionTest.cs b/crypto/test/src/test/RegressionTest.cs
index 9b55dffa6..6d323e84f 100644
--- a/crypto/test/src/test/RegressionTest.cs
+++ b/crypto/test/src/test/RegressionTest.cs
@@ -64,6 +64,7 @@ namespace Org.BouncyCastle.Tests
 			new MqvTest(),
 			new CMacTest(),
 			new Crl5Test(),
+            new SM4Test()
 		};
 
 		public static void MainOld(
diff --git a/crypto/test/src/test/SM4Test.cs b/crypto/test/src/test/SM4Test.cs
new file mode 100644
index 000000000..5d36b3431
--- /dev/null
+++ b/crypto/test/src/test/SM4Test.cs
@@ -0,0 +1,149 @@
+using System;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tests
+{
+    /**
+     * basic test class for SM4
+     */
+	[TestFixture]
+    public class SM4Test
+        : BaseBlockCipherTest
+    {
+        internal static readonly string[] cipherTests =
+        {
+            "128",
+            "0123456789abcdeffedcba9876543210",
+            "0123456789abcdeffedcba9876543210",
+            "681edf34d206965e86b3e94f536e4246"
+        };
+
+        public SM4Test()
+            : base("SM4")
+        {
+        }
+
+        public void DoTest(
+            int         strength,
+            byte[]      keyBytes,
+            byte[]      input,
+            byte[]      output)
+        {
+            KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
+
+            IBufferedCipher inCipher = CipherUtilities.GetCipher("SM4/ECB/NoPadding");
+            IBufferedCipher outCipher = CipherUtilities.GetCipher("SM4/ECB/NoPadding");
+
+            try
+            {
+                outCipher.Init(true, key);
+            }
+            catch (Exception e)
+            {
+                Fail("SM4 failed initialisation - " + e, e);
+            }
+
+            try
+            {
+                inCipher.Init(false, key);
+            }
+            catch (Exception e)
+            {
+                Fail("SM4 failed initialisation - " + e, e);
+            }
+
+            //
+            // encryption pass
+            //
+			MemoryStream bOut = new MemoryStream();
+
+			CipherStream cOut = new CipherStream(bOut, null, outCipher);
+
+            try
+            {
+                for (int i = 0; i != input.Length / 2; i++)
+                {
+                    cOut.WriteByte(input[i]);
+                }
+                cOut.Write(input, input.Length / 2, input.Length - input.Length / 2);
+                cOut.Close();
+            }
+            catch (IOException e)
+            {
+                Fail("SM4 failed encryption - " + e, e);
+            }
+
+            byte[] bytes = bOut.ToArray();
+
+            if (!AreEqual(bytes, output))
+            {
+				Fail("SM4 failed encryption - expected "
+					+ Hex.ToHexString(output) + " got "
+					+ Hex.ToHexString(bytes));
+            }
+
+            //
+            // decryption pass
+            //
+			MemoryStream bIn = new MemoryStream(bytes, false);
+
+			CipherStream cIn = new CipherStream(bIn, inCipher, null);
+
+            try
+            {
+//				DataInputStream dIn = new DataInputStream(cIn);
+				BinaryReader dIn = new BinaryReader(cIn);
+
+				bytes = new byte[input.Length];
+
+				for (int i = 0; i != input.Length / 2; i++)
+				{
+//					bytes[i] = (byte)dIn.read();
+					bytes[i] = dIn.ReadByte();
+				}
+
+				int remaining = bytes.Length - input.Length / 2;
+//				dIn.readFully(bytes, input.Length / 2, remaining);
+				byte[] extra = dIn.ReadBytes(remaining);
+				if (extra.Length < remaining)
+					throw new EndOfStreamException();
+				extra.CopyTo(bytes, input.Length / 2);
+            }
+            catch (Exception e)
+            {
+                Fail("SM4 failed encryption - " + e, e);
+            }
+
+            if (!AreEqual(bytes, input))
+            {
+				Fail("SM4 failed decryption - expected "
+					+ Hex.ToHexString(input) + " got "
+					+ Hex.ToHexString(bytes));
+            }
+        }
+
+        public override void PerformTest()
+        {
+            for (int i = 0; i != cipherTests.Length; i += 4)
+            {
+                DoTest(int.Parse(cipherTests[i]),
+                    Hex.Decode(cipherTests[i + 1]),
+                    Hex.Decode(cipherTests[i + 2]),
+                    Hex.Decode(cipherTests[i + 3]));
+            }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new SM4Test());
+        }
+    }
+}
diff --git a/crypto/test/src/util/test/SimpleTest.cs b/crypto/test/src/util/test/SimpleTest.cs
index 45b8a6a06..2e24964e2 100644
--- a/crypto/test/src/util/test/SimpleTest.cs
+++ b/crypto/test/src/util/test/SimpleTest.cs
@@ -27,6 +27,21 @@ namespace Org.BouncyCastle.Utilities.Test
             throw new TestFailedException(SimpleTestResult.Failed(this, message));
         }
 
+        internal void Fail(
+            string		message,
+            Exception	throwable)
+        {
+            throw new TestFailedException(SimpleTestResult.Failed(this, message, throwable));
+        }
+
+		internal void Fail(
+            string message,
+            object expected,
+            object found)
+        {
+            throw new TestFailedException(SimpleTestResult.Failed(this, message, expected, found));
+        }
+
         internal void IsTrue(bool value)
         {
             if (!value)
@@ -39,22 +54,44 @@ namespace Org.BouncyCastle.Utilities.Test
                 throw new TestFailedException(SimpleTestResult.Failed(this, message));
         }
 
-        internal void Fail(
-            string		message,
-            Exception	throwable)
+        internal void IsEquals(object a, object b)
         {
-            throw new TestFailedException(SimpleTestResult.Failed(this, message, throwable));
+            if (!a.Equals(b))
+                throw new TestFailedException(SimpleTestResult.Failed(this, "no message"));
         }
 
-		internal void Fail(
-            string message,
-            object expected,
-            object found)
+        internal void IsEquals(int a, int b)
         {
-            throw new TestFailedException(SimpleTestResult.Failed(this, message, expected, found));
+            if (a != b)
+                throw new TestFailedException(SimpleTestResult.Failed(this, "no message"));
+        }
+
+        internal void IsEquals(string message, bool a, bool b)
+        {
+            if (a != b)
+                throw new TestFailedException(SimpleTestResult.Failed(this, message));
+        }
+
+        internal void IsEquals(string message, long a, long b)
+        {
+            if (a != b)
+                throw new TestFailedException(SimpleTestResult.Failed(this, message));
+        }
+
+        internal void IsEquals(string message, object a, object b)
+        {
+            if (a == null && b == null)
+                return;
+
+            if (a == null)
+                throw new TestFailedException(SimpleTestResult.Failed(this, message));
+            if (b == null)
+                throw new TestFailedException(SimpleTestResult.Failed(this, message));
+            if (!a.Equals(b))
+                throw new TestFailedException(SimpleTestResult.Failed(this, message));
         }
 
-		internal bool AreEqual(
+        internal bool AreEqual(
             byte[] a,
             byte[] b)
         {