summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/BouncyCastle.Crypto.csproj62
-rw-r--r--crypto/src/asn1/Asn1GeneralizedTime.cs9
-rw-r--r--crypto/src/asn1/Asn1Null.cs22
-rw-r--r--crypto/src/asn1/Asn1ObjectDescriptor.cs22
-rw-r--r--crypto/src/asn1/Asn1OctetString.cs23
-rw-r--r--crypto/src/asn1/Asn1OutputStream.cs16
-rw-r--r--crypto/src/asn1/Asn1RelativeOid.cs22
-rw-r--r--crypto/src/asn1/Asn1Sequence.cs23
-rw-r--r--crypto/src/asn1/Asn1Set.cs23
-rw-r--r--crypto/src/asn1/Asn1TaggedObject.cs21
-rw-r--r--crypto/src/asn1/ConstructedBitStream.cs52
-rw-r--r--crypto/src/asn1/ConstructedOctetStream.cs52
-rw-r--r--crypto/src/asn1/DERExternal.cs22
-rw-r--r--crypto/src/asn1/DefiniteLengthInputStream.cs21
-rw-r--r--crypto/src/asn1/DerBMPString.cs22
-rw-r--r--crypto/src/asn1/DerBitString.cs23
-rw-r--r--crypto/src/asn1/DerBoolean.cs22
-rw-r--r--crypto/src/asn1/DerEnumerated.cs22
-rw-r--r--crypto/src/asn1/DerGeneralString.cs22
-rw-r--r--crypto/src/asn1/DerGraphicString.cs22
-rw-r--r--crypto/src/asn1/DerIA5String.cs22
-rw-r--r--crypto/src/asn1/DerInteger.cs22
-rw-r--r--crypto/src/asn1/DerNumericString.cs22
-rw-r--r--crypto/src/asn1/DerObjectIdentifier.cs22
-rw-r--r--crypto/src/asn1/DerPrintableString.cs22
-rw-r--r--crypto/src/asn1/DerT61String.cs22
-rw-r--r--crypto/src/asn1/DerUTF8String.cs22
-rw-r--r--crypto/src/asn1/DerUniversalString.cs25
-rw-r--r--crypto/src/asn1/DerVideotexString.cs22
-rw-r--r--crypto/src/asn1/DerVisibleString.cs25
-rw-r--r--crypto/src/asn1/IndefiniteLengthInputStream.cs23
-rw-r--r--crypto/src/asn1/cmp/CertReqTemplateContent.cs5
-rw-r--r--crypto/src/asn1/cmp/CmpObjectIdentifiers.cs3
-rw-r--r--crypto/src/asn1/cmp/CrlSource.cs2
-rw-r--r--crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs5
-rw-r--r--crypto/src/asn1/cms/OtherRevocationInfoFormat.cs8
-rw-r--r--crypto/src/asn1/cms/Time.cs8
-rw-r--r--crypto/src/asn1/cryptlib/CryptlibObjectIdentifiers.cs11
-rw-r--r--crypto/src/asn1/x509/KeyPurposeId.cs90
-rw-r--r--crypto/src/asn1/x509/Time.cs8
-rw-r--r--crypto/src/bcpg/ArmoredInputStream.cs21
-rw-r--r--crypto/src/bcpg/ArmoredOutputStream.cs11
-rw-r--r--crypto/src/bcpg/BcpgInputStream.cs41
-rw-r--r--crypto/src/bcpg/BcpgOutputStream.cs112
-rw-r--r--crypto/src/bcpg/ECDHPublicBCPGKey.cs16
-rw-r--r--crypto/src/bcpg/ECSecretBCPGKey.cs11
-rw-r--r--crypto/src/bcpg/EdDsaPublicBcpgKey.cs25
-rw-r--r--crypto/src/bcpg/EdSecretBcpgKey.cs43
-rw-r--r--crypto/src/bcpg/PublicKeyPacket.cs47
-rw-r--r--crypto/src/bcpg/SignaturePacket.cs100
-rw-r--r--crypto/src/cms/CMSAuthenticatedDataGenerator.cs18
-rw-r--r--crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs9
-rw-r--r--crypto/src/cms/CMSCompressedDataStreamGenerator.cs10
-rw-r--r--crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs9
-rw-r--r--crypto/src/cms/CMSProcessableFile.cs3
-rw-r--r--crypto/src/cms/CMSSignedData.cs62
-rw-r--r--crypto/src/cms/CMSSignedDataParser.cs9
-rw-r--r--crypto/src/cms/CMSSignedDataStreamGenerator.cs9
-rw-r--r--crypto/src/cms/CMSSignedGenerator.cs25
-rw-r--r--crypto/src/cms/CMSSignedHelper.cs50
-rw-r--r--crypto/src/cms/CMSTypedStream.cs7
-rw-r--r--crypto/src/cms/CMSUtils.cs52
-rw-r--r--crypto/src/cms/OriginatorInfoGenerator.cs59
-rw-r--r--crypto/src/crypto/engines/Grain128AEADEngine.cs6
-rw-r--r--crypto/src/crypto/engines/Salsa20Engine.cs101
-rw-r--r--crypto/src/crypto/engines/SerpentEngine.cs10
-rw-r--r--crypto/src/crypto/engines/SerpentEngineBase.cs12
-rw-r--r--crypto/src/crypto/engines/SkipjackEngine.cs40
-rw-r--r--crypto/src/crypto/engines/TnepresEngine.cs10
-rw-r--r--crypto/src/crypto/generators/HkdfBytesGenerator.cs (renamed from crypto/src/crypto/generators/HKdfBytesGenerator.cs)0
-rw-r--r--crypto/src/crypto/generators/SCrypt.cs107
-rw-r--r--crypto/src/crypto/modes/CfbBlockCipher.cs8
-rw-r--r--crypto/src/crypto/modes/ChaCha20Poly1305.cs13
-rw-r--r--crypto/src/crypto/modes/OCBBlockCipher.cs2
-rw-r--r--crypto/src/crypto/operators/Asn1Signature.cs25
-rw-r--r--crypto/src/crypto/parameters/HkdfParameters.cs (renamed from crypto/src/crypto/parameters/HKdfParameters.cs)0
-rw-r--r--crypto/src/crypto/prng/CryptoApiRandomGenerator.cs14
-rw-r--r--crypto/src/math/ec/ECFieldElement.cs8
-rw-r--r--crypto/src/math/ec/LongArray.cs155
-rw-r--r--crypto/src/ocsp/RevokedStatus.cs45
-rw-r--r--crypto/src/openpgp/EdDsaSigner.cs72
-rw-r--r--crypto/src/openpgp/PgpEncryptedData.cs29
-rw-r--r--crypto/src/openpgp/PgpEncryptedDataGenerator.cs129
-rw-r--r--crypto/src/openpgp/PgpKdfParameters.cs21
-rw-r--r--crypto/src/openpgp/PgpLiteralDataGenerator.cs2
-rw-r--r--crypto/src/openpgp/PgpObjectFactory.cs5
-rw-r--r--crypto/src/openpgp/PgpOnePassSignature.cs78
-rw-r--r--crypto/src/openpgp/PgpPbeEncryptedData.cs5
-rw-r--r--crypto/src/openpgp/PgpPublicKey.cs311
-rw-r--r--crypto/src/openpgp/PgpPublicKeyEncryptedData.cs156
-rw-r--r--crypto/src/openpgp/PgpPublicKeyRing.cs26
-rw-r--r--crypto/src/openpgp/PgpSecretKey.cs105
-rw-r--r--crypto/src/openpgp/PgpSecretKeyRing.cs47
-rw-r--r--crypto/src/openpgp/PgpSignature.cs151
-rw-r--r--crypto/src/openpgp/PgpSignatureGenerator.cs193
-rw-r--r--crypto/src/openpgp/PgpUtilities.cs57
-rw-r--r--crypto/src/openpgp/PgpV3SignatureGenerator.cs105
-rw-r--r--crypto/src/openpgp/Rfc6637Utilities.cs14
-rw-r--r--crypto/src/openpgp/WrappedGeneratorStream.cs5
-rw-r--r--crypto/src/pqc/crypto/cmce/CmceEngine.cs14
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/PolyVecK.cs31
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/PolyVecL.cs18
-rw-r--r--crypto/src/pqc/crypto/crystals/kyber/KyberEngine.cs2
-rw-r--r--crypto/src/pqc/crypto/crystals/kyber/KyberIndCpa.cs2
-rw-r--r--crypto/src/pqc/crypto/falcon/FalconKeyPairGenerator.cs39
-rw-r--r--crypto/src/pqc/crypto/falcon/FalconKeyParameters.cs2
-rw-r--r--crypto/src/pqc/crypto/falcon/FalconNIST.cs5
-rw-r--r--crypto/src/pqc/crypto/falcon/FalconParameters.cs15
-rw-r--r--crypto/src/pqc/crypto/falcon/FalconPrivateKeyParameters.cs14
-rw-r--r--crypto/src/pqc/crypto/falcon/FalconPublicKeyParameters.cs4
-rw-r--r--crypto/src/pqc/crypto/falcon/FalconSigner.cs20
-rw-r--r--crypto/src/pqc/crypto/hqc/ReedSolomon.cs1
-rw-r--r--crypto/src/pqc/crypto/lms/Composer.cs7
-rw-r--r--crypto/src/pqc/crypto/lms/HSS.cs30
-rw-r--r--crypto/src/pqc/crypto/lms/HSSSignature.cs70
-rw-r--r--crypto/src/pqc/crypto/lms/LMOtsParameters.cs3
-rw-r--r--crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs2
-rw-r--r--crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs4
-rw-r--r--crypto/src/pqc/crypto/lms/LMS.cs6
-rw-r--r--crypto/src/pqc/crypto/lms/LMSKeyParameters.cs2
-rw-r--r--crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs37
-rw-r--r--crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs2
-rw-r--r--crypto/src/pqc/crypto/lms/LMSSignature.cs2
-rw-r--r--crypto/src/pqc/crypto/lms/LM_OTS.cs2
-rw-r--r--crypto/src/pqc/crypto/lms/LmsUtils.cs2
-rw-r--r--crypto/src/pqc/crypto/picnic/KMatrices.cs24
-rw-r--r--crypto/src/pqc/crypto/picnic/Msg.cs6
-rw-r--r--crypto/src/pqc/crypto/picnic/PicnicEngine.cs417
-rw-r--r--crypto/src/pqc/crypto/picnic/Signature2.cs11
-rw-r--r--crypto/src/pqc/crypto/picnic/Tape.cs187
-rw-r--r--crypto/src/pqc/crypto/picnic/Tree.cs35
-rw-r--r--crypto/src/pqc/crypto/picnic/View.cs8
-rw-r--r--crypto/src/pqc/crypto/sike/Fpx.cs128
-rw-r--r--crypto/src/pqc/crypto/sike/Isogeny.cs89
-rw-r--r--crypto/src/pqc/crypto/sike/SIDH.cs102
-rw-r--r--crypto/src/pqc/crypto/sike/SIDH_Compressed.cs217
-rw-r--r--crypto/src/pqc/crypto/sike/SIKEEngine.cs414
-rw-r--r--crypto/src/pqc/crypto/sike/SIKEKEMExtractor.cs5
-rw-r--r--crypto/src/pqc/crypto/sike/SIKEKEMGenerator.cs6
-rw-r--r--crypto/src/pqc/crypto/sike/SIKEKeyGenerationParameters.cs3
-rw-r--r--crypto/src/pqc/crypto/sike/SIKEKeyPairGenerator.cs3
-rw-r--r--crypto/src/pqc/crypto/sike/SIKEKeyParameters.cs3
-rw-r--r--crypto/src/pqc/crypto/sike/SIKEParameters.cs3
-rw-r--r--crypto/src/pqc/crypto/sike/SIKEPrivateKeyParameters.cs3
-rw-r--r--crypto/src/pqc/crypto/sike/SIKEPublicKeyParameters.cs3
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusParameters.cs11
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPrivateKeyParameters.cs4
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPublicKeyParameters.cs2
-rw-r--r--crypto/src/pqc/crypto/utils/PqcUtilities.cs27
-rw-r--r--crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs4
-rw-r--r--crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs6
-rw-r--r--crypto/src/pqc/crypto/utils/PublicKeyFactory.cs19
-rw-r--r--crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs2
-rw-r--r--crypto/src/security/DotNetUtilities.cs118
-rw-r--r--crypto/src/security/JksStore.cs5
-rw-r--r--crypto/src/security/PrivateKeyFactory.cs104
-rw-r--r--crypto/src/security/PublicKeyFactory.cs8
-rw-r--r--crypto/src/security/SignerUtilities.cs4
-rw-r--r--crypto/src/tls/AbstractTlsPeer.cs2
-rw-r--r--crypto/src/tls/CombinedHash.cs8
-rw-r--r--crypto/src/tls/DatagramReceiver.cs5
-rw-r--r--crypto/src/tls/DatagramSender.cs5
-rw-r--r--crypto/src/tls/DeferredHash.cs16
-rw-r--r--crypto/src/tls/DtlsClientProtocol.cs4
-rw-r--r--crypto/src/tls/DtlsRecordLayer.cs171
-rw-r--r--crypto/src/tls/DtlsServerProtocol.cs2
-rw-r--r--crypto/src/tls/DtlsTransport.cs111
-rw-r--r--crypto/src/tls/RecordStream.cs51
-rw-r--r--crypto/src/tls/TlsPeer.cs7
-rw-r--r--crypto/src/tls/TlsProtocol.cs141
-rw-r--r--crypto/src/tls/TlsStream.cs14
-rw-r--r--crypto/src/tls/TlsUtilities.cs9
-rw-r--r--crypto/src/tls/crypto/TlsCipher.cs5
-rw-r--r--crypto/src/tls/crypto/TlsHash.cs4
-rw-r--r--crypto/src/tls/crypto/TlsHashSink.cs10
-rw-r--r--crypto/src/tls/crypto/TlsMac.cs4
-rw-r--r--crypto/src/tls/crypto/TlsMacSink.cs10
-rw-r--r--crypto/src/tls/crypto/TlsNullNullCipher.cs10
-rw-r--r--crypto/src/tls/crypto/impl/TlsAeadCipher.cs89
-rw-r--r--crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs2
-rw-r--r--crypto/src/tls/crypto/impl/TlsBlockCipher.cs76
-rw-r--r--crypto/src/tls/crypto/impl/TlsNullCipher.cs12
-rw-r--r--crypto/src/tls/crypto/impl/TlsSuiteHmac.cs26
-rw-r--r--crypto/src/tls/crypto/impl/TlsSuiteMac.cs4
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs15
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs7
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs5
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsHash.cs7
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs7
-rw-r--r--crypto/src/tsp/TSPUtil.cs2
-rw-r--r--crypto/src/util/Arrays.cs13
-rw-r--r--crypto/src/util/io/BaseInputStream.cs4
-rw-r--r--crypto/src/util/io/BaseOutputStream.cs3
-rw-r--r--crypto/src/util/io/FilterStream.cs23
194 files changed, 4586 insertions, 2343 deletions
diff --git a/crypto/src/BouncyCastle.Crypto.csproj b/crypto/src/BouncyCastle.Crypto.csproj
index 44925aebd..8c77860b1 100644
--- a/crypto/src/BouncyCastle.Crypto.csproj
+++ b/crypto/src/BouncyCastle.Crypto.csproj
@@ -1,31 +1,57 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net6.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>net6.0;netstandard2.0;net461</TargetFrameworks>
     <RootNamespace>Org.BouncyCastle</RootNamespace>
-    <AssemblyOriginatorKeyFile>..\..\BouncyCastle.snk</AssemblyOriginatorKeyFile>
+    <AssemblyOriginatorKeyFile>..\..\BouncyCastle.NET.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
 	<NoWarn>1591</NoWarn>
 
-    <Authors />
+    <AssemblyName>BouncyCastle.Cryptography</AssemblyName>
+    <AssemblyTitle>BouncyCastle.NET Cryptography ($(TargetFramework))</AssemblyTitle>
+    <Authors>Legion of the Bouncy Castle Inc.</Authors>
     <Company>Legion of the Bouncy Castle Inc.</Company>
     <Copyright>Copyright © Legion of the Bouncy Castle Inc. 2000-2022</Copyright>
-    <DebugType>embedded</DebugType>
     <Description>BouncyCastle.NET is a popular cryptography library for .NET</Description>
-    <EmbedUntrackedSources>true</EmbedUntrackedSources>	  
-    <PackageIconUrl>https://www.bouncycastle.org/images/csharp_logo.gif</PackageIconUrl>
+    <PackageIcon>packageIcon.png</PackageIcon>
+    <PackageIconUrl>https://www.bouncycastle.org/images/nuget_packageIcon.png</PackageIconUrl>
     <PackageId>BouncyCastle.Cryptography</PackageId>
-    <PackageLicenseFile>License.html</PackageLicenseFile>
+    <PackageLicenseFile>LICENSE.md</PackageLicenseFile>
     <PackageProjectUrl>https://www.bouncycastle.org/csharp/</PackageProjectUrl>
+    <PackageReadmeFile>README.md</PackageReadmeFile>
     <PackageReleaseNotes>https://www.bouncycastle.org/csharp/</PackageReleaseNotes>
-    <PackageTags>bouncycastle cryptography dtls encryption security tls</PackageTags>
+    <PackageTags>bouncycastle cryptography dtls encryption open-source post-quantum security tls</PackageTags>
 	<Product>BouncyCastle.NET</Product>
-    <!--<PublishRepositoryUrl>true</PublishRepositoryUrl>-->
     <RepositoryType>git</RepositoryType>
     <RepositoryUrl>https://github.com/bcgit/bc-csharp</RepositoryUrl>
     <Title>BouncyCastle.NET Cryptography</Title>
   </PropertyGroup>
 
+  <!--Source-Level Debugging-->
+  <PropertyGroup>
+    <DebugType>embedded</DebugType>
+    <EmbedAllSources>true</EmbedAllSources>
+  </PropertyGroup>
+
+  <!--Package Validation-->
+  <PropertyGroup>
+    <EnablePackageValidation>true</EnablePackageValidation>
+
+    <!-- TODO: Enable this once there is a baseline version to compare to. -->
+    <!--<PackageValidationBaselineVersion>2.0.0</PackageValidationBaselineVersion>-->
+
+	<!-- In case we disable signing for local builds, ignore identity mismatch with baseline version. -->
+    <NoWarn Condition="'$(SignAssembly)' != 'true'">$(NoWarn);CP0003</NoWarn>
+
+	<!--
+	  We added Span-based variant methods to several APIs. Code that uses those methods or implements the
+	  affected interfaces (or abstract classes) will not be backward compatible.
+
+	  TODO: Use suppressions for each individual case of a Span-based method.
+    -->
+    <NoWarn>$(NoWarn);CP0005;CP0006</NoWarn>
+  </PropertyGroup>
+
   <PropertyGroup Condition="'$(Configuration)'=='Debug'">
     <DefineConstants>DEBUG;TRACE</DefineConstants>
   </PropertyGroup>
@@ -38,27 +64,25 @@
   <ItemGroup>
     <None Remove="**\*.properties" />
     <EmbeddedResource Include="**\*.properties" />
+    <None Include="..\..\LICENSE.md" Pack="true" PackagePath="\" />
+    <None Include="..\..\packageIcon.png" Pack="true" PackagePath="\" />
+    <None Include="..\..\README.md" Pack="true" PackagePath="\" />
   </ItemGroup>
 
   <ItemGroup>
-    <None Include="..\License.html">
-      <Pack>True</Pack>
-      <PackagePath>\</PackagePath>
-    </None>
-  </ItemGroup>
-  <ItemGroup>
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
-      <PrivateAssets>all</PrivateAssets>
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-    </PackageReference>
     <PackageReference Include="Nerdbank.GitVersioning" Version="3.5.119">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
   </ItemGroup>
 
+  <Target Name="FixAssemblyAttributes" AfterTargets="GetBuildVersion">
+    <PropertyGroup>
+		<!-- Here we can override/use any MSBuild properties set by Nerdbank.GitVersioning -->
+    </PropertyGroup>
+  </Target>
 </Project>
diff --git a/crypto/src/asn1/Asn1GeneralizedTime.cs b/crypto/src/asn1/Asn1GeneralizedTime.cs
index e844c8ca2..139384c1a 100644
--- a/crypto/src/asn1/Asn1GeneralizedTime.cs
+++ b/crypto/src/asn1/Asn1GeneralizedTime.cs
@@ -11,8 +11,7 @@ namespace Org.BouncyCastle.Asn1
      * Base class representing the ASN.1 GeneralizedTime type.
      * <p>
      * The main difference between these and UTC time is a 4 digit year.
-     * </p>
-     * <p>
+     * </p><p>
      * One second resolution date+time on UTC timezone (Z)
      * with 4 digit year (valid from 0001 to 9999).
      * </p><p>
@@ -24,18 +23,18 @@ namespace Org.BouncyCastle.Asn1
      *
      * <h3>11: Restrictions on BER employed by both CER and DER</h3>
      * <h4>11.7 GeneralizedTime </h4>
-     * <p>
+     * </p><p>
      * <b>11.7.1</b> The encoding shall terminate with a "Z",
      * as described in the ITU-T Rec. X.680 | ISO/IEC 8824-1 clause on
      * GeneralizedTime.
      * </p><p>
      * <b>11.7.2</b> The seconds element shall always be present.
-     * </p>
-     * <p>
+     * </p><p>
      * <b>11.7.3</b> The fractional-seconds elements, if present,
      * shall omit all trailing zeros; if the elements correspond to 0,
      * they shall be wholly omitted, and the decimal point element also
      * shall be omitted.
+     * </p>
      */
     public class Asn1GeneralizedTime
         : Asn1Object
diff --git a/crypto/src/asn1/Asn1Null.cs b/crypto/src/asn1/Asn1Null.cs
index 9ea9b4375..77304c0fb 100644
--- a/crypto/src/asn1/Asn1Null.cs
+++ b/crypto/src/asn1/Asn1Null.cs
@@ -25,21 +25,23 @@ namespace Org.BouncyCastle.Asn1
 
         public static Asn1Null GetInstance(object obj)
         {
-            if (obj == null || obj is Asn1Null)
-            {
-                return (Asn1Null)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1Null asn1Null)
+                return asn1Null;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is Asn1Null)
-                    return (Asn1Null)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is Asn1Null converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (Asn1Null)Meta.Instance.FromByteArray((byte[])obj);
+                    return (Asn1Null)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/Asn1ObjectDescriptor.cs b/crypto/src/asn1/Asn1ObjectDescriptor.cs
index 9c99f441e..13521a744 100644
--- a/crypto/src/asn1/Asn1ObjectDescriptor.cs
+++ b/crypto/src/asn1/Asn1ObjectDescriptor.cs
@@ -36,21 +36,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static Asn1ObjectDescriptor GetInstance(object obj)
         {
-            if (obj == null || obj is Asn1ObjectDescriptor)
-            {
-                return (Asn1ObjectDescriptor)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1ObjectDescriptor asn1ObjectDescriptor)
+                return asn1ObjectDescriptor;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is Asn1ObjectDescriptor)
-                    return (Asn1ObjectDescriptor)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is Asn1ObjectDescriptor converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (Asn1ObjectDescriptor)Meta.Instance.FromByteArray((byte[])obj);
+                    return (Asn1ObjectDescriptor)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/Asn1OctetString.cs b/crypto/src/asn1/Asn1OctetString.cs
index d34686134..8f7da8800 100644
--- a/crypto/src/asn1/Asn1OctetString.cs
+++ b/crypto/src/asn1/Asn1OctetString.cs
@@ -36,22 +36,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static Asn1OctetString GetInstance(object obj)
         {
-            if (obj == null || obj is Asn1OctetString)
-            {
-                return (Asn1OctetString)obj;
-            }
-            //else if (obj is Asn1OctetStringParser)
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1OctetString asn1OctetString)
+                return asn1OctetString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is Asn1OctetString)
-                    return (Asn1OctetString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is Asn1OctetString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (Asn1OctetString)Meta.Instance.FromByteArray((byte[])obj);
+                    return (Asn1OctetString)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/Asn1OutputStream.cs b/crypto/src/asn1/Asn1OutputStream.cs
index 59178ea31..163e3848c 100644
--- a/crypto/src/asn1/Asn1OutputStream.cs
+++ b/crypto/src/asn1/Asn1OutputStream.cs
@@ -1,5 +1,10 @@
 using System;
 using System.IO;
+using System.Diagnostics;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+using System.Buffers.Binary;
+using System.Numerics;
+#endif
 
 using Org.BouncyCastle.Utilities.IO;
 
@@ -73,15 +78,19 @@ namespace Org.BouncyCastle.Asn1
         {
             if (dl < 128)
             {
+                Debug.Assert(dl >= 0);
                 WriteByte((byte)dl);
                 return;
             }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            Span<byte> stack = stackalloc byte[5];
+            Span<byte> encoding = stackalloc byte[5];
+            BinaryPrimitives.WriteUInt32BigEndian(encoding[1..], (uint)dl);
+            int leadingZeroBytes = BitOperations.LeadingZeroCount((uint)dl) / 8;
+            encoding[leadingZeroBytes] = (byte)(0x84 - leadingZeroBytes);
+            Write(encoding[leadingZeroBytes..]);
 #else
             byte[] stack = new byte[5];
-#endif
             int pos = stack.Length;
 
             do
@@ -94,9 +103,6 @@ namespace Org.BouncyCastle.Asn1
             int count = stack.Length - pos;
             stack[--pos] = (byte)(0x80 | count);
 
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            Write(stack[pos..]);
-#else
             Write(stack, pos, count + 1);
 #endif
         }
diff --git a/crypto/src/asn1/Asn1RelativeOid.cs b/crypto/src/asn1/Asn1RelativeOid.cs
index a1997864d..3c4bf237a 100644
--- a/crypto/src/asn1/Asn1RelativeOid.cs
+++ b/crypto/src/asn1/Asn1RelativeOid.cs
@@ -29,21 +29,23 @@ namespace Org.BouncyCastle.Asn1
 
         public static Asn1RelativeOid GetInstance(object obj)
         {
-            if (obj == null || obj is Asn1RelativeOid)
-            {
-                return (Asn1RelativeOid)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1RelativeOid asn1RelativeOid)
+                return asn1RelativeOid;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is Asn1RelativeOid)
-                    return (Asn1RelativeOid)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is Asn1RelativeOid converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (Asn1RelativeOid)FromByteArray((byte[])obj);
+                    return (Asn1RelativeOid)FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/Asn1Sequence.cs b/crypto/src/asn1/Asn1Sequence.cs
index 1a123e26d..a8191de99 100644
--- a/crypto/src/asn1/Asn1Sequence.cs
+++ b/crypto/src/asn1/Asn1Sequence.cs
@@ -30,22 +30,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static Asn1Sequence GetInstance(object obj)
         {
-            if (obj == null || obj is Asn1Sequence)
-            {
-                return (Asn1Sequence)obj;
-            }
-            //else if (obj is Asn1SequenceParser)
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1Sequence asn1Sequence)
+                return asn1Sequence;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is Asn1Sequence)
-                    return (Asn1Sequence)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is Asn1Sequence converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (Asn1Sequence)Meta.Instance.FromByteArray((byte[])obj);
+                    return (Asn1Sequence)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/Asn1Set.cs b/crypto/src/asn1/Asn1Set.cs
index faec50eb0..2b3810e43 100644
--- a/crypto/src/asn1/Asn1Set.cs
+++ b/crypto/src/asn1/Asn1Set.cs
@@ -31,22 +31,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static Asn1Set GetInstance(object obj)
         {
-            if (obj == null || obj is Asn1Set)
-            {
-                return (Asn1Set)obj;
-            }
-            //else if (obj is Asn1SetParser)
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1Set asn1Set)
+                return asn1Set;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is Asn1Set)
-                    return (Asn1Set)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is Asn1Set converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (Asn1Set)Meta.Instance.FromByteArray((byte[])obj);
+                    return (Asn1Set)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs
index 46aa137a8..63ab6a5d0 100644
--- a/crypto/src/asn1/Asn1TaggedObject.cs
+++ b/crypto/src/asn1/Asn1TaggedObject.cs
@@ -21,22 +21,23 @@ namespace Org.BouncyCastle.Asn1
 
         public static Asn1TaggedObject GetInstance(object obj)
 		{
-            if (obj == null || obj is Asn1TaggedObject) 
-            {
-                return (Asn1TaggedObject)obj;
-            }
-            //else if (obj is Asn1TaggedObjectParser)
-            else if (obj is IAsn1Convertible asn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1TaggedObject asn1TaggedObject)
+                return asn1TaggedObject;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
                 Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
-                if (asn1Object is Asn1TaggedObject taggedObject)
-                    return taggedObject;
+                if (asn1Object is Asn1TaggedObject converted)
+                    return converted;
             }
-            else if (obj is byte[] byteArray)
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return CheckedCast(FromByteArray(byteArray));
+                    return CheckedCast(FromByteArray(bytes));
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/ConstructedBitStream.cs b/crypto/src/asn1/ConstructedBitStream.cs
index 49f54fc1b..f089dac75 100644
--- a/crypto/src/asn1/ConstructedBitStream.cs
+++ b/crypto/src/asn1/ConstructedBitStream.cs
@@ -33,6 +33,9 @@ namespace Org.BouncyCastle.Asn1
         {
             Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Read(buffer.AsSpan(offset, count));
+#else
             if (count < 1)
                 return 0;
 
@@ -75,8 +78,57 @@ namespace Org.BouncyCastle.Asn1
                     m_currentStream = m_currentParser.GetBitStream();
                 }
             }
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            if (buffer.IsEmpty)
+                return 0;
+
+            if (m_currentStream == null)
+            {
+                if (!m_first)
+                    return 0;
+
+                m_currentParser = GetNextParser();
+                if (m_currentParser == null)
+                    return 0;
+
+                m_first = false;
+                m_currentStream = m_currentParser.GetBitStream();
+            }
+
+            int totalRead = 0;
+
+            for (;;)
+            {
+                int numRead = m_currentStream.Read(buffer[totalRead..]);
+
+                if (numRead > 0)
+                {
+                    totalRead += numRead;
+
+                    if (totalRead == buffer.Length)
+                        return totalRead;
+                }
+                else
+                {
+                    m_padBits = m_currentParser.PadBits;
+                    m_currentParser = GetNextParser();
+                    if (m_currentParser == null)
+                    {
+                        m_currentStream = null;
+                        return totalRead;
+                    }
+
+                    m_currentStream = m_currentParser.GetBitStream();
+                }
+            }
+        }
+#endif
+
         public override int ReadByte()
         {
             if (m_currentStream == null)
diff --git a/crypto/src/asn1/ConstructedOctetStream.cs b/crypto/src/asn1/ConstructedOctetStream.cs
index 12aa14e74..d005f9fe7 100644
--- a/crypto/src/asn1/ConstructedOctetStream.cs
+++ b/crypto/src/asn1/ConstructedOctetStream.cs
@@ -1,3 +1,4 @@
+using System;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
@@ -22,6 +23,9 @@ namespace Org.BouncyCastle.Asn1
 		{
 			Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return Read(buffer.AsSpan(offset, count));
+#else
 			if (count < 1)
                 return 0;
 
@@ -63,8 +67,56 @@ namespace Org.BouncyCastle.Asn1
 					m_currentStream = next.GetOctetStream();
 				}
 			}
+#endif
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public override int Read(Span<byte> buffer)
+		{
+			if (buffer.IsEmpty)
+                return 0;
+
+			if (m_currentStream == null)
+			{
+				if (!m_first)
+					return 0;
+
+                Asn1OctetStringParser next = GetNextParser();
+                if (next == null)
+                    return 0;
+
+				m_first = false;
+				m_currentStream = next.GetOctetStream();
+			}
+
+			int totalRead = 0;
+
+			for (;;)
+			{
+				int numRead = m_currentStream.Read(buffer[totalRead..]);
+
+				if (numRead > 0)
+				{
+					totalRead += numRead;
+
+					if (totalRead == buffer.Length)
+						return totalRead;
+				}
+				else
+				{
+                    Asn1OctetStringParser next = GetNextParser();
+                    if (next == null)
+					{
+						m_currentStream = null;
+						return totalRead;
+					}
+
+					m_currentStream = next.GetOctetStream();
+				}
+			}
+		}
+#endif
+
 		public override int ReadByte()
 		{
 			if (m_currentStream == null)
diff --git a/crypto/src/asn1/DERExternal.cs b/crypto/src/asn1/DERExternal.cs
index 9fba95165..207930062 100644
--- a/crypto/src/asn1/DERExternal.cs
+++ b/crypto/src/asn1/DERExternal.cs
@@ -25,21 +25,23 @@ namespace Org.BouncyCastle.Asn1
 
         public static DerExternal GetInstance(object obj)
         {
-            if (obj == null || obj is DerExternal)
-            {
-                return (DerExternal)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerExternal derExternal)
+                return derExternal;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerExternal)
-                    return (DerExternal)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerExternal converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerExternal)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerExternal)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DefiniteLengthInputStream.cs b/crypto/src/asn1/DefiniteLengthInputStream.cs
index ed5bd2446..89f0d5a62 100644
--- a/crypto/src/asn1/DefiniteLengthInputStream.cs
+++ b/crypto/src/asn1/DefiniteLengthInputStream.cs
@@ -79,6 +79,27 @@ namespace Org.BouncyCastle.Asn1
             return numRead;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            if (_remaining == 0)
+                return 0;
+
+            int toRead = System.Math.Min(buffer.Length, _remaining);
+            int numRead = _in.Read(buffer[..toRead]);
+
+            if (numRead < 1)
+                throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining);
+
+            if ((_remaining -= numRead) == 0)
+            {
+                SetParentEofDetect();
+            }
+
+            return numRead;
+        }
+#endif
+
         internal void ReadAllIntoByteArray(byte[] buf)
         {
             if (_remaining != buf.Length)
diff --git a/crypto/src/asn1/DerBMPString.cs b/crypto/src/asn1/DerBMPString.cs
index a289eed1b..284a4b830 100644
--- a/crypto/src/asn1/DerBMPString.cs
+++ b/crypto/src/asn1/DerBMPString.cs
@@ -31,21 +31,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerBmpString GetInstance(object obj)
         {
-            if (obj == null || obj is DerBmpString)
-            {
-                return (DerBmpString)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerBmpString derBmpString)
+                return derBmpString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerBmpString)
-                    return (DerBmpString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerBmpString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerBmpString)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerBmpString)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerBitString.cs b/crypto/src/asn1/DerBitString.cs
index 8aab88353..44b3bb95a 100644
--- a/crypto/src/asn1/DerBitString.cs
+++ b/crypto/src/asn1/DerBitString.cs
@@ -38,22 +38,23 @@ namespace Org.BouncyCastle.Asn1
 		 */
 		public static DerBitString GetInstance(object obj)
 		{
-			if (obj == null || obj is DerBitString)
-			{
-				return (DerBitString)obj;
-			}
-            //else if (obj is Asn1BitStringParser)
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+			if (obj is DerBitString derBitString)
+				return derBitString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerBitString)
-                    return (DerBitString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerBitString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return GetInstance(FromByteArray((byte[])obj));
+                    return GetInstance(FromByteArray(bytes));
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerBoolean.cs b/crypto/src/asn1/DerBoolean.cs
index ad578ae80..6256db6e0 100644
--- a/crypto/src/asn1/DerBoolean.cs
+++ b/crypto/src/asn1/DerBoolean.cs
@@ -30,21 +30,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerBoolean GetInstance(object obj)
         {
-            if (obj == null || obj is DerBoolean)
-            {
-                return (DerBoolean)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerBoolean derBoolean)
+                return derBoolean;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerBoolean)
-                    return (DerBoolean)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerBoolean converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerBoolean)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerBoolean)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerEnumerated.cs b/crypto/src/asn1/DerEnumerated.cs
index 920b3dc8e..b85c5a43e 100644
--- a/crypto/src/asn1/DerEnumerated.cs
+++ b/crypto/src/asn1/DerEnumerated.cs
@@ -28,21 +28,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerEnumerated GetInstance(object obj)
         {
-            if (obj == null || obj is DerEnumerated)
-            {
-                return (DerEnumerated)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerEnumerated derEnumerated)
+                return derEnumerated;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerEnumerated)
-                    return (DerEnumerated)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerEnumerated converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerEnumerated)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerEnumerated)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerGeneralString.cs b/crypto/src/asn1/DerGeneralString.cs
index e6637732a..6a378307d 100644
--- a/crypto/src/asn1/DerGeneralString.cs
+++ b/crypto/src/asn1/DerGeneralString.cs
@@ -22,21 +22,23 @@ namespace Org.BouncyCastle.Asn1
 
         public static DerGeneralString GetInstance(object obj)
         {
-            if (obj == null || obj is DerGeneralString)
-            {
-                return (DerGeneralString) obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerGeneralString derGeneralString)
+                return derGeneralString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerGeneralString)
-                    return (DerGeneralString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerGeneralString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerGeneralString)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerGeneralString)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerGraphicString.cs b/crypto/src/asn1/DerGraphicString.cs
index cb32d14eb..85330eb33 100644
--- a/crypto/src/asn1/DerGraphicString.cs
+++ b/crypto/src/asn1/DerGraphicString.cs
@@ -29,21 +29,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerGraphicString GetInstance(object obj)
         {
-            if (obj == null || obj is DerGraphicString)
-            {
-                return (DerGraphicString)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerGraphicString derGraphicString)
+                return derGraphicString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerGraphicString)
-                    return (DerGraphicString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerGraphicString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerGraphicString)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerGraphicString)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerIA5String.cs b/crypto/src/asn1/DerIA5String.cs
index a56879831..de2860130 100644
--- a/crypto/src/asn1/DerIA5String.cs
+++ b/crypto/src/asn1/DerIA5String.cs
@@ -30,21 +30,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerIA5String GetInstance(object obj)
         {
-            if (obj == null || obj is DerIA5String)
-            {
-                return (DerIA5String)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerIA5String derIA5String)
+                return derIA5String;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerIA5String)
-                    return (DerIA5String)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerIA5String converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerIA5String)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerIA5String)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerInteger.cs b/crypto/src/asn1/DerInteger.cs
index c8d4e47df..05a790743 100644
--- a/crypto/src/asn1/DerInteger.cs
+++ b/crypto/src/asn1/DerInteger.cs
@@ -42,21 +42,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerInteger GetInstance(object obj)
         {
-            if (obj == null || obj is DerInteger)
-            {
-                return (DerInteger)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerInteger derInteger)
+                return derInteger;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerInteger)
-                    return (DerInteger)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerInteger converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerInteger)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerInteger)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerNumericString.cs b/crypto/src/asn1/DerNumericString.cs
index 693ff7d6e..819d946b1 100644
--- a/crypto/src/asn1/DerNumericString.cs
+++ b/crypto/src/asn1/DerNumericString.cs
@@ -30,21 +30,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerNumericString GetInstance(object obj)
         {
-            if (obj == null || obj is DerNumericString)
-            {
-                return (DerNumericString)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerNumericString derNumericString)
+                return derNumericString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerNumericString)
-                    return (DerNumericString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerNumericString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerNumericString)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerNumericString)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerObjectIdentifier.cs b/crypto/src/asn1/DerObjectIdentifier.cs
index b10f8f8b6..cb5771958 100644
--- a/crypto/src/asn1/DerObjectIdentifier.cs
+++ b/crypto/src/asn1/DerObjectIdentifier.cs
@@ -34,21 +34,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerObjectIdentifier GetInstance(object obj)
         {
-            if (obj == null || obj is DerObjectIdentifier)
-            {
-                return (DerObjectIdentifier)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerObjectIdentifier derObjectIdentifier)
+                return derObjectIdentifier;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerObjectIdentifier)
-                    return (DerObjectIdentifier)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerObjectIdentifier converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerObjectIdentifier)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerObjectIdentifier)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerPrintableString.cs b/crypto/src/asn1/DerPrintableString.cs
index 3c44a2d52..5830afa47 100644
--- a/crypto/src/asn1/DerPrintableString.cs
+++ b/crypto/src/asn1/DerPrintableString.cs
@@ -30,21 +30,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerPrintableString GetInstance(object obj)
         {
-            if (obj == null || obj is DerPrintableString)
-            {
-                return (DerPrintableString)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerPrintableString derPrintableString)
+                return derPrintableString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerPrintableString)
-                    return (DerPrintableString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerPrintableString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerPrintableString)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerPrintableString)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerT61String.cs b/crypto/src/asn1/DerT61String.cs
index a0e4f1d22..45f57ae0a 100644
--- a/crypto/src/asn1/DerT61String.cs
+++ b/crypto/src/asn1/DerT61String.cs
@@ -30,21 +30,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerT61String GetInstance(object obj)
         {
-            if (obj == null || obj is DerT61String)
-            {
-                return (DerT61String)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerT61String derT61String)
+                return derT61String;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerT61String)
-                    return (DerT61String)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerT61String converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerT61String)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerT61String)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerUTF8String.cs b/crypto/src/asn1/DerUTF8String.cs
index d15a19d39..9472f5082 100644
--- a/crypto/src/asn1/DerUTF8String.cs
+++ b/crypto/src/asn1/DerUTF8String.cs
@@ -30,21 +30,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerUtf8String GetInstance(object obj)
         {
-            if (obj == null || obj is DerUtf8String)
-            {
-                return (DerUtf8String)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerUtf8String derUtf8String)
+                return derUtf8String;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerUtf8String)
-                    return (DerUtf8String)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerUtf8String converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerUtf8String)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerUtf8String)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerUniversalString.cs b/crypto/src/asn1/DerUniversalString.cs
index e4e93bd7d..1183ed1f3 100644
--- a/crypto/src/asn1/DerUniversalString.cs
+++ b/crypto/src/asn1/DerUniversalString.cs
@@ -32,24 +32,25 @@ namespace Org.BouncyCastle.Asn1
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerUniversalString GetInstance(
-            object obj)
+        public static DerUniversalString GetInstance(object obj)
         {
-            if (obj == null || obj is DerUniversalString)
-            {
-                return (DerUniversalString)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerUniversalString derUniversalString)
+                return derUniversalString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerUniversalString)
-                    return (DerUniversalString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerUniversalString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerUniversalString)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerUniversalString)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerVideotexString.cs b/crypto/src/asn1/DerVideotexString.cs
index a5fbe0602..636af0499 100644
--- a/crypto/src/asn1/DerVideotexString.cs
+++ b/crypto/src/asn1/DerVideotexString.cs
@@ -29,21 +29,23 @@ namespace Org.BouncyCastle.Asn1
          */
         public static DerVideotexString GetInstance(object obj)
         {
-            if (obj == null || obj is DerVideotexString)
-            {
-                return (DerVideotexString)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerVideotexString derVideotexString)
+                return derVideotexString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerVideotexString)
-                    return (DerVideotexString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerVideotexString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerVideotexString)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerVideotexString)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/DerVisibleString.cs b/crypto/src/asn1/DerVisibleString.cs
index 359370040..77fe54c9a 100644
--- a/crypto/src/asn1/DerVisibleString.cs
+++ b/crypto/src/asn1/DerVisibleString.cs
@@ -28,24 +28,25 @@ namespace Org.BouncyCastle.Asn1
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerVisibleString GetInstance(
-            object obj)
+        public static DerVisibleString GetInstance(object obj)
         {
-            if (obj == null || obj is DerVisibleString)
-            {
-                return (DerVisibleString)obj;
-            }
-            else if (obj is IAsn1Convertible)
+            if (obj == null)
+                return null;
+
+            if (obj is DerVisibleString derVisibleString)
+                return derVisibleString;
+
+            if (obj is IAsn1Convertible asn1Convertible)
             {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerVisibleString)
-                    return (DerVisibleString)asn1Object;
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is DerVisibleString converted)
+                    return converted;
             }
-            else if (obj is byte[])
+            else if (obj is byte[] bytes)
             {
                 try
                 {
-                    return (DerVisibleString)Meta.Instance.FromByteArray((byte[])obj);
+                    return (DerVisibleString)Meta.Instance.FromByteArray(bytes);
                 }
                 catch (IOException e)
                 {
diff --git a/crypto/src/asn1/IndefiniteLengthInputStream.cs b/crypto/src/asn1/IndefiniteLengthInputStream.cs
index 1c8bd9a15..e192e9e8b 100644
--- a/crypto/src/asn1/IndefiniteLengthInputStream.cs
+++ b/crypto/src/asn1/IndefiniteLengthInputStream.cs
@@ -57,7 +57,28 @@ namespace Org.BouncyCastle.Asn1
 			return numRead + 1;
 		}
 
-		public override int ReadByte()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            // Only use this optimisation if we aren't checking for 00
+            if (_eofOn00 || buffer.Length <= 1)
+                return base.Read(buffer);
+
+            if (_lookAhead < 0)
+                return 0;
+
+            int numRead = _in.Read(buffer[1..]);
+            if (numRead <= 0)
+                throw new EndOfStreamException();
+
+            buffer[0] = (byte)_lookAhead;
+            _lookAhead = RequireByte();
+
+            return numRead + 1;
+        }
+#endif
+
+        public override int ReadByte()
 		{
             if (_eofOn00 && _lookAhead <= 0)
             {
diff --git a/crypto/src/asn1/cmp/CertReqTemplateContent.cs b/crypto/src/asn1/cmp/CertReqTemplateContent.cs
index b229cd28b..c25c71ad1 100644
--- a/crypto/src/asn1/cmp/CertReqTemplateContent.cs
+++ b/crypto/src/asn1/cmp/CertReqTemplateContent.cs
@@ -9,12 +9,13 @@ namespace Org.BouncyCastle.Asn1.Cmp
      * GenRep:    {id-it 19}, CertReqTemplateContent | &lt; absent &gt;
      * <p>
      * CertReqTemplateValue  ::= CertReqTemplateContent
-     * <p>
+     * </p><p>
      * CertReqTemplateContent ::= SEQUENCE {
      * certTemplate           CertTemplate,
      * keySpec                Controls OPTIONAL }
-     * <p>
+     * </p><p>
      * Controls  ::= SEQUENCE SIZE (1..MAX) OF AttributeTypeAndValue
+     * </p>
      */
     public class CertReqTemplateContent
         : Asn1Encodable
diff --git a/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs b/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
index fa83841a4..1b3227c47 100644
--- a/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
+++ b/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
@@ -234,8 +234,9 @@ namespace Org.BouncyCastle.Asn1.Cmp
          * 1.2.840.113549.1.9.16.1.21
          * <p>
          * id-ct   OBJECT IDENTIFIER ::= { id-smime  1 }  -- content types
-         * <p>
+         * </p><p>
          * id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21}
+         * </p>
          */
         public static readonly DerObjectIdentifier ct_encKeyWithID = new DerObjectIdentifier("1.2.840.113549.1.9.16.1.21");
 
diff --git a/crypto/src/asn1/cmp/CrlSource.cs b/crypto/src/asn1/cmp/CrlSource.cs
index 13aaa526a..9e2526ec2 100644
--- a/crypto/src/asn1/cmp/CrlSource.cs
+++ b/crypto/src/asn1/cmp/CrlSource.cs
@@ -12,7 +12,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
      * CRLSource ::= CHOICE {
      * dpn          [0] DistributionPointName,
      * issuer       [1] GeneralNames }
-     * <p>
+     * </p>
      */
     public class CrlSource
         : Asn1Encodable, IAsn1Choice
diff --git a/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs b/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs
index b1eaf616d..696b08b94 100644
--- a/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs
+++ b/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs
@@ -7,14 +7,15 @@ namespace Org.BouncyCastle.Asn1.Cmp
      * GenRep:    {id-it 18}, RootCaKeyUpdateContent | &lt; absent &gt;
      * <p>
      * RootCaCertValue ::= CMPCertificate
-     * <p>
+     * </p><p>
      * RootCaKeyUpdateValue ::= RootCaKeyUpdateContent
-     * <p>
+     * </p><p>
      * RootCaKeyUpdateContent ::= SEQUENCE {
      * newWithNew       CMPCertificate,
      * newWithOld   [0] CMPCertificate OPTIONAL,
      * oldWithNew   [1] CMPCertificate OPTIONAL
      * }
+     * </p>
      */
     public class RootCaKeyUpdateContent
         : Asn1Encodable
diff --git a/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs b/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs
index 78354896f..f6335cdac 100644
--- a/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs
+++ b/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs
@@ -1,6 +1,4 @@
-using System;
-
-namespace Org.BouncyCastle.Asn1.Cms
+namespace Org.BouncyCastle.Asn1.Cms
 {
     public class OtherRevocationInfoFormat
         : Asn1Encodable
@@ -44,8 +42,8 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public static OtherRevocationInfoFormat GetInstance(object obj)
         {
-            if (obj is OtherRevocationInfoFormat)
-                return (OtherRevocationInfoFormat)obj;
+            if (obj is OtherRevocationInfoFormat otherRevocationInfoFormat)
+                return otherRevocationInfoFormat;
             if (obj != null)
                 return new OtherRevocationInfoFormat(Asn1Sequence.GetInstance(obj));
             return null;
diff --git a/crypto/src/asn1/cms/Time.cs b/crypto/src/asn1/cms/Time.cs
index 89f1e4dae..67c73285b 100644
--- a/crypto/src/asn1/cms/Time.cs
+++ b/crypto/src/asn1/cms/Time.cs
@@ -35,17 +35,15 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public Time(DateTime date)
         {
-            string d = date.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture) + "Z";
+            DateTime d = date.ToUniversalTime();
 
-			int year = int.Parse(d.Substring(0, 4));
-
-			if (year < 1950 || year > 2049)
+			if (d.Year < 1950 || d.Year > 2049)
             {
                 time = new DerGeneralizedTime(d);
             }
             else
             {
-                time = new DerUtcTime(d.Substring(2));
+                time = new DerUtcTime(d);
             }
         }
 
diff --git a/crypto/src/asn1/cryptlib/CryptlibObjectIdentifiers.cs b/crypto/src/asn1/cryptlib/CryptlibObjectIdentifiers.cs
new file mode 100644
index 000000000..e7208bab2
--- /dev/null
+++ b/crypto/src/asn1/cryptlib/CryptlibObjectIdentifiers.cs
@@ -0,0 +1,11 @@
+namespace Org.BouncyCastle.Asn1.Cryptlib
+{
+    internal class CryptlibObjectIdentifiers
+    {
+        internal static readonly DerObjectIdentifier cryptlib = new DerObjectIdentifier("1.3.6.1.4.1.3029");
+
+        internal static readonly DerObjectIdentifier ecc = cryptlib.Branch("1.5");
+
+        internal static readonly DerObjectIdentifier curvey25519 = ecc.Branch("1");
+    }
+}
diff --git a/crypto/src/asn1/x509/KeyPurposeId.cs b/crypto/src/asn1/x509/KeyPurposeId.cs
index 1a564b97a..d0b9bb7e6 100644
--- a/crypto/src/asn1/x509/KeyPurposeId.cs
+++ b/crypto/src/asn1/x509/KeyPurposeId.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Org.BouncyCastle.Asn1.X509
 {
     /**
@@ -9,30 +11,86 @@ namespace Org.BouncyCastle.Asn1.X509
     public sealed class KeyPurposeID
         : DerObjectIdentifier
     {
-        private const string IdKP = "1.3.6.1.5.5.7.3";
+        private const string id_kp = "1.3.6.1.5.5.7.3";
 
-		private KeyPurposeID(
-			string id)
+		private KeyPurposeID(string id)
 			: base(id)
         {
         }
 
 		public static readonly KeyPurposeID AnyExtendedKeyUsage = new KeyPurposeID(X509Extensions.ExtendedKeyUsage.Id + ".0");
-        public static readonly KeyPurposeID IdKPServerAuth = new KeyPurposeID(IdKP + ".1");
-        public static readonly KeyPurposeID IdKPClientAuth = new KeyPurposeID(IdKP + ".2");
-        public static readonly KeyPurposeID IdKPCodeSigning = new KeyPurposeID(IdKP + ".3");
-        public static readonly KeyPurposeID IdKPEmailProtection = new KeyPurposeID(IdKP + ".4");
-        public static readonly KeyPurposeID IdKPIpsecEndSystem = new KeyPurposeID(IdKP + ".5");
-        public static readonly KeyPurposeID IdKPIpsecTunnel = new KeyPurposeID(IdKP + ".6");
-        public static readonly KeyPurposeID IdKPIpsecUser = new KeyPurposeID(IdKP + ".7");
-        public static readonly KeyPurposeID IdKPTimeStamping = new KeyPurposeID(IdKP + ".8");
-        public static readonly KeyPurposeID IdKPOcspSigning = new KeyPurposeID(IdKP + ".9");
-
-		//
+
+        public static readonly KeyPurposeID id_kp_serverAuth = new KeyPurposeID(id_kp + ".1");
+        public static readonly KeyPurposeID id_kp_clientAuth = new KeyPurposeID(id_kp + ".2");
+        public static readonly KeyPurposeID id_kp_codeSigning = new KeyPurposeID(id_kp + ".3");
+        public static readonly KeyPurposeID id_kp_emailProtection = new KeyPurposeID(id_kp + ".4");
+        public static readonly KeyPurposeID id_kp_ipsecEndSystem = new KeyPurposeID(id_kp + ".5");
+        public static readonly KeyPurposeID id_kp_ipsecTunnel = new KeyPurposeID(id_kp + ".6");
+        public static readonly KeyPurposeID id_kp_ipsecUser = new KeyPurposeID(id_kp + ".7");
+        public static readonly KeyPurposeID id_kp_timeStamping = new KeyPurposeID(id_kp + ".8");
+        public static readonly KeyPurposeID id_kp_OCSPSigning = new KeyPurposeID(id_kp + ".9");
+        public static readonly KeyPurposeID id_kp_dvcs = new KeyPurposeID(id_kp + ".10");
+        public static readonly KeyPurposeID id_kp_sbgpCertAAServerAuth = new KeyPurposeID(id_kp + ".11");
+        public static readonly KeyPurposeID id_kp_scvp_responder = new KeyPurposeID(id_kp + ".12");
+        public static readonly KeyPurposeID id_kp_eapOverPPP = new KeyPurposeID(id_kp + ".13");
+        public static readonly KeyPurposeID id_kp_eapOverLAN = new KeyPurposeID(id_kp + ".14");
+        public static readonly KeyPurposeID id_kp_scvpServer = new KeyPurposeID(id_kp + ".15");
+        public static readonly KeyPurposeID id_kp_scvpClient = new KeyPurposeID(id_kp + ".16");
+        public static readonly KeyPurposeID id_kp_ipsecIKE = new KeyPurposeID(id_kp + ".17");
+        public static readonly KeyPurposeID id_kp_capwapAC = new KeyPurposeID(id_kp + ".18");
+        public static readonly KeyPurposeID id_kp_capwapWTP = new KeyPurposeID(id_kp + ".19");
+
+        public static readonly KeyPurposeID id_kp_cmcCA = new KeyPurposeID(id_kp + ".27");
+        public static readonly KeyPurposeID id_kp_cmcRA = new KeyPurposeID(id_kp + ".28");
+        public static readonly KeyPurposeID id_kp_cmKGA = new KeyPurposeID(id_kp + ".32");
+
+        //
         // microsoft key purpose ids
         //
-        public static readonly KeyPurposeID IdKPSmartCardLogon = new KeyPurposeID("1.3.6.1.4.1.311.20.2.2");
+        public static readonly KeyPurposeID id_kp_smartcardlogon = new KeyPurposeID("1.3.6.1.4.1.311.20.2.2");
+
+        public static readonly KeyPurposeID id_kp_macAddress = new KeyPurposeID("1.3.6.1.1.1.1.22");
+
+        /// <summary>Microsoft Server Gated Crypto (msSGC).</summary>
+        /// <remarks>see https://www.alvestrand.no/objectid/1.3.6.1.4.1.311.10.3.3.html</remarks>
+        public static readonly KeyPurposeID id_kp_msSGC = new KeyPurposeID("1.3.6.1.4.1.311.10.3.3");
+
+        private const string id_pkinit = "1.3.6.1.5.2.3";
+
+        public static readonly KeyPurposeID scSysNodeNumber = new KeyPurposeID(id_pkinit + ".0");
+        public static readonly KeyPurposeID id_pkinit_authData = new KeyPurposeID(id_pkinit + ".1");
+        public static readonly KeyPurposeID id_pkinit_DHKeyData = new KeyPurposeID(id_pkinit + ".2");
+        public static readonly KeyPurposeID id_pkinit_rkeyData = new KeyPurposeID(id_pkinit + ".3");
+        public static readonly KeyPurposeID keyPurposeClientAuth = new KeyPurposeID(id_pkinit + ".4");
+        public static readonly KeyPurposeID keyPurposeKdc = new KeyPurposeID(id_pkinit + ".5");
+
+        /// <summary>Netscape Server Gated Crypto (nsSGC).</summary>
+        /// <remarks>see https://www.alvestrand.no/objectid/2.16.840.1.113730.4.1.html</remarks>
+        public static readonly KeyPurposeID id_kp_nsSGC = new KeyPurposeID("2.16.840.1.113730.4.1");
+
+        [Obsolete("Use 'id_kp_serverAuth' instead")]
+        public static readonly KeyPurposeID IdKPServerAuth = id_kp_serverAuth;
+        [Obsolete("Use 'id_kp_clientAuth' instead")]
+        public static readonly KeyPurposeID IdKPClientAuth = id_kp_clientAuth;
+        [Obsolete("Use 'id_kp_codeSigning' instead")]
+        public static readonly KeyPurposeID IdKPCodeSigning = id_kp_codeSigning;
+        [Obsolete("Use 'id_kp_emailProtection' instead")]
+        public static readonly KeyPurposeID IdKPEmailProtection = id_kp_emailProtection;
+        [Obsolete("Use 'id_kp_ipsecEndSystem' instead")]
+        public static readonly KeyPurposeID IdKPIpsecEndSystem = id_kp_ipsecEndSystem;
+        [Obsolete("Use 'id_kp_ipsecTunnel' instead")]
+        public static readonly KeyPurposeID IdKPIpsecTunnel = id_kp_ipsecTunnel;
+        [Obsolete("Use 'id_kp_ipsecUser' instead")]
+        public static readonly KeyPurposeID IdKPIpsecUser = id_kp_ipsecUser;
+        [Obsolete("Use 'id_kp_timeStamping' instead")]
+        public static readonly KeyPurposeID IdKPTimeStamping = id_kp_timeStamping;
+        [Obsolete("Use 'id_kp_OCSPSigning' instead")]
+        public static readonly KeyPurposeID IdKPOcspSigning = id_kp_OCSPSigning;
+
+        [Obsolete("Use 'id_kp_smartcardlogon' instead")]
+        public static readonly KeyPurposeID IdKPSmartCardLogon = id_kp_smartcardlogon;
 
-        public static readonly KeyPurposeID IdKPMacAddress = new KeyPurposeID("1.3.6.1.1.1.1.22");
+        [Obsolete("Use 'id_kp_macAddress' instead")]
+        public static readonly KeyPurposeID IdKPMacAddress = id_kp_macAddress;
     }
 }
diff --git a/crypto/src/asn1/x509/Time.cs b/crypto/src/asn1/x509/Time.cs
index 1a6ac15c0..8260043aa 100644
--- a/crypto/src/asn1/x509/Time.cs
+++ b/crypto/src/asn1/x509/Time.cs
@@ -35,17 +35,15 @@ namespace Org.BouncyCastle.Asn1.X509
          */
         public Time(DateTime date)
         {
-            string d = date.ToUniversalTime().ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture) + "Z";
+            DateTime d = date.ToUniversalTime();
 
-            int year = int.Parse(d.Substring(0, 4));
-
-            if (year < 1950 || year > 2049)
+            if (d.Year < 1950 || d.Year > 2049)
             {
                 time = new DerGeneralizedTime(d);
             }
             else
             {
-                time = new DerUtcTime(d.Substring(2));
+                time = new DerUtcTime(d);
             }
         }
 
diff --git a/crypto/src/bcpg/ArmoredInputStream.cs b/crypto/src/bcpg/ArmoredInputStream.cs
index 1c5ebd7c5..4fbb8baae 100644
--- a/crypto/src/bcpg/ArmoredInputStream.cs
+++ b/crypto/src/bcpg/ArmoredInputStream.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Text;
@@ -330,6 +331,26 @@ namespace Org.BouncyCastle.Bcpg
             return pos;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            /*
+             * TODO Currently can't return partial data when exception thrown (breaking test case), so we don't inherit
+             * the base class implementation. Probably the reason is that throws don't mark this instance as 'failed'.
+             */
+            int pos = 0;
+            while (pos < buffer.Length)
+            {
+                int b = ReadByte();
+                if (b < 0)
+                    break;
+
+                buffer[pos++] = (byte)b;
+            }
+            return pos;
+        }
+#endif
+
         public override int ReadByte()
         {
             if (start)
diff --git a/crypto/src/bcpg/ArmoredOutputStream.cs b/crypto/src/bcpg/ArmoredOutputStream.cs
index 7f3bcc2ef..bfed1972a 100644
--- a/crypto/src/bcpg/ArmoredOutputStream.cs
+++ b/crypto/src/bcpg/ArmoredOutputStream.cs
@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
+using System.Reflection;
 
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Collections;
@@ -98,7 +99,15 @@ namespace Org.BouncyCastle.Bcpg
         private static readonly string    footerStart = "-----END PGP ";
         private static readonly string    footerTail = "-----";
 
-        private static readonly string Version = "BCPG C# v" + typeof(ArmoredOutputStream).Assembly.GetName().Version;
+        private static string CreateVersion()
+        {
+            var assembly = Assembly.GetExecutingAssembly();
+            var title = assembly.GetCustomAttribute<AssemblyTitleAttribute>().Title;
+            var version = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
+            return title + " v" + version;
+        }
+
+        private static readonly string Version = CreateVersion();
 
         private readonly IDictionary<string, IList<string>> m_headers;
 
diff --git a/crypto/src/bcpg/BcpgInputStream.cs b/crypto/src/bcpg/BcpgInputStream.cs
index 7a19a90dd..3b6f61bbc 100644
--- a/crypto/src/bcpg/BcpgInputStream.cs
+++ b/crypto/src/bcpg/BcpgInputStream.cs
@@ -57,6 +57,21 @@ namespace Org.BouncyCastle.Bcpg
             return 1;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+			if (!next)
+				return m_in.Read(buffer);
+
+			if (nextB < 0)
+				return 0;
+
+            buffer[0] = (byte)nextB;
+            next = false;
+            return 1;
+        }
+#endif
+
         public byte[] ReadAll()
         {
 			return Streams.ReadAll(this);
@@ -312,9 +327,8 @@ namespace Org.BouncyCastle.Bcpg
 						int readLen = (dataLength > count || dataLength < 0) ? count : dataLength;
 						int len = m_in.Read(buffer, offset, readLen);
 						if (len < 1)
-						{
 							throw new EndOfStreamException("Premature end of stream in PartialInputStream");
-						}
+
 						dataLength -= len;
 						return len;
 					}
@@ -324,6 +338,29 @@ namespace Org.BouncyCastle.Bcpg
 				return 0;
 			}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override int Read(Span<byte> buffer)
+            {
+				do
+				{
+					if (dataLength != 0)
+					{
+                        int count = buffer.Length;
+						int readLen = (dataLength > count || dataLength < 0) ? count : dataLength;
+						int len = m_in.Read(buffer[..readLen]);
+						if (len < 1)
+							throw new EndOfStreamException("Premature end of stream in PartialInputStream");
+
+						dataLength -= len;
+						return len;
+					}
+				}
+				while (partial && ReadPartialDataLength() >= 0);
+
+				return 0;
+            }
+#endif
+
             private int ReadPartialDataLength()
             {
                 int l = m_in.ReadByte();
diff --git a/crypto/src/bcpg/BcpgOutputStream.cs b/crypto/src/bcpg/BcpgOutputStream.cs
index fbdd75bff..690686d88 100644
--- a/crypto/src/bcpg/BcpgOutputStream.cs
+++ b/crypto/src/bcpg/BcpgOutputStream.cs
@@ -164,7 +164,7 @@ namespace Org.BouncyCastle.Bcpg
 
             if (partialBuffer != null)
             {
-                PartialFlush(true);
+                PartialFlushLast();
                 partialBuffer = null;
             }
 
@@ -215,19 +215,26 @@ namespace Org.BouncyCastle.Bcpg
             }
         }
 
-        private void PartialFlush(bool isLast)
+        private void PartialFlush()
         {
-            if (isLast)
-            {
-                WriteNewPacketLength(partialOffset);
-                outStr.Write(partialBuffer, 0, partialOffset);
-            }
-            else
-            {
-                outStr.WriteByte((byte)(0xE0 | partialPower));
-                outStr.Write(partialBuffer, 0, partialBufferLength);
-            }
+            outStr.WriteByte((byte)(0xE0 | partialPower));
+            outStr.Write(partialBuffer, 0, partialBufferLength);
+            partialOffset = 0;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void PartialFlush(ref ReadOnlySpan<byte> buffer)
+        {
+            outStr.WriteByte((byte)(0xE0 | partialPower));
+            outStr.Write(buffer[..partialBufferLength]);
+            buffer = buffer[partialBufferLength..];
+        }
+#endif
 
+        private void PartialFlushLast()
+        {
+            WriteNewPacketLength(partialOffset);
+            outStr.Write(partialBuffer, 0, partialOffset);
             partialOffset = 0;
         }
 
@@ -235,40 +242,71 @@ namespace Org.BouncyCastle.Bcpg
         {
             Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            PartialWrite(buffer.AsSpan(offset, count));
+#else
             if (partialOffset == partialBufferLength)
             {
-                PartialFlush(false);
+                PartialFlush();
             }
 
             if (count <= (partialBufferLength - partialOffset))
             {
                 Array.Copy(buffer, offset, partialBuffer, partialOffset, count);
                 partialOffset += count;
+                return;
             }
-            else
+
+            int diff = partialBufferLength - partialOffset;
+            Array.Copy(buffer, offset, partialBuffer, partialOffset, diff);
+            offset += diff;
+            count -= diff;
+            PartialFlush();
+            while (count > partialBufferLength)
             {
-                int diff = partialBufferLength - partialOffset;
-                Array.Copy(buffer, offset, partialBuffer, partialOffset, diff);
-                offset += diff;
-                count -= diff;
-                PartialFlush(false);
-                while (count > partialBufferLength)
-                {
-                    Array.Copy(buffer, offset, partialBuffer, 0, partialBufferLength);
-                    offset += partialBufferLength;
-                    count -= partialBufferLength;
-                    PartialFlush(false);
-                }
-                Array.Copy(buffer, offset, partialBuffer, 0, count);
-                partialOffset += count;
+                Array.Copy(buffer, offset, partialBuffer, 0, partialBufferLength);
+                offset += partialBufferLength;
+                count -= partialBufferLength;
+                PartialFlush();
             }
+            Array.Copy(buffer, offset, partialBuffer, 0, count);
+            partialOffset = count;
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void PartialWrite(ReadOnlySpan<byte> buffer)
+        {
+            if (partialOffset == partialBufferLength)
+            {
+                PartialFlush();
+            }
+
+            if (buffer.Length <= (partialBufferLength - partialOffset))
+            {
+                buffer.CopyTo(partialBuffer.AsSpan(partialOffset));
+                partialOffset += buffer.Length;
+                return;
+            }
+
+            int diff = partialBufferLength - partialOffset;
+            buffer[..diff].CopyTo(partialBuffer.AsSpan(partialOffset));
+            buffer = buffer[diff..];
+            PartialFlush();
+            while (buffer.Length > partialBufferLength)
+            {
+                PartialFlush(ref buffer);
+            }
+            buffer.CopyTo(partialBuffer);
+            partialOffset = buffer.Length;
+        }
+#endif
+
         private void PartialWriteByte(byte value)
         {
             if (partialOffset == partialBufferLength)
             {
-                PartialFlush(false);
+                PartialFlush();
             }
 
             partialBuffer[partialOffset++] = value;
@@ -286,6 +324,20 @@ namespace Org.BouncyCastle.Bcpg
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            if (partialBuffer != null)
+            {
+                PartialWrite(buffer);
+            }
+            else
+            {
+                outStr.Write(buffer);
+            }
+        }
+#endif
+
         public override void WriteByte(byte value)
         {
             if (partialBuffer != null)
@@ -370,7 +422,7 @@ namespace Org.BouncyCastle.Bcpg
         {
             if (partialBuffer != null)
             {
-                PartialFlush(true);
+                PartialFlushLast();
                 Array.Clear(partialBuffer, 0, partialBuffer.Length);
                 partialBuffer = null;
             }
diff --git a/crypto/src/bcpg/ECDHPublicBCPGKey.cs b/crypto/src/bcpg/ECDHPublicBCPGKey.cs
index 5b6d9460e..e43100d3a 100644
--- a/crypto/src/bcpg/ECDHPublicBCPGKey.cs
+++ b/crypto/src/bcpg/ECDHPublicBCPGKey.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Math.EC;
 
 namespace Org.BouncyCastle.Bcpg
@@ -51,6 +52,21 @@ namespace Org.BouncyCastle.Bcpg
             VerifySymmetricKeyAlgorithm();
         }
 
+        public ECDHPublicBcpgKey(
+            DerObjectIdentifier oid,
+            BigInteger point,
+            HashAlgorithmTag hashAlgorithm,
+            SymmetricKeyAlgorithmTag symmetricKeyAlgorithm)
+            : base(oid, point)
+        {
+            reserved = 1;
+            hashFunctionId = hashAlgorithm;
+            symAlgorithmId = symmetricKeyAlgorithm;
+
+            VerifyHashAlgorithm();
+            VerifySymmetricKeyAlgorithm();
+        }
+
         public virtual byte Reserved
         {
             get { return reserved; }
diff --git a/crypto/src/bcpg/ECSecretBCPGKey.cs b/crypto/src/bcpg/ECSecretBCPGKey.cs
index 22e0a3473..eef632263 100644
--- a/crypto/src/bcpg/ECSecretBCPGKey.cs
+++ b/crypto/src/bcpg/ECSecretBCPGKey.cs
@@ -1,6 +1,5 @@
 using System;
 
-using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Math;
 
 namespace Org.BouncyCastle.Bcpg
@@ -9,18 +8,18 @@ namespace Org.BouncyCastle.Bcpg
     public class ECSecretBcpgKey
         : BcpgObject, IBcpgKey
     {
-        internal MPInteger x;
+        internal readonly MPInteger m_x;
 
         public ECSecretBcpgKey(
             BcpgInputStream bcpgIn)
         {
-            this.x = new MPInteger(bcpgIn);
+            m_x = new MPInteger(bcpgIn);
         }
 
         public ECSecretBcpgKey(
             BigInteger x)
         {
-            this.x = new MPInteger(x);
+            m_x = new MPInteger(x);
         }
 
 		/// <summary>The format, as a string, always "PGP".</summary>
@@ -45,12 +44,12 @@ namespace Org.BouncyCastle.Bcpg
         public override void Encode(
             BcpgOutputStream bcpgOut)
         {
-            bcpgOut.WriteObject(x);
+            bcpgOut.WriteObject(m_x);
         }
 
         public virtual BigInteger X
         {
-            get { return x.Value; }
+            get { return m_x.Value; }
         }
     }
 }
diff --git a/crypto/src/bcpg/EdDsaPublicBcpgKey.cs b/crypto/src/bcpg/EdDsaPublicBcpgKey.cs
new file mode 100644
index 000000000..f3250b746
--- /dev/null
+++ b/crypto/src/bcpg/EdDsaPublicBcpgKey.cs
@@ -0,0 +1,25 @@
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    public sealed class EdDsaPublicBcpgKey
+        : ECPublicBcpgKey
+    {
+        internal EdDsaPublicBcpgKey(BcpgInputStream bcpgIn)
+            : base(bcpgIn)
+        {
+        }
+
+        public EdDsaPublicBcpgKey(DerObjectIdentifier oid, ECPoint point)
+            : base(oid, point)
+        {
+        }
+
+        public EdDsaPublicBcpgKey(DerObjectIdentifier oid, BigInteger encodedPoint)
+            : base(oid, encodedPoint)
+        {
+        }
+    }
+}
diff --git a/crypto/src/bcpg/EdSecretBcpgKey.cs b/crypto/src/bcpg/EdSecretBcpgKey.cs
new file mode 100644
index 000000000..5b53f558d
--- /dev/null
+++ b/crypto/src/bcpg/EdSecretBcpgKey.cs
@@ -0,0 +1,43 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    public sealed class EdSecretBcpgKey
+        : BcpgObject, IBcpgKey
+    {
+        internal readonly MPInteger m_x;
+
+        public EdSecretBcpgKey(BcpgInputStream bcpgIn)
+        {
+            m_x = new MPInteger(bcpgIn);
+        }
+
+        public EdSecretBcpgKey(BigInteger x)
+        {
+            m_x = new MPInteger(x);
+        }
+
+        public string Format => "PGP";
+
+        public override byte[] GetEncoded()
+        {
+            try
+            {
+                return base.GetEncoded();
+            }
+            catch (Exception)
+            {
+                return null;
+            }
+        }
+
+        public override void Encode(BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WriteObject(m_x);
+        }
+
+        public BigInteger X => m_x.Value;
+    }
+}
diff --git a/crypto/src/bcpg/PublicKeyPacket.cs b/crypto/src/bcpg/PublicKeyPacket.cs
index bbed941dc..40c696a37 100644
--- a/crypto/src/bcpg/PublicKeyPacket.cs
+++ b/crypto/src/bcpg/PublicKeyPacket.cs
@@ -28,30 +28,33 @@ namespace Org.BouncyCastle.Bcpg
                 validDays = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
             }
 
-            algorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+            algorithm = (PublicKeyAlgorithmTag)bcpgIn.ReadByte();
 
-            switch ((PublicKeyAlgorithmTag) algorithm)
+            switch (algorithm)
             {
-                case PublicKeyAlgorithmTag.RsaEncrypt:
-                case PublicKeyAlgorithmTag.RsaGeneral:
-                case PublicKeyAlgorithmTag.RsaSign:
-                    key = new RsaPublicBcpgKey(bcpgIn);
-                    break;
-                case PublicKeyAlgorithmTag.Dsa:
-                    key = new DsaPublicBcpgKey(bcpgIn);
-                    break;
-                case PublicKeyAlgorithmTag.ElGamalEncrypt:
-                case PublicKeyAlgorithmTag.ElGamalGeneral:
-                    key = new ElGamalPublicBcpgKey(bcpgIn);
-                    break;
-                case PublicKeyAlgorithmTag.ECDH:
-                    key = new ECDHPublicBcpgKey(bcpgIn);
-                    break;
-                case PublicKeyAlgorithmTag.ECDsa:
-                    key = new ECDsaPublicBcpgKey(bcpgIn);
-                    break;
-                default:
-                    throw new IOException("unknown PGP public key algorithm encountered");
+            case PublicKeyAlgorithmTag.RsaEncrypt:
+            case PublicKeyAlgorithmTag.RsaGeneral:
+            case PublicKeyAlgorithmTag.RsaSign:
+                key = new RsaPublicBcpgKey(bcpgIn);
+                break;
+            case PublicKeyAlgorithmTag.Dsa:
+                key = new DsaPublicBcpgKey(bcpgIn);
+                break;
+            case PublicKeyAlgorithmTag.ElGamalEncrypt:
+            case PublicKeyAlgorithmTag.ElGamalGeneral:
+                key = new ElGamalPublicBcpgKey(bcpgIn);
+                break;
+            case PublicKeyAlgorithmTag.ECDH:
+                key = new ECDHPublicBcpgKey(bcpgIn);
+                break;
+            case PublicKeyAlgorithmTag.ECDsa:
+                key = new ECDsaPublicBcpgKey(bcpgIn);
+                break;
+            case PublicKeyAlgorithmTag.EdDsa:
+                key = new EdDsaPublicBcpgKey(bcpgIn);
+                break;
+            default:
+                throw new IOException("unknown PGP public key algorithm encountered");
             }
         }
 
diff --git a/crypto/src/bcpg/SignaturePacket.cs b/crypto/src/bcpg/SignaturePacket.cs
index dd9cc78e3..a0e8588b3 100644
--- a/crypto/src/bcpg/SignaturePacket.cs
+++ b/crypto/src/bcpg/SignaturePacket.cs
@@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Bcpg
 {
 	/// <remarks>Generic signature packet.</remarks>
     public class SignaturePacket
-        : ContainedPacket //, PublicKeyAlgorithmTag
+        : ContainedPacket
     {
 		private int						version;
         private int						signatureType;
@@ -128,41 +128,38 @@ namespace Org.BouncyCastle.Bcpg
                 case PublicKeyAlgorithmTag.RsaGeneral:
                 case PublicKeyAlgorithmTag.RsaSign:
                     MPInteger v = new MPInteger(bcpgIn);
-					signature = new MPInteger[]{ v };
+					signature = new MPInteger[1]{ v };
                     break;
 				case PublicKeyAlgorithmTag.Dsa:
                     MPInteger r = new MPInteger(bcpgIn);
                     MPInteger s = new MPInteger(bcpgIn);
-					signature = new MPInteger[]{ r, s };
+					signature = new MPInteger[2]{ r, s };
                     break;
                 case PublicKeyAlgorithmTag.ElGamalEncrypt: // yep, this really does happen sometimes.
                 case PublicKeyAlgorithmTag.ElGamalGeneral:
                     MPInteger p = new MPInteger(bcpgIn);
                     MPInteger g = new MPInteger(bcpgIn);
                     MPInteger y = new MPInteger(bcpgIn);
-					signature = new MPInteger[]{ p, g, y };
+					signature = new MPInteger[3]{ p, g, y };
                     break;
                 case PublicKeyAlgorithmTag.ECDsa:
+                case PublicKeyAlgorithmTag.EdDsa:
                     MPInteger ecR = new MPInteger(bcpgIn);
                     MPInteger ecS = new MPInteger(bcpgIn);
-                    signature = new MPInteger[]{ ecR, ecS };
+                    signature = new MPInteger[2]{ ecR, ecS };
                     break;
                 default:
-					if (keyAlgorithm >= PublicKeyAlgorithmTag.Experimental_1 && keyAlgorithm <= PublicKeyAlgorithmTag.Experimental_11)
-					{
-						signature = null;
-						MemoryStream bOut = new MemoryStream();
-						int ch;
-						while ((ch = bcpgIn.ReadByte()) >= 0)
-						{
-							bOut.WriteByte((byte) ch);
-						}
-						signatureEncoding = bOut.ToArray();
-					}
-					else
+					if (keyAlgorithm < PublicKeyAlgorithmTag.Experimental_1 || keyAlgorithm > PublicKeyAlgorithmTag.Experimental_11)
+                        throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
+
+                    signature = null;
+					MemoryStream bOut = new MemoryStream();
+					int ch;
+					while ((ch = bcpgIn.ReadByte()) >= 0)
 					{
-						throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
+						bOut.WriteByte((byte) ch);
 					}
+					signatureEncoding = bOut.ToArray();
 					break;
             }
         }
@@ -268,56 +265,53 @@ namespace Org.BouncyCastle.Bcpg
         */
         public byte[] GetSignatureTrailer()
         {
-            byte[] trailer = null;
-
 			if (version == 3)
             {
-                trailer = new byte[5];
-
-				long time = creationTime / 1000L;
+                long time = creationTime / 1000L;
 
+                byte[] trailer = new byte[5];
 				trailer[0] = (byte)signatureType;
                 trailer[1] = (byte)(time >> 24);
                 trailer[2] = (byte)(time >> 16);
-                trailer[3] = (byte)(time >> 8);
-                trailer[4] = (byte)(time);
+                trailer[3] = (byte)(time >>  8);
+                trailer[4] = (byte)(time      );
+                return trailer;
             }
-            else
-            {
-                MemoryStream sOut = new MemoryStream();
 
-				sOut.WriteByte((byte)this.Version);
-                sOut.WriteByte((byte)this.SignatureType);
-                sOut.WriteByte((byte)this.KeyAlgorithm);
-                sOut.WriteByte((byte)this.HashAlgorithm);
+            MemoryStream sOut = new MemoryStream();
 
-				MemoryStream hOut = new MemoryStream();
-                SignatureSubpacket[] hashed = this.GetHashedSubPackets();
+			sOut.WriteByte((byte)Version);
+            sOut.WriteByte((byte)SignatureType);
+            sOut.WriteByte((byte)KeyAlgorithm);
+            sOut.WriteByte((byte)HashAlgorithm);
 
-				for (int i = 0; i != hashed.Length; i++)
-                {
-                    hashed[i].Encode(hOut);
-                }
-
-				byte[] data = hOut.ToArray();
+            // Mark position an reserve two bytes for length
+            long lengthPosition = sOut.Position;
+            sOut.WriteByte(0x00);
+            sOut.WriteByte(0x00);
 
-				sOut.WriteByte((byte)(data.Length >> 8));
-                sOut.WriteByte((byte)data.Length);
-                sOut.Write(data, 0, data.Length);
+            SignatureSubpacket[] hashed = GetHashedSubPackets();
+			for (int i = 0; i != hashed.Length; i++)
+            {
+                hashed[i].Encode(sOut);
+            }
 
-				byte[] hData = sOut.ToArray();
+            ushort dataLength = Convert.ToUInt16(sOut.Position - lengthPosition - 2);
+            uint hDataLength = Convert.ToUInt32(sOut.Position);
 
-				sOut.WriteByte((byte)this.Version);
-                sOut.WriteByte((byte)0xff);
-                sOut.WriteByte((byte)(hData.Length>> 24));
-                sOut.WriteByte((byte)(hData.Length >> 16));
-                sOut.WriteByte((byte)(hData.Length >> 8));
-                sOut.WriteByte((byte)(hData.Length));
+			sOut.WriteByte((byte)Version);
+            sOut.WriteByte(0xff);
+            sOut.WriteByte((byte)(hDataLength >> 24));
+            sOut.WriteByte((byte)(hDataLength >> 16));
+            sOut.WriteByte((byte)(hDataLength >>  8));
+            sOut.WriteByte((byte)(hDataLength      ));
 
-				trailer = sOut.ToArray();
-            }
+            // Reset position and fill in length
+            sOut.Position = lengthPosition;
+            sOut.WriteByte((byte)(dataLength >> 8));
+            sOut.WriteByte((byte)(dataLength     ));
 
-			return trailer;
+            return sOut.ToArray();
         }
 
 		public PublicKeyAlgorithmTag KeyAlgorithm
diff --git a/crypto/src/cms/CMSAuthenticatedDataGenerator.cs b/crypto/src/cms/CMSAuthenticatedDataGenerator.cs
index 6c68bccd1..f6827157c 100644
--- a/crypto/src/cms/CMSAuthenticatedDataGenerator.cs
+++ b/crypto/src/cms/CMSAuthenticatedDataGenerator.cs
@@ -62,9 +62,7 @@ namespace Org.BouncyCastle.Cms
 
 				Asn1Encodable asn1Params = GenerateAsn1Parameters(macOid, encKeyBytes);
 
-				ICipherParameters cipherParameters;
-				macAlgId = GetAlgorithmIdentifier(
-				macOid, encKey, asn1Params, out cipherParameters);
+				macAlgId = GetAlgorithmIdentifier(macOid, encKey, asn1Params, out var cipherParameters);
 
 				IMac mac = MacUtilities.GetMac(macOid);
 				// TODO Confirm no ParametersWithRandom needed
@@ -72,7 +70,7 @@ namespace Org.BouncyCastle.Cms
 //	            mac.Init(cipherParameters);
 				mac.Init(encKey);
 
-				MemoryStream bOut = new MemoryStream();
+				var bOut = new MemoryStream();
 				Stream mOut = new TeeOutputStream(bOut, new MacSink(mac));
 
 				content.Write(mOut);
@@ -97,7 +95,7 @@ namespace Org.BouncyCastle.Cms
 				throw new CmsException("exception decoding algorithm parameters.", e);
 			}
 
-			Asn1EncodableVector recipientInfos = new Asn1EncodableVector();
+			var recipientInfos = new Asn1EncodableVector();
 
 			foreach (RecipientInfoGenerator rig in recipientInfoGenerators) 
 			{
@@ -115,11 +113,11 @@ namespace Org.BouncyCastle.Cms
 				}
 			}
 			
-			ContentInfo eci = new ContentInfo(CmsObjectIdentifiers.Data, encContent);
-			
-			ContentInfo contentInfo = new ContentInfo(
-			CmsObjectIdentifiers.AuthenticatedData,
-			new AuthenticatedData(null, new DerSet(recipientInfos), macAlgId, null, eci, null, macResult, null));
+			var eci = new ContentInfo(CmsObjectIdentifiers.Data, encContent);
+
+			var contentInfo = new ContentInfo(
+				CmsObjectIdentifiers.AuthenticatedData,
+				new AuthenticatedData(null, new DerSet(recipientInfos), macAlgId, null, eci, null, macResult, null));
 			
 			return new CmsAuthenticatedData(contentInfo);
 		}
diff --git a/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs b/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
index b2c5cac28..6348431a2 100644
--- a/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
@@ -237,7 +237,14 @@ namespace Org.BouncyCastle.Cms
                 macStream.Write(buffer, offset, count);
             }
 
-			public override void WriteByte(byte value)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void Write(ReadOnlySpan<byte> buffer)
+            {
+                macStream.Write(buffer);
+            }
+#endif
+
+            public override void WriteByte(byte value)
 			{
 				macStream.WriteByte(value);
 			}
diff --git a/crypto/src/cms/CMSCompressedDataStreamGenerator.cs b/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
index 1594500cd..3669c0b3a 100644
--- a/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
@@ -4,6 +4,7 @@ using System.IO;
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Cms;
 using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.IO;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.IO;
 
@@ -118,7 +119,14 @@ namespace Org.BouncyCastle.Cms
 				_out.Write(buffer, offset, count);
 			}
 
-			public override void WriteByte(byte value)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void Write(ReadOnlySpan<byte> buffer)
+			{
+                _out.Write(buffer);
+            }
+#endif
+
+            public override void WriteByte(byte value)
 			{
 				_out.WriteByte(value);
 			}
diff --git a/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs b/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
index 6a362e13f..ad356a208 100644
--- a/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
@@ -240,7 +240,14 @@ namespace Org.BouncyCastle.Cms
 				_out.Write(buffer, offset, count);
 			}
 
-			public override void WriteByte(byte value)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void Write(ReadOnlySpan<byte> buffer)
+            {
+                _out.Write(buffer);
+            }
+#endif
+
+            public override void WriteByte(byte value)
 			{
 				_out.WriteByte(value);
 			}
diff --git a/crypto/src/cms/CMSProcessableFile.cs b/crypto/src/cms/CMSProcessableFile.cs
index 9444f885d..255c8432f 100644
--- a/crypto/src/cms/CMSProcessableFile.cs
+++ b/crypto/src/cms/CMSProcessableFile.cs
@@ -1,5 +1,3 @@
-#if !PORTABLE || DOTNET
-using System;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
@@ -42,4 +40,3 @@ namespace Org.BouncyCastle.Cms
 		}
 	}
 }
-#endif
diff --git a/crypto/src/cms/CMSSignedData.cs b/crypto/src/cms/CMSSignedData.cs
index 3d4ce05a6..773e15be0 100644
--- a/crypto/src/cms/CMSSignedData.cs
+++ b/crypto/src/cms/CMSSignedData.cs
@@ -204,6 +204,11 @@ namespace Org.BouncyCastle.Cms
 			return Helper.GetCrls(signedData.CRLs);
 		}
 
+        public IStore<Asn1Encodable> GetOtherRevInfos(DerObjectIdentifier otherRevInfoFormat)
+		{
+			return Helper.GetOtherRevInfos(signedData.CRLs, otherRevInfoFormat);
+		}
+
 		/// <summary>
 		/// Return the <c>DerObjectIdentifier</c> associated with the encapsulated
 		/// content info structure carried in the signed data.
@@ -308,7 +313,7 @@ namespace Org.BouncyCastle.Cms
 			return cms;
 		}
 
-		/**
+        /**
 		* Replace the certificate and CRL information associated with this
 		* CmsSignedData object with the new one passed in.
 		*
@@ -318,48 +323,69 @@ namespace Org.BouncyCastle.Cms
 		* @return a new signed data object.
 		* @exception CmsException if there is an error processing the stores
 		*/
-		public static CmsSignedData ReplaceCertificatesAndCrls(CmsSignedData signedData, IStore<X509Certificate> x509Certs,
-			IStore<X509Crl> x509Crls, IStore<X509V2AttributeCertificate> x509AttrCerts)
+        public static CmsSignedData ReplaceCertificatesAndCrls(CmsSignedData signedData,
+            IStore<X509Certificate> x509Certs, IStore<X509Crl> x509Crls)
 		{
-			//
-			// copy
-			//
-			CmsSignedData cms = new CmsSignedData(signedData);
+            return ReplaceCertificatesAndRevocations(signedData, x509Certs, x509Crls, null, null);
+		}
+
+        public static CmsSignedData ReplaceCertificatesAndCrls(CmsSignedData signedData,
+			IStore<X509Certificate> x509Certs, IStore<X509Crl> x509Crls,
+			IStore<X509V2AttributeCertificate> x509AttrCerts)
+		{
+            return ReplaceCertificatesAndRevocations(signedData, x509Certs, x509Crls, x509AttrCerts, null);
+        }
+
+        public static CmsSignedData ReplaceCertificatesAndRevocations(CmsSignedData signedData,
+            IStore<X509Certificate> x509Certs, IStore<X509Crl> x509Crls,
+            IStore<X509V2AttributeCertificate> x509AttrCerts, IStore<OtherRevocationInfoFormat> otherRevocationInfos)
+        {
+            //
+            // copy
+            //
+            CmsSignedData cms = new CmsSignedData(signedData);
 
 			//
 			// replace the certs and crls in the SignedData object
 			//
 			Asn1Set certSet = null;
-			Asn1Set crlSet = null;
+			Asn1Set revocationSet = null;
 
 			if (x509Certs != null || x509AttrCerts != null)
 			{
-				var certs = new List<Asn1Encodable>();
-
+				var certificates = new List<Asn1Encodable>();
 				if (x509Certs != null)
 				{
-					certs.AddRange(CmsUtilities.GetCertificatesFromStore(x509Certs));
+					certificates.AddRange(CmsUtilities.GetCertificatesFromStore(x509Certs));
 				}
 				if (x509AttrCerts != null)
 				{
-					certs.AddRange(CmsUtilities.GetAttributeCertificatesFromStore(x509AttrCerts));
+					certificates.AddRange(CmsUtilities.GetAttributeCertificatesFromStore(x509AttrCerts));
 				}
 
-				Asn1Set berSet = CmsUtilities.CreateBerSetFromList(certs);
+				Asn1Set berSet = CmsUtilities.CreateBerSetFromList(certificates);
 				if (berSet.Count > 0)
 				{
 					certSet = berSet;
 				}
 			}
 
-			if (x509Crls != null)
+			if (x509Crls != null || otherRevocationInfos != null)
 			{
-				var crls = CmsUtilities.GetCrlsFromStore(x509Crls);
+				var revocations = new List<Asn1Encodable>();
+				if (x509Crls != null)
+				{
+					revocations.AddRange(CmsUtilities.GetCrlsFromStore(x509Crls));
+				}
+				if (otherRevocationInfos != null)
+				{
+                    revocations.AddRange(CmsUtilities.GetOtherRevocationInfosFromStore(otherRevocationInfos));
+                }
 
-				Asn1Set berSet = CmsUtilities.CreateBerSetFromList(crls);
+				Asn1Set berSet = CmsUtilities.CreateBerSetFromList(revocations);
 				if (berSet.Count > 0)
 				{
-					crlSet = berSet;
+					revocationSet = berSet;
 				}
 			}
 
@@ -371,7 +397,7 @@ namespace Org.BouncyCastle.Cms
 				old.DigestAlgorithms,
 				old.EncapContentInfo,
 				certSet,
-				crlSet,
+				revocationSet,
 				old.SignerInfos);
 
 			//
diff --git a/crypto/src/cms/CMSSignedDataParser.cs b/crypto/src/cms/CMSSignedDataParser.cs
index 78e29e6a3..c5dc795a8 100644
--- a/crypto/src/cms/CMSSignedDataParser.cs
+++ b/crypto/src/cms/CMSSignedDataParser.cs
@@ -275,7 +275,14 @@ namespace Org.BouncyCastle.Cms
 			return Helper.GetCrls(_crlSet);
 		}
 
-		private void PopulateCertCrlSets()
+        public IStore<Asn1Encodable> GetOtherRevInfos(DerObjectIdentifier otherRevInfoFormat)
+        {
+            PopulateCertCrlSets();
+
+            return Helper.GetOtherRevInfos(_crlSet, otherRevInfoFormat);
+        }
+
+        private void PopulateCertCrlSets()
 		{
 			if (_isCertCrlParsed)
 				return;
diff --git a/crypto/src/cms/CMSSignedDataStreamGenerator.cs b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
index f934b9259..3f2da5f7e 100644
--- a/crypto/src/cms/CMSSignedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
@@ -783,7 +783,14 @@ namespace Org.BouncyCastle.Cms
 				_out.Write(buffer, offset, count);
 			}
 
-			public override void WriteByte(byte value)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void Write(ReadOnlySpan<byte> buffer)
+            {
+                _out.Write(buffer);
+            }
+#endif
+
+            public override void WriteByte(byte value)
 			{
 				_out.WriteByte(value);
 			}
diff --git a/crypto/src/cms/CMSSignedGenerator.cs b/crypto/src/cms/CMSSignedGenerator.cs
index c16f6e83c..fd40de469 100644
--- a/crypto/src/cms/CMSSignedGenerator.cs
+++ b/crypto/src/cms/CMSSignedGenerator.cs
@@ -129,12 +129,10 @@ namespace Org.BouncyCastle.Cms
             m_algorithms["GOST3411WITHGOST3410-2001"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001;
             m_algorithms["GOST3411WITHECGOST3410-2012-256"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256;
             m_algorithms["GOST3411WITHECGOST3410-2012-512"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512;
-            m_algorithms["GOST3411WITHGOST3410-2012-256"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256;
-            m_algorithms["GOST3411WITHGOST3410-2012-512"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512;
+            m_algorithms["GOST3411-2012-256WITHECGOST3410"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256;
             m_algorithms["GOST3411-2012-256WITHECGOST3410-2012-256"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256;
+            m_algorithms["GOST3411-2012-512WITHECGOST3410"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512;
             m_algorithms["GOST3411-2012-512WITHECGOST3410-2012-512"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512;
-            m_algorithms["GOST3411-2012-256WITHGOST3410-2012-256"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256;
-            m_algorithms["GOST3411-2012-512WITHGOST3410-2012-512"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512;
             m_algorithms["SHA1WITHPLAIN-ECDSA"] = BsiObjectIdentifiers.ecdsa_plain_SHA1;
             m_algorithms["SHA224WITHPLAIN-ECDSA"] = BsiObjectIdentifiers.ecdsa_plain_SHA224;
             m_algorithms["SHA256WITHPLAIN-ECDSA"] = BsiObjectIdentifiers.ecdsa_plain_SHA256;
@@ -508,6 +506,8 @@ namespace Org.BouncyCastle.Cms
         public static readonly string EncryptionRsaPss = PkcsObjectIdentifiers.IdRsassaPss.Id;
         public static readonly string EncryptionGost3410 = CryptoProObjectIdentifiers.GostR3410x94.Id;
         public static readonly string EncryptionECGost3410 = CryptoProObjectIdentifiers.GostR3410x2001.Id;
+        public static readonly string EncryptionECGost3410_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256.Id;
+        public static readonly string EncryptionECGost3410_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512.Id;
 
         internal List<Asn1Encodable> _certs = new List<Asn1Encodable>();
         internal List<Asn1Encodable> _crls = new List<Asn1Encodable>();
@@ -588,6 +588,23 @@ namespace Org.BouncyCastle.Cms
             _crls.AddRange(CmsUtilities.GetCrlsFromStore(crlStore));
         }
 
+        public void AddOtherRevocationInfo(OtherRevocationInfoFormat otherRevocationInfo)
+        {
+            CmsUtilities.ValidateOtherRevocationInfo(otherRevocationInfo);
+            _crls.Add(new DerTaggedObject(false, 1, otherRevocationInfo));
+        }
+
+        public void AddOtherRevocationInfos(IStore<OtherRevocationInfoFormat> otherRevocationInfoStore)
+        {
+            _crls.AddRange(CmsUtilities.GetOtherRevocationInfosFromStore(otherRevocationInfoStore));
+        }
+
+        public void AddOtherRevocationInfos(DerObjectIdentifier otherRevInfoFormat,
+            IStore<Asn1Encodable> otherRevInfoStore)
+        {
+            _crls.AddRange(CmsUtilities.GetOtherRevocationInfosFromStore(otherRevInfoStore, otherRevInfoFormat));
+        }
+
         /**
 		 * Add a store of precalculated signers to the generator.
 		 *
diff --git a/crypto/src/cms/CMSSignedHelper.cs b/crypto/src/cms/CMSSignedHelper.cs
index 8df9e8f01..9db39549b 100644
--- a/crypto/src/cms/CMSSignedHelper.cs
+++ b/crypto/src/cms/CMSSignedHelper.cs
@@ -2,8 +2,10 @@ using System;
 using System.Collections.Generic;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
 using Org.BouncyCastle.Asn1.CryptoPro;
 using Org.BouncyCastle.Asn1.Eac;
+using Org.BouncyCastle.Asn1.Esf;
 using Org.BouncyCastle.Asn1.Nist;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
@@ -85,8 +87,8 @@ namespace Org.BouncyCastle.Cms
 			AddEntries(EacObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
             AddEntries(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, "GOST3411", "GOST3410");
             AddEntries(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001, "GOST3411", "ECGOST3410");
-            AddEntries(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411_2012_256", "ECGOST3410");
-            AddEntries(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411_2012_512", "ECGOST3410");
+            AddEntries(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256", "ECGOST3410");
+            AddEntries(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512", "ECGOST3410");
 
             m_encryptionAlgs.Add(X9ObjectIdentifiers.IdDsa.Id, "DSA");
 			m_encryptionAlgs.Add(PkcsObjectIdentifiers.RsaEncryption.Id, "RSA");
@@ -119,8 +121,8 @@ namespace Org.BouncyCastle.Cms
 			m_digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, "RIPEMD256");
 			m_digestAlgs.Add(CryptoProObjectIdentifiers.GostR3411.Id,  "GOST3411");
 			m_digestAlgs.Add("1.3.6.1.4.1.5849.1.2.1",  "GOST3411");
-            m_digestAlgs.Add(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.Id, "GOST3411_2012_256");
-            m_digestAlgs.Add(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.Id, "GOST3411_2012_512");
+            m_digestAlgs.Add(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.Id, "GOST3411-2012-256");
+            m_digestAlgs.Add(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.Id, "GOST3411-2012-512");
 
             m_digestAliases.Add("SHA1", new string[]{ "SHA-1" });
 			m_digestAliases.Add("SHA224", new string[]{ "SHA-224" });
@@ -261,6 +263,22 @@ namespace Org.BouncyCastle.Cms
                 {
                     encOID = CmsSignedGenerator.EncryptionECGost3410;
                 }
+                else if (ecPrivKey.Parameters is ECGost3410Parameters ecGost3410Parameters)
+                {
+					var digestParamSet = ecGost3410Parameters.DigestParamSet;
+                    if (digestParamSet.Equals(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256))
+					{
+                        encOID = CmsSignedGenerator.EncryptionECGost3410_2012_256;
+                    }
+                    else if (digestParamSet.Equals(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512))
+					{
+                        encOID = CmsSignedGenerator.EncryptionECGost3410_2012_512;
+                    }
+                    else
+					{
+                        throw new ArgumentException("can't determine GOST3410 algorithm");
+                    }
+                }
                 else
 				{
 					// TODO Should we insist on algName being one of "EC" or "ECDSA", as Java does?
@@ -332,5 +350,29 @@ namespace Org.BouncyCastle.Cms
 			}
 			return CollectionUtilities.CreateStore(contents);
 		}
+
+        internal IStore<Asn1Encodable> GetOtherRevInfos(Asn1Set crlSet, DerObjectIdentifier otherRevInfoFormat)
+        {
+            var contents = new List<Asn1Encodable>();
+            if (crlSet != null && otherRevInfoFormat != null)
+            {
+                foreach (Asn1Encodable ae in crlSet)
+                {
+                    if (ae != null && ae.ToAsn1Object() is Asn1TaggedObject taggedObject)
+                    {
+						if (taggedObject.HasContextTag(1))
+						{
+                            var otherRevocationInfo = OtherRevocationInfoFormat.GetInstance(taggedObject, false);
+
+							if (otherRevInfoFormat.Equals(otherRevocationInfo.InfoFormat))
+							{
+								contents.Add(otherRevocationInfo.Info);
+							}
+                        }
+                    }
+                }
+            }
+            return CollectionUtilities.CreateStore(contents);
+        }
     }
 }
diff --git a/crypto/src/cms/CMSTypedStream.cs b/crypto/src/cms/CMSTypedStream.cs
index d4ca62256..7a66f0c74 100644
--- a/crypto/src/cms/CMSTypedStream.cs
+++ b/crypto/src/cms/CMSTypedStream.cs
@@ -59,13 +59,6 @@ namespace Org.BouncyCastle.Cms
 			{
 			}
 
-#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            public override void CopyTo(Stream destination, int bufferSize)
-            {
-				s.CopyTo(destination, bufferSize);
-            }
-#endif
-
             public override int Read(byte[]	buf, int off, int len)
 			{
 				return Streams.ReadFully(s, buf, off, len);
diff --git a/crypto/src/cms/CMSUtils.cs b/crypto/src/cms/CMSUtils.cs
index 6800c1d2a..1a1577c4e 100644
--- a/crypto/src/cms/CMSUtils.cs
+++ b/crypto/src/cms/CMSUtils.cs
@@ -4,6 +4,7 @@ using System.IO;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Ocsp;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Utilities.Collections;
 using Org.BouncyCastle.Utilities.IO;
@@ -112,12 +113,46 @@ namespace Org.BouncyCastle.Cms
                 foreach (var crl in crlStore.EnumerateMatches(null))
                 {
                     result.Add(crl.CertificateList);
-                }
+				}
 			}
 			return result;
 		}
 
-		internal static Asn1Set CreateBerSetFromList(IEnumerable<Asn1Encodable> elements)
+        internal static List<Asn1TaggedObject> GetOtherRevocationInfosFromStore(
+			IStore<OtherRevocationInfoFormat> otherRevocationInfoStore)
+        {
+            var result = new List<Asn1TaggedObject>();
+            if (otherRevocationInfoStore != null)
+            {
+                foreach (var otherRevocationInfo in otherRevocationInfoStore.EnumerateMatches(null))
+                {
+                    ValidateOtherRevocationInfo(otherRevocationInfo);
+
+                    result.Add(new DerTaggedObject(false, 1, otherRevocationInfo));
+                }
+            }
+            return result;
+        }
+
+        internal static List<DerTaggedObject> GetOtherRevocationInfosFromStore(IStore<Asn1Encodable> otherRevInfoStore,
+            DerObjectIdentifier otherRevInfoFormat)
+        {
+			var result = new List<DerTaggedObject>();
+			if (otherRevInfoStore != null && otherRevInfoFormat != null)
+			{
+				foreach (var otherRevInfo in otherRevInfoStore.EnumerateMatches(null))
+				{
+                    var otherRevocationInfo = new OtherRevocationInfoFormat(otherRevInfoFormat, otherRevInfo);
+
+                    ValidateOtherRevocationInfo(otherRevocationInfo);
+
+                    result.Add(new DerTaggedObject(false, 1, otherRevocationInfo));
+				}
+			}
+			return result;
+        }
+
+        internal static Asn1Set CreateBerSetFromList(IEnumerable<Asn1Encodable> elements)
 		{
 			Asn1EncodableVector v = new Asn1EncodableVector();
 
@@ -157,5 +192,16 @@ namespace Org.BouncyCastle.Cms
 			TbsCertificateStructure tbsCert = GetTbsCertificateStructure(cert);
 			return new IssuerAndSerialNumber(tbsCert.Issuer, tbsCert.SerialNumber.Value);
 		}
-	}
+
+        internal static void ValidateOtherRevocationInfo(OtherRevocationInfoFormat otherRevocationInfo)
+        {
+            if (CmsObjectIdentifiers.id_ri_ocsp_response.Equals(otherRevocationInfo.InfoFormat))
+			{
+				OcspResponse ocspResponse = OcspResponse.GetInstance(otherRevocationInfo.Info);
+
+                if (OcspResponseStatus.Successful != ocspResponse.ResponseStatus.IntValueExact)
+                    throw new ArgumentException("cannot add unsuccessful OCSP response to CMS SignedData");
+            }
+        }
+    }
 }
diff --git a/crypto/src/cms/OriginatorInfoGenerator.cs b/crypto/src/cms/OriginatorInfoGenerator.cs
index d7d24dcc4..ec6d2d8d8 100644
--- a/crypto/src/cms/OriginatorInfoGenerator.cs
+++ b/crypto/src/cms/OriginatorInfoGenerator.cs
@@ -1,9 +1,7 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Cms;
-using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Utilities.Collections;
 using Org.BouncyCastle.X509;
 
@@ -11,30 +9,63 @@ namespace Org.BouncyCastle.Cms
 {
     public class OriginatorInfoGenerator
     {
-        private readonly List<X509CertificateStructure> origCerts;
-        private readonly List<CertificateList> origCrls;
+        private readonly List<Asn1Encodable> origCerts;
+        private readonly List<Asn1Encodable> origCrls;
 
         public OriginatorInfoGenerator(X509Certificate origCert)
         {
-            this.origCerts = new List<X509CertificateStructure>();
+            this.origCerts = new List<Asn1Encodable>{ origCert.CertificateStructure };
             this.origCrls = null;
-            origCerts.Add(origCert.CertificateStructure);
         }
 
-        public OriginatorInfoGenerator(IStore<X509Certificate> origCerts)
-            : this(origCerts, null)
+        public OriginatorInfoGenerator(IStore<X509Certificate> x509Certs)
+            : this(x509Certs, null, null, null)
         {
         }
 
-        public OriginatorInfoGenerator(IStore<X509Certificate> origCerts, IStore<X509Crl> origCrls)
+        public OriginatorInfoGenerator(IStore<X509Certificate> x509Certs, IStore<X509Crl> x509Crls)
+            : this(x509Certs, x509Crls, null, null)
         {
-            this.origCerts = CmsUtilities.GetCertificatesFromStore(origCerts);
-            this.origCrls = origCrls == null ? null : CmsUtilities.GetCrlsFromStore(origCrls);
         }
- 
+
+        public OriginatorInfoGenerator(IStore<X509Certificate> x509Certs, IStore<X509Crl> x509Crls,
+            IStore<X509V2AttributeCertificate> x509AttrCerts, IStore<OtherRevocationInfoFormat> otherRevocationInfos)
+        {
+            List<Asn1Encodable> certificates = null;
+            if (x509Certs != null || x509AttrCerts != null)
+            {
+                certificates = new List<Asn1Encodable>();
+                if (x509Certs != null)
+                {
+                    certificates.AddRange(CmsUtilities.GetCertificatesFromStore(x509Certs));
+                }
+                if (x509AttrCerts != null)
+                {
+                    certificates.AddRange(CmsUtilities.GetAttributeCertificatesFromStore(x509AttrCerts));
+                }
+            }
+
+            List<Asn1Encodable> revocations = null;
+            if (x509Crls != null || otherRevocationInfos != null)
+            {
+                revocations = new List<Asn1Encodable>();
+                if (x509Crls != null)
+                {
+                    revocations.AddRange(CmsUtilities.GetCrlsFromStore(x509Crls));
+                }
+                if (otherRevocationInfos != null)
+                {
+                    revocations.AddRange(CmsUtilities.GetOtherRevocationInfosFromStore(otherRevocationInfos));
+                }
+            }
+
+            this.origCerts = certificates;
+            this.origCrls = revocations;
+        }
+
         public virtual OriginatorInfo Generate()
         {
-            Asn1Set certSet = CmsUtilities.CreateDerSetFromList(origCerts);
+            Asn1Set certSet = origCerts == null ? null : CmsUtilities.CreateDerSetFromList(origCerts);
             Asn1Set crlSet = origCrls == null ? null : CmsUtilities.CreateDerSetFromList(origCrls);
             return new OriginatorInfo(certSet, crlSet);
         }
diff --git a/crypto/src/crypto/engines/Grain128AEADEngine.cs b/crypto/src/crypto/engines/Grain128AEADEngine.cs
index 174d010f3..c05cb0115 100644
--- a/crypto/src/crypto/engines/Grain128AEADEngine.cs
+++ b/crypto/src/crypto/engines/Grain128AEADEngine.cs
@@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Crypto.Engines
          */
         public void Init(bool forEncryption, ICipherParameters param)
         {
-            /**
+            /*
              * Grain encryption and decryption is completely symmetrical, so the
              * 'forEncryption' is irrelevant.
              */
@@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             if (keyBytes.Length != 16)
                 throw new ArgumentException("Grain-128AEAD key must be 128 bits long");
 
-            /**
+            /*
              * Initialize variables.
              */
             workingIV = new byte[keyBytes.Length];
@@ -238,7 +238,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             workingKey = keyBytes;
             workingIV = ivBytes;
 
-            /**
+            /*
              * Load NFSR and LFSR
              */
             Pack.LE_To_UInt32(workingKey, 0, nfsr);
diff --git a/crypto/src/crypto/engines/Salsa20Engine.cs b/crypto/src/crypto/engines/Salsa20Engine.cs
index 1ccf68902..7c2c1e1f9 100644
--- a/crypto/src/crypto/engines/Salsa20Engine.cs
+++ b/crypto/src/crypto/engines/Salsa20Engine.cs
@@ -252,11 +252,12 @@ namespace Org.BouncyCastle.Crypto.Engines
 			Pack.UInt32_To_LE(x, output, 0);
 		}
 
-		internal static void SalsaCore(int rounds, uint[] input, uint[] x)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static void SalsaCore(int rounds, ReadOnlySpan<uint> input, Span<uint> output)
 		{
-			if (input.Length != 16)
+			if (input.Length < 16)
 				throw new ArgumentException();
-			if (x.Length != 16)
+			if (output.Length < 16)
 				throw new ArgumentException();
 			if (rounds % 2 != 0)
 				throw new ArgumentException("Number of rounds must be even");
@@ -266,7 +267,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			{
 				Vector128<uint> b0, b1, b2, b3;
 				{
-                    var I = MemoryMarshal.AsBytes(input.AsSpan(0, 16));
+                    var I = MemoryMarshal.AsBytes(input[..16]);
 					var t0 = MemoryMarshal.Read<Vector128<short>>(I[0x00..0x10]);
                     var t1 = MemoryMarshal.Read<Vector128<short>>(I[0x10..0x20]);
                     var t2 = MemoryMarshal.Read<Vector128<short>>(I[0x20..0x30]);
@@ -315,7 +316,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                     var v2 = Sse41.Blend(u0, u2, 0x0F);
                     var v3 = Sse41.Blend(u1, u3, 0x3C);
 
-                    var X = MemoryMarshal.AsBytes(x.AsSpan(0, 16));
+                    var X = MemoryMarshal.AsBytes(output[..16]);
                     MemoryMarshal.Write(X[0x00..0x10], ref v0);
                     MemoryMarshal.Write(X[0x10..0x20], ref v1);
                     MemoryMarshal.Write(X[0x20..0x30], ref v2);
@@ -355,23 +356,81 @@ namespace Org.BouncyCastle.Crypto.Engines
                 QuarterRound(ref x15, ref x12, ref x13, ref x14);
 			}
 
-			x[ 0] = x00 + input[ 0];
-			x[ 1] = x01 + input[ 1];
-			x[ 2] = x02 + input[ 2];
-			x[ 3] = x03 + input[ 3];
-			x[ 4] = x04 + input[ 4];
-			x[ 5] = x05 + input[ 5];
-			x[ 6] = x06 + input[ 6];
-			x[ 7] = x07 + input[ 7];
-			x[ 8] = x08 + input[ 8];
-			x[ 9] = x09 + input[ 9];
-			x[10] = x10 + input[10];
-			x[11] = x11 + input[11];
-			x[12] = x12 + input[12];
-			x[13] = x13 + input[13];
-			x[14] = x14 + input[14];
-			x[15] = x15 + input[15];
+			output[ 0] = x00 + input[ 0];
+			output[ 1] = x01 + input[ 1];
+			output[ 2] = x02 + input[ 2];
+			output[ 3] = x03 + input[ 3];
+			output[ 4] = x04 + input[ 4];
+			output[ 5] = x05 + input[ 5];
+			output[ 6] = x06 + input[ 6];
+			output[ 7] = x07 + input[ 7];
+			output[ 8] = x08 + input[ 8];
+			output[ 9] = x09 + input[ 9];
+			output[10] = x10 + input[10];
+			output[11] = x11 + input[11];
+			output[12] = x12 + input[12];
+			output[13] = x13 + input[13];
+			output[14] = x14 + input[14];
+			output[15] = x15 + input[15];
 		}
+#else
+		internal static void SalsaCore(int rounds, uint[] input, uint[] output)
+		{
+			if (input.Length < 16)
+				throw new ArgumentException();
+			if (output.Length < 16)
+				throw new ArgumentException();
+			if (rounds % 2 != 0)
+				throw new ArgumentException("Number of rounds must be even");
+
+			uint x00 = input[ 0];
+			uint x01 = input[ 1];
+			uint x02 = input[ 2];
+			uint x03 = input[ 3];
+			uint x04 = input[ 4];
+			uint x05 = input[ 5];
+			uint x06 = input[ 6];
+			uint x07 = input[ 7];
+			uint x08 = input[ 8];
+			uint x09 = input[ 9];
+			uint x10 = input[10];
+			uint x11 = input[11];
+			uint x12 = input[12];
+			uint x13 = input[13];
+			uint x14 = input[14];
+			uint x15 = input[15];
+
+			for (int i = rounds; i > 0; i -= 2)
+			{
+				QuarterRound(ref x00, ref x04, ref x08, ref x12);
+                QuarterRound(ref x05, ref x09, ref x13, ref x01);
+                QuarterRound(ref x10, ref x14, ref x02, ref x06);
+                QuarterRound(ref x15, ref x03, ref x07, ref x11);
+
+                QuarterRound(ref x00, ref x01, ref x02, ref x03);
+                QuarterRound(ref x05, ref x06, ref x07, ref x04);
+                QuarterRound(ref x10, ref x11, ref x08, ref x09);
+                QuarterRound(ref x15, ref x12, ref x13, ref x14);
+			}
+
+			output[ 0] = x00 + input[ 0];
+			output[ 1] = x01 + input[ 1];
+			output[ 2] = x02 + input[ 2];
+			output[ 3] = x03 + input[ 3];
+			output[ 4] = x04 + input[ 4];
+			output[ 5] = x05 + input[ 5];
+			output[ 6] = x06 + input[ 6];
+			output[ 7] = x07 + input[ 7];
+			output[ 8] = x08 + input[ 8];
+			output[ 9] = x09 + input[ 9];
+			output[10] = x10 + input[10];
+			output[11] = x11 + input[11];
+			output[12] = x12 + input[12];
+			output[13] = x13 + input[13];
+			output[14] = x14 + input[14];
+			output[15] = x15 + input[15];
+		}
+#endif
 
 		internal void ResetLimitCounter()
 		{
diff --git a/crypto/src/crypto/engines/SerpentEngine.cs b/crypto/src/crypto/engines/SerpentEngine.cs
index bec459215..47f714a64 100644
--- a/crypto/src/crypto/engines/SerpentEngine.cs
+++ b/crypto/src/crypto/engines/SerpentEngine.cs
@@ -26,7 +26,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @param key  The user-key bytes (multiples of 4) to use.
         * @exception ArgumentException
         */
-        protected override int[] MakeWorkingKey(byte[] key)
+        internal override int[] MakeWorkingKey(byte[] key)
         {
             //
             // pad key to 256 bits
@@ -151,7 +151,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        protected override void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        internal override void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             X0 = (int)Pack.LE_To_UInt32(input);
             X1 = (int)Pack.LE_To_UInt32(input[4..]);
@@ -197,7 +197,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_LE((uint)(wKey[131] ^ X3), output[12..]);
         }
 
-        protected override void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        internal override void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             X0 = wKey[128] ^ (int)Pack.LE_To_UInt32(input);
             X1 = wKey[129] ^ (int)Pack.LE_To_UInt32(input[4..]);
@@ -274,7 +274,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_LE((uint)(X3 ^ wKey[3]), output[12..]);
         }
 #else
-        protected override void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff)
+        internal override void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             X0 = (int)Pack.LE_To_UInt32(input, inOff);
             X1 = (int)Pack.LE_To_UInt32(input, inOff + 4);
@@ -320,7 +320,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_LE((uint)(wKey[131] ^ X3), output, outOff + 12);
         }
 
-        protected override void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff)
+        internal override void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             X0 = wKey[128] ^ (int)Pack.LE_To_UInt32(input, inOff);
             X1 = wKey[129] ^ (int)Pack.LE_To_UInt32(input, inOff + 4);
diff --git a/crypto/src/crypto/engines/SerpentEngineBase.cs b/crypto/src/crypto/engines/SerpentEngineBase.cs
index 0ce3a0e4f..44020f06f 100644
--- a/crypto/src/crypto/engines/SerpentEngineBase.cs
+++ b/crypto/src/crypto/engines/SerpentEngineBase.cs
@@ -18,7 +18,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         protected int X0, X1, X2, X3;    // registers
 
-        protected SerpentEngineBase()
+        internal SerpentEngineBase()
         {
         }
 
@@ -474,14 +474,14 @@ namespace Org.BouncyCastle.Crypto.Engines
             X0 = Integers.RotateRight(x0, 13);
         }
 
-        protected abstract int[] MakeWorkingKey(byte[] key);
+        internal abstract int[] MakeWorkingKey(byte[] key);
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        protected abstract void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output);
-        protected abstract void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output);
+        internal abstract void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output);
+        internal abstract void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output);
 #else
-        protected abstract void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff);
-        protected abstract void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff);
+        internal abstract void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff);
+        internal abstract void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff);
 #endif
     }
 }
diff --git a/crypto/src/crypto/engines/SkipjackEngine.cs b/crypto/src/crypto/engines/SkipjackEngine.cs
index 4a5355963..c6a3e458e 100644
--- a/crypto/src/crypto/engines/SkipjackEngine.cs
+++ b/crypto/src/crypto/engines/SkipjackEngine.cs
@@ -8,12 +8,12 @@ namespace Org.BouncyCastle.Crypto.Engines
     /**
     * a class that provides a basic SKIPJACK engine.
     */
-    public class SkipjackEngine
+    public sealed class SkipjackEngine
 		: IBlockCipher
     {
-        const int BLOCK_SIZE = 8;
+        private const int BLOCK_SIZE = 8;
 
-        static readonly short [] ftable =
+        private static readonly short[] ftable =
         {
             0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9,
             0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28,
@@ -44,14 +44,12 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public virtual void Init(
-            bool				forEncryption,
-            ICipherParameters	parameters)
+        public void Init(bool forEncryption, ICipherParameters parameters)
         {
-            if (!(parameters is KeyParameter))
+            if (!(parameters is KeyParameter keyParameter))
 	            throw new ArgumentException("invalid parameter passed to SKIPJACK init - " + Platform.GetTypeName(parameters));
 
-			byte[] keyBytes = ((KeyParameter)parameters).GetKey();
+			byte[] keyBytes = keyParameter.GetKey();
 
             this.encrypting = forEncryption;
             this.key0 = new int[32];
@@ -63,26 +61,26 @@ namespace Org.BouncyCastle.Crypto.Engines
             // expand the key to 128 bytes in 4 parts (saving us a modulo, multiply
             // and an addition).
             //
-            for (int i = 0; i < 32; i ++)
+            for (int i = 0; i < 32; i++)
             {
-                key0[i] = keyBytes[(i * 4) % 10] & 0xff;
-                key1[i] = keyBytes[(i * 4 + 1) % 10] & 0xff;
-                key2[i] = keyBytes[(i * 4 + 2) % 10] & 0xff;
-                key3[i] = keyBytes[(i * 4 + 3) % 10] & 0xff;
+                key0[i] = keyBytes[(i * 4 + 0) % 10];
+                key1[i] = keyBytes[(i * 4 + 1) % 10];
+                key2[i] = keyBytes[(i * 4 + 2) % 10];
+                key3[i] = keyBytes[(i * 4 + 3) % 10];
             }
         }
 
-        public virtual string AlgorithmName
+        public string AlgorithmName
         {
             get { return "SKIPJACK"; }
         }
 
-        public virtual int GetBlockSize()
+        public int GetBlockSize()
         {
             return BLOCK_SIZE;
         }
 
-        public virtual int ProcessBlock(byte[] input, int inOff, byte[]	output, int outOff)
+        public int ProcessBlock(byte[] input, int inOff, byte[]	output, int outOff)
         {
             if (key1 == null)
                 throw new InvalidOperationException("SKIPJACK engine not initialised");
@@ -114,7 +112,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             if (key1 == null)
                 throw new InvalidOperationException("SKIPJACK engine not initialised");
@@ -156,7 +154,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public virtual int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             int w1 = (input[0] << 8) + (input[1] & 0xff);
             int w2 = (input[2] << 8) + (input[3] & 0xff);
@@ -200,7 +198,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public virtual int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             int w2 = (input[0] << 8) + (input[1] & 0xff);
             int w1 = (input[2] << 8) + (input[3] & 0xff);
@@ -245,7 +243,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         }
 
 #else
-        public virtual int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
+        private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
             int w1 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff);
             int w2 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff);
@@ -289,7 +287,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public virtual int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
+        private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
             int w2 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff);
             int w1 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff);
diff --git a/crypto/src/crypto/engines/TnepresEngine.cs b/crypto/src/crypto/engines/TnepresEngine.cs
index 50f5a58c4..7751e20bc 100644
--- a/crypto/src/crypto/engines/TnepresEngine.cs
+++ b/crypto/src/crypto/engines/TnepresEngine.cs
@@ -33,7 +33,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @param key  The user-key bytes (multiples of 4) to use.
         * @exception ArgumentException
         */
-        protected override int[] MakeWorkingKey(byte[] key)
+        internal override int[] MakeWorkingKey(byte[] key)
         {
             //
             // pad key to 256 bits
@@ -158,7 +158,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        protected override void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        internal override void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             X3 = (int)Pack.BE_To_UInt32(input);
             X2 = (int)Pack.BE_To_UInt32(input[4..]);
@@ -204,7 +204,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_BE((uint)(wKey[128] ^ X0), output[12..]);
         }
 
-        protected override void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        internal override void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             X3 = wKey[131] ^ (int)Pack.BE_To_UInt32(input);
             X2 = wKey[130] ^ (int)Pack.BE_To_UInt32(input[4..]);
@@ -281,7 +281,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_BE((uint)(X0 ^ wKey[0]), output[12..]);
         }
 #else
-        protected override void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff)
+        internal override void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             X3 = (int)Pack.BE_To_UInt32(input, inOff);
             X2 = (int)Pack.BE_To_UInt32(input, inOff + 4);
@@ -327,7 +327,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_BE((uint)(wKey[128] ^ X0), output, outOff + 12);
         }
 
-        protected override void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff)
+        internal override void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             X3 = wKey[131] ^ (int)Pack.BE_To_UInt32(input, inOff);
             X2 = wKey[130] ^ (int)Pack.BE_To_UInt32(input, inOff + 4);
diff --git a/crypto/src/crypto/generators/HKdfBytesGenerator.cs b/crypto/src/crypto/generators/HkdfBytesGenerator.cs
index 43cd66525..43cd66525 100644
--- a/crypto/src/crypto/generators/HKdfBytesGenerator.cs
+++ b/crypto/src/crypto/generators/HkdfBytesGenerator.cs
diff --git a/crypto/src/crypto/generators/SCrypt.cs b/crypto/src/crypto/generators/SCrypt.cs
index 1a8d4a003..fef842be2 100644
--- a/crypto/src/crypto/generators/SCrypt.cs
+++ b/crypto/src/crypto/generators/SCrypt.cs
@@ -102,7 +102,76 @@ namespace Org.BouncyCastle.Crypto.Generators
 			return key.GetKey();
 		}
 
-		private static void SMix(uint[] B, int BOff, int N, int d, int r)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private static void SMix(uint[] B, int BOff, int N, int d, int r)
+		{
+            int powN = Integers.NumberOfTrailingZeros(N);
+            int blocksPerChunk = N >> d;
+            int chunkCount = 1 << d, chunkMask = blocksPerChunk - 1, chunkPow = powN - d;
+
+			int BCount = r * 32;
+
+			uint[] blockY = new uint[BCount];
+
+            uint[][] VV = new uint[chunkCount][];
+
+			try
+			{
+                var X = B.AsSpan(BOff, BCount);
+
+                for (int c = 0; c < chunkCount; ++c)
+                {
+                    uint[] V = new uint[blocksPerChunk * BCount];
+                    VV[c] = V;
+
+                    Nat.Copy(BCount, X, V);
+                    int off = 0;
+                    for (int i = 1; i < blocksPerChunk; ++i)
+                    {
+                        BlockMix(V.AsSpan(off, BCount), V.AsSpan(off + BCount));
+                        off += BCount;
+                    }
+                    BlockMix(V.AsSpan()[^BCount..], X);
+                }
+
+                uint mask = (uint)N - 1;
+                for (int i = 0; i < N; ++i)
+                {
+                    int j = (int)(X[BCount - 16] & mask);
+                    uint[] V = VV[j >> chunkPow];
+                    int VOff = (j & chunkMask) * BCount;
+                    Nat.Xor(BCount, V.AsSpan(VOff), X, blockY);
+                    BlockMix(blockY, X);
+                }
+            }
+            finally
+			{
+				ClearAll(VV);
+                Clear(blockY);
+			}
+		}
+
+        private static void BlockMix(Span<uint> B, Span<uint> Y)
+		{
+            int BCount = B.Length;
+            int half = BCount >> 1;
+            var y1 = B[^16..];
+
+            for (int pos = 0; pos < BCount; pos += 32)
+            {
+                var b0 = B[pos..];
+                var y0 = Y[(pos >> 1)..];
+                Nat512.Xor(y1, b0, y0);
+                Salsa20Engine.SalsaCore(8, y0, y0);
+
+                var b1 = b0[16..];
+                    y1 = y0[half..];
+                Nat512.Xor(y0, b1, y1);
+                Salsa20Engine.SalsaCore(8, y1, y1);
+            }
+        }
+#else
+        private static void SMix(uint[] B, int BOff, int N, int d, int r)
 		{
             int powN = Integers.NumberOfTrailingZeros(N);
             int blocksPerChunk = N >> d;
@@ -111,7 +180,6 @@ namespace Org.BouncyCastle.Crypto.Generators
 			int BCount = r * 32;
 
 			uint[] blockX1 = new uint[16];
-			uint[] blockX2 = new uint[16];
 			uint[] blockY = new uint[BCount];
 
 			uint[] X = new uint[BCount];
@@ -131,10 +199,10 @@ namespace Org.BouncyCastle.Crypto.Generators
                     {
                         Array.Copy(X, 0, V, off, BCount);
                         off += BCount;
-                        BlockMix(X, blockX1, blockX2, blockY, r);
+                        BlockMix(X, blockX1, blockY, r);
                         Array.Copy(blockY, 0, V, off, BCount);
                         off += BCount;
-                        BlockMix(blockY, blockX1, blockX2, X, r);
+                        BlockMix(blockY, blockX1, X, r);
                     }
                 }
 
@@ -146,7 +214,7 @@ namespace Org.BouncyCastle.Crypto.Generators
                     int VOff = (j & chunkMask) * BCount;
                     Nat.Xor(BCount, V, VOff, X, 0, blockY, 0);
 
-                    BlockMix(blockY, blockX1, blockX2, X, r);
+                    BlockMix(blockY, blockX1, X, r);
                 }
 
 				Array.Copy(X, 0, B, BOff, BCount);
@@ -154,29 +222,30 @@ namespace Org.BouncyCastle.Crypto.Generators
 			finally
 			{
 				ClearAll(VV);
-				ClearAll(X, blockX1, blockX2, blockY);
+				ClearAll(X, blockX1, blockY);
 			}
 		}
 
-		private static void BlockMix(uint[] B, uint[] X1, uint[] X2, uint[] Y, int r)
+        private static void BlockMix(uint[] B, uint[] X1, uint[] Y, int r)
 		{
-			Array.Copy(B, B.Length - 16, X1, 0, 16);
+            Array.Copy(B, B.Length - 16, X1, 0, 16);
 
-			int BOff = 0, YOff = 0, halfLen = B.Length >> 1;
+            int BOff = 0, YOff = 0, halfLen = B.Length >> 1;
 
-			for (int i = 2 * r; i > 0; --i)
-			{
-                Nat512.Xor(X1, 0, B, BOff, X2, 0);
+            for (int i = 2 * r; i > 0; --i)
+            {
+                Nat512.XorTo(B, BOff, X1, 0);
 
-				Salsa20Engine.SalsaCore(8, X2, X1);
-				Array.Copy(X1, 0, Y, YOff, 16);
+            	Salsa20Engine.SalsaCore(8, X1, X1);
+            	Array.Copy(X1, 0, Y, YOff, 16);
 
-				YOff = halfLen + BOff - YOff;
-				BOff += 16;
-			}
-		}
+            	YOff = halfLen + BOff - YOff;
+            	BOff += 16;
+            }
+        }
+#endif
 
-		private static void Clear(Array array)
+        private static void Clear(Array array)
 		{
 			if (array != null)
 			{
diff --git a/crypto/src/crypto/modes/CfbBlockCipher.cs b/crypto/src/crypto/modes/CfbBlockCipher.cs
index abcdca959..7bce9843f 100644
--- a/crypto/src/crypto/modes/CfbBlockCipher.cs
+++ b/crypto/src/crypto/modes/CfbBlockCipher.cs
@@ -129,7 +129,7 @@ namespace Org.BouncyCastle.Crypto.Modes
 #endif
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             Check.DataLength(input, blockSize, "input buffer too short");
             Check.OutputLength(output, blockSize, "output buffer too short");
@@ -150,7 +150,7 @@ namespace Org.BouncyCastle.Crypto.Modes
             return blockSize;
         }
 
-        public int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             Check.DataLength(input, blockSize, "input buffer too short");
             Check.OutputLength(output, blockSize, "output buffer too short");
@@ -171,7 +171,7 @@ namespace Org.BouncyCastle.Crypto.Modes
             return blockSize;
         }
 #else
-        public int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
+        private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
             Check.DataLength(input, inOff, blockSize, "input buffer too short");
             Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
@@ -192,7 +192,7 @@ namespace Org.BouncyCastle.Crypto.Modes
             return blockSize;
         }
 
-        public int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
+        private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
             Check.DataLength(input, inOff, blockSize, "input buffer too short");
             Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
diff --git a/crypto/src/crypto/modes/ChaCha20Poly1305.cs b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
index 299387cdf..2fce81e22 100644
--- a/crypto/src/crypto/modes/ChaCha20Poly1305.cs
+++ b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
@@ -763,6 +763,18 @@ namespace Org.BouncyCastle.Crypto.Modes
 
         private void InitMac()
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> firstBlock = stackalloc byte[64];
+            try
+            {
+                mChacha20.ProcessBytes(firstBlock, firstBlock);
+                mPoly1305.Init(new KeyParameter(firstBlock[..32]));
+            }
+            finally
+            {
+                firstBlock.Fill(0x00);
+            }
+#else
             byte[] firstBlock = new byte[64];
             try
             {
@@ -773,6 +785,7 @@ namespace Org.BouncyCastle.Crypto.Modes
             {
                 Array.Clear(firstBlock, 0, 64);
             }
+#endif
         }
 
         private void PadMac(ulong count)
diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs
index 8cc701cca..e67b4e9af 100644
--- a/crypto/src/crypto/modes/OCBBlockCipher.cs
+++ b/crypto/src/crypto/modes/OCBBlockCipher.cs
@@ -13,7 +13,7 @@ namespace Org.BouncyCastle.Crypto.Modes
     /// <para>https://mailarchive.ietf.org/arch/msg/cfrg/qLTveWOdTJcLn4HP3ev-vrj05Vg/</para>
     /// Text reproduced below:
     /// <para>
-    /// Phillip Rogaway&lt;rogaway@cs.ucdavis.edu&rt; Sat, 27 February 2021 02:46 UTC
+    /// Phillip Rogaway&lt;rogaway@cs.ucdavis.edu&gt; Sat, 27 February 2021 02:46 UTC
     ///
     /// I can confirm that I have abandoned all OCB patents and placed into the public domain all OCB-related IP of
     /// mine. While I have been telling people this for quite some time, I don't think I ever made a proper announcement
diff --git a/crypto/src/crypto/operators/Asn1Signature.cs b/crypto/src/crypto/operators/Asn1Signature.cs
index db2d0759e..ea8d28771 100644
--- a/crypto/src/crypto/operators/Asn1Signature.cs
+++ b/crypto/src/crypto/operators/Asn1Signature.cs
@@ -6,6 +6,7 @@ using Org.BouncyCastle.Asn1.CryptoPro;
 using Org.BouncyCastle.Asn1.Nist;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Rosstandart;
 using Org.BouncyCastle.Asn1.TeleTrust;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Asn1.X9;
@@ -92,12 +93,16 @@ namespace Org.BouncyCastle.Crypto.Operators
 			m_algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
 			m_algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
 			m_algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
-
-			//
-			// According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
-			// The parameters field SHALL be NULL for RSA based signature algorithms.
-			//
-			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1);
+            m_algorithms.Add("GOST3411-2012-256WITHECGOST3410", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+            m_algorithms.Add("GOST3411-2012-256WITHECGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+            m_algorithms.Add("GOST3411-2012-512WITHECGOST3410", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
+            m_algorithms.Add("GOST3411-2012-512WITHECGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
+
+            //
+            // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
+            // The parameters field SHALL be NULL for RSA based signature algorithms.
+            //
+            noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1);
 			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224);
 			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256);
 			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384);
@@ -189,6 +194,14 @@ namespace Org.BouncyCastle.Crypto.Operators
             {
                 return "GOST3411";
             }
+            else if (RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.Equals(digestAlgOID))
+            {
+                return "GOST3411-2012-256";
+            }
+            else if (RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.Equals(digestAlgOID))
+            {
+                return "GOST3411-2012-512";
+            }
             else
             {
                 return digestAlgOID.Id;
diff --git a/crypto/src/crypto/parameters/HKdfParameters.cs b/crypto/src/crypto/parameters/HkdfParameters.cs
index 6d1465e4c..6d1465e4c 100644
--- a/crypto/src/crypto/parameters/HKdfParameters.cs
+++ b/crypto/src/crypto/parameters/HkdfParameters.cs
diff --git a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
index 44a9c261f..dcd3baa1c 100644
--- a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
+++ b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
@@ -7,7 +7,7 @@ namespace Org.BouncyCastle.Crypto.Prng
     /// Uses RandomNumberGenerator.Create() to get randomness generator
     /// </summary>
     public sealed class CryptoApiRandomGenerator
-        : IRandomGenerator
+        : IRandomGenerator, IDisposable
     {
         private readonly RandomNumberGenerator m_randomNumberGenerator;
 
@@ -18,7 +18,8 @@ namespace Org.BouncyCastle.Crypto.Prng
 
         public CryptoApiRandomGenerator(RandomNumberGenerator randomNumberGenerator)
         {
-            m_randomNumberGenerator = randomNumberGenerator;
+            m_randomNumberGenerator = randomNumberGenerator ??
+                throw new ArgumentNullException(nameof(randomNumberGenerator));
         }
 
         #region IRandomGenerator Members
@@ -76,5 +77,14 @@ namespace Org.BouncyCastle.Crypto.Prng
 #endif
 
         #endregion
+
+        #region IDisposable Members
+
+        public void Dispose()
+        {
+            m_randomNumberGenerator.Dispose();
+        }
+
+        #endregion
     }
 }
diff --git a/crypto/src/math/ec/ECFieldElement.cs b/crypto/src/math/ec/ECFieldElement.cs
index 330708088..3afc843cd 100644
--- a/crypto/src/math/ec/ECFieldElement.cs
+++ b/crypto/src/math/ec/ECFieldElement.cs
@@ -785,9 +785,9 @@ namespace Org.BouncyCastle.Math.EC
             LongArray ab = ax.Multiply(bx, m, ks);
             LongArray xy = xx.Multiply(yx, m, ks);
 
-            if (ab == ax || ab == bx)
+            if (LongArray.AreAliased(ref ab, ref ax) || LongArray.AreAliased(ref ab, ref bx))
             {
-                ab = (LongArray)ab.Copy();
+                ab = ab.Copy();
             }
 
             ab.AddShiftedByWords(xy, 0);
@@ -827,9 +827,9 @@ namespace Org.BouncyCastle.Math.EC
             LongArray aa = ax.Square(m, ks);
             LongArray xy = xx.Multiply(yx, m, ks);
 
-            if (aa == ax)
+            if (LongArray.AreAliased(ref aa, ref ax))
             {
-                aa = (LongArray)aa.Copy();
+                aa = aa.Copy();
             }
 
             aa.AddShiftedByWords(xy, 0);
diff --git a/crypto/src/math/ec/LongArray.cs b/crypto/src/math/ec/LongArray.cs
index 90ca49c77..aa36de215 100644
--- a/crypto/src/math/ec/LongArray.cs
+++ b/crypto/src/math/ec/LongArray.cs
@@ -1,27 +1,32 @@
 using System;
 using System.Text;
-
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Math.EC
 {
-    internal sealed class LongArray
+    internal struct LongArray
     {
+        internal static bool AreAliased(ref LongArray a, ref LongArray b)
+        {
+            return a.m_data == b.m_data;
+        }
+
         // TODO make m fixed for the LongArray, and hence compute T once and for all
 
         private ulong[] m_data;
 
-        public LongArray(int intLen)
+        internal LongArray(int intLen)
         {
             m_data = new ulong[intLen];
         }
 
-        public LongArray(ulong[] data)
+        internal LongArray(ulong[] data)
         {
             m_data = data;
         }
 
-        public LongArray(ulong[] data, int off, int len)
+        internal LongArray(ulong[] data, int off, int len)
         {
             if (off == 0 && len == data.Length)
             {
@@ -34,16 +39,14 @@ namespace Org.BouncyCastle.Math.EC
             }
         }
 
-        public LongArray(BigInteger bigInt)
+        internal LongArray(BigInteger bigInt)
         {
             if (bigInt == null || bigInt.SignValue < 0)
-            {
-                throw new ArgumentException("invalid F2m field value", "bigInt");
-            }
+                throw new ArgumentException("invalid F2m field value", nameof(bigInt));
 
             if (bigInt.SignValue == 0)
             {
-                m_data = new ulong[]{ 0UL };
+                m_data = new ulong[1]{ 0UL };
                 return;
             }
 
@@ -93,51 +96,44 @@ namespace Org.BouncyCastle.Math.EC
             Array.Copy(m_data, 0, z, zOff, m_data.Length);
         }
 
-        public bool IsOne()
+        internal bool IsOne()
         {
             ulong[] a = m_data;
             int aLen = a.Length;
             if (aLen < 1 || a[0] != 1UL)
-            {
                 return false;
-            }
+
             for (int i = 1; i < aLen; ++i)
             {
                 if (a[i] != 0UL)
-                {
                     return false;
-                }
             }
             return true;
         }
 
-        public bool IsZero()
+        internal bool IsZero()
         {
             ulong[] a = m_data;
             for (int i = 0; i < a.Length; ++i)
             {
                 if (a[i] != 0UL)
-                {
                     return false;
-                }
             }
             return true;
         }
 
-        public int GetUsedLength()
+        internal int GetUsedLength()
         {
             return GetUsedLengthFrom(m_data.Length);
         }
 
-        public int GetUsedLengthFrom(int from)
+        internal int GetUsedLengthFrom(int from)
         {
             ulong[] a = m_data;
             from = System.Math.Min(from, a.Length);
 
             if (from < 1)
-            {
                 return 0;
-            }
 
             // Check if first element will act as sentinel
             if (a[0] != 0UL)
@@ -160,16 +156,15 @@ namespace Org.BouncyCastle.Math.EC
             return 0;
         }
 
-        public int Degree()
+        internal int Degree()
         {
             int i = m_data.Length;
             ulong w;
             do
             {
                 if (i == 0)
-                {
                     return 0;
-                }
+
                 w = m_data[--i];
             }
             while (w == 0UL);
@@ -184,9 +179,8 @@ namespace Org.BouncyCastle.Math.EC
             do
             {
                 if (i == 0)
-                {
                     return 0;
-                }
+
                 w = m_data[--i];
             }
             while (w == 0);
@@ -206,13 +200,11 @@ namespace Org.BouncyCastle.Math.EC
             return newInts;
         }
 
-        public BigInteger ToBigInteger()
+        internal BigInteger ToBigInteger()
         {
             int usedLen = GetUsedLength();
             if (usedLen == 0)
-            {
                 return BigInteger.Zero;
-            }
 
             ulong highestInt = m_data[usedLen - 1];
             byte[] temp = new byte[8];
@@ -273,12 +265,10 @@ namespace Org.BouncyCastle.Math.EC
             return prev;
         }
 
-        public LongArray AddOne()
+        internal LongArray AddOne()
         {
             if (m_data.Length == 0)
-            {
-                return new LongArray(new ulong[]{ 1UL });
-            }
+                return new LongArray(new ulong[1]{ 1UL });
 
             int resultLen = System.Math.Max(1, GetUsedLength());
             ulong[] data = ResizedData(resultLen);
@@ -333,13 +323,11 @@ namespace Org.BouncyCastle.Math.EC
             return prev;
         }
 
-        public void AddShiftedByWords(LongArray other, int words)
+        internal void AddShiftedByWords(LongArray other, int words)
         {
             int otherUsedLen = other.GetUsedLength();
             if (otherUsedLen == 0)
-            {
                 return;
-            }
 
             int minLen = otherUsedLen + words;
             if (minLen > m_data.Length)
@@ -352,18 +340,12 @@ namespace Org.BouncyCastle.Math.EC
 
         private static void Add(ulong[] x, int xOff, ulong[] y, int yOff, int count)
         {
-            for (int i = 0; i < count; ++i)
-            {
-                x[xOff + i] ^= y[yOff + i];
-            }
+            Nat.XorTo64(count, y, yOff, x, xOff);
         }
 
         private static void Add(ulong[] x, int xOff, ulong[] y, int yOff, ulong[] z, int zOff, int count)
         {
-            for (int i = 0; i < count; ++i)
-            {
-                z[zOff + i] = x[xOff + i] ^ y[yOff + i];
-            }
+            Nat.Xor64(count, x, xOff, y, yOff, z, zOff);
         }
 
         private static void AddBoth(ulong[] x, int xOff, ulong[] y1, int y1Off, ulong[] y2, int y2Off, int count)
@@ -393,7 +375,7 @@ namespace Org.BouncyCastle.Math.EC
             }
         }
 
-        public bool TestBitZero()
+        internal bool TestBitZero()
         {
             return m_data.Length > 0 && (m_data[0] & 1UL) != 0;
         }
@@ -439,21 +421,18 @@ namespace Org.BouncyCastle.Math.EC
             }
         }
 
-        public LongArray ModMultiplyLD(LongArray other, int m, int[] ks)
+        internal LongArray ModMultiplyLD(LongArray other, int m, int[] ks)
         {
             /*
              * Find out the degree of each argument and handle the zero cases
              */
             int aDeg = Degree();
             if (aDeg == 0)
-            {
                 return this;
-            }
+
             int bDeg = other.Degree();
             if (bDeg == 0)
-            {
                 return other;
-            }
 
             /*
              * Swap if necessary so that A is the smaller argument
@@ -476,9 +455,7 @@ namespace Org.BouncyCastle.Math.EC
             {
                 ulong a0 = A.m_data[0];
                 if (a0 == 1UL)
-                {
                     return B;
-                }
 
                 /*
                  * Fast path for small A, with performance dependent only on the number of set bits
@@ -571,21 +548,18 @@ namespace Org.BouncyCastle.Math.EC
             return ReduceResult(c, 0, cLen, m, ks);
         }
 
-        public LongArray ModMultiply(LongArray other, int m, int[] ks)
+        internal LongArray ModMultiply(LongArray other, int m, int[] ks)
         {
             /*
              * Find out the degree of each argument and handle the zero cases
              */
             int aDeg = Degree();
             if (aDeg == 0)
-            {
                 return this;
-            }
+
             int bDeg = other.Degree();
             if (bDeg == 0)
-            {
                 return other;
-            }
 
             /*
              * Swap if necessary so that A is the smaller argument
@@ -608,9 +582,7 @@ namespace Org.BouncyCastle.Math.EC
             {
                 ulong a0 = A.m_data[0];
                 if (a0 == 1UL)
-                {
                     return B;
-                }
 
                 /*
                  * Fast path for small A, with performance dependent only on the number of set bits
@@ -681,9 +653,8 @@ namespace Org.BouncyCastle.Math.EC
                     uint v = (uint)aVal & MASK; aVal >>= 4;
                     AddBoth(c, cOff, T0, ti[u], T1, ti[v], bMax);
                     if (aVal == 0UL)
-                    {
                         break;
-                    }
+
                     cOff += cLen;
                 }
             }
@@ -702,28 +673,25 @@ namespace Org.BouncyCastle.Math.EC
             return ReduceResult(c, 0, cLen, m, ks);
         }
 
-        //public LongArray ModReduce(int m, int[] ks)
+        //internal LongArray ModReduce(int m, int[] ks)
         //{
         //    ulong[] buf = Arrays.Clone(m_data);
         //    int rLen = ReduceInPlace(buf, 0, buf.Length, m, ks);
         //    return new LongArray(buf, 0, rLen);
         //}
 
-        public LongArray Multiply(LongArray other, int m, int[] ks)
+        internal LongArray Multiply(LongArray other, int m, int[] ks)
         {
             /*
              * Find out the degree of each argument and handle the zero cases
              */
             int aDeg = Degree();
             if (aDeg == 0)
-            {
                 return this;
-            }
+
             int bDeg = other.Degree();
             if (bDeg == 0)
-            {
                 return other;
-            }
 
             /*
              * Swap if necessary so that A is the smaller argument
@@ -746,9 +714,7 @@ namespace Org.BouncyCastle.Math.EC
             {
                 ulong a0 = A.m_data[0];
                 if (a0 == 1UL)
-                {
                     return B;
-                }
 
                 /*
                  * Fast path for small A, with performance dependent only on the number of set bits
@@ -820,9 +786,8 @@ namespace Org.BouncyCastle.Math.EC
                     uint v = (uint)aVal & MASK; aVal >>= 4;
                     AddBoth(c, cOff, T0, ti[u], T1, ti[v], bMax);
                     if (aVal == 0UL)
-                    {
                         break;
-                    }
+
                     cOff += cLen;
                 }
             }
@@ -842,7 +807,7 @@ namespace Org.BouncyCastle.Math.EC
             return new LongArray(c, 0, cLen);
         }
 
-        public void Reduce(int m, int[] ks)
+        internal void Reduce(int m, int[] ks)
         {
             ulong[] buf = m_data;
             int rLen = ReduceInPlace(buf, 0, buf.Length, m, ks);
@@ -863,9 +828,7 @@ namespace Org.BouncyCastle.Math.EC
         {
             int mLen = (m + 63) >> 6;
             if (len < mLen)
-            {
                 return len;
-            }
 
             int numBits = System.Math.Min(len << 6, (m << 1) - 1); // TODO use actual degree?
             int excessBits = (len << 6) - numBits;
@@ -994,19 +957,19 @@ namespace Org.BouncyCastle.Math.EC
             }
         }
 
-        public LongArray ModSquare(int m, int[] ks)
+        internal LongArray ModSquare(int m, int[] ks)
         {
             int len = GetUsedLength();
             if (len == 0)
                 return this;
 
             ulong[] r = new ulong[len << 1];
-            Raw.Interleave.Expand64To128(m_data, 0, len, r, 0);
+            Interleave.Expand64To128(m_data, 0, len, r, 0);
 
             return new LongArray(r, 0, ReduceInPlace(r, 0, r.Length, m, ks));
         }
 
-        public LongArray ModSquareN(int n, int m, int[] ks)
+        internal LongArray ModSquareN(int n, int m, int[] ks)
         {
             int len = GetUsedLength();
             if (len == 0)
@@ -1018,21 +981,21 @@ namespace Org.BouncyCastle.Math.EC
 
             while (--n >= 0)
             {
-                Raw.Interleave.Expand64To128(r, 0, len, r, 0);
+                Interleave.Expand64To128(r, 0, len, r, 0);
                 len = ReduceInPlace(r, 0, r.Length, m, ks);
             }
 
             return new LongArray(r, 0, len);
         }
 
-        public LongArray Square(int m, int[] ks)
+        internal LongArray Square(int m, int[] ks)
         {
             int len = GetUsedLength();
             if (len == 0)
                 return this;
 
             ulong[] r = new ulong[len << 1];
-            Raw.Interleave.Expand64To128(m_data, 0, len, r, 0);
+            Interleave.Expand64To128(m_data, 0, len, r, 0);
 
             return new LongArray(r, 0, r.Length);
         }
@@ -1147,7 +1110,7 @@ namespace Org.BouncyCastle.Math.EC
     //        return t4.ModMultiply(t1, m, ks);
     //    }
 
-        public LongArray ModInverse(int m, int[] ks)
+        internal LongArray ModInverse(int m, int[] ks)
         {
             /*
              * Fermat's Little Theorem
@@ -1188,16 +1151,13 @@ namespace Org.BouncyCastle.Math.EC
              */
             int uzDegree = Degree();
             if (uzDegree == 0)
-            {
                 throw new InvalidOperationException();
-            }
+
             if (uzDegree == 1)
-            {
                 return this;
-            }
 
             // u(z) := a(z)
-            LongArray uz = (LongArray)Copy();
+            LongArray uz = Copy();
 
             int t = (m + 63) >> 6;
 
@@ -1237,9 +1197,7 @@ namespace Org.BouncyCastle.Math.EC
 
                 int duv2 = uv[b].DegreeFrom(duv1);
                 if (duv2 == 0)
-                {
                     return gg[1 - b];
-                }
 
                 {
                     int dgg2 = ggDeg[1 - b];
@@ -1263,26 +1221,25 @@ namespace Org.BouncyCastle.Math.EC
 
         public override bool Equals(object obj)
         {
-            return Equals(obj as LongArray);
+            if (obj is LongArray longArray)
+                return Equals(ref longArray);
+
+            return false;
         }
 
-        public bool Equals(LongArray other)
+        internal bool Equals(ref LongArray other)
         {
-            if (this == other)
+            if (AreAliased(ref this, ref other))
                 return true;
-            if (null == other)
-                return false;
+
             int usedLen = GetUsedLength();
             if (other.GetUsedLength() != usedLen)
-            {
                 return false;
-            }
+
             for (int i = 0; i < usedLen; i++)
             {
                 if (m_data[i] != other.m_data[i])
-                {
                     return false;
-                }
             }
             return true;
         }
@@ -1311,9 +1268,7 @@ namespace Org.BouncyCastle.Math.EC
         {
             int i = GetUsedLength();
             if (i == 0)
-            {
                 return "0";
-            }
 
             StringBuilder sb = new StringBuilder(i * 64);
             sb.Append(Convert.ToString((long)m_data[--i], 2));
diff --git a/crypto/src/ocsp/RevokedStatus.cs b/crypto/src/ocsp/RevokedStatus.cs
index 903e50ef5..a37bdade9 100644
--- a/crypto/src/ocsp/RevokedStatus.cs
+++ b/crypto/src/ocsp/RevokedStatus.cs
@@ -6,52 +6,49 @@ using Org.BouncyCastle.Asn1.X509;
 
 namespace Org.BouncyCastle.Ocsp
 {
-	/**
-	 * wrapper for the RevokedInfo object
-	 */
-	public class RevokedStatus
+    /// <summary>Wrapper for the RevokedInfo object</summary>
+    public class RevokedStatus
 		: CertificateStatus
 	{
-		internal readonly RevokedInfo info;
+		private readonly RevokedInfo m_revokedInfo;
 
-		public RevokedStatus(
-			RevokedInfo info)
+		public RevokedStatus(RevokedInfo revokedInfo)
 		{
-			this.info = info;
+			m_revokedInfo = revokedInfo;
 		}
 
-		public RevokedStatus(
-			DateTime	revocationDate,
-			int			reason)
+		public RevokedStatus(DateTime revocationDate)
 		{
-			this.info = new RevokedInfo(new Asn1GeneralizedTime(revocationDate), new CrlReason(reason));
+			m_revokedInfo = new RevokedInfo(new Asn1GeneralizedTime(revocationDate));
+		}
+
+        public RevokedStatus(DateTime revocationDate, int reason)
+		{
+			m_revokedInfo = new RevokedInfo(new Asn1GeneralizedTime(revocationDate), new CrlReason(reason));
 		}
 
 		public DateTime RevocationTime
 		{
-			get { return info.RevocationTime.ToDateTime(); }
+			get { return m_revokedInfo.RevocationTime.ToDateTime(); }
 		}
 
 		public bool HasRevocationReason
 		{
-			get { return (info.RevocationReason != null); }
+			get { return m_revokedInfo.RevocationReason != null; }
 		}
 
-		/**
-		 * return the revocation reason. Note: this field is optional, test for it
-		 * with hasRevocationReason() first.
-		 * @exception InvalidOperationException if a reason is asked for and none is avaliable
-		 */
-		public int RevocationReason
+        /// <summary>Return the revocation reason, if there is one.</summary>
+		/// <remarks>This field is optional; test for it with <see cref="HasRevocationReason"/> first.</remarks>
+		/// <returns>The revocation reason, if available.</returns>
+		/// <exception cref="InvalidOperationException">If no revocation reason is available.</exception>
+        public int RevocationReason
 		{
 			get
 			{
-				if (info.RevocationReason == null)
-				{
+				if (m_revokedInfo.RevocationReason == null)
 					throw new InvalidOperationException("attempt to get a reason where none is available");
-				}
 
-                return info.RevocationReason.IntValueExact;
+                return m_revokedInfo.RevocationReason.IntValueExact;
 			}
 		}
 	}
diff --git a/crypto/src/openpgp/EdDsaSigner.cs b/crypto/src/openpgp/EdDsaSigner.cs
new file mode 100644
index 000000000..0e15ac609
--- /dev/null
+++ b/crypto/src/openpgp/EdDsaSigner.cs
@@ -0,0 +1,72 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+    internal sealed class EdDsaSigner
+        : ISigner
+    {
+        private readonly ISigner m_signer;
+        private readonly IDigest m_digest;
+        private readonly byte[] m_digBuf;
+
+        internal EdDsaSigner(ISigner signer, IDigest digest)
+        {
+            m_signer = signer;
+            m_digest = digest;
+            m_digBuf = new byte[digest.GetDigestSize()];
+        }
+
+        public string AlgorithmName => m_signer.AlgorithmName;
+
+        public void Init(bool forSigning, ICipherParameters cipherParameters)
+        {
+            m_signer.Init(forSigning, cipherParameters);
+            m_digest.Reset();
+        }
+
+        public void Update(byte b)
+        {
+            m_digest.Update(b);
+        }
+
+        public void BlockUpdate(byte[] input, int inOff, int inLen)
+        {
+            m_digest.BlockUpdate(input, inOff, inLen);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            m_digest.BlockUpdate(input);
+        }
+#endif
+
+        public byte[] GenerateSignature()
+        {
+            m_digest.DoFinal(m_digBuf, 0);
+
+            m_signer.BlockUpdate(m_digBuf, 0, m_digBuf.Length);
+
+            return m_signer.GenerateSignature();
+        }
+
+        public bool VerifySignature(byte[] signature)
+        {
+            m_digest.DoFinal(m_digBuf, 0);
+
+            m_signer.BlockUpdate(m_digBuf, 0, m_digBuf.Length);
+
+            return m_signer.VerifySignature(signature);
+        }
+
+        public void Reset()
+        {
+            Arrays.Clear(m_digBuf);
+            m_signer.Reset();
+            m_digest.Reset();
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpEncryptedData.cs b/crypto/src/openpgp/PgpEncryptedData.cs
index 5cdc0d533..bad4cb8cd 100644
--- a/crypto/src/openpgp/PgpEncryptedData.cs
+++ b/crypto/src/openpgp/PgpEncryptedData.cs
@@ -54,6 +54,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             {
 				Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                return Read(buffer.AsSpan(offset, count));
+#else
                 int avail = bufEnd - bufStart;
 
                 int pos = offset;
@@ -73,8 +76,34 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 bufStart += count;
 
                 return pos + count - offset;
+#endif
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override int Read(Span<byte> buffer)
+            {
+                int avail = bufEnd - bufStart;
+
+                int pos = 0, count = buffer.Length;
+                while (count > avail)
+                {
+                    lookAhead.AsSpan(bufStart, avail).CopyTo(buffer[pos..]);
+
+                    bufStart += avail;
+                    pos += avail;
+                    count -= avail;
+
+                    if ((avail = FillBuffer()) < 1)
+                        return pos;
+                }
+
+                lookAhead.AsSpan(bufStart, count).CopyTo(buffer[pos..]);
+                bufStart += count;
+
+                return pos + count;
+            }
+#endif
+
             public override int ReadByte()
             {
                 if (bufStart < bufEnd)
diff --git a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
index 589895522..69e0d5dbc 100644
--- a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
+++ b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
@@ -2,17 +2,21 @@ using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
-
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cryptlib;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.IO;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
-using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
-	/// <remarks>Generator for encrypted objects.</remarks>
+    /// <remarks>Generator for encrypted objects.</remarks>
     public class PgpEncryptedDataGenerator
 		: IStreamGenerator
     {
@@ -99,54 +103,108 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
             private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random)
             {
+                var cryptoPublicKey = pubKey.GetKey();
+
                 if (pubKey.Algorithm != PublicKeyAlgorithmTag.ECDH)
                 {
                     IBufferedCipher c;
 				    switch (pubKey.Algorithm)
                     {
-                        case PublicKeyAlgorithmTag.RsaEncrypt:
-                        case PublicKeyAlgorithmTag.RsaGeneral:
-                            c = CipherUtilities.GetCipher("RSA//PKCS1Padding");
-                            break;
-                        case PublicKeyAlgorithmTag.ElGamalEncrypt:
-                        case PublicKeyAlgorithmTag.ElGamalGeneral:
-                            c = CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding");
-                            break;
-                        case PublicKeyAlgorithmTag.Dsa:
-                            throw new PgpException("Can't use DSA for encryption.");
-                        case PublicKeyAlgorithmTag.ECDsa:
-                            throw new PgpException("Can't use ECDSA for encryption.");
-                        default:
-                            throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
+                    case PublicKeyAlgorithmTag.RsaEncrypt:
+                    case PublicKeyAlgorithmTag.RsaGeneral:
+                        c = CipherUtilities.GetCipher("RSA//PKCS1Padding");
+                        break;
+                    case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                    case PublicKeyAlgorithmTag.ElGamalGeneral:
+                        c = CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding");
+                        break;
+                    case PublicKeyAlgorithmTag.Dsa:
+                        throw new PgpException("Can't use DSA for encryption.");
+                    case PublicKeyAlgorithmTag.ECDsa:
+                        throw new PgpException("Can't use ECDSA for encryption.");
+                    case PublicKeyAlgorithmTag.EdDsa:
+                        throw new PgpException("Can't use EdDSA for encryption.");
+                    default:
+                        throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
                     }
 
-                    AsymmetricKeyParameter akp = pubKey.GetKey();
-				    c.Init(true, new ParametersWithRandom(akp, random));
+				    c.Init(true, new ParametersWithRandom(cryptoPublicKey, random));
                     return c.DoFinal(sessionInfo);
                 }
 
-                ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKey.PublicKeyPacket.Key;
+                ECDHPublicBcpgKey ecPubKey = (ECDHPublicBcpgKey)pubKey.PublicKeyPacket.Key;
+                var curveOid = ecPubKey.CurveOid;
+
+                if (EdECObjectIdentifiers.id_X25519.Equals(curveOid) ||
+                    CryptlibObjectIdentifiers.curvey25519.Equals(curveOid))
+                {
+                    X25519KeyPairGenerator gen = new X25519KeyPairGenerator();
+                    gen.Init(new X25519KeyGenerationParameters(random));
+
+                    AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair();
+
+                    X25519Agreement agreement = new X25519Agreement();
+                    agreement.Init(ephKp.Private);
+
+                    byte[] secret = new byte[agreement.AgreementSize];
+                    agreement.CalculateAgreement(cryptoPublicKey, secret, 0);
+
+                    byte[] ephPubEncoding = new byte[1 + X25519PublicKeyParameters.KeySize];
+                    ephPubEncoding[0] = 0x40;
+                    ((X25519PublicKeyParameters)ephKp.Public).Encode(ephPubEncoding, 1);
+
+                    return EncryptSessionInfo(ecPubKey, sessionInfo, secret, ephPubEncoding, random);
+                }
+                else if (EdECObjectIdentifiers.id_X448.Equals(curveOid))
+                {
+                    X448KeyPairGenerator gen = new X448KeyPairGenerator();
+                    gen.Init(new X448KeyGenerationParameters(random));
+
+                    AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair();
+
+                    X448Agreement agreement = new X448Agreement();
+                    agreement.Init(ephKp.Private);
+
+                    byte[] secret = new byte[agreement.AgreementSize];
+                    agreement.CalculateAgreement(cryptoPublicKey, secret, 0);
 
-                // Generate the ephemeral key pair
-                IAsymmetricCipherKeyPairGenerator gen = GeneratorUtilities.GetKeyPairGenerator("ECDH");
-                gen.Init(new ECKeyGenerationParameters(ecKey.CurveOid, random));
+                    byte[] ephPubEncoding = new byte[1 + X448PublicKeyParameters.KeySize];
+                    ephPubEncoding[0] = 0x40;
+                    ((X448PublicKeyParameters)ephKp.Public).Encode(ephPubEncoding, 1);
 
-                AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair();
-                ECPrivateKeyParameters ephPriv = (ECPrivateKeyParameters)ephKp.Private;
-                ECPublicKeyParameters ephPub = (ECPublicKeyParameters)ephKp.Public;
+                    return EncryptSessionInfo(ecPubKey, sessionInfo, secret, ephPubEncoding, random);
+                }
+                else
+                {
+                    // Generate the ephemeral key pair
+                    ECDomainParameters ecParams = ((ECPublicKeyParameters)cryptoPublicKey).Parameters;
+                    ECKeyPairGenerator gen = new ECKeyPairGenerator();
+                    gen.Init(new ECKeyGenerationParameters(ecParams, random));
 
-                ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey.GetKey();
-                ECPoint S = pub.Q.Multiply(ephPriv.D).Normalize();
+                    AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair();
 
-                KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, S));
+                    ECDHBasicAgreement agreement = new ECDHBasicAgreement();
+                    agreement.Init(ephKp.Private);
+                    BigInteger S = agreement.CalculateAgreement(cryptoPublicKey);
+                    byte[] secret = BigIntegers.AsUnsignedByteArray(agreement.GetFieldSize(), S);
+
+                    byte[] ephPubEncoding = ((ECPublicKeyParameters)ephKp.Public).Q.GetEncoded(false);
+                    return EncryptSessionInfo(ecPubKey, sessionInfo, secret, ephPubEncoding, random);
+                }
+            }
+
+            private byte[] EncryptSessionInfo(ECDHPublicBcpgKey ecPubKey, byte[] sessionInfo, byte[] secret,
+                byte[] ephPubEncoding, SecureRandom random)
+            {
+                var key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, secret));
 
-                IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm);
+                IWrapper w = PgpUtilities.CreateWrapper(ecPubKey.SymmetricKeyAlgorithm);
                 w.Init(true, new ParametersWithRandom(key, random));
 
                 byte[] paddedSessionData = PgpPad.PadSessionData(sessionInfo, sessionKeyObfuscation);
 
                 byte[] C = w.Wrap(paddedSessionData, 0, paddedSessionData.Length);
-                byte[] VB = new MPInteger(MPInteger.ToMpiBigInteger(ephPub.Q)).GetEncoded();
+                byte[] VB = new MPInteger(new BigInteger(1, ephPubEncoding)).GetEncoded();
 
                 byte[] rv = new byte[VB.Length + 1 + C.Length];
 
@@ -165,7 +223,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 {
                 case PublicKeyAlgorithmTag.RsaEncrypt:
                 case PublicKeyAlgorithmTag.RsaGeneral:
-                    data = new byte[][] { ConvertToEncodedMpi(encryptedSessionInfo) };
+                    data = new byte[1][] { ConvertToEncodedMpi(encryptedSessionInfo) };
                     break;
                 case PublicKeyAlgorithmTag.ElGamalEncrypt:
                 case PublicKeyAlgorithmTag.ElGamalGeneral:
@@ -176,13 +234,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     Array.Copy(encryptedSessionInfo, 0, b1, 0, halfLength);
                     Array.Copy(encryptedSessionInfo, halfLength, b2, 0, halfLength);
 
-                    data = new byte[][] {
+                    data = new byte[2][] {
                         ConvertToEncodedMpi(b1),
                         ConvertToEncodedMpi(b2),
                     };
                     break;
                 case PublicKeyAlgorithmTag.ECDH:
-                    data = new byte[][]{ encryptedSessionInfo };
+                    data = new byte[1][]{ encryptedSessionInfo };
                     break;
                 default:
                     throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
@@ -489,8 +547,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
 				if (withIntegrityPacket)
                 {
-					string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
-					IDigest digest = DigestUtilities.GetDigest(digestName);
+                    IDigest digest = PgpUtilities.CreateDigest(HashAlgorithmTag.Sha1);
 					myOut = digestOut = new DigestStream(myOut, null, digest);
                 }
 
diff --git a/crypto/src/openpgp/PgpKdfParameters.cs b/crypto/src/openpgp/PgpKdfParameters.cs
new file mode 100644
index 000000000..c78448939
--- /dev/null
+++ b/crypto/src/openpgp/PgpKdfParameters.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+    internal sealed class PgpKdfParameters
+        //: IPgpAlgorithmParameters
+    {
+        private readonly HashAlgorithmTag m_hashAlgorithm;
+        private readonly SymmetricKeyAlgorithmTag m_symmetricWrapAlgorithm;
+
+        public PgpKdfParameters(HashAlgorithmTag hashAlgorithm, SymmetricKeyAlgorithmTag symmetricWrapAlgorithm)
+        {
+            m_hashAlgorithm = hashAlgorithm;
+            m_symmetricWrapAlgorithm = symmetricWrapAlgorithm;
+        }
+
+        public HashAlgorithmTag HashAlgorithm => m_hashAlgorithm;
+
+        public SymmetricKeyAlgorithmTag SymmetricWrapAlgorithm => m_symmetricWrapAlgorithm;
+    }
+}
diff --git a/crypto/src/openpgp/PgpLiteralDataGenerator.cs b/crypto/src/openpgp/PgpLiteralDataGenerator.cs
index 7672659ca..f9a9e7cad 100644
--- a/crypto/src/openpgp/PgpLiteralDataGenerator.cs
+++ b/crypto/src/openpgp/PgpLiteralDataGenerator.cs
@@ -141,7 +141,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			return new WrappedGeneratorStream(this, pkOut);
 		}
 
-#if !PORTABLE || DOTNET
         /// <summary>
 		/// <p>
 		/// Open a literal data packet for the passed in <c>FileInfo</c> object, returning
@@ -163,7 +162,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
 			return Open(outStr, format, file.Name, file.Length, file.LastWriteTime);
         }
-#endif
 
 		/// <summary>
 		/// Close the literal data packet - this is equivalent to calling Close()
diff --git a/crypto/src/openpgp/PgpObjectFactory.cs b/crypto/src/openpgp/PgpObjectFactory.cs
index f7bf89507..068b85154 100644
--- a/crypto/src/openpgp/PgpObjectFactory.cs
+++ b/crypto/src/openpgp/PgpObjectFactory.cs
@@ -72,9 +72,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 }
             case PacketTag.PublicKey:
                 return new PgpPublicKeyRing(bcpgIn);
-			// TODO Make PgpPublicKey a PgpObject or return a PgpPublicKeyRing
-			//case PacketTag.PublicSubkey:
-			//	return PgpPublicKeyRing.ReadSubkey(bcpgIn);
+			case PacketTag.PublicSubkey:
+				return PgpPublicKeyRing.ReadSubkey(bcpgIn);
             case PacketTag.CompressedData:
                 return new PgpCompressedData(bcpgIn);
             case PacketTag.LiteralData:
diff --git a/crypto/src/openpgp/PgpOnePassSignature.cs b/crypto/src/openpgp/PgpOnePassSignature.cs
index 2fab5137e..c14e72bf7 100644
--- a/crypto/src/openpgp/PgpOnePassSignature.cs
+++ b/crypto/src/openpgp/PgpOnePassSignature.cs
@@ -2,7 +2,9 @@ using System;
 using System.IO;
 
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
@@ -11,10 +13,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
     {
         private static OnePassSignaturePacket Cast(Packet packet)
         {
-            if (!(packet is OnePassSignaturePacket))
-                throw new IOException("unexpected packet in stream: " + packet);
+            if (packet is OnePassSignaturePacket onePassSignaturePacket)
+                return onePassSignaturePacket;
 
-            return (OnePassSignaturePacket)packet;
+            throw new IOException("unexpected packet in stream: " + packet);
         }
 
         private readonly OnePassSignaturePacket sigPack;
@@ -36,15 +38,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         }
 
 		/// <summary>Initialise the signature object for verification.</summary>
-        public void InitVerify(
-            PgpPublicKey pubKey)
+        public void InitVerify(PgpPublicKey pubKey)
         {
 			lastb = 0;
+            AsymmetricKeyParameter key = pubKey.GetKey();
 
-			try
+            try
 			{
-				sig = SignerUtilities.GetSigner(
-					PgpUtilities.GetSignatureName(sigPack.KeyAlgorithm, sigPack.HashAlgorithm));
+				sig = PgpUtilities.CreateSigner(sigPack.KeyAlgorithm, sigPack.HashAlgorithm, key);
 			}
 			catch (Exception e)
 			{
@@ -53,7 +54,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
 			try
             {
-                sig.Init(false, pubKey.GetKey());
+                sig.Init(false, key);
             }
 			catch (InvalidKeyException e)
             {
@@ -61,12 +62,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-		public void Update(
-            byte b)
+		public void Update(byte b)
         {
 			if (signatureType == PgpSignature.CanonicalTextDocument)
 			{
-				doCanonicalUpdateByte(b);
+				DoCanonicalUpdateByte(b);
 			}
 			else
 			{
@@ -74,18 +74,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			}
         }
 
-		private void doCanonicalUpdateByte(
-			byte b)
+		private void DoCanonicalUpdateByte(byte b)
 		{
 			if (b == '\r')
 			{
-				doUpdateCRLF();
+				DoUpdateCRLF();
 			}
 			else if (b == '\n')
 			{
 				if (lastb != '\r')
 				{
-					doUpdateCRLF();
+					DoUpdateCRLF();
 				}
 			}
 			else
@@ -96,51 +95,57 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			lastb = b;
 		}
 
-		private void doUpdateCRLF()
+		private void DoUpdateCRLF()
 		{
 			sig.Update((byte)'\r');
 			sig.Update((byte)'\n');
 		}
 
-		public void Update(
-            byte[] bytes)
+		public void Update(params byte[] bytes)
+        {
+            Update(bytes, 0, bytes.Length);
+        }
+
+        public void Update(byte[] bytes, int off, int length)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Update(bytes.AsSpan(off, length));
+#else
             if (signatureType == PgpSignature.CanonicalTextDocument)
             {
-                for (int i = 0; i != bytes.Length; i++)
+                int finish = off + length;
+
+                for (int i = off; i != finish; i++)
                 {
-                    doCanonicalUpdateByte(bytes[i]);
+                    DoCanonicalUpdateByte(bytes[i]);
                 }
             }
             else
             {
-                sig.BlockUpdate(bytes, 0, bytes.Length);
+                sig.BlockUpdate(bytes, off, length);
             }
+#endif
         }
 
-        public void Update(
-            byte[]  bytes,
-            int     off,
-            int     length)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
         {
             if (signatureType == PgpSignature.CanonicalTextDocument)
             {
-                int finish = off + length;
-
-                for (int i = off; i != finish; i++)
+                for (int i = 0; i < input.Length; ++i)
                 {
-                    doCanonicalUpdateByte(bytes[i]);
+                    DoCanonicalUpdateByte(input[i]);
                 }
             }
             else
             {
-                sig.BlockUpdate(bytes, off, length);
+                sig.BlockUpdate(input);
             }
         }
+#endif
 
-		/// <summary>Verify the calculated signature against the passed in PgpSignature.</summary>
-        public bool Verify(
-            PgpSignature pgpSig)
+        /// <summary>Verify the calculated signature against the passed in PgpSignature.</summary>
+        public bool Verify(PgpSignature pgpSig)
         {
             byte[] trailer = pgpSig.GetSignatureTrailer();
 
@@ -171,15 +176,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
 		public byte[] GetEncoded()
         {
-            MemoryStream bOut = new MemoryStream();
+            var bOut = new MemoryStream();
 
             Encode(bOut);
 
             return bOut.ToArray();
         }
 
-		public void Encode(
-            Stream outStr)
+		public void Encode(Stream outStr)
         {
             BcpgOutputStream.Wrap(outStr).WritePacket(sigPack);
         }
diff --git a/crypto/src/openpgp/PgpPbeEncryptedData.cs b/crypto/src/openpgp/PgpPbeEncryptedData.cs
index f43f2f512..7920f54ea 100644
--- a/crypto/src/openpgp/PgpPbeEncryptedData.cs
+++ b/crypto/src/openpgp/PgpPbeEncryptedData.cs
@@ -97,10 +97,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 				{
 					truncStream = new TruncatedStream(encStream);
 
-					string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
-					IDigest digest = DigestUtilities.GetDigest(digestName);
+                    IDigest digest = PgpUtilities.CreateDigest(HashAlgorithmTag.Sha1);
 
-					encStream = new DigestStream(truncStream, digest, null);
+                    encStream = new DigestStream(truncStream, digest, null);
 				}
 
 				if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length)
diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs
index cdb8efd36..400cda071 100644
--- a/crypto/src/openpgp/PgpPublicKey.cs
+++ b/crypto/src/openpgp/PgpPublicKey.cs
@@ -3,13 +3,18 @@ using System.Collections.Generic;
 using System.IO;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cryptlib;
+using Org.BouncyCastle.Asn1.EdEC;
 using Org.BouncyCastle.Asn1.Gnu;
+using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Math.EC.Rfc7748;
+using Org.BouncyCastle.Math.EC.Rfc8032;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Collections;
@@ -18,7 +23,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
     /// <remarks>General class to handle a PGP public key object.</remarks>
     public class PgpPublicKey
+        : PgpObject
     {
+        // We default to these as they are specified as mandatory in RFC 6631.
+        private static readonly PgpKdfParameters DefaultKdfParameters = new PgpKdfParameters(HashAlgorithmTag.Sha256,
+            SymmetricKeyAlgorithmTag.Aes128);
+
         public static byte[] CalculateFingerprint(PublicKeyPacket publicPk)
         {
             IBcpgKey key = publicPk.Key;
@@ -30,7 +40,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
                 try
                 {
-                    digest = DigestUtilities.GetDigest("MD5");
+                    digest = PgpUtilities.CreateDigest(HashAlgorithmTag.MD5);
+
                     UpdateDigest(digest, rK.Modulus);
                     UpdateDigest(digest, rK.PublicExponent);
                 }
@@ -45,7 +56,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 {
                     byte[] kBytes = publicPk.GetEncodedContents();
 
-                    digest = DigestUtilities.GetDigest("SHA1");
+                    digest = PgpUtilities.CreateDigest(HashAlgorithmTag.Sha1);
 
                     digest.Update(0x99);
                     digest.Update((byte)(kBytes.Length >> 8));
@@ -124,27 +135,38 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 {
                     this.keyStrength = ((ElGamalPublicBcpgKey)key).P.BitLength;
                 }
-                else if (key is ECPublicBcpgKey)
+                else if (key is EdDsaPublicBcpgKey eddsaK)
                 {
-                    DerObjectIdentifier curveOid = ((ECPublicBcpgKey)key).CurveOid;
-                    if (GnuObjectIdentifiers.Ed25519.Equals(curveOid)
-                        //|| CryptlibObjectIdentifiers.curvey25519.Equals(curveOid)
-                        )
+                    var curveOid = eddsaK.CurveOid;
+                    if (EdECObjectIdentifiers.id_Ed25519.Equals(curveOid) ||
+                        GnuObjectIdentifiers.Ed25519.Equals(curveOid) ||
+                        EdECObjectIdentifiers.id_X25519.Equals(curveOid) ||
+                        CryptlibObjectIdentifiers.curvey25519.Equals(curveOid))
                     {
                         this.keyStrength = 256;
                     }
+                    else if (EdECObjectIdentifiers.id_Ed448.Equals(curveOid) ||
+                        EdECObjectIdentifiers.id_X448.Equals(curveOid))
+                    {
+                        this.keyStrength = 448;
+                    }
                     else
                     {
-                        X9ECParametersHolder ecParameters = ECKeyPairGenerator.FindECCurveByOidLazy(curveOid);
-
-                        if (ecParameters != null)
-                        {
-                            this.keyStrength = ecParameters.Curve.FieldSize;
-                        }
-                        else
-                        {
-                            this.keyStrength = -1; // unknown
-                        }
+                        this.keyStrength = -1; // unknown
+                    }
+                }
+                else if (key is ECPublicBcpgKey ecK)
+                {
+                    var curveOid = ecK.CurveOid;
+                    X9ECParametersHolder ecParameters = ECKeyPairGenerator.FindECCurveByOidLazy(curveOid);
+
+                    if (ecParameters != null)
+                    {
+                        this.keyStrength = ecParameters.Curve.FieldSize;
+                    }
+                    else
+                    {
+                        this.keyStrength = -1; // unknown
                     }
                 }
             }
@@ -165,7 +187,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         public PgpPublicKey(PublicKeyAlgorithmTag algorithm, AsymmetricKeyParameter pubKey, DateTime time)
         {
             if (pubKey.IsPrivate)
-                throw new ArgumentException("Expected a public key", "pubKey");
+                throw new ArgumentException("Expected a public key", nameof(pubKey));
 
             IBcpgKey bcpgKey;
             if (pubKey is RsaKeyParameters rK)
@@ -178,6 +200,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
                 bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y);
             }
+            else if (pubKey is ElGamalPublicKeyParameters eK)
+            {
+                ElGamalParameters eS = eK.Parameters;
+
+                bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y);
+            }
             else if (pubKey is ECPublicKeyParameters ecK)
             {
                 if (algorithm == PublicKeyAlgorithmTag.ECDH)
@@ -194,11 +222,39 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     throw new PgpException("unknown EC algorithm");
                 }
             }
-            else if (pubKey is ElGamalPublicKeyParameters eK)
+            else if (pubKey is Ed25519PublicKeyParameters ed25519PubKey)
             {
-                ElGamalParameters eS = eK.Parameters;
+                byte[] pointEnc = new byte[1 + Ed25519PublicKeyParameters.KeySize];
+                pointEnc[0] = 0x40;
+                ed25519PubKey.Encode(pointEnc, 1);
+                bcpgKey = new EdDsaPublicBcpgKey(GnuObjectIdentifiers.Ed25519, new BigInteger(1, pointEnc));
+            }
+            else if (pubKey is Ed448PublicKeyParameters ed448PubKey)
+            {
+                byte[] pointEnc = new byte[Ed448PublicKeyParameters.KeySize];
+                ed448PubKey.Encode(pointEnc, 0);
+                bcpgKey = new EdDsaPublicBcpgKey(EdECObjectIdentifiers.id_Ed448, new BigInteger(1, pointEnc));
+            }
+            else if (pubKey is X25519PublicKeyParameters x25519PubKey)
+            {
+                byte[] pointEnc = new byte[1 + X25519PublicKeyParameters.KeySize];
+                pointEnc[0] = 0x40;
+                x25519PubKey.Encode(pointEnc, 1);
 
-                bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y);
+                PgpKdfParameters kdfParams = DefaultKdfParameters;
+
+                bcpgKey = new ECDHPublicBcpgKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, pointEnc),
+                    kdfParams.HashAlgorithm, kdfParams.SymmetricWrapAlgorithm);
+            }
+            else if (pubKey is X448PublicKeyParameters x448PubKey)
+            {
+                byte[] pointEnc = new byte[X448PublicKeyParameters.KeySize];
+                x448PubKey.Encode(pointEnc, 0);
+
+                PgpKdfParameters kdfParams = DefaultKdfParameters;
+
+                bcpgKey = new ECDHPublicBcpgKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, pointEnc),
+                    kdfParams.HashAlgorithm, kdfParams.SymmetricWrapAlgorithm);
             }
             else
             {
@@ -473,24 +529,89 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             {
                 switch (publicPk.Algorithm)
                 {
-                    case PublicKeyAlgorithmTag.RsaEncrypt:
-                    case PublicKeyAlgorithmTag.RsaGeneral:
-                    case PublicKeyAlgorithmTag.RsaSign:
-                        RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)publicPk.Key;
-                        return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent);
-                    case PublicKeyAlgorithmTag.Dsa:
-                        DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey)publicPk.Key;
-                        return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G));
-                    case PublicKeyAlgorithmTag.ECDsa:
-                        return GetECKey("ECDSA");
-                    case PublicKeyAlgorithmTag.ECDH:
-                        return GetECKey("ECDH");
-                    case PublicKeyAlgorithmTag.ElGamalEncrypt:
-                    case PublicKeyAlgorithmTag.ElGamalGeneral:
-                        ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey)publicPk.Key;
-                        return new ElGamalPublicKeyParameters(elK.Y, new ElGamalParameters(elK.P, elK.G));
-                    default:
-                        throw new PgpException("unknown public key algorithm encountered");
+                case PublicKeyAlgorithmTag.RsaEncrypt:
+                case PublicKeyAlgorithmTag.RsaGeneral:
+                case PublicKeyAlgorithmTag.RsaSign:
+                    RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)publicPk.Key;
+                    return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent);
+                case PublicKeyAlgorithmTag.Dsa:
+                    DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey)publicPk.Key;
+                    return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G));
+                case PublicKeyAlgorithmTag.ECDsa:
+                    ECDsaPublicBcpgKey ecdsaK = (ECDsaPublicBcpgKey)publicPk.Key;
+                    return GetECKey("ECDSA", ecdsaK);
+                case PublicKeyAlgorithmTag.ECDH:
+                {
+                    ECDHPublicBcpgKey ecdhK = (ECDHPublicBcpgKey)publicPk.Key;
+                    var curveOid = ecdhK.CurveOid;
+
+                    if (EdECObjectIdentifiers.id_X25519.Equals(curveOid) ||
+                        CryptlibObjectIdentifiers.curvey25519.Equals(curveOid))
+                    {
+                        byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + X25519.PointSize, ecdhK.EncodedPoint);
+                        if (pEnc[0] != 0x40)
+                            throw new ArgumentException("Invalid X25519 public key");
+
+                        return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            // TODO Span variant
+                            Arrays.CopyOfRange(pEnc, 1, pEnc.Length)));
+                    }
+                    else if (EdECObjectIdentifiers.id_X448.Equals(curveOid))
+                    {
+                        byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + X448.PointSize, ecdhK.EncodedPoint);
+                        if (pEnc[0] != 0x40)
+                            throw new ArgumentException("Invalid X448 public key");
+
+                        return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            // TODO Span variant
+                            Arrays.CopyOfRange(pEnc, 1, pEnc.Length)));
+                    }
+                    else
+                    {
+                        return GetECKey("ECDH", ecdhK);
+                    }
+                }
+                case PublicKeyAlgorithmTag.EdDsa:
+                {
+                    EdDsaPublicBcpgKey eddsaK = (EdDsaPublicBcpgKey)publicPk.Key;
+                    var curveOid = eddsaK.CurveOid;
+
+                    if (EdECObjectIdentifiers.id_Ed25519.Equals(curveOid) ||
+                        GnuObjectIdentifiers.Ed25519.Equals(curveOid))
+                    {
+                        byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + Ed25519.PublicKeySize, eddsaK.EncodedPoint);
+                        if (pEnc[0] != 0x40)
+                            throw new ArgumentException("Invalid Ed25519 public key");
+
+                        return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            // TODO Span variant
+                            Arrays.CopyOfRange(pEnc, 1, pEnc.Length)));
+                    }
+                    else if (EdECObjectIdentifiers.id_Ed448.Equals(curveOid))
+                    {
+                        byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + Ed448.PublicKeySize, eddsaK.EncodedPoint);
+                        if (pEnc[0] != 0x40)
+                            throw new ArgumentException("Invalid Ed448 public key");
+
+                        return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            // TODO Span variant
+                            Arrays.CopyOfRange(pEnc, 1, pEnc.Length)));
+                    }
+                    else 
+                    {
+                        throw new InvalidOperationException();
+                    }
+                }
+                case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                case PublicKeyAlgorithmTag.ElGamalGeneral:
+                    ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey)publicPk.Key;
+                    return new ElGamalPublicKeyParameters(elK.Y, new ElGamalParameters(elK.P, elK.G));
+                default:
+                    throw new PgpException("unknown public key algorithm encountered");
                 }
             }
             catch (PgpException e)
@@ -503,9 +624,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-        private ECPublicKeyParameters GetECKey(string algorithm)
+        private ECPublicKeyParameters GetECKey(string algorithm, ECPublicBcpgKey ecK)
         {
-            ECPublicBcpgKey ecK = (ECPublicBcpgKey)publicPk.Key;
             X9ECParameters x9 = ECKeyPairGenerator.FindECCurveByOid(ecK.CurveOid);
             BigInteger encodedPoint = ecK.EncodedPoint;
 
@@ -563,7 +683,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         public IEnumerable<PgpSignature> GetSignaturesForId(string id)
         {
             if (id == null)
-                throw new ArgumentNullException("id");
+                throw new ArgumentNullException(nameof(id));
 
             var result = new List<PgpSignature>();
             bool userIdFound = false;
@@ -580,13 +700,48 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return userIdFound ? CollectionUtilities.Proxy(result) : null;
         }
 
+        private IEnumerable<PgpSignature> GetSignaturesForID(UserIdPacket id)
+        {
+            var signatures = new List<PgpSignature>();
+            bool userIdFound = false;
+
+            for (int i = 0; i != ids.Count; i++)
+            {
+                if (id.Equals(ids[i]))
+                {
+                    userIdFound = true;
+                    signatures.AddRange(idSigs[i]);
+                }
+            }
+
+            return userIdFound ? signatures : null;
+        }
+
+        /// <summary>Return any signatures associated with the passed in key identifier keyID.</summary>
+        /// <param name="keyID">the key id to be matched.</param>
+        /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects issued by the key with keyID.</returns>
+        public IEnumerable<PgpSignature> GetSignaturesForKeyID(long keyID)
+        {
+            var sigs = new List<PgpSignature>();
+
+            foreach (var sig in GetSignatures())
+            {
+                if (sig.KeyId == keyID)
+                {
+                    sigs.Add(sig);
+                }
+            }
+
+            return CollectionUtilities.Proxy(sigs);
+        }
+
         /// <summary>Allows enumeration of signatures associated with the passed in user attributes.</summary>
         /// <param name="userAttributes">The vector of user attributes to be matched.</param>
         /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns>
         public IEnumerable<PgpSignature> GetSignaturesForUserAttribute(PgpUserAttributeSubpacketVector userAttributes)
         {
             if (userAttributes == null)
-                throw new ArgumentNullException("userAttributes");
+                throw new ArgumentNullException(nameof(userAttributes));
 
             var result = new List<PgpSignature>();
             bool attributeFound = false;
@@ -648,11 +803,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
          */
         public IEnumerable<PgpSignature> GetKeySignatures()
         {
-            var result = subSigs;
-            if (result == null)
-            {
-                result = new List<PgpSignature>(keySigs);
-            }
+            var result = subSigs ?? new List<PgpSignature>(keySigs);
 
             return CollectionUtilities.Proxy(result);
         }
@@ -947,56 +1098,38 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         /// <param name="key">The key the certifications are to be removed from.</param>
         /// <param name="certification">The certfication to be removed.</param>
         /// <returns>The modified key, null if the certification was not found.</returns>
-        public static PgpPublicKey RemoveCertification(
-            PgpPublicKey	key,
-            PgpSignature	certification)
+        public static PgpPublicKey RemoveCertification(PgpPublicKey	key, PgpSignature certification)
         {
-            PgpPublicKey returnKey = new PgpPublicKey(key);
-            var sigs = returnKey.subSigs != null
-                ?	returnKey.subSigs
-                :	returnKey.keySigs;
+            var returnKey = new PgpPublicKey(key);
+            var sigs = returnKey.subSigs ?? returnKey.keySigs;
 
-//			bool found = sigs.Remove(certification);
-            int pos = sigs.IndexOf(certification);
-            bool found = pos >= 0;
+            if (sigs.Remove(certification))
+                return returnKey;
 
-            if (found)
+            // TODO Java uses getRawUserIDs
+            foreach (string id in key.GetUserIds())
             {
-                sigs.RemoveAt(pos);
+                if (ContainsSignature(key.GetSignaturesForId(id), certification))
+                    return RemoveCertification(returnKey, id, certification);
             }
-            else
-            {
-                foreach (string id in key.GetUserIds())
-                {
-                    foreach (object sig in key.GetSignaturesForId(id))
-                    {
-                        // TODO Is this the right type of equality test?
-                        if (certification == sig)
-                        {
-                            found = true;
-                            returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification);
-                        }
-                    }
-                }
 
-                if (!found)
-                {
-                    foreach (PgpUserAttributeSubpacketVector id in key.GetUserAttributes())
-                    {
-                        foreach (object sig in key.GetSignaturesForUserAttribute(id))
-                        {
-                            // TODO Is this the right type of equality test?
-                            if (certification == sig)
-                            {
-                                found = true;
-                                returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification);
-                            }
-                        }
-                    }
-                }
+            foreach (PgpUserAttributeSubpacketVector id in key.GetUserAttributes())
+            {
+                if (ContainsSignature(key.GetSignaturesForUserAttribute(id), certification))
+                    return RemoveCertification(returnKey, id, certification);
             }
 
             return returnKey;
         }
+
+        private static bool ContainsSignature(IEnumerable<PgpSignature> signatures, PgpSignature signature)
+        {
+            foreach (PgpSignature candidate in signatures)
+            {
+                if (signature == candidate)
+                    return true;
+            }
+            return false;
+        }
     }
 }
diff --git a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
index 04fe3ad37..8c6fcda53 100644
--- a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
+++ b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
@@ -1,19 +1,20 @@
 using System;
 using System.IO;
 
-using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Asn1.Cryptlib;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.IO;
-using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
-using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Asn1.EdEC;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
-	/// <remarks>A public key encrypted data object.</remarks>
+    /// <remarks>A public key encrypted data object.</remarks>
     public class PgpPublicKeyEncryptedData
         : PgpEncryptedData
     {
@@ -139,10 +140,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 {
                     truncStream = new TruncatedStream(encStream);
 
-					string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
-					IDigest digest = DigestUtilities.GetDigest(digestName);
+                    IDigest digest = PgpUtilities.CreateDigest(HashAlgorithmTag.Sha1);
 
-					encStream = new DigestStream(truncStream, digest, null);
+                    encStream = new DigestStream(truncStream, digest, null);
                 }
 
 				if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length)
@@ -189,76 +189,114 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		{
             byte[][] secKeyData = keyData.GetEncSessionKey();
 
-            if (keyData.Algorithm == PublicKeyAlgorithmTag.ECDH)
+            if (keyData.Algorithm != PublicKeyAlgorithmTag.ECDH)
             {
-                ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)privKey.PublicKeyPacket.Key;
-                X9ECParameters x9Params = ECKeyPairGenerator.FindECCurveByOid(ecKey.CurveOid);
+                IBufferedCipher cipher = GetKeyCipher(keyData.Algorithm);
+
+                try
+			    {
+                    cipher.Init(false, privKey.Key);
+			    }
+			    catch (InvalidKeyException e)
+			    {
+				    throw new PgpException("error setting asymmetric cipher", e);
+			    }
+
+                if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
+				    || keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral)
+			    {
+                    byte[] bi = secKeyData[0];
+
+                    cipher.ProcessBytes(bi, 2, bi.Length - 2);
+			    }
+			    else
+			    {
+				    ElGamalPrivateKeyParameters k = (ElGamalPrivateKeyParameters)privKey.Key;
+				    int size = (k.Parameters.P.BitLength + 7) / 8;
+
+                    ProcessEncodedMpi(cipher, size, secKeyData[0]);
+                    ProcessEncodedMpi(cipher, size, secKeyData[1]);
+			    }
+
+                try
+			    {
+                    return cipher.DoFinal();
+			    }
+			    catch (Exception e)
+			    {
+				    throw new PgpException("exception decrypting secret key", e);
+			    }
+            }
 
-                byte[] enc = secKeyData[0];
+            ECDHPublicBcpgKey ecPubKey = (ECDHPublicBcpgKey)privKey.PublicKeyPacket.Key;
+            byte[] enc = secKeyData[0];
 
-                int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
-                if ((2 + pLen + 1) > enc.Length) 
-                    throw new PgpException("encoded length out of range");
+            int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
+            if ((2 + pLen + 1) > enc.Length) 
+                throw new PgpException("encoded length out of range");
 
-                byte[] pEnc = new byte[pLen];
-                Array.Copy(enc, 2, pEnc, 0, pLen);
+            byte[] pEnc = new byte[pLen];
+            Array.Copy(enc, 2, pEnc, 0, pLen);
 
-                int keyLen = enc[pLen + 2];
-                if ((2 + pLen + 1 + keyLen) > enc.Length)
-                    throw new PgpException("encoded length out of range");
+            int keyLen = enc[pLen + 2];
+            if ((2 + pLen + 1 + keyLen) > enc.Length)
+                throw new PgpException("encoded length out of range");
 
-                byte[] keyEnc = new byte[keyLen];
-                Array.Copy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.Length);
+            byte[] keyEnc = new byte[keyLen];
+            Array.Copy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.Length);
 
-                ECPoint publicPoint = x9Params.Curve.DecodePoint(pEnc);
+            var curveOid = ecPubKey.CurveOid;
+            byte[] secret;
 
-                ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters)privKey.Key;
-                ECPoint S = publicPoint.Multiply(privKeyParams.D).Normalize();
+            if (EdECObjectIdentifiers.id_X25519.Equals(curveOid) ||
+                CryptlibObjectIdentifiers.curvey25519.Equals(curveOid))
+            {
+                // skip the 0x40 header byte.
+                if (pEnc.Length != (1 + X25519PublicKeyParameters.KeySize) || 0x40 != pEnc[0])
+                    throw new ArgumentException("Invalid X25519 public key");
 
-                KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, S));
+                X25519PublicKeyParameters ephPub = new X25519PublicKeyParameters(pEnc, 1);
 
-                IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm);
-                w.Init(false, key);
+                X25519Agreement agreement = new X25519Agreement();
+                agreement.Init(privKey.Key);
 
-                return PgpPad.UnpadSessionData(w.Unwrap(keyEnc, 0, keyEnc.Length));
+                secret = new byte[agreement.AgreementSize];
+                agreement.CalculateAgreement(ephPub, secret, 0);
             }
+            else if (EdECObjectIdentifiers.id_X448.Equals(curveOid))
+            {
+                // skip the 0x40 header byte.
+                if (pEnc.Length != (1 + X448PublicKeyParameters.KeySize) || 0x40 != pEnc[0])
+                    throw new ArgumentException("Invalid X448 public key");
 
-            IBufferedCipher cipher = GetKeyCipher(keyData.Algorithm);
+                X448PublicKeyParameters ephPub = new X448PublicKeyParameters(pEnc, 1);
 
-            try
-			{
-                cipher.Init(false, privKey.Key);
-			}
-			catch (InvalidKeyException e)
-			{
-				throw new PgpException("error setting asymmetric cipher", e);
-			}
+                X448Agreement agreement = new X448Agreement();
+                agreement.Init(privKey.Key);
 
-            if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
-				|| keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral)
-			{
-                byte[] bi = secKeyData[0];
+                secret = new byte[agreement.AgreementSize];
+                agreement.CalculateAgreement(ephPub, secret, 0);
+            }
+            else
+            {
+                ECDomainParameters ecParameters = ((ECPrivateKeyParameters)privKey.Key).Parameters;
 
-                cipher.ProcessBytes(bi, 2, bi.Length - 2);
-			}
-			else
-			{
-				ElGamalPrivateKeyParameters k = (ElGamalPrivateKeyParameters)privKey.Key;
-				int size = (k.Parameters.P.BitLength + 7) / 8;
+                ECPublicKeyParameters ephPub = new ECPublicKeyParameters(ecParameters.Curve.DecodePoint(pEnc),
+                    ecParameters);
 
-                ProcessEncodedMpi(cipher, size, secKeyData[0]);
-                ProcessEncodedMpi(cipher, size, secKeyData[1]);
-			}
+                ECDHBasicAgreement agreement = new ECDHBasicAgreement();
+                agreement.Init(privKey.Key);
+                BigInteger S = agreement.CalculateAgreement(ephPub);
+                secret = BigIntegers.AsUnsignedByteArray(agreement.GetFieldSize(), S);
+            }
 
-            try
-			{
-                return cipher.DoFinal();
-			}
-			catch (Exception e)
-			{
-				throw new PgpException("exception decrypting secret key", e);
-			}
-		}
+            KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, secret));
+
+            IWrapper w = PgpUtilities.CreateWrapper(ecPubKey.SymmetricKeyAlgorithm);
+            w.Init(false, key);
+
+            return PgpPad.UnpadSessionData(w.Unwrap(keyEnc, 0, keyEnc.Length));
+        }
 
         private static void ProcessEncodedMpi(IBufferedCipher cipher, int size, byte[] mpiEnc)
         {
diff --git a/crypto/src/openpgp/PgpPublicKeyRing.cs b/crypto/src/openpgp/PgpPublicKeyRing.cs
index 4aa15384c..ebbb95634 100644
--- a/crypto/src/openpgp/PgpPublicKeyRing.cs
+++ b/crypto/src/openpgp/PgpPublicKeyRing.cs
@@ -68,19 +68,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         /// <summary>Return the first public key in the ring.</summary>
         public virtual PgpPublicKey GetPublicKey()
         {
-            return (PgpPublicKey) keys[0];
+            return keys[0];
         }
 
         /// <summary>Return the public key referred to by the passed in key ID if it is present.</summary>
-        public virtual PgpPublicKey GetPublicKey(
-            long keyId)
+        public virtual PgpPublicKey GetPublicKey(long keyId)
         {
             foreach (PgpPublicKey k in keys)
             {
                 if (keyId == k.KeyId)
-                {
                     return k;
-                }
             }
 
             return null;
@@ -168,23 +165,24 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         /// <returns>A new <c>PgpPublicKeyRing</c>, or null if pubKey is not found.</returns>
         public static PgpPublicKeyRing RemovePublicKey(PgpPublicKeyRing pubRing, PgpPublicKey pubKey)
         {
-            var keys = new List<PgpPublicKey>(pubRing.keys);
+            int count = pubRing.keys.Count;
+            long keyID = pubKey.KeyId;
+
+            var result = new List<PgpPublicKey>(count);
             bool found = false;
 
-            // TODO Is there supposed to be at most a single match?
-            int pos = keys.Count;
-            while (--pos >= 0)
+            foreach (var key in pubRing.keys)
             {
-                PgpPublicKey key = keys[pos];
-
-                if (key.KeyId == pubKey.KeyId)
+                if (key.KeyId == keyID)
                 {
                     found = true;
-                    keys.RemoveAt(pos);
+                    continue;
                 }
+
+                result.Add(key);
             }
 
-            return found ? new PgpPublicKeyRing(keys) : null;
+            return found ? new PgpPublicKeyRing(result) : null;
         }
 
         internal static PublicKeyPacket ReadPublicKeyPacket(BcpgInputStream bcpgInput)
diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs
index 51a45703a..0103cc187 100644
--- a/crypto/src/openpgp/PgpSecretKey.cs
+++ b/crypto/src/openpgp/PgpSecretKey.cs
@@ -2,10 +2,17 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cryptlib;
+using Org.BouncyCastle.Asn1.EdEC;
+using Org.BouncyCastle.Asn1.Gnu;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC.Rfc8032;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 
@@ -13,6 +20,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
     /// <remarks>General class to handle a PGP secret key object.</remarks>
     public class PgpSecretKey
+        : PgpObject
     {
         private readonly SecretKeyPacket	secret;
         private readonly PgpPublicKey		pub;
@@ -52,10 +60,39 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     secKey = new DsaSecretBcpgKey(dsK.X);
                     break;
                 case PublicKeyAlgorithmTag.ECDH:
+                {
+                    if (privKey.Key is ECPrivateKeyParameters ecdhK)
+                    {
+                        secKey = new ECSecretBcpgKey(ecdhK.D);
+                    }
+                    else
+                    {
+                        // 'reverse' because the native format for X25519 private keys is little-endian
+                        X25519PrivateKeyParameters xK = (X25519PrivateKeyParameters)privKey.Key;
+                        secKey = new ECSecretBcpgKey(new BigInteger(1, Arrays.ReverseInPlace(xK.GetEncoded())));
+                    }
+                    break;
+                }
                 case PublicKeyAlgorithmTag.ECDsa:
                     ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey.Key;
                     secKey = new ECSecretBcpgKey(ecK.D);
                     break;
+                case PublicKeyAlgorithmTag.EdDsa:
+                {
+                    if (privKey.Key is Ed25519PrivateKeyParameters ed25519K)
+                    {
+                        secKey = new EdSecretBcpgKey(new BigInteger(1, ed25519K.GetEncoded()));
+                    }
+                    else if (privKey.Key is Ed448PrivateKeyParameters ed448K)
+                    {
+                        secKey = new EdSecretBcpgKey(new BigInteger(1, ed448K.GetEncoded()));
+                    }
+                    else
+                    {
+                        throw new PgpException("unknown EdDSA key class");
+                    }
+                    break;
+                }
                 case PublicKeyAlgorithmTag.ElGamalEncrypt:
                 case PublicKeyAlgorithmTag.ElGamalGeneral:
                     ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) privKey.Key;
@@ -625,6 +662,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 return null;
 
             PublicKeyPacket pubPk = secret.PublicKeyPacket;
+
             try
             {
                 byte[] data = ExtractKeyData(rawPassPhrase, clearPassPhrase);
@@ -655,11 +693,65 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     privateKey = new DsaPrivateKeyParameters(dsaPriv.X, dsaParams);
                     break;
                 case PublicKeyAlgorithmTag.ECDH:
-                    privateKey = GetECKey("ECDH", bcpgIn);
+                {
+                    ECDHPublicBcpgKey ecdhPub = (ECDHPublicBcpgKey)pubPk.Key;
+                    ECSecretBcpgKey ecdhPriv = new ECSecretBcpgKey(bcpgIn);
+                    var curveOid = ecdhPub.CurveOid;
+
+                    if (EdECObjectIdentifiers.id_X25519.Equals(curveOid) ||
+                        CryptlibObjectIdentifiers.curvey25519.Equals(curveOid))
+                    {
+                        // 'reverse' because the native format for X25519 private keys is little-endian
+                        privateKey = PrivateKeyFactory.CreateKey(new PrivateKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            new DerOctetString(Arrays.ReverseInPlace(BigIntegers.AsUnsignedByteArray(ecdhPriv.X)))));
+                    }
+                    else if (EdECObjectIdentifiers.id_X448.Equals(curveOid))
+                    {
+                        // 'reverse' because the native format for X448 private keys is little-endian
+                        privateKey = PrivateKeyFactory.CreateKey(new PrivateKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            new DerOctetString(Arrays.ReverseInPlace(BigIntegers.AsUnsignedByteArray(ecdhPriv.X)))));
+                    }
+                    else
+                    {
+                        privateKey = new ECPrivateKeyParameters("ECDH", ecdhPriv.X, ecdhPub.CurveOid);
+                    }
                     break;
+                }
                 case PublicKeyAlgorithmTag.ECDsa:
-                    privateKey = GetECKey("ECDSA", bcpgIn);
+                {
+                    ECPublicBcpgKey ecdsaPub = (ECPublicBcpgKey)pubPk.Key;
+                    ECSecretBcpgKey ecdsaPriv = new ECSecretBcpgKey(bcpgIn);
+
+                    privateKey = new ECPrivateKeyParameters("ECDSA", ecdsaPriv.X, ecdsaPub.CurveOid);
                     break;
+                }
+                case PublicKeyAlgorithmTag.EdDsa:
+                {
+                    EdDsaPublicBcpgKey eddsaPub = (EdDsaPublicBcpgKey)pubPk.Key;
+                    EdSecretBcpgKey ecdsaPriv = new EdSecretBcpgKey(bcpgIn);
+
+                    var curveOid = eddsaPub.CurveOid;
+                    if (EdECObjectIdentifiers.id_Ed25519.Equals(curveOid) ||
+                        GnuObjectIdentifiers.Ed25519.Equals(curveOid))
+                    {
+                        privateKey = PrivateKeyFactory.CreateKey(new PrivateKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            new DerOctetString(BigIntegers.AsUnsignedByteArray(Ed25519.SecretKeySize, ecdsaPriv.X))));
+                    }
+                    else if (EdECObjectIdentifiers.id_Ed448.Equals(curveOid))
+                    {
+                        privateKey = PrivateKeyFactory.CreateKey(new PrivateKeyInfo(
+                            new AlgorithmIdentifier(curveOid),
+                            new DerOctetString(BigIntegers.AsUnsignedByteArray(Ed448.SecretKeySize, ecdsaPriv.X))));
+                    }
+                    else 
+                    {
+                        throw new InvalidOperationException();
+                    }
+                    break;
+                }
                 case PublicKeyAlgorithmTag.ElGamalEncrypt:
                 case PublicKeyAlgorithmTag.ElGamalGeneral:
                     ElGamalPublicBcpgKey elPub = (ElGamalPublicBcpgKey)pubPk.Key;
@@ -683,13 +775,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-        private ECPrivateKeyParameters GetECKey(string algorithm, BcpgInputStream bcpgIn)
-        {
-            ECPublicBcpgKey ecdsaPub = (ECPublicBcpgKey)secret.PublicKeyPacket.Key;
-            ECSecretBcpgKey ecdsaPriv = new ECSecretBcpgKey(bcpgIn);
-            return new ECPrivateKeyParameters(algorithm, ecdsaPriv.X, ecdsaPub.CurveOid);
-        }
-
         private static byte[] Checksum(
             bool	useSha1,
             byte[]	bytes,
@@ -699,7 +784,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             {
                 try
                 {
-                    IDigest dig = DigestUtilities.GetDigest("SHA1");
+                    IDigest dig = PgpUtilities.CreateDigest(HashAlgorithmTag.Sha1);
                     dig.BlockUpdate(bytes, 0, length);
                     return DigestUtilities.DoFinal(dig);
                 }
diff --git a/crypto/src/openpgp/PgpSecretKeyRing.cs b/crypto/src/openpgp/PgpSecretKeyRing.cs
index 637cb45f8..a070aa132 100644
--- a/crypto/src/openpgp/PgpSecretKeyRing.cs
+++ b/crypto/src/openpgp/PgpSecretKeyRing.cs
@@ -71,11 +71,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             // revocation and direct signatures
             var keySigs = ReadSignaturesAndTrust(bcpgInput);
 
-            IList<object> ids;
-            IList<TrustPacket> idTrusts;
-            IList<IList<PgpSignature>> idSigs;
-
-            ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
+            ReadUserIDs(bcpgInput, out var ids, out var idTrusts, out var idSigs);
 
             keys.Add(new PgpSecretKey(secret,
                 new PgpPublicKey(secret.PublicKeyPacket, trust, keySigs, ids, idTrusts, idSigs)));
@@ -119,6 +115,43 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return keys[0].PublicKey;
         }
 
+        /**
+         * Return any keys carrying a signature issued by the key represented by keyID.
+         *
+         * @param keyID the key id to be matched against.
+         * @return an iterator (possibly empty) of PGPPublicKey objects carrying signatures from keyID.
+         */
+        public IEnumerable<PgpPublicKey> GetKeysWithSignaturesBy(long keyID)
+        {
+            var keysWithSigs = new List<PgpPublicKey>();
+
+            foreach (var k in GetPublicKeys())
+            {
+                var sigIt = k.GetSignaturesForKeyID(keyID).GetEnumerator();
+
+                if (sigIt.MoveNext())
+                {
+                    keysWithSigs.Add(k);
+                }
+            }
+
+            return CollectionUtilities.Proxy(keysWithSigs);
+        }
+
+        public IEnumerable<PgpPublicKey> GetPublicKeys()
+        {
+            var pubKeys = new List<PgpPublicKey>();
+
+            foreach (var secretKey in keys)
+            {
+                pubKeys.Add(secretKey.PublicKey);
+            }
+
+            pubKeys.AddRange(extraPubKeys);
+
+            return CollectionUtilities.Proxy(pubKeys);
+        }
+
         /// <summary>Return the master private key.</summary>
         public PgpSecretKey GetSecretKey()
         {
@@ -156,7 +189,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
         public byte[] GetEncoded()
         {
-            MemoryStream bOut = new MemoryStream();
+            var bOut = new MemoryStream();
             Encode(bOut);
             return bOut.ToArray();
         }
@@ -165,7 +198,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             Stream outStr)
         {
             if (outStr == null)
-                throw new ArgumentNullException("outStr");
+                throw new ArgumentNullException(nameof(outStr));
 
             foreach (PgpSecretKey key in keys)
             {
diff --git a/crypto/src/openpgp/PgpSignature.cs b/crypto/src/openpgp/PgpSignature.cs
index da00d43eb..9b596f279 100644
--- a/crypto/src/openpgp/PgpSignature.cs
+++ b/crypto/src/openpgp/PgpSignature.cs
@@ -3,6 +3,8 @@ using System.IO;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC.Rfc8032;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Date;
@@ -14,10 +16,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
     {
         private static SignaturePacket Cast(Packet packet)
         {
-            if (!(packet is SignaturePacket))
-                throw new IOException("unexpected packet in stream: " + packet);
+			if (packet is SignaturePacket signaturePacket)
+				return signaturePacket;
 
-            return (SignaturePacket)packet;
+            throw new IOException("unexpected packet in stream: " + packet);
         }
 
         public const int BinaryDocument = 0x00;
@@ -56,24 +58,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
         }
 
-        internal PgpSignature(
-            SignaturePacket	sigPacket,
-            TrustPacket		trustPacket)
+        internal PgpSignature(SignaturePacket sigPacket, TrustPacket trustPacket)
         {
-			if (sigPacket == null)
-				throw new ArgumentNullException("sigPacket");
-
-			this.sigPck = sigPacket;
+			this.sigPck = sigPacket ?? throw new ArgumentNullException(nameof(sigPacket));
 			this.signatureType = sigPck.SignatureType;
 			this.trustPck = trustPacket;
         }
 
-		private void GetSig()
-        {
-            this.sig = SignerUtilities.GetSigner(
-				PgpUtilities.GetSignatureName(sigPck.KeyAlgorithm, sigPck.HashAlgorithm));
-        }
-
 		/// <summary>The OpenPGP version number for this signature.</summary>
 		public int Version
 		{
@@ -98,17 +89,19 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return IsCertification(SignatureType);
         }
 
-		public void InitVerify(
-            PgpPublicKey pubKey)
+		public void InitVerify(PgpPublicKey pubKey)
         {
 			lastb = 0;
+			AsymmetricKeyParameter key = pubKey.GetKey();
+
             if (sig == null)
-            {
-                GetSig();
+			{
+                this.sig = PgpUtilities.CreateSigner(sigPck.KeyAlgorithm, sigPck.HashAlgorithm, key);
             }
+
             try
             {
-                sig.Init(false, pubKey.GetKey());
+                sig.Init(false, key);
             }
             catch (InvalidKeyException e)
             {
@@ -116,12 +109,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-        public void Update(
-            byte b)
+        public void Update(byte b)
         {
             if (signatureType == CanonicalTextDocument)
             {
-				doCanonicalUpdateByte(b);
+				DoCanonicalUpdateByte(b);
             }
             else
             {
@@ -129,18 +121,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-		private void doCanonicalUpdateByte(
-			byte b)
+		private void DoCanonicalUpdateByte(byte b)
 		{
 			if (b == '\r')
 			{
-				doUpdateCRLF();
+				DoUpdateCRLF();
 			}
 			else if (b == '\n')
 			{
 				if (lastb != '\r')
 				{
-					doUpdateCRLF();
+					DoUpdateCRLF();
 				}
 			}
 			else
@@ -151,39 +142,56 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			lastb = b;
 		}
 
-		private void doUpdateCRLF()
+		private void DoUpdateCRLF()
 		{
 			sig.Update((byte)'\r');
 			sig.Update((byte)'\n');
 		}
 
-		public void Update(
-            params byte[] bytes)
+		public void Update(params byte[] bytes)
         {
 			Update(bytes, 0, bytes.Length);
         }
 
-		public void Update(
-            byte[]	bytes,
-            int		off,
-            int		length)
+		public void Update(byte[] bytes, int off, int length)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Update(bytes.AsSpan(off, length));
+#else
             if (signatureType == CanonicalTextDocument)
             {
                 int finish = off + length;
 
 				for (int i = off; i != finish; i++)
                 {
-                    doCanonicalUpdateByte(bytes[i]);
+                    DoCanonicalUpdateByte(bytes[i]);
                 }
             }
             else
             {
                 sig.BlockUpdate(bytes, off, length);
             }
+#endif
         }
 
-		public bool Verify()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            if (signatureType == CanonicalTextDocument)
+            {
+                for (int i = 0; i < input.Length; ++i)
+                {
+                    DoCanonicalUpdateByte(input[i]);
+                }
+            }
+            else
+            {
+                sig.BlockUpdate(input);
+            }
+        }
+#endif
+
+        public bool Verify()
         {
             byte[] trailer = GetSignatureTrailer();
             sig.BlockUpdate(trailer, 0, trailer.Length);
@@ -234,7 +242,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			//
 			try
 			{
-				MemoryStream bOut = new MemoryStream();
+				var bOut = new MemoryStream();
 				foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray())
 				{
 					packet.Encode(bOut);
@@ -248,7 +256,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
 			this.Update(sigPck.GetSignatureTrailer());
 
-			return sig.VerifySignature(this.GetSignature());
+			return sig.VerifySignature(GetSignature());
 		}
 
 		/// <summary>
@@ -345,15 +353,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
 		public PgpSignatureSubpacketVector GetHashedSubPackets()
         {
-            return createSubpacketVector(sigPck.GetHashedSubPackets());
+            return CreateSubpacketVector(sigPck.GetHashedSubPackets());
         }
 
 		public PgpSignatureSubpacketVector GetUnhashedSubPackets()
         {
-            return createSubpacketVector(sigPck.GetUnhashedSubPackets());
+            return CreateSubpacketVector(sigPck.GetUnhashedSubPackets());
         }
 
-		private PgpSignatureSubpacketVector createSubpacketVector(SignatureSubpacket[] pcks)
+		private static PgpSignatureSubpacketVector CreateSubpacketVector(SignatureSubpacket[] pcks)
 		{
 			return pcks == null ? null : new PgpSignatureSubpacketVector(pcks);
 		}
@@ -369,10 +377,39 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 				{
 					signature = sigValues[0].Value.ToByteArrayUnsigned();
 				}
-				else
-				{
-					try
+                else if (KeyAlgorithm == PublicKeyAlgorithmTag.EdDsa)
+                {
+					if (sigValues.Length != 2)
+						throw new InvalidOperationException();
+
+					BigInteger v0 = sigValues[0].Value;
+                    BigInteger v1 = sigValues[1].Value;
+
+					if (v0.BitLength == 918 &&
+                        v1.Equals(BigInteger.Zero) &&
+						v0.ShiftRight(912).Equals(BigInteger.ValueOf(0x40)))
+					{
+						signature = new byte[Ed448.SignatureSize];
+						BigIntegers.AsUnsignedByteArray(v0.ClearBit(918), signature, 0, signature.Length);
+					}
+					else if (v0.BitLength <= 256 && v1.BitLength <= 256)
+					{
+                        signature = new byte[Ed25519.SignatureSize];
+                        BigIntegers.AsUnsignedByteArray(sigValues[0].Value, signature,  0, 32);
+                        BigIntegers.AsUnsignedByteArray(sigValues[1].Value, signature, 32, 32);
+                    }
+                    else
 					{
+                        throw new InvalidOperationException();
+                    }
+                }
+                else
+                {
+                    if (sigValues.Length != 2)
+                        throw new InvalidOperationException();
+
+                    try
+                    {
 						signature = new DerSequence(
 							new DerInteger(sigValues[0].Value),
 							new DerInteger(sigValues[1].Value)).GetEncoded();
@@ -394,17 +431,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		// TODO Handle the encoding stuff by subclassing BcpgObject?
 		public byte[] GetEncoded()
         {
-            MemoryStream bOut = new MemoryStream();
+            var bOut = new MemoryStream();
 
 			Encode(bOut);
 
 			return bOut.ToArray();
         }
 
-		public void Encode(
-            Stream outStream)
+		public void Encode(Stream outStream)
         {
-            BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStream);
+            var bcpgOut = BcpgOutputStream.Wrap(outStream);
 
 			bcpgOut.WritePacket(sigPck);
 
@@ -414,8 +450,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-		private byte[] GetEncodedPublicKey(
-			PgpPublicKey pubKey) 
+		private static byte[] GetEncodedPublicKey(PgpPublicKey pubKey) 
 		{
 			try
 			{
@@ -436,13 +471,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
             switch (signatureType)
             {
-                case DefaultCertification:
-                case NoCertification:
-                case CasualCertification:
-                case PositiveCertification:
-                    return true;
-                default:
-                    return false;
+            case DefaultCertification:
+            case NoCertification:
+            case CasualCertification:
+            case PositiveCertification:
+                return true;
+            default:
+                return false;
             }
         }
     }
diff --git a/crypto/src/openpgp/PgpSignatureGenerator.cs b/crypto/src/openpgp/PgpSignatureGenerator.cs
index c5309689f..12edf9f89 100644
--- a/crypto/src/openpgp/PgpSignatureGenerator.cs
+++ b/crypto/src/openpgp/PgpSignatureGenerator.cs
@@ -5,19 +5,20 @@ using Org.BouncyCastle.Bcpg.Sig;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC.Rfc8032;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
 	/// <remarks>Generator for PGP signatures.</remarks>
-	// TODO Should be able to implement ISigner?
     public class PgpSignatureGenerator
     {
 		private static readonly SignatureSubpacket[] EmptySignatureSubpackets = new SignatureSubpacket[0];
 
-		private PublicKeyAlgorithmTag	keyAlgorithm;
-        private HashAlgorithmTag		hashAlgorithm;
+		private readonly PublicKeyAlgorithmTag keyAlgorithm;
+        private readonly HashAlgorithmTag hashAlgorithm;
+
         private PgpPrivateKey			privKey;
         private ISigner					sig;
         private IDigest					dig;
@@ -35,33 +36,39 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             this.keyAlgorithm = keyAlgorithm;
             this.hashAlgorithm = hashAlgorithm;
 
-			dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm));
-            sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm));
+			dig = PgpUtilities.CreateDigest(hashAlgorithm);
         }
 
 		/// <summary>Initialise the generator for signing.</summary>
         public void InitSign(
             int				sigType,
-            PgpPrivateKey	key)
+            PgpPrivateKey	privKey)
         {
-			InitSign(sigType, key, null);
+			InitSign(sigType, privKey, null);
         }
 
 		/// <summary>Initialise the generator for signing.</summary>
 		public void InitSign(
 			int				sigType,
-			PgpPrivateKey	key,
+			PgpPrivateKey privKey,
 			SecureRandom	random)
 		{
-			this.privKey = key;
+			this.privKey = privKey;
 			this.signatureType = sigType;
 
-			try
+			AsymmetricKeyParameter key = privKey.Key;
+
+			if (sig == null)
 			{
-				ICipherParameters cp = key.Key;
+                this.sig = PgpUtilities.CreateSigner(keyAlgorithm, hashAlgorithm, key);
+            }
+
+            try
+			{
+				ICipherParameters cp = key;
 				if (random != null)
 				{
-					cp = new ParametersWithRandom(key.Key, random);
+					cp = new ParametersWithRandom(cp, random);
 				}
 
 				sig.Init(true, cp);
@@ -75,72 +82,68 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			lastb = 0;
 		}
 
-		public void Update(
-            byte b)
+		public void Update(byte b)
         {
             if (signatureType == PgpSignature.CanonicalTextDocument)
             {
-				doCanonicalUpdateByte(b);
+				DoCanonicalUpdateByte(b);
             }
             else
             {
-				doUpdateByte(b);
+				DoUpdateByte(b);
             }
         }
 
-		private void doCanonicalUpdateByte(
-			byte b)
+		private void DoCanonicalUpdateByte(byte b)
 		{
 			if (b == '\r')
 			{
-				doUpdateCRLF();
+				DoUpdateCRLF();
 			}
 			else if (b == '\n')
 			{
 				if (lastb != '\r')
 				{
-					doUpdateCRLF();
+					DoUpdateCRLF();
 				}
 			}
 			else
 			{
-				doUpdateByte(b);
+				DoUpdateByte(b);
 			}
 
 			lastb = b;
 		}
 
-		private void doUpdateCRLF()
+		private void DoUpdateCRLF()
 		{
-			doUpdateByte((byte)'\r');
-			doUpdateByte((byte)'\n');
+			DoUpdateByte((byte)'\r');
+			DoUpdateByte((byte)'\n');
 		}
 
-		private void doUpdateByte(
-			byte b)
+		private void DoUpdateByte(byte b)
 		{
 			sig.Update(b);
 			dig.Update(b);
 		}
 
-		public void Update(
-            params byte[] b)
+		public void Update(params byte[] b)
         {
 			Update(b, 0, b.Length);
         }
 
-		public void Update(
-            byte[]	b,
-            int		off,
-            int		len)
+		public void Update(byte[] b, int off, int len)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Update(b.AsSpan(off, len));
+#else
             if (signatureType == PgpSignature.CanonicalTextDocument)
             {
                 int finish = off + len;
 
 				for (int i = off; i != finish; i++)
                 {
-                    doCanonicalUpdateByte(b[i]);
+                    DoCanonicalUpdateByte(b[i]);
                 }
             }
             else
@@ -148,9 +151,28 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 sig.BlockUpdate(b, off, len);
                 dig.BlockUpdate(b, off, len);
             }
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            if (signatureType == PgpSignature.CanonicalTextDocument)
+            {
+                for (int i = 0; i < input.Length; ++i)
+                {
+                    DoCanonicalUpdateByte(input[i]);
+                }
+            }
+            else
+            {
+                sig.BlockUpdate(input);
+                dig.BlockUpdate(input);
+            }
         }
+#endif
 
-		public void SetHashedSubpackets(
+        public void SetHashedSubpackets(
             PgpSignatureSubpacketVector hashedPackets)
         {
 			hashed = hashedPackets == null
@@ -180,15 +202,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
 			SignatureSubpacket[] hPkts = hashed, unhPkts = unhashed;
 
-			if (!packetPresent(hashed, SignatureSubpacketTag.CreationTime))
+			if (!IsPacketPresent(hashed, SignatureSubpacketTag.CreationTime))
 			{
-				hPkts = insertSubpacket(hPkts, new SignatureCreationTime(false, DateTime.UtcNow));
+				hPkts = InsertSubpacket(hPkts, new SignatureCreationTime(false, DateTime.UtcNow));
 			}
 
-			if (!packetPresent(hashed, SignatureSubpacketTag.IssuerKeyId)
-				&& !packetPresent(unhashed, SignatureSubpacketTag.IssuerKeyId))
+			if (!IsPacketPresent(hashed, SignatureSubpacketTag.IssuerKeyId)
+				&& !IsPacketPresent(unhashed, SignatureSubpacketTag.IssuerKeyId))
 			{
-				unhPkts = insertSubpacket(unhPkts, new IssuerKeyId(false, privKey.KeyId));
+				unhPkts = InsertSubpacket(unhPkts, new IssuerKeyId(false, privKey.KeyId));
 			}
 
 			int version = 4;
@@ -239,17 +261,41 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
 			byte[] sigBytes = sig.GenerateSignature();
 			byte[] digest = DigestUtilities.DoFinal(dig);
-			byte[] fingerPrint = new byte[] { digest[0], digest[1] };
+			byte[] fingerPrint = new byte[2]{ digest[0], digest[1] };
 
-			// an RSA signature
-			bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign
-				|| keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral;
-
-			MPInteger[] sigValues = isRsa
-				?	PgpUtilities.RsaSigToMpi(sigBytes)
-				:	PgpUtilities.DsaSigToMpi(sigBytes);
+			MPInteger[] sigValues;
+            if (keyAlgorithm == PublicKeyAlgorithmTag.EdDsa)
+            {
+                int sigLen = sigBytes.Length;
+                if (sigLen == Ed25519.SignatureSize)
+				{
+					sigValues = new MPInteger[2]{
+						new MPInteger(new BigInteger(1, sigBytes,  0, 32)),
+						new MPInteger(new BigInteger(1, sigBytes, 32, 32))
+					};
+				}
+                else if (sigLen == Ed448.SignatureSize)
+                {
+                    sigValues = new MPInteger[2]{
+                        new MPInteger(new BigInteger(1, Arrays.Prepend(sigBytes, 0x40))),
+                        new MPInteger(BigInteger.Zero)
+                    };
+                }
+                else
+				{
+					throw new InvalidOperationException();
+				}
+            }
+			else if (keyAlgorithm == PublicKeyAlgorithmTag.RsaSign || keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral)
+			{
+                sigValues = PgpUtilities.RsaSigToMpi(sigBytes);
+            }
+			else
+			{
+                sigValues = PgpUtilities.DsaSigToMpi(sigBytes);
+            }
 
-			return new PgpSignature(
+            return new PgpSignature(
 				new SignaturePacket(signatureType, privKey.KeyId, keyAlgorithm,
 					hashAlgorithm, hPkts, unhPkts, fingerPrint, sigValues));
         }
@@ -258,9 +304,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		/// <param name="id">The ID we are certifying against the public key.</param>
 		/// <param name="pubKey">The key we are certifying against the ID.</param>
 		/// <returns>The certification.</returns>
-        public PgpSignature GenerateCertification(
-            string			id,
-            PgpPublicKey	pubKey)
+        public PgpSignature GenerateCertification(string id, PgpPublicKey pubKey)
         {
 			UpdateWithPublicKey(pubKey);
 
@@ -276,9 +320,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		/// <param name="userAttributes">The ID we are certifying against the public key.</param>
 		/// <param name="pubKey">The key we are certifying against the ID.</param>
 		/// <returns>The certification.</returns>
-		public PgpSignature GenerateCertification(
-			PgpUserAttributeSubpacketVector	userAttributes,
-			PgpPublicKey					pubKey)
+		public PgpSignature GenerateCertification(PgpUserAttributeSubpacketVector userAttributes, PgpPublicKey pubKey)
 		{
 			UpdateWithPublicKey(pubKey);
 
@@ -287,7 +329,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			//
 			try
 			{
-				MemoryStream bOut = new MemoryStream();
+				var bOut = new MemoryStream();
 				foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray())
 				{
 					packet.Encode(bOut);
@@ -299,16 +341,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 				throw new PgpException("cannot encode subpacket array", e);
 			}
 
-			return this.Generate();
+			return Generate();
 		}
 
 		/// <summary>Generate a certification for the passed in key against the passed in master key.</summary>
 		/// <param name="masterKey">The key we are certifying against.</param>
 		/// <param name="pubKey">The key we are certifying.</param>
 		/// <returns>The certification.</returns>
-        public PgpSignature GenerateCertification(
-            PgpPublicKey	masterKey,
-            PgpPublicKey	pubKey)
+        public PgpSignature GenerateCertification(PgpPublicKey masterKey, PgpPublicKey pubKey)
         {
 			UpdateWithPublicKey(masterKey);
 			UpdateWithPublicKey(pubKey);
@@ -319,16 +359,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		/// <summary>Generate a certification, such as a revocation, for the passed in key.</summary>
 		/// <param name="pubKey">The key we are certifying.</param>
 		/// <returns>The certification.</returns>
-        public PgpSignature GenerateCertification(
-            PgpPublicKey pubKey)
+        public PgpSignature GenerateCertification(PgpPublicKey pubKey)
         {
 			UpdateWithPublicKey(pubKey);
 
 			return Generate();
         }
 
-		private byte[] GetEncodedPublicKey(
-			PgpPublicKey pubKey) 
+		private static byte[] GetEncodedPublicKey(PgpPublicKey pubKey) 
 		{
 			try
 			{
@@ -340,42 +378,31 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			}
 		}
 
-		private bool packetPresent(
-			SignatureSubpacket[]	packets,
-			SignatureSubpacketTag	type)
+		private static bool IsPacketPresent(SignatureSubpacket[] packets, SignatureSubpacketTag type)
 		{
 			for (int i = 0; i != packets.Length; i++)
 			{
 				if (packets[i].SubpacketType == type)
-				{
 					return true;
-				}
 			}
 
 			return false;
 		}
 
-		private SignatureSubpacket[] insertSubpacket(
-			SignatureSubpacket[]	packets,
-			SignatureSubpacket		subpacket)
+		private static SignatureSubpacket[] InsertSubpacket(SignatureSubpacket[] packets, SignatureSubpacket subpacket)
 		{
-			SignatureSubpacket[] tmp = new SignatureSubpacket[packets.Length + 1];
-			tmp[0] = subpacket;
-			packets.CopyTo(tmp, 1);
-			return tmp;
+			return Arrays.Prepend(packets, subpacket);
 		}
 
-		private void UpdateWithIdData(
-			int		header,
-			byte[]	idBytes)
+		private void UpdateWithIdData(int header, byte[] idBytes)
 		{
-			this.Update(
+			Update(
 				(byte) header,
 				(byte)(idBytes.Length >> 24),
 				(byte)(idBytes.Length >> 16),
 				(byte)(idBytes.Length >> 8),
 				(byte)(idBytes.Length));
-			this.Update(idBytes);
+			Update(idBytes);
 		}
 
 		private void UpdateWithPublicKey(
@@ -383,11 +410,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		{
 			byte[] keyBytes = GetEncodedPublicKey(key);
 
-			this.Update(
-				(byte) 0x99,
+			Update(
+				0x99,
 				(byte)(keyBytes.Length >> 8),
 				(byte)(keyBytes.Length));
-			this.Update(keyBytes);
+			Update(keyBytes);
 		}
 	}
 }
diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs
index f33969ea8..2642f3497 100644
--- a/crypto/src/openpgp/PgpUtilities.cs
+++ b/crypto/src/openpgp/PgpUtilities.cs
@@ -9,7 +9,9 @@ using Org.BouncyCastle.Asn1.Sec;
 using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Pqc.Crypto.SphincsPlus;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
@@ -114,7 +116,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             if (NameToHashID.TryGetValue(name, out var hashAlgorithmTag))
                 return (int)hashAlgorithmTag;
 
-            throw new ArgumentException("unable to map " + name + " to a hash id", "name");
+            throw new ArgumentException("unable to map " + name + " to a hash id", nameof(name));
         }
 
         /**
@@ -152,6 +154,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 case PublicKeyAlgorithmTag.ECDsa:
                     encAlg = "ECDSA";
                     break;
+                case PublicKeyAlgorithmTag.EdDsa:
+                    encAlg = "EdDSA";
+                    break;
                 case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases.
 				case PublicKeyAlgorithmTag.ElGamalGeneral:
 					encAlg = "ElGamal";
@@ -163,7 +168,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			return GetDigestName(hashAlgorithm) + "with" + encAlg;
         }
 
-	public static string GetSymmetricCipherName(
+	    public static string GetSymmetricCipherName(
             SymmetricKeyAlgorithmTag algorithm)
         {
             switch (algorithm)
@@ -301,11 +306,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 				IDigest digest;
 				if (s2k != null)
                 {
-					string digestName = GetDigestName(s2k.HashAlgorithm);
-
                     try
                     {
-						digest = DigestUtilities.GetDigest(digestName);
+                        digest = CreateDigest(s2k.HashAlgorithm);
                     }
                     catch (Exception e)
                     {
@@ -368,7 +371,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 {
                     try
                     {
-                        digest = DigestUtilities.GetDigest("MD5");
+                        digest = CreateDigest(HashAlgorithmTag.MD5);
 
 						for (int i = 0; i != loopCount; i++)
                         {
@@ -407,7 +410,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             return MakeKey(algorithm, keyBytes);
         }
 
-#if !PORTABLE || DOTNET
         /// <summary>Write out the passed in file as a literal data packet.</summary>
         public static void WriteFileToLiteralData(
             Stream		output,
@@ -452,12 +454,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 Platform.Dispose(inputStream);
             }
         }
-#endif
 
 		private const int ReadAhead = 60;
 
-		private static bool IsPossiblyBase64(
-            int ch)
+		private static bool IsPossiblyBase64(int ch)
         {
             return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
                     || (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '/')
@@ -473,7 +473,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
 			// TODO Remove this restriction?
 			if (!inputStream.CanSeek)
-				throw new ArgumentException("inputStream must be seek-able", "inputStream");
+				throw new ArgumentException("inputStream must be seek-able", nameof(inputStream));
 
 			long markedPos = inputStream.Position;
 
@@ -552,6 +552,41 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
+        internal static IDigest CreateDigest(HashAlgorithmTag hashAlgorithm)
+        {
+            return DigestUtilities.GetDigest(GetDigestName(hashAlgorithm));
+        }
+
+        internal static ISigner CreateSigner(PublicKeyAlgorithmTag publicKeyAlgorithm, HashAlgorithmTag hashAlgorithm,
+            AsymmetricKeyParameter key)
+        {
+            switch (publicKeyAlgorithm)
+            {
+            case PublicKeyAlgorithmTag.EdDsa:
+            {
+                ISigner signer;
+                if (key is Ed25519PrivateKeyParameters || key is Ed25519PublicKeyParameters)
+                {
+                    signer = new Ed25519Signer();
+                }
+                else if (key is Ed448PrivateKeyParameters || key is Ed448PublicKeyParameters)
+                {
+                    signer = new Ed448Signer(Arrays.EmptyBytes);
+                }
+                else
+                {
+                    throw new InvalidOperationException();
+                }
+
+                return new EdDsaSigner(signer, CreateDigest(hashAlgorithm));
+            }
+            default:
+            {
+                return SignerUtilities.GetSigner(GetSignatureName(publicKeyAlgorithm, hashAlgorithm));
+            }
+            }
+        }
+
         internal static IWrapper CreateWrapper(SymmetricKeyAlgorithmTag encAlgorithm)
         {
             switch (encAlgorithm)
diff --git a/crypto/src/openpgp/PgpV3SignatureGenerator.cs b/crypto/src/openpgp/PgpV3SignatureGenerator.cs
index c7113e0ae..324dbd768 100644
--- a/crypto/src/openpgp/PgpV3SignatureGenerator.cs
+++ b/crypto/src/openpgp/PgpV3SignatureGenerator.cs
@@ -1,3 +1,5 @@
+using System;
+
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Security;
@@ -6,11 +8,11 @@ using Org.BouncyCastle.Utilities.Date;
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
 	/// <remarks>Generator for old style PGP V3 Signatures.</remarks>
-	// TODO Should be able to implement ISigner?
 	public class PgpV3SignatureGenerator
     {
-        private PublicKeyAlgorithmTag keyAlgorithm;
-        private HashAlgorithmTag hashAlgorithm;
+        private readonly PublicKeyAlgorithmTag keyAlgorithm;
+        private readonly HashAlgorithmTag hashAlgorithm;
+
         private PgpPrivateKey privKey;
         private ISigner sig;
         private IDigest    dig;
@@ -22,36 +24,40 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             PublicKeyAlgorithmTag	keyAlgorithm,
             HashAlgorithmTag		hashAlgorithm)
         {
+            if (keyAlgorithm == PublicKeyAlgorithmTag.EdDsa)
+                throw new ArgumentException("Invalid algorithm for V3 signature", nameof(keyAlgorithm));
+
             this.keyAlgorithm = keyAlgorithm;
             this.hashAlgorithm = hashAlgorithm;
 
-            dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm));
-            sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm));
+            dig = PgpUtilities.CreateDigest(hashAlgorithm);
         }
 
 		/// <summary>Initialise the generator for signing.</summary>
-		public void InitSign(
-			int				sigType,
-			PgpPrivateKey	key)
+		public void InitSign(int sigType, PgpPrivateKey privKey)
 		{
-			InitSign(sigType, key, null);
+			InitSign(sigType, privKey, null);
 		}
 
 		/// <summary>Initialise the generator for signing.</summary>
-        public void InitSign(
-            int				sigType,
-            PgpPrivateKey	key,
-			SecureRandom	random)
+        public void InitSign(int sigType, PgpPrivateKey privKey, SecureRandom random)
         {
-            this.privKey = key;
+            this.privKey = privKey;
             this.signatureType = sigType;
 
-			try
+            AsymmetricKeyParameter key = privKey.Key;
+
+            if (sig == null)
             {
-				ICipherParameters cp = key.Key;
+                this.sig = PgpUtilities.CreateSigner(keyAlgorithm, hashAlgorithm, key);
+            }
+
+            try
+            {
+				ICipherParameters cp = key;
 				if (random != null)
 				{
-					cp = new ParametersWithRandom(key.Key, random);
+					cp = new ParametersWithRandom(cp, random);
 				}
 
 				sig.Init(true, cp);
@@ -65,93 +71,98 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             lastb = 0;
         }
 
-		public void Update(
-            byte b)
+		public void Update(byte b)
         {
             if (signatureType == PgpSignature.CanonicalTextDocument)
             {
-				doCanonicalUpdateByte(b);
+				DoCanonicalUpdateByte(b);
             }
             else
             {
-				doUpdateByte(b);
+				DoUpdateByte(b);
             }
         }
 
-		private void doCanonicalUpdateByte(
-			byte b)
+		private void DoCanonicalUpdateByte(byte b)
 		{
 			if (b == '\r')
 			{
-				doUpdateCRLF();
+				DoUpdateCRLF();
 			}
 			else if (b == '\n')
 			{
 				if (lastb != '\r')
 				{
-					doUpdateCRLF();
+					DoUpdateCRLF();
 				}
 			}
 			else
 			{
-				doUpdateByte(b);
+				DoUpdateByte(b);
 			}
 
 			lastb = b;
 		}
 
-		private void doUpdateCRLF()
+		private void DoUpdateCRLF()
 		{
-			doUpdateByte((byte)'\r');
-			doUpdateByte((byte)'\n');
+			DoUpdateByte((byte)'\r');
+			DoUpdateByte((byte)'\n');
 		}
 
-		private void doUpdateByte(
+		private void DoUpdateByte(
 			byte b)
 		{
 			sig.Update(b);
 			dig.Update(b);
 		}
 
-		public void Update(
-            byte[] b)
+		public void Update(params byte[] b)
+        {
+            Update(b, 0, b.Length);
+        }
+
+		public void Update(byte[] b, int off, int len)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Update(b.AsSpan(off, len));
+#else
             if (signatureType == PgpSignature.CanonicalTextDocument)
             {
-                for (int i = 0; i != b.Length; i++)
+                int finish = off + len;
+
+				for (int i = off; i != finish; i++)
                 {
-                    doCanonicalUpdateByte(b[i]);
+                    DoCanonicalUpdateByte(b[i]);
                 }
             }
             else
             {
-                sig.BlockUpdate(b, 0, b.Length);
-                dig.BlockUpdate(b, 0, b.Length);
+                sig.BlockUpdate(b, off, len);
+                dig.BlockUpdate(b, off, len);
             }
+#endif
         }
 
-		public void Update(
-            byte[]	b,
-            int		off,
-            int		len)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
         {
             if (signatureType == PgpSignature.CanonicalTextDocument)
             {
-                int finish = off + len;
-
-				for (int i = off; i != finish; i++)
+                for (int i = 0; i < input.Length; ++i)
                 {
-                    doCanonicalUpdateByte(b[i]);
+                    DoCanonicalUpdateByte(input[i]);
                 }
             }
             else
             {
-                sig.BlockUpdate(b, off, len);
-                dig.BlockUpdate(b, off, len);
+                sig.BlockUpdate(input);
+                dig.BlockUpdate(input);
             }
         }
+#endif
 
-		/// <summary>Return the one pass header associated with the current signature.</summary>
+        /// <summary>Return the one pass header associated with the current signature.</summary>
         public PgpOnePassSignature GenerateOnePassVersion(
             bool isNested)
         {
diff --git a/crypto/src/openpgp/Rfc6637Utilities.cs b/crypto/src/openpgp/Rfc6637Utilities.cs
index 5d992ec51..e1405f481 100644
--- a/crypto/src/openpgp/Rfc6637Utilities.cs
+++ b/crypto/src/openpgp/Rfc6637Utilities.cs
@@ -69,11 +69,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
         public static byte[] CreateKey(PublicKeyPacket pubKeyData, ECPoint s)
         {
+            return CreateKey(pubKeyData, s.AffineXCoord.GetEncoded());
+        }
+
+        public static byte[] CreateKey(PublicKeyPacket pubKeyData, byte[] secret)
+        {
             byte[] userKeyingMaterial = CreateUserKeyingMaterial(pubKeyData);
 
             ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;
 
-            return Kdf(ecKey.HashAlgorithm, s, GetKeyLength(ecKey.SymmetricKeyAlgorithm), userKeyingMaterial);
+            return Kdf(ecKey.HashAlgorithm, secret, GetKeyLength(ecKey.SymmetricKeyAlgorithm), userKeyingMaterial);
         }
 
         // RFC 6637 - Section 8
@@ -116,12 +121,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         //         ZB = x;
         //         MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param );
         //   return oBits leftmost bits of MB.
-        private static byte[] Kdf(HashAlgorithmTag digestAlg, ECPoint s, int keyLen, byte[] parameters)
+        private static byte[] Kdf(HashAlgorithmTag digestAlg, byte[] ZB, int keyLen, byte[] parameters)
         {
-            byte[] ZB = s.XCoord.GetEncoded();
-
-            string digestName = PgpUtilities.GetDigestName(digestAlg);
-			IDigest digest = DigestUtilities.GetDigest(digestName);
+            IDigest digest = PgpUtilities.CreateDigest(digestAlg);
 
             digest.Update(0x00);
             digest.Update(0x00);
diff --git a/crypto/src/openpgp/WrappedGeneratorStream.cs b/crypto/src/openpgp/WrappedGeneratorStream.cs
index c54ee0b3b..6f96dc9b8 100644
--- a/crypto/src/openpgp/WrappedGeneratorStream.cs
+++ b/crypto/src/openpgp/WrappedGeneratorStream.cs
@@ -13,10 +13,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		internal WrappedGeneratorStream(IStreamGenerator generator, Stream s)
 			: base(s)
 		{
-			if (generator == null)
-				throw new ArgumentNullException(nameof(generator));
-
-			m_generator = generator;
+			m_generator = generator ?? throw new ArgumentNullException(nameof(generator));
 		}
 
         protected override void Dispose(bool disposing)
diff --git a/crypto/src/pqc/crypto/cmce/CmceEngine.cs b/crypto/src/pqc/crypto/cmce/CmceEngine.cs
index 10b08e708..96595ecc0 100644
--- a/crypto/src/pqc/crypto/cmce/CmceEngine.cs
+++ b/crypto/src/pqc/crypto/cmce/CmceEngine.cs
@@ -173,7 +173,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Cmce
                     {
                         buf[i] = perm[i];
                         buf[i] <<= 31;
-                        buf[i] |= i;
+                        buf[i] |= (uint)i;
                         buf[i] &= 0x7fffffffffffffffL; // getting rid of signed longs
                     }
                     Sort64(buf, 0, buf.Length);
@@ -1162,7 +1162,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Cmce
             {
                 for (x = 0; x < n; ++x)
                 {
-                    temp[(int)x] = ((GetQShort(temp, (int)(qIndex + x)) ^ 1) << 16) | GetQShort(temp, (int)((qIndex) + (x ^ 1)));
+                    ushort t0 = (ushort)GetQShort(temp, (int)(qIndex + x));
+                    ushort t1 = (ushort)GetQShort(temp, (int)(qIndex + (x ^ 1)));
+                    temp[(int)x] = ((t0 ^ 1) << 16) | t1;
                 }
             }
             Sort32(temp, 0, (int)n); /* A = (id<<16)+pibar */
@@ -1181,7 +1183,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Cmce
 
             for (x = 0; x < n; ++x)
             {
-                temp[(int)x] = (int)((temp[(int)x] << 16) | x); /* A = (pibar<<16)+id */
+                temp[(int)x] = (int)((uint)(temp[(int)x] << 16) | x); /* A = (pibar<<16)+id */
             }
             Sort32(temp, 0, (int)n); /* A = (id<<16)+pibar^-1 */
 
@@ -1204,7 +1206,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Cmce
 
                     for (x = 0; x < n; ++x)
                     {
-                        temp[(int)x] = (int)(((temp[(int)(n + x)] & ~0x3ff) << 6) | x); /* A = (p<<16)+id */
+                        temp[(int)x] = (int)(((uint)(temp[(int)(n + x)] & ~0x3ff) << 6) | x); /* A = (p<<16)+id */
                     }
                     Sort32(temp, 0, (int)n); /* A = (id<<16)+p^{-1} */
 
@@ -1241,7 +1243,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Cmce
                     /* B = (p<<16)+c */
                     for (x = 0; x < n; ++x)
                     {
-                        temp[(int)x] = (int)((temp[(int)(n + x)] & ~0xffff) | x);
+                        temp[(int)x] = (int)((uint)(temp[(int)(n + x)] & ~0xffff) | x);
                     }
                     Sort32(temp, 0, (int)n); /* A = (id<<16)+p^(-1) */
                     for (x = 0; x < n; ++x)
@@ -1375,7 +1377,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Cmce
             {
                 buf[i] = perm[i];
                 buf[i] <<= 31;
-                buf[i] |= i;
+                buf[i] |= (uint)i;
                 // buf[i] &= 0x7fffffffffffffffL; // getting rid of signed longs
             }
             // Sort32 the buffer
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/PolyVecK.cs b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecK.cs
index facbc1f33..315ce6ab0 100644
--- a/crypto/src/pqc/crypto/crystals/dilithium/PolyVecK.cs
+++ b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecK.cs
@@ -2,21 +2,20 @@
 {
     internal class PolyVecK
     {
-        public Poly[] Vec;
-        private DilithiumEngine Engine;
-        private int Mode;
-        private int PolyVecBytes;
-        private int K;
-        private int L;
+        public readonly Poly[] Vec;
+        private readonly DilithiumEngine Engine;
+        //private readonly int Mode;
+        private readonly int K;
+        //private readonly int L;
 
         public PolyVecK(DilithiumEngine Engine)
         {
             this.Engine = Engine;
-            Mode = Engine.Mode;
+            //Mode = Engine.Mode;
             K = Engine.K;
-            L = Engine.L;
+            //L = Engine.L;
             Vec = new Poly[K];
-            
+
             for (int i = 0; i < K; i++)
             {
                 Vec[i] = new Poly(Engine);
@@ -25,9 +24,8 @@
 
         public void UniformEta(byte[] seed, ushort nonce)
         {
-            int i;
             ushort n = nonce;
-            for (i = 0; i < K; i++)
+            for (int i = 0; i < K; i++)
             {
                 Vec[i].UniformEta(seed, n++);
             }
@@ -99,8 +97,7 @@
 
         public void PackW1(byte[] r)
         {
-            int i;
-            for (i = 0; i < K; i++)
+            for (int i = 0; i < K; i++)
             {
                 Vec[i].PackW1(r, i * Engine.PolyW1PackedBytes);
             }
@@ -119,21 +116,18 @@
             for (int i = 0; i < K; ++i)
             {
                 if (Vec[i].CheckNorm(bound))
-                {
                     return true;
-                }
             }
             return false;
         }
 
         public int MakeHint(PolyVecK v0, PolyVecK v1)
         {
-            int i, s = 0;
-            for (i = 0; i < K; ++i)
+            int s = 0;
+            for (int i = 0; i < K; ++i)
             {
                 s += Vec[i].PolyMakeHint(v0.Vec[i], v1.Vec[i]);
             }
-
             return s;
         }
 
@@ -152,6 +146,5 @@
                 Vec[i].ShiftLeft();
             }
         }
-
     }
 }
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/PolyVecL.cs b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecL.cs
index 58c286768..0a87c2070 100644
--- a/crypto/src/pqc/crypto/crystals/dilithium/PolyVecL.cs
+++ b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecL.cs
@@ -2,17 +2,16 @@
 {
     internal class PolyVecL
     {
-        public Poly[] Vec;
-        private DilithiumEngine Engine;
-        private int Mode;
-        private int PolyVecBytes;
-        private int L;
-        private int K;
+        public readonly Poly[] Vec;
+        //private DilithiumEngine Engine;
+        //private int Mode;
+        private readonly int L;
+        private readonly int K;
 
         public PolyVecL(DilithiumEngine Engine)
         {
-            this.Engine = Engine;
-            Mode = Engine.Mode;
+            //this.Engine = Engine;
+            //Mode = Engine.Mode;
             L = Engine.L;
             K = Engine.K;
             Vec = new Poly[L];
@@ -24,8 +23,7 @@
 
         public void UniformEta(byte[] seed, ushort nonce)
         {
-            int i;
-            for (i = 0; i < L; i++)
+            for (int i = 0; i < L; i++)
             {
                 Vec[i].UniformEta(seed, nonce++);
             }
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberEngine.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberEngine.cs
index bf9d5ee3c..e30115a95 100644
--- a/crypto/src/pqc/crypto/crystals/kyber/KyberEngine.cs
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberEngine.cs
@@ -138,7 +138,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
 
         internal void KemDecrypt(byte[] sharedSecret, byte[] cipherText, byte[] secretKey)
         {
-            int i;
             byte[] buf = new byte[2 * SymBytes], kr = new byte[2 * SymBytes], cmp = new byte[CipherTextBytes];
             byte[] pk = Arrays.CopyOfRange(secretKey, IndCpaSecretKeyBytes, secretKey.Length);
             m_indCpa.Decrypt(buf, cipherText, secretKey);
@@ -161,7 +160,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
 
         private void CMov(byte[] r, byte[] x, int len, bool b)
         {
-            int i;
             if (b)
             {
                 Array.Copy(x, 0, r, 0, len);
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberIndCpa.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberIndCpa.cs
index b3be4770d..9400b776e 100644
--- a/crypto/src/pqc/crypto/crystals/kyber/KyberIndCpa.cs
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberIndCpa.cs
@@ -138,7 +138,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
 
         private void PackPublicKey(out byte[] pk, PolyVec pkpv, byte[] seed)
         {
-            int i;
             pk = new byte[m_engine.IndCpaPublicKeyBytes];
             pkpv.ToBytes(pk);
             Array.Copy(seed, 0, pk, m_engine.PolyVecBytes, KyberEngine.SymBytes);
@@ -146,7 +145,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
 
         private void UnpackPublicKey(PolyVec pkpv, byte[] seed, byte[] pk)
         {
-            int i;
             pkpv.FromBytes(pk);
             Array.Copy(pk, m_engine.PolyVecBytes, seed, 0, KyberEngine.SymBytes);
         }
diff --git a/crypto/src/pqc/crypto/falcon/FalconKeyPairGenerator.cs b/crypto/src/pqc/crypto/falcon/FalconKeyPairGenerator.cs
index eafd1a9eb..68f90c97f 100644
--- a/crypto/src/pqc/crypto/falcon/FalconKeyPairGenerator.cs
+++ b/crypto/src/pqc/crypto/falcon/FalconKeyPairGenerator.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon
     {
         private FalconKeyGenerationParameters parameters;
         private SecureRandom random;
-        private FalconNIST nist;
+        private FalconNist nist;
         private uint logn;
         private uint noncelen;
 
@@ -18,32 +18,31 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon
         {
             this.parameters = (FalconKeyGenerationParameters)param;
             this.random = param.Random;
-            this.logn = ((FalconKeyGenerationParameters)param).Parameters.LogN;
-            this.noncelen = ((FalconKeyGenerationParameters)param).Parameters.NonceLength;
-            this.nist = new FalconNIST(random, logn, noncelen);
+            this.logn = (uint)((FalconKeyGenerationParameters)param).Parameters.LogN;
+            this.noncelen = (uint)((FalconKeyGenerationParameters)param).Parameters.NonceLength;
+            this.nist = new FalconNist(random, logn, noncelen);
             int n = 1 << (int)this.logn;
-            int sk_coeff_size = 8;
-            if (n == 1024)
-            {
-                sk_coeff_size = 5;
-            }
-            else if (n == 256 || n == 512)
-            {
-                sk_coeff_size = 6;
-            }
-            else if (n == 64 || n == 128)
-            {
-                sk_coeff_size = 7;
-            }
+            //int sk_coeff_size = 8;
+            //if (n == 1024)
+            //{
+            //    sk_coeff_size = 5;
+            //}
+            //else if (n == 256 || n == 512)
+            //{
+            //    sk_coeff_size = 6;
+            //}
+            //else if (n == 64 || n == 128)
+            //{
+            //    sk_coeff_size = 7;
+            //}
 
             this.pk_size = 1 + (14 * n / 8);
         }
 
         public AsymmetricCipherKeyPair GenerateKeyPair()
         {
-            byte[] pk, sk, f, g, F;
-            nist.crypto_sign_keypair(out pk, out f, out g, out F);
-            FalconParameters p = ((FalconKeyGenerationParameters)this.parameters).Parameters;
+            nist.crypto_sign_keypair(out byte[] pk, out byte[] f, out byte[] g, out byte[] F);
+            FalconParameters p = this.parameters.Parameters;
             FalconPrivateKeyParameters privk = new FalconPrivateKeyParameters(p, f, g, F, pk);
             FalconPublicKeyParameters pubk = new FalconPublicKeyParameters(p, pk);
             return new AsymmetricCipherKeyPair(pubk, privk);
diff --git a/crypto/src/pqc/crypto/falcon/FalconKeyParameters.cs b/crypto/src/pqc/crypto/falcon/FalconKeyParameters.cs
index 95a546994..bb1252706 100644
--- a/crypto/src/pqc/crypto/falcon/FalconKeyParameters.cs
+++ b/crypto/src/pqc/crypto/falcon/FalconKeyParameters.cs
@@ -3,7 +3,7 @@ using Org.BouncyCastle.Crypto;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Falcon
 {
-    public class FalconKeyParameters 
+    public abstract class FalconKeyParameters 
         : AsymmetricKeyParameter
     {
         private FalconParameters parameters;
diff --git a/crypto/src/pqc/crypto/falcon/FalconNIST.cs b/crypto/src/pqc/crypto/falcon/FalconNIST.cs
index cce790734..0d2ba46e0 100644
--- a/crypto/src/pqc/crypto/falcon/FalconNIST.cs
+++ b/crypto/src/pqc/crypto/falcon/FalconNIST.cs
@@ -1,10 +1,11 @@
 using System;
+
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Falcon
 {
-    class FalconNIST
+    internal class FalconNist
     {
         private FalconCodec codec;
         private FalconVrfy vrfy;
@@ -26,7 +27,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon
             return this.CRYPTO_BYTES;
         }
 
-        internal FalconNIST(SecureRandom random, uint logn, uint noncelen) {
+        internal FalconNist(SecureRandom random, uint logn, uint noncelen) {
             this.logn = logn;
             this.codec = new FalconCodec();
             this.common = new FalconCommon();
diff --git a/crypto/src/pqc/crypto/falcon/FalconParameters.cs b/crypto/src/pqc/crypto/falcon/FalconParameters.cs
index 4a9bc598f..10d22a241 100644
--- a/crypto/src/pqc/crypto/falcon/FalconParameters.cs
+++ b/crypto/src/pqc/crypto/falcon/FalconParameters.cs
@@ -1,16 +1,17 @@
 using Org.BouncyCastle.Crypto;
+using System;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Falcon
 {
     public sealed class FalconParameters 
         : ICipherParameters
     {
-        public static FalconParameters falcon_512 = new FalconParameters("falcon512", 9, 40);
-        public static FalconParameters falcon_1024 = new FalconParameters("falcon1024", 10, 40);
+        public static readonly FalconParameters falcon_512 = new FalconParameters("falcon512", 9, 40);
+        public static readonly FalconParameters falcon_1024 = new FalconParameters("falcon1024", 10, 40);
 
-        private string name;
-        private uint logn;
-        private uint nonce_length;
+        private readonly string name;
+        private readonly uint logn;
+        private readonly uint nonce_length;
 
         private FalconParameters(string name, uint logn, uint nonce_length)
         {
@@ -19,9 +20,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon
             this.nonce_length = nonce_length;
         }
 
-        public uint LogN => logn;
+        public int LogN => Convert.ToInt32(logn);
 
-        public uint NonceLength => nonce_length;
+        public int NonceLength => Convert.ToInt32(nonce_length);
 
         public string Name => name;
     }
diff --git a/crypto/src/pqc/crypto/falcon/FalconPrivateKeyParameters.cs b/crypto/src/pqc/crypto/falcon/FalconPrivateKeyParameters.cs
index c912a222c..12b055add 100644
--- a/crypto/src/pqc/crypto/falcon/FalconPrivateKeyParameters.cs
+++ b/crypto/src/pqc/crypto/falcon/FalconPrivateKeyParameters.cs
@@ -3,13 +3,13 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Falcon
 {
-    public class FalconPrivateKeyParameters
+    public sealed class FalconPrivateKeyParameters
         : FalconKeyParameters
     {
-        private byte[] pk;
-        private byte[] f;
-        private byte[] g;
-        private byte[] F;
+        private readonly byte[] pk;
+        private readonly byte[] f;
+        private readonly byte[] g;
+        private readonly byte[] F;
 
         public FalconPrivateKeyParameters(FalconParameters parameters, byte[] f, byte[] g, byte[] F, byte[] pk_encoded)
             : base(true, parameters)
@@ -30,7 +30,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon
             return Arrays.Clone(pk);
         }
 
-        public byte[] GetSpolyf()
+        public byte[] GetSpolyLittleF()
         {
             return Arrays.Clone(f);
         }
@@ -40,7 +40,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon
             return Arrays.Clone(g);
         }
 
-        public byte[] GetSpolyF()
+        public byte[] GetSpolyBigF()
         {
             return Arrays.Clone(F);
         }
diff --git a/crypto/src/pqc/crypto/falcon/FalconPublicKeyParameters.cs b/crypto/src/pqc/crypto/falcon/FalconPublicKeyParameters.cs
index 72bb11948..fa6b627ab 100644
--- a/crypto/src/pqc/crypto/falcon/FalconPublicKeyParameters.cs
+++ b/crypto/src/pqc/crypto/falcon/FalconPublicKeyParameters.cs
@@ -2,10 +2,10 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Falcon
 {
-    public class FalconPublicKeyParameters
+    public sealed class FalconPublicKeyParameters
         : FalconKeyParameters
     {
-        private byte[] publicKey;
+        private readonly byte[] publicKey;
 
         public FalconPublicKeyParameters(FalconParameters parameters, byte[] h)
             : base(false, parameters)
diff --git a/crypto/src/pqc/crypto/falcon/FalconSigner.cs b/crypto/src/pqc/crypto/falcon/FalconSigner.cs
index f581386ee..e77e84102 100644
--- a/crypto/src/pqc/crypto/falcon/FalconSigner.cs
+++ b/crypto/src/pqc/crypto/falcon/FalconSigner.cs
@@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon
         : IMessageSigner
     {
         private byte[] encodedkey;
-        private FalconNIST nist;
+        private FalconNist nist;
 
         public void Init(bool forSigning, ICipherParameters param)
         {
@@ -19,29 +19,29 @@ namespace Org.BouncyCastle.Pqc.Crypto.Falcon
                 {
                     FalconPrivateKeyParameters skparam = (FalconPrivateKeyParameters)withRandom.Parameters;
                     encodedkey = skparam.GetEncoded();
-                    nist = new FalconNIST(
+                    nist = new FalconNist(
                         withRandom.Random,
-                        skparam.Parameters.LogN,
-                        skparam.Parameters.NonceLength);
+                        (uint)skparam.Parameters.LogN,
+                        (uint)skparam.Parameters.NonceLength);
                 }
                 else
                 {
                     FalconPrivateKeyParameters skparam = (FalconPrivateKeyParameters)param;
                     encodedkey = ((FalconPrivateKeyParameters)param).GetEncoded();
-                    nist = new FalconNIST(
+                    nist = new FalconNist(
                         CryptoServicesRegistrar.GetSecureRandom(),
-                        skparam.Parameters.LogN,
-                        skparam.Parameters.NonceLength);
+                        (uint)skparam.Parameters.LogN,
+                        (uint)skparam.Parameters.NonceLength);
                 }
             }
             else
             {
                 FalconPublicKeyParameters pkparam = (FalconPublicKeyParameters)param;
                 encodedkey = pkparam.GetEncoded();
-                nist = new FalconNIST(
+                nist = new FalconNist(
                     CryptoServicesRegistrar.GetSecureRandom(),
-                    pkparam.Parameters.LogN,
-                    pkparam.Parameters.NonceLength);
+                    (uint)pkparam.Parameters.LogN,
+                    (uint)pkparam.Parameters.NonceLength);
             }
         }
 
diff --git a/crypto/src/pqc/crypto/hqc/ReedSolomon.cs b/crypto/src/pqc/crypto/hqc/ReedSolomon.cs
index 8e7fc664d..25a8c7997 100644
--- a/crypto/src/pqc/crypto/hqc/ReedSolomon.cs
+++ b/crypto/src/pqc/crypto/hqc/ReedSolomon.cs
@@ -28,7 +28,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Hqc
                 for (int j = 0; j < paramG; j++)
                 {
                     tmp[j] = GFCalculator.mult(gateValue, rsPoly[j]);
-                    int n = 1;
                 }
 
                 for (int j = n1 - paramK - 1; j > 0; j--)
diff --git a/crypto/src/pqc/crypto/lms/Composer.cs b/crypto/src/pqc/crypto/lms/Composer.cs
index b897f4b68..6ad044e34 100644
--- a/crypto/src/pqc/crypto/lms/Composer.cs
+++ b/crypto/src/pqc/crypto/lms/Composer.cs
@@ -1,4 +1,3 @@
-using System;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
@@ -38,7 +37,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return this;
         }
 
-        public Composer U16Str(uint n)
+        public Composer U16Str(int n)
         {
             n &= 0xFFFF;
             bos.WriteByte((byte)(n >> 8));
@@ -72,7 +71,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return this;
         }
 
-        public Composer Bytes(byte[][] arrays)
+        public Composer Bytes2(byte[][] arrays)
         {
             foreach (byte[] array in arrays)
             {
@@ -81,7 +80,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return this;
         }
 
-        public Composer Bytes(byte[][] arrays, int start, int end)
+        public Composer Bytes2(byte[][] arrays, int start, int end)
         {
             int j = start;
             while (j != end)
diff --git a/crypto/src/pqc/crypto/lms/HSS.cs b/crypto/src/pqc/crypto/lms/HSS.cs
index 9c21198e4..4634088c7 100644
--- a/crypto/src/pqc/crypto/lms/HSS.cs
+++ b/crypto/src/pqc/crypto/lms/HSS.cs
@@ -39,17 +39,19 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                         0,
                         I,
                         1 << lms.LMSigParameters.H,
-                        rootSeed);
+                        rootSeed,
+                        isPlaceholder: false);
                 }
                 else
                 {
-                    keys[t] = new PlaceholderLMSPrivateKey(
+                    keys[t] = new LmsPrivateKeyParameters(
                         lms.LMSigParameters,
                         lms.LMOtsParameters,
                         -1,
                         zero,
                         1 << lms.LMSigParameters.H,
-                        zero);
+                        zero,
+                        isPlaceholder: true);
                 }
                 hssKeyMaxIndex <<= lms.LMSigParameters.H;
             }
@@ -161,7 +163,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public static bool VerifySignature(HssPublicKeyParameters publicKey, HssSignature signature, byte[] message)
         {
-            int Nspk = signature.GetlMinus1();
+            int Nspk = signature.GetLMinus1();
             if (Nspk + 1 != publicKey.L)
                 return false;
 
@@ -196,25 +198,5 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             }
             return Lms.VerifySignature(key, sigList[Nspk], message);
         }
-
-        private class PlaceholderLMSPrivateKey
-            : LmsPrivateKeyParameters
-        {
-            internal PlaceholderLMSPrivateKey(LMSigParameters lmsParameter, LMOtsParameters otsParameters, int q,
-                byte[] I, int maxQ, byte[] masterSecret)
-                : base(lmsParameter, otsParameters, q, I, maxQ, masterSecret)
-            {
-            }
-
-            internal override LMOtsPrivateKey GetNextOtsPrivateKey()
-            {
-                throw new Exception("placeholder only");
-            }
-
-            public override LmsPublicKeyParameters GetPublicKey()
-            {
-                throw new Exception("placeholder only");
-            }
-        }
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/HSSSignature.cs b/crypto/src/pqc/crypto/lms/HSSSignature.cs
index 7c4599835..21f0397c8 100644
--- a/crypto/src/pqc/crypto/lms/HSSSignature.cs
+++ b/crypto/src/pqc/crypto/lms/HSSSignature.cs
@@ -9,15 +9,15 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
     public sealed class HssSignature
         : IEncodable
     {
-        private int lMinus1;
-        private LmsSignedPubKey[] signedPubKey;
-        private LmsSignature signature;
+        private readonly int m_lMinus1;
+        private readonly LmsSignedPubKey[] m_signedPubKey;
+        private readonly LmsSignature m_signature;
 
         public HssSignature(int lMinus1, LmsSignedPubKey[] signedPubKey, LmsSignature signature)
         {
-            this.lMinus1 = lMinus1;
-            this.signedPubKey = signedPubKey;
-            this.signature = signature;
+            m_lMinus1 = lMinus1;
+            m_signedPubKey = signedPubKey;
+            m_signature = signature;
         }
 
         /**
@@ -73,81 +73,63 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             throw new ArgumentException($"cannot parse {src}");
         }
 
-        // FIXME
-        public int GetlMinus1()
+        public int GetLMinus1()
         {
-            return lMinus1;
+            return m_lMinus1;
         }
 
+        // FIXME
         public LmsSignedPubKey[] GetSignedPubKeys()
         {
-            return signedPubKey;
+            return m_signedPubKey;
         }
 
-        public LmsSignature Signature => signature;
+        public LmsSignature Signature => m_signature;
 
-        public override bool Equals(Object o)
+        public override bool Equals(object other)
         {
-            if (this == o)
-            {
+            if (this == other)
                 return true;
-            }
-
-            if (o == null || GetType() != o.GetType())
-            {
+            if (!(other is HssSignature that))
                 return false;
-            }
 
-            HssSignature signature1 = (HssSignature) o;
-
-            if (lMinus1 != signature1.lMinus1)
-            {
+            if (this.m_lMinus1 != that.m_lMinus1)
                 return false;
-            }
-
-            // FIXME
-            // Probably incorrect - comparing Object[] arrays with Arrays.equals
 
-            if (signedPubKey.Length != signature1.signedPubKey.Length)
-            {
+            if (this.m_signedPubKey.Length != that.m_signedPubKey.Length)
                 return false;
-            }
 
-            for (int t = 0; t < signedPubKey.Length; t++)
+            for (int t = 0; t < m_signedPubKey.Length; t++)
             {
-                if (!signedPubKey[t].Equals(signature1.signedPubKey[t]))
-                {
+                if (!this.m_signedPubKey[t].Equals(that.m_signedPubKey[t]))
                     return false;
-                }
             }
 
-            return signature != null ? signature.Equals(signature1.signature) : signature1.signature == null;
+            return Equals(this.m_signature, that.m_signature);
         }
 
         public override int GetHashCode()
         {
-            int result = lMinus1;
-            result = 31 * result + signedPubKey.GetHashCode();
-            result = 31 * result + (signature != null ? signature.GetHashCode() : 0);
+            int result = m_lMinus1;
+            result = 31 * result + m_signedPubKey.GetHashCode();
+            result = 31 * result + (m_signature != null ? m_signature.GetHashCode() : 0);
             return result;
         }
 
         public byte[] GetEncoded()
         {
             Composer composer = Composer.Compose();
-            composer.U32Str(lMinus1);
-            if (signedPubKey != null)
+            composer.U32Str(m_lMinus1);
+            if (m_signedPubKey != null)
             {
-                foreach (LmsSignedPubKey sigPub in signedPubKey)
+                foreach (LmsSignedPubKey sigPub in m_signedPubKey)
                 {
                     composer.Bytes(sigPub);
                 }
             }
 
-            composer.Bytes(signature);
+            composer.Bytes(m_signature);
             return composer.Build();
-
         }
-
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/LMOtsParameters.cs b/crypto/src/pqc/crypto/lms/LMOtsParameters.cs
index e05b1650a..60bf28d50 100644
--- a/crypto/src/pqc/crypto/lms/LMOtsParameters.cs
+++ b/crypto/src/pqc/crypto/lms/LMOtsParameters.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 
 using Org.BouncyCastle.Asn1;
@@ -53,7 +54,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public int Ls => m_ls;
 
-        public uint SigLen => m_sigLen;
+        public int SigLen => Convert.ToInt32(m_sigLen);
 
         public DerObjectIdentifier DigestOid => m_digestOid;
 
diff --git a/crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs b/crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs
index e9df6fbfd..20b717af6 100644
--- a/crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs
+++ b/crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs
@@ -30,7 +30,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             LmsUtilities.ByteArray(m_I, ctx);
             LmsUtilities.U32Str(m_q, ctx);
-            LmsUtilities.U16Str(LMOts.D_MESG, ctx);
+            LmsUtilities.U16Str((short)LMOts.D_MESG, ctx);
             LmsUtilities.ByteArray(C, ctx);
 
             return new LmsContext(this, sigParams, ctx, C, path);
diff --git a/crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs b/crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs
index 09e8b2951..ef3d4aced 100644
--- a/crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs
+++ b/crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs
@@ -109,7 +109,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             LmsUtilities.ByteArray(m_I, ctx);
             LmsUtilities.U32Str(m_q, ctx);
-            LmsUtilities.U16Str(LMOts.D_MESG, ctx);
+            LmsUtilities.U16Str((short)LMOts.D_MESG, ctx);
             LmsUtilities.ByteArray(signature.C, ctx);
 
             return new LmsContext(this, signature, ctx);
@@ -121,7 +121,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             LmsUtilities.ByteArray(m_I, ctx);
             LmsUtilities.U32Str(m_q, ctx);
-            LmsUtilities.U16Str(LMOts.D_MESG, ctx);
+            LmsUtilities.U16Str((short)LMOts.D_MESG, ctx);
             LmsUtilities.ByteArray(signature.OtsSignature.C, ctx);
 
             return new LmsContext(this, signature, ctx);
diff --git a/crypto/src/pqc/crypto/lms/LMS.cs b/crypto/src/pqc/crypto/lms/LMS.cs
index 3c17b0a1e..6174d3889 100644
--- a/crypto/src/pqc/crypto/lms/LMS.cs
+++ b/crypto/src/pqc/crypto/lms/LMS.cs
@@ -100,7 +100,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             H.BlockUpdate(I, 0, I.Length);
             LmsUtilities.U32Str(node_num, H);
-            LmsUtilities.U16Str(D_LEAF, H);
+            LmsUtilities.U16Str((short)D_LEAF, H);
             H.BlockUpdate(Kc, 0, Kc.Length);
             H.DoFinal(tmp, 0);
 
@@ -113,7 +113,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                     // is odd
                     H.BlockUpdate(I, 0, I.Length);
                     LmsUtilities.U32Str(node_num / 2, H);
-                    LmsUtilities.U16Str(D_INTR, H);
+                    LmsUtilities.U16Str((short)D_INTR, H);
                     H.BlockUpdate(path[i], 0, path[i].Length);
                     H.BlockUpdate(tmp, 0, tmp.Length);
                     H.DoFinal(tmp, 0);
@@ -122,7 +122,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 {
                     H.BlockUpdate(I, 0, I.Length);
                     LmsUtilities.U32Str(node_num / 2, H);
-                    LmsUtilities.U16Str(D_INTR, H);
+                    LmsUtilities.U16Str((short)D_INTR, H);
                     H.BlockUpdate(tmp, 0, tmp.Length);
                     H.BlockUpdate(path[i], 0, path[i].Length);
                     H.DoFinal(tmp, 0);
diff --git a/crypto/src/pqc/crypto/lms/LMSKeyParameters.cs b/crypto/src/pqc/crypto/lms/LMSKeyParameters.cs
index b35ba36c4..aaddfb823 100644
--- a/crypto/src/pqc/crypto/lms/LMSKeyParameters.cs
+++ b/crypto/src/pqc/crypto/lms/LMSKeyParameters.cs
@@ -6,7 +6,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
     public abstract class LmsKeyParameters
         : AsymmetricKeyParameter, IEncodable
     {
-        protected LmsKeyParameters(bool isPrivateKey)
+        internal LmsKeyParameters(bool isPrivateKey)
             : base(isPrivateKey)
         {
         }
diff --git a/crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs b/crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs
index 25ca81938..278cbb04b 100644
--- a/crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs
+++ b/crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs
@@ -9,7 +9,7 @@ using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
-    public class LmsPrivateKeyParameters
+    public sealed class LmsPrivateKeyParameters
         : LmsKeyParameters, ILmsContextBasedSigner
     {
         private static CacheKey T1 = new CacheKey(1);
@@ -34,6 +34,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         private IDigest tDigest;
 
         private int q;
+        private readonly bool m_isPlaceholder;
 
         //
         // These are not final because they can be generated.
@@ -41,9 +42,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         //
         private LmsPublicKeyParameters publicKey;
 
-
         public LmsPrivateKeyParameters(LMSigParameters lmsParameter, LMOtsParameters otsParameters, int q, byte[] I,
             int maxQ, byte[] masterSecret)
+            : this(lmsParameter, otsParameters, q, I, maxQ, masterSecret, false)
+        {
+        }
+
+        internal LmsPrivateKeyParameters(LMSigParameters lmsParameter, LMOtsParameters otsParameters, int q, byte[] I,
+            int maxQ, byte[] masterSecret, bool isPlaceholder)
             : base(true)
         {
             this.parameters = lmsParameter;
@@ -55,6 +61,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             this.maxCacheR = 1 << (parameters.H + 1);
             this.tCache = new Dictionary<CacheKey, byte[]>();
             this.tDigest = DigestUtilities.GetDigest(lmsParameter.DigestOid);
+            this.m_isPlaceholder = isPlaceholder;
         }
 
         private LmsPrivateKeyParameters(LmsPrivateKeyParameters parent, int q, int maxQ)
@@ -203,8 +210,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             }
         }
 
-        internal virtual LMOtsPrivateKey GetNextOtsPrivateKey()
+        internal LMOtsPrivateKey GetNextOtsPrivateKey()
         {
+            if (m_isPlaceholder)
+                throw new Exception("placeholder only");
+
             lock (this)
             {
                 if (q >= maxQ)
@@ -264,8 +274,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return maxQ - q;
         }
 
-        public virtual LmsPublicKeyParameters GetPublicKey()
+        public LmsPublicKeyParameters GetPublicKey()
         {
+            if (m_isPlaceholder)
+                throw new Exception("placeholder only");
+
             lock (this)
             {
                 if (publicKey == null)
@@ -276,7 +289,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             }
         }
 
-        byte[] FindT(int r)
+        internal byte[] FindT(int r)
         {
             if (r < maxCacheR)
             {
@@ -290,14 +303,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         {
             lock (tCache)
             {
-                byte[] t;
-                if (!tCache.TryGetValue(key, out t))
-                {
-                    t = CalcT(key.index);
-                    tCache[key] = t;
-                }
+                if (tCache.TryGetValue(key, out byte[] t))
+                    return t;
 
-                return t;
+                return tCache[key] = CalcT(key.index);
             }
         }
 
@@ -315,7 +324,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             {
                 LmsUtilities.ByteArray(this.GetI(), tDigest);
                 LmsUtilities.U32Str(r, tDigest);
-                LmsUtilities.U16Str(Lms.D_LEAF, tDigest);
+                LmsUtilities.U16Str((short)Lms.D_LEAF, tDigest);
                 //
                 // These can be pre generated at the time of key generation and held within the private key.
                 // However it will cost memory to have them stick around.
@@ -334,7 +343,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             LmsUtilities.ByteArray(this.GetI(), tDigest);
             LmsUtilities.U32Str(r, tDigest);
-            LmsUtilities.U16Str(Lms.D_INTR, tDigest);
+            LmsUtilities.U16Str((short)Lms.D_INTR, tDigest);
             LmsUtilities.ByteArray(t2r, tDigest);
             LmsUtilities.ByteArray(t2rPlus1, tDigest);
             T = new byte[tDigest.GetDigestSize()];
diff --git a/crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs b/crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs
index fa12b47c3..f8d0970af 100644
--- a/crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs
+++ b/crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs
@@ -6,7 +6,7 @@ using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
-    public class LmsPublicKeyParameters
+    public sealed class LmsPublicKeyParameters
         : LmsKeyParameters, ILmsContextBasedVerifier
     {
         private LMSigParameters parameterSet;
diff --git a/crypto/src/pqc/crypto/lms/LMSSignature.cs b/crypto/src/pqc/crypto/lms/LMSSignature.cs
index f5d355297..a1ae475c1 100644
--- a/crypto/src/pqc/crypto/lms/LMSSignature.cs
+++ b/crypto/src/pqc/crypto/lms/LMSSignature.cs
@@ -123,7 +123,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 .U32Str(q)
                 .Bytes(otsSignature.GetEncoded())
                 .U32Str(parameter.ID)
-                .Bytes(y)
+                .Bytes2(y)
                 .Build();
         }
 
diff --git a/crypto/src/pqc/crypto/lms/LM_OTS.cs b/crypto/src/pqc/crypto/lms/LM_OTS.cs
index c3cd3da90..0aa5c580e 100644
--- a/crypto/src/pqc/crypto/lms/LM_OTS.cs
+++ b/crypto/src/pqc/crypto/lms/LM_OTS.cs
@@ -220,7 +220,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             IDigest finalContext = DigestUtilities.GetDigest(parameter.DigestOid);
             LmsUtilities.ByteArray(I, finalContext);
             LmsUtilities.U32Str(q, finalContext);
-            LmsUtilities.U16Str(D_PBLC, finalContext);
+            LmsUtilities.U16Str((short)D_PBLC, finalContext);
 
             byte[] tmp = Composer.Compose()
                 .Bytes(I)
diff --git a/crypto/src/pqc/crypto/lms/LmsUtils.cs b/crypto/src/pqc/crypto/lms/LmsUtils.cs
index da3f457d2..e99dfe585 100644
--- a/crypto/src/pqc/crypto/lms/LmsUtils.cs
+++ b/crypto/src/pqc/crypto/lms/LmsUtils.cs
@@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             d.Update((byte)(n));
         }
 
-        public static void U16Str(ushort n, IDigest d)
+        public static void U16Str(short n, IDigest d)
         {
             d.Update((byte)(n >> 8));
             d.Update((byte)(n));
diff --git a/crypto/src/pqc/crypto/picnic/KMatrices.cs b/crypto/src/pqc/crypto/picnic/KMatrices.cs
index 64e6be00a..a6d280985 100644
--- a/crypto/src/pqc/crypto/picnic/KMatrices.cs
+++ b/crypto/src/pqc/crypto/picnic/KMatrices.cs
@@ -1,5 +1,3 @@
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 {
     internal class KMatrices
@@ -9,36 +7,35 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         private int columns;
         private uint[] data;
 
-        public KMatrices(int nmatrices, int rows, int columns, uint[] data)
+        internal KMatrices(int nmatrices, int rows, int columns, uint[] data)
         {
             this.nmatrices = nmatrices;
             this.rows = rows;
             this.columns = columns;
             this.data = data;
         }
-        
 
-        public int GetNmatrices()
+        internal int GetNmatrices()
         {
             return nmatrices;
         }
 
-        public int GetSize()
+        internal int GetSize()
         {
             return rows * columns;
         }
 
-        public int GetRows()
+        internal int GetRows()
         {
             return rows;
         }
 
-        public int GetColumns()
+        internal int GetColumns()
         {
             return columns;
         }
 
-        public uint[] GetData()
+        internal uint[] GetData()
         {
             return data;
         }
@@ -48,20 +45,21 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         : KMatrices
     {
         private int matrixPointer;
-        public int GetMatrixPointer()
+
+        internal int GetMatrixPointer()
         {
             return matrixPointer;
         }
 
-        public void SetMatrixPointer(int matrixPointer)
+        internal void SetMatrixPointer(int matrixPointer)
         {
             this.matrixPointer = matrixPointer;
         }
 
-        public KMatricesWithPointer(KMatrices m)
+        internal KMatricesWithPointer(KMatrices m)
             : base(m.GetNmatrices(), m.GetRows(), m.GetColumns(), m.GetData())
         {
             this.matrixPointer = 0;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/picnic/Msg.cs b/crypto/src/pqc/crypto/picnic/Msg.cs
index 4a8c145e7..a9a13b383 100644
--- a/crypto/src/pqc/crypto/picnic/Msg.cs
+++ b/crypto/src/pqc/crypto/picnic/Msg.cs
@@ -1,12 +1,12 @@
 namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 {
-    public class Msg
+    internal class Msg
     {
         internal byte[][] msgs; // One for each player
         internal int pos;
         internal int unopened; // Index of the unopened party, or -1 if all parties opened (when signing)
 
-        public Msg(PicnicEngine engine)
+        internal Msg(PicnicEngine engine)
         {
             msgs = new byte[engine.numMPCParties][]; // engine.andSizeBytes 
             for (int i = 0; i < engine.numMPCParties; i++)
@@ -17,4 +17,4 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             unopened = -1;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/picnic/PicnicEngine.cs b/crypto/src/pqc/crypto/picnic/PicnicEngine.cs
index 0e2a4b54f..5557ddcff 100644
--- a/crypto/src/pqc/crypto/picnic/PicnicEngine.cs
+++ b/crypto/src/pqc/crypto/picnic/PicnicEngine.cs
@@ -8,17 +8,17 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 {
-    public class PicnicEngine
+    internal sealed class PicnicEngine
     {
         // same for all parameter sets
-        protected internal static readonly int saltSizeBytes = 32;
+        internal static readonly int saltSizeBytes = 32;
         private static readonly uint MAX_DIGEST_SIZE = 64;
 
         private static readonly int WORD_SIZE_BITS = 32; // the word size for the implementation. Not a LowMC parameter
         private static readonly uint LOWMC_MAX_STATE_SIZE = 64;
-        protected internal static readonly uint LOWMC_MAX_WORDS = (LOWMC_MAX_STATE_SIZE / 4);
-        protected internal static readonly uint LOWMC_MAX_KEY_BITS = 256;
-        protected internal static readonly uint LOWMC_MAX_AND_GATES = (3 * 38 * 10 + 4); /* Rounded to nearest byte */
+        internal static readonly uint LOWMC_MAX_WORDS = (LOWMC_MAX_STATE_SIZE / 4);
+        internal static readonly uint LOWMC_MAX_KEY_BITS = 256;
+        internal static readonly uint LOWMC_MAX_AND_GATES = (3 * 38 * 10 + 4); /* Rounded to nearest byte */
         private static readonly uint MAX_AUX_BYTES = ((LOWMC_MAX_AND_GATES + LOWMC_MAX_KEY_BITS) / 8 + 1);
 
         /* Maximum lengths in bytes */
@@ -28,8 +28,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         /** Largest serialized public key size, in bytes */
         private static readonly uint PICNIC_MAX_PRIVATEKEY_SIZE = (3 * PICNIC_MAX_LOWMC_BLOCK_SIZE + 2);
 
-        /** Largest serialized private key size, in bytes */
-        private static readonly uint PICNIC_MAX_SIGNATURE_SIZE = 209522;
+        //private static readonly uint PICNIC_MAX_SIGNATURE_SIZE = 209522;
 
         /** Largest signature size, in bytes */
 
@@ -47,45 +46,45 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 
 
         // varies between parameter sets
-        protected internal int numRounds;
+        internal int numRounds;
         private int numSboxes;
-        protected internal int stateSizeBits;
-        protected internal int stateSizeBytes;
-        protected internal int stateSizeWords;
-        protected internal int andSizeBytes;
+        internal int stateSizeBits;
+        internal int stateSizeBytes;
+        internal int stateSizeWords;
+        internal int andSizeBytes;
         private int UnruhGWithoutInputBytes;
-        protected internal int UnruhGWithInputBytes;
-        protected internal int numMPCRounds; // T
-        protected internal int numOpenedRounds; // u
-        protected internal int numMPCParties; // N
-        protected internal int seedSizeBytes;
-        protected internal int digestSizeBytes;
-        protected internal int pqSecurityLevel;
+        internal int UnruhGWithInputBytes;
+        internal int numMPCRounds; // T
+        internal int numOpenedRounds; // u
+        internal int numMPCParties; // N
+        internal int seedSizeBytes;
+        internal int digestSizeBytes;
+        internal int pqSecurityLevel;
 
         ///
         private uint transform;
 
         private int parameters;
-        protected internal IXof digest;
+        internal IXof digest;
         private int signatureLength;
 
-        public int GetSecretKeySize()
+        internal int GetSecretKeySize()
         {
             return CRYPTO_SECRETKEYBYTES;
         }
 
-        public int GetPublicKeySize()
+        internal int GetPublicKeySize()
         {
             return CRYPTO_PUBLICKEYBYTES;
         }
 
-        public int GetSignatureSize(int messageLength)
+        internal int GetSignatureSize(int messageLength)
         {
             return CRYPTO_BYTES + messageLength;
         }
 
         //todo dont do this
-        public int GetTrueSignatureSize()
+        internal int GetTrueSignatureSize()
         {
             return signatureLength + 4;
         }
@@ -95,174 +94,174 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             parameters = picnicParams;
             switch (parameters)
             {
-                case 1:
-                case 2:
-                    /*Picnic_L1_FS
-                      Picnic_L1_UR*/
-                    pqSecurityLevel = 64;
-                    stateSizeBits = 128;
-                    numMPCRounds = 219;
-                    numMPCParties = 3;
-                    numSboxes = 10;
-                    numRounds = 20;
-                    digestSizeBytes = 32;
-                    break;
-                case 3:
-                case 4:
-                    /* Picnic_L3_FS
-                       Picnic_L3_UR*/
-                    pqSecurityLevel = 96;
-                    stateSizeBits = 192;
-                    numMPCRounds = 329;
-                    numMPCParties = 3;
-                    numSboxes = 10;
-                    numRounds = 30;
-                    digestSizeBytes = 48;
-                    break;
-                case 5:
-                case 6:
-                    /* Picnic_L5_FS
-                       Picnic_L5_UR*/
-                    pqSecurityLevel = 128;
-                    stateSizeBits = 256;
-                    numMPCRounds = 438;
-                    numMPCParties = 3;
-                    numSboxes = 10;
-                    numRounds = 38;
-                    digestSizeBytes = 64;
-                    break;
-                case 7:
-                    /*Picnic3_L1*/
-                    pqSecurityLevel = 64;
-                    stateSizeBits = 129;
-                    numMPCRounds = 250;
-                    numOpenedRounds = 36;
-                    numMPCParties = 16;
-                    numSboxes = 43;
-                    numRounds = 4;
-                    digestSizeBytes = 32;
-                    break;
-                case 8:
-                    /*Picnic3_L3*/
-                    pqSecurityLevel = 96;
-                    stateSizeBits = 192;
-                    numMPCRounds = 419;
-                    numOpenedRounds = 52;
-                    numMPCParties = 16;
-                    numSboxes = 64;
-                    numRounds = 4;
-                    digestSizeBytes = 48;
-                    break;
-                case 9:
-                    /*Picnic3_L5*/
-                    pqSecurityLevel = 128;
-                    stateSizeBits = 255;
-                    numMPCRounds = 601;
-                    numOpenedRounds = 68;
-                    numMPCParties = 16;
-                    numSboxes = 85;
-                    numRounds = 4;
-                    digestSizeBytes = 64;
-                    break;
-                case 10:
-                    /*Picnic_L1_full*/
-                    pqSecurityLevel = 64;
-                    stateSizeBits = 129;
-                    numMPCRounds = 219;
-                    numMPCParties = 3;
-                    numSboxes = 43;
-                    numRounds = 4;
-                    digestSizeBytes = 32;
-                    break;
-                case 11:
-                    /*Picnic_L3_full*/
-                    pqSecurityLevel = 96;
-                    stateSizeBits = 192;
-                    numMPCRounds = 329;
-                    numMPCParties = 3;
-                    numSboxes = 64;
-                    numRounds = 4;
-                    digestSizeBytes = 48;
-                    break;
-                case 12:
-                    /*Picnic_L5_full*/
-                    pqSecurityLevel = 128;
-                    stateSizeBits = 255;
-                    numMPCRounds = 438;
-                    numMPCParties = 3;
-                    numSboxes = 85;
-                    numRounds = 4;
-                    digestSizeBytes = 64;
-                    break;
+            case 1:
+            case 2:
+                /*Picnic_L1_FS
+                    Picnic_L1_UR*/
+                pqSecurityLevel = 64;
+                stateSizeBits = 128;
+                numMPCRounds = 219;
+                numMPCParties = 3;
+                numSboxes = 10;
+                numRounds = 20;
+                digestSizeBytes = 32;
+                break;
+            case 3:
+            case 4:
+                /* Picnic_L3_FS
+                    Picnic_L3_UR*/
+                pqSecurityLevel = 96;
+                stateSizeBits = 192;
+                numMPCRounds = 329;
+                numMPCParties = 3;
+                numSboxes = 10;
+                numRounds = 30;
+                digestSizeBytes = 48;
+                break;
+            case 5:
+            case 6:
+                /* Picnic_L5_FS
+                    Picnic_L5_UR*/
+                pqSecurityLevel = 128;
+                stateSizeBits = 256;
+                numMPCRounds = 438;
+                numMPCParties = 3;
+                numSboxes = 10;
+                numRounds = 38;
+                digestSizeBytes = 64;
+                break;
+            case 7:
+                /*Picnic3_L1*/
+                pqSecurityLevel = 64;
+                stateSizeBits = 129;
+                numMPCRounds = 250;
+                numOpenedRounds = 36;
+                numMPCParties = 16;
+                numSboxes = 43;
+                numRounds = 4;
+                digestSizeBytes = 32;
+                break;
+            case 8:
+                /*Picnic3_L3*/
+                pqSecurityLevel = 96;
+                stateSizeBits = 192;
+                numMPCRounds = 419;
+                numOpenedRounds = 52;
+                numMPCParties = 16;
+                numSboxes = 64;
+                numRounds = 4;
+                digestSizeBytes = 48;
+                break;
+            case 9:
+                /*Picnic3_L5*/
+                pqSecurityLevel = 128;
+                stateSizeBits = 255;
+                numMPCRounds = 601;
+                numOpenedRounds = 68;
+                numMPCParties = 16;
+                numSboxes = 85;
+                numRounds = 4;
+                digestSizeBytes = 64;
+                break;
+            case 10:
+                /*Picnic_L1_full*/
+                pqSecurityLevel = 64;
+                stateSizeBits = 129;
+                numMPCRounds = 219;
+                numMPCParties = 3;
+                numSboxes = 43;
+                numRounds = 4;
+                digestSizeBytes = 32;
+                break;
+            case 11:
+                /*Picnic_L3_full*/
+                pqSecurityLevel = 96;
+                stateSizeBits = 192;
+                numMPCRounds = 329;
+                numMPCParties = 3;
+                numSboxes = 64;
+                numRounds = 4;
+                digestSizeBytes = 48;
+                break;
+            case 12:
+                /*Picnic_L5_full*/
+                pqSecurityLevel = 128;
+                stateSizeBits = 255;
+                numMPCRounds = 438;
+                numMPCParties = 3;
+                numSboxes = 85;
+                numRounds = 4;
+                digestSizeBytes = 64;
+                break;
             }
 
             switch (parameters)
             {
-                case 1: /*Picnic_L1_FS*/
-                    CRYPTO_SECRETKEYBYTES = 49;
-                    CRYPTO_PUBLICKEYBYTES = 33;
-                    CRYPTO_BYTES = 34036;
-                    break;
-                case 2: /* Picnic_L1_UR*/
-                    CRYPTO_SECRETKEYBYTES = 49;
-                    CRYPTO_PUBLICKEYBYTES = 33;
-                    CRYPTO_BYTES = 53965;
-                    break;
-                case 3: /*Picnic_L3_FS*/
-                    CRYPTO_SECRETKEYBYTES = 73;
-                    CRYPTO_PUBLICKEYBYTES = 49;
-                    CRYPTO_BYTES = 76784;
-                    break;
-                case 4: /*Picnic_L3_UR*/
-                    CRYPTO_SECRETKEYBYTES = 73;
-                    CRYPTO_PUBLICKEYBYTES = 49;
-                    CRYPTO_BYTES = 121857;
-                    break;
-                case 5: /*Picnic_L5_FS*/
-                    CRYPTO_SECRETKEYBYTES = 97;
-                    CRYPTO_PUBLICKEYBYTES = 65;
-                    CRYPTO_BYTES = 132876;
-                    break;
-                case 6: /*Picnic_L5_UR*/
-                    CRYPTO_SECRETKEYBYTES = 97;
-                    CRYPTO_PUBLICKEYBYTES = 65;
-                    CRYPTO_BYTES = 209526;
-                    break;
-                case 7: /*Picnic3_L1*/
-                    CRYPTO_SECRETKEYBYTES = 52;
-                    CRYPTO_PUBLICKEYBYTES = 35;
-                    CRYPTO_BYTES = 14612;
-                    break;
-                case 8: /*Picnic3_L3*/
-                    CRYPTO_SECRETKEYBYTES = 73;
-                    CRYPTO_PUBLICKEYBYTES = 49;
-                    CRYPTO_BYTES = 35028;
-                    break;
-                case 9: /*Picnic3_L5*/
-                    CRYPTO_SECRETKEYBYTES = 97;
-                    CRYPTO_PUBLICKEYBYTES = 65;
-                    CRYPTO_BYTES = 61028;
-                    break;
-                case 10: /*Picnic_L1_full*/
-                    CRYPTO_SECRETKEYBYTES = 52;
-                    CRYPTO_PUBLICKEYBYTES = 35;
-                    CRYPTO_BYTES = 32061;
-                    break;
-                case 11: /*Picnic_L3_full*/
-                    CRYPTO_SECRETKEYBYTES = 73;
-                    CRYPTO_PUBLICKEYBYTES = 49;
-                    CRYPTO_BYTES = 71179;
-                    break;
-                case 12: /*Picnic_L5_full*/
-                    CRYPTO_SECRETKEYBYTES = 97;
-                    CRYPTO_PUBLICKEYBYTES = 65;
-                    CRYPTO_BYTES = 126286;
-                    break;
-                default:
-                    CRYPTO_SECRETKEYBYTES = -1;
-                    CRYPTO_PUBLICKEYBYTES = -1;
-                    CRYPTO_BYTES = -1;
-                    break;
+            case 1: /*Picnic_L1_FS*/
+                CRYPTO_SECRETKEYBYTES = 49;
+                CRYPTO_PUBLICKEYBYTES = 33;
+                CRYPTO_BYTES = 34036;
+                break;
+            case 2: /* Picnic_L1_UR*/
+                CRYPTO_SECRETKEYBYTES = 49;
+                CRYPTO_PUBLICKEYBYTES = 33;
+                CRYPTO_BYTES = 53965;
+                break;
+            case 3: /*Picnic_L3_FS*/
+                CRYPTO_SECRETKEYBYTES = 73;
+                CRYPTO_PUBLICKEYBYTES = 49;
+                CRYPTO_BYTES = 76784;
+                break;
+            case 4: /*Picnic_L3_UR*/
+                CRYPTO_SECRETKEYBYTES = 73;
+                CRYPTO_PUBLICKEYBYTES = 49;
+                CRYPTO_BYTES = 121857;
+                break;
+            case 5: /*Picnic_L5_FS*/
+                CRYPTO_SECRETKEYBYTES = 97;
+                CRYPTO_PUBLICKEYBYTES = 65;
+                CRYPTO_BYTES = 132876;
+                break;
+            case 6: /*Picnic_L5_UR*/
+                CRYPTO_SECRETKEYBYTES = 97;
+                CRYPTO_PUBLICKEYBYTES = 65;
+                CRYPTO_BYTES = 209526;
+                break;
+            case 7: /*Picnic3_L1*/
+                CRYPTO_SECRETKEYBYTES = 52;
+                CRYPTO_PUBLICKEYBYTES = 35;
+                CRYPTO_BYTES = 14612;
+                break;
+            case 8: /*Picnic3_L3*/
+                CRYPTO_SECRETKEYBYTES = 73;
+                CRYPTO_PUBLICKEYBYTES = 49;
+                CRYPTO_BYTES = 35028;
+                break;
+            case 9: /*Picnic3_L5*/
+                CRYPTO_SECRETKEYBYTES = 97;
+                CRYPTO_PUBLICKEYBYTES = 65;
+                CRYPTO_BYTES = 61028;
+                break;
+            case 10: /*Picnic_L1_full*/
+                CRYPTO_SECRETKEYBYTES = 52;
+                CRYPTO_PUBLICKEYBYTES = 35;
+                CRYPTO_BYTES = 32061;
+                break;
+            case 11: /*Picnic_L3_full*/
+                CRYPTO_SECRETKEYBYTES = 73;
+                CRYPTO_PUBLICKEYBYTES = 49;
+                CRYPTO_BYTES = 71179;
+                break;
+            case 12: /*Picnic_L5_full*/
+                CRYPTO_SECRETKEYBYTES = 97;
+                CRYPTO_PUBLICKEYBYTES = 65;
+                CRYPTO_BYTES = 126286;
+                break;
+            default:
+                CRYPTO_SECRETKEYBYTES = -1;
+                CRYPTO_PUBLICKEYBYTES = -1;
+                CRYPTO_BYTES = -1;
+                break;
             }
 
             // calculated depending on above parameters
@@ -304,7 +303,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             digest = new ShakeDigest(shakeSize);
         }
 
-        public bool crypto_sign_open(byte[] m, byte[] sm, byte[] pk)
+        internal bool crypto_sign_open(byte[] m, byte[] sm, byte[] pk)
         {
             uint sigLen = Pack.LE_To_UInt32(sm, 0);
             byte[] m_from_sm = Arrays.CopyOfRange(sm, 4, 4 + m.Length);
@@ -432,7 +431,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return status;
         }
 
-        void VerifyProof(Signature.Proof proof, View view1, View view2, int challenge, byte[] salt, 
+        private void VerifyProof(Signature.Proof proof, View view1, View view2, int challenge, byte[] salt, 
             uint roundNumber, byte[] tmp, uint[] plaintext, Tape tape)
         {
             Array.Copy(proof.communicatedBits, 0, view2.communicatedBits, 0, andSizeBytes);
@@ -534,7 +533,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             mpc_LowMC_verify(view1, view2, tape, tmp_ints, plaintext, challenge);
         }
 
-        void mpc_LowMC_verify(View view1, View view2, Tape tapes, uint[] tmp, uint[] plaintext,  int challenge)
+        private void mpc_LowMC_verify(View view1, View view2, Tape tapes, uint[] tmp, uint[] plaintext,  int challenge)
         {
             Utils.Fill(tmp, 0, tmp.Length, 0);
 
@@ -576,7 +575,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             Array.Copy(tmp, 3 * stateSizeWords, view2.outputShare, 0, stateSizeWords);
         }
 
-        void mpc_substitution_verify(uint[] state, Tape rand, View view1, View view2)
+        private void mpc_substitution_verify(uint[] state, Tape rand, View view1, View view2)
         {
             uint[] a = new uint[2];
             uint[] b = new uint[2];
@@ -611,7 +610,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             }
         }
 
-        void mpc_AND_verify(uint[] in1, uint[] in2, uint[] output, Tape rand, View view1, View view2)
+        private void mpc_AND_verify(uint[] in1, uint[] in2, uint[] output, Tape rand, View view1, View view2)
         {
             uint[] r = {Utils.GetBit(rand.tapes[0], rand.pos), Utils.GetBit(rand.tapes[1], rand.pos)};
 
@@ -646,7 +645,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 
         }
 
-
         private int DeserializeSignature(Signature sig, byte[] sigBytes, uint sigBytesLen, int sigBytesOffset)
         {
             Signature.Proof[] proofs = sig.proofs;
@@ -1086,7 +1084,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return true;
         }
 
-        public void crypto_sign(byte[] sm, byte[] m, byte[] sk)
+        internal void crypto_sign(byte[] sm, byte[] m, byte[] sk)
         {
             picnic_sign(sk, m, sm);
             Array.Copy(m, 0, sm, 4, m.Length);
@@ -1142,7 +1140,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 
         /*** Serialization functions ***/
 
-        int SerializeSignature(Signature sig, byte[] sigBytes, int sigOffset)
+        private int SerializeSignature(Signature sig, byte[] sigBytes, int sigOffset)
         {
             Signature.Proof[] proofs = sig.proofs;
             byte[] challengeBits = sig.challengeBits;
@@ -1204,7 +1202,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return sigByteIndex - sigOffset;
         }
 
-        int GetChallenge(byte[] challenge, int round)
+        private int GetChallenge(byte[] challenge, int round)
         {
             return (Utils.GetBit(challenge, 2 * round + 1) << 1) | Utils.GetBit(challenge, 2 * round);
         }
@@ -1418,7 +1416,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         }
 
         /* Caller must allocate the first parameter */
-        void Prove(Signature.Proof proof, int challenge, byte[] seeds, int seedsOffset,
+        private void Prove(Signature.Proof proof, int challenge, byte[] seeds, int seedsOffset,
             View[] views, byte[][] commitments, byte[][] gs)
         {
             if (challenge == 0)
@@ -1456,7 +1454,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             }
         }
 
-        void H3(uint[] circuitOutput, uint[] plaintext, uint[][][] viewOutputs,
+        private void H3(uint[] circuitOutput, uint[] plaintext, uint[][][] viewOutputs,
             byte[][][] AS, byte[] challengeBits, byte[] salt,
             byte[] message, byte[][][] gs)
         {
@@ -1659,7 +1657,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             int stateOffset;
             for (int i = 0; i < numSboxes * 3; i += 3)
             {
-
                 for (int j = 0; j < 3; j++)
                 {
                     stateOffset = ((3 + j) * stateSizeWords) * 32;
@@ -1748,7 +1745,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             digest.Update((byte) 2);
             digest.BlockUpdate(seed, seedOffset, seedSizeBytes);
             digest.OutputFinal(tape, 0, digestSizeBytes);
-//        Console.Error.Write("tape: " + Hex.toHexString(tape));
 
             /* Expand the hashed seed, salt, round and player indices, and output
              * length to create the tape. */
@@ -1949,7 +1945,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return missingLeaves;
         }
 
-
         private void HCP(byte[] challengeHash, uint[] challengeC, uint[] challengeP, byte[][] Ch,
             byte[] hCv, byte[] salt, uint[] pubKey, uint[] plaintext, byte[] message)
         {
@@ -1971,7 +1966,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             }
         }
 
-        static int BitsToChunks(int chunkLenBits, byte[] input, int inputLen, uint[] chunks)
+        private static int BitsToChunks(int chunkLenBits, byte[] input, int inputLen, uint[] chunks)
         {
             if (chunkLenBits > inputLen * 8)
             {
@@ -1992,7 +1987,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return chunkCount;
         }
 
-        static uint AppendUnique(uint[] list,  uint value,  uint position)
+        private static uint AppendUnique(uint[] list,  uint value,  uint position)
         {
             if (position == 0)
             {
@@ -2171,7 +2166,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return true;
         }
 
-        static uint Extend(uint bit)
+        private static uint Extend(uint bit)
         {
             return ~(bit - 1);
         }
@@ -2233,7 +2228,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             }
         }
 
-        protected internal void aux_mpc_sbox(uint[] input, uint[] output, Tape tape)
+        internal void aux_mpc_sbox(uint[] input, uint[] output, Tape tape)
         {
             for (int i = 0; i < numSboxes * 3; i += 3)
             {
@@ -2341,7 +2336,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             digest.OutputFinal(saltAndRoot, 0, saltAndRoot.Length);
         }
 
-        static bool is_picnic3(int parameters)
+        private static bool is_picnic3(int parameters)
         {
             return parameters == 7 /*Picnic3_L1*/ ||
                    parameters == 8 /*Picnic3_L3*/ ||
@@ -2349,7 +2344,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         }
 
         //todo return int;
-        public void crypto_sign_keypair(byte[] pk, byte[] sk, SecureRandom random)
+        internal void crypto_sign_keypair(byte[] pk, byte[] sk, SecureRandom random)
         {
             // set array sizes
             byte[] plaintext_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
@@ -2474,7 +2469,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             }
         }
 
-        protected internal void xor_array(uint[] output, uint[] in1, uint[] in2, int in2_offset, int length)
+        internal void xor_array(uint[] output, uint[] in1, uint[] in2, int in2_offset, int length)
         {
             for (int i = 0; i < length; i++)
             {
@@ -2482,12 +2477,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             }
         }
 
-        protected internal void matrix_mul(uint[] output, uint[] state, uint[] matrix, int matrixOffset)
+        internal void matrix_mul(uint[] output, uint[] state, uint[] matrix, int matrixOffset)
         {
             matrix_mul_offset(output, 0, state, 0, matrix, matrixOffset);
         }
 
-        protected void matrix_mul_offset(uint[] output, int outputOffset, uint[] state, int stateOffset, uint[] matrix,
+        internal void matrix_mul_offset(uint[] output, int outputOffset, uint[] state, int stateOffset, uint[] matrix,
             int matrixOffset)
         {
             // Use temp to correctly handle the case when state = output
diff --git a/crypto/src/pqc/crypto/picnic/Signature2.cs b/crypto/src/pqc/crypto/picnic/Signature2.cs
index 7659fb314..c6f44380c 100644
--- a/crypto/src/pqc/crypto/picnic/Signature2.cs
+++ b/crypto/src/pqc/crypto/picnic/Signature2.cs
@@ -1,7 +1,7 @@
 
 namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 {
-    public class Signature2
+    internal class Signature2
     {
         internal byte[] salt;
         internal byte[] iSeedInfo; // Info required to recompute the tree of all initial seeds
@@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         internal Proof2[] proofs; // One proof for each online execution the verifier checks
 
         //todo initialize in engine!
-        public Signature2(PicnicEngine engine)
+        internal Signature2(PicnicEngine engine)
         {
             challengeHash = new byte[engine.digestSizeBytes];
             salt = new byte[PicnicEngine.saltSizeBytes];
@@ -23,7 +23,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             proofs = new Proof2[engine.numMPCRounds];
         }
 
-        public class Proof2
+        internal class Proof2
         {
             internal byte[] seedInfo; // Information required to compute the tree with seeds of of all opened parties
             internal int seedInfoLen; // Length of seedInfo buffer
@@ -32,7 +32,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             internal byte[] input; // Masked input used in online execution
             internal byte[] msgs; // Broadcast messages of unopened party P[t]
 
-            public Proof2(PicnicEngine engine)
+            internal Proof2(PicnicEngine engine)
             {
                 seedInfo = null;
                 seedInfoLen = 0;
@@ -45,6 +45,3 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         }
     }
 }
-
-
-
diff --git a/crypto/src/pqc/crypto/picnic/Tape.cs b/crypto/src/pqc/crypto/picnic/Tape.cs
index dd1a44de8..c433b3267 100644
--- a/crypto/src/pqc/crypto/picnic/Tape.cs
+++ b/crypto/src/pqc/crypto/picnic/Tape.cs
@@ -1,125 +1,128 @@
 using Org.BouncyCastle.Crypto.Utilities;
-using Org.BouncyCastle.Pqc.Crypto.Picnic;
 using Org.BouncyCastle.Utilities;
 
-public class Tape
+namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 {
-    internal byte[][] tapes;
-    internal int pos;
-    int nTapes;
-
-    private PicnicEngine engine;
-    public Tape(PicnicEngine engine)
+    internal class Tape
     {
-        this.engine = engine;
-        tapes = new byte[engine.numMPCParties][]; //[2 * engine.andSizeBytes];
-        for (int i = 0; i < engine.numMPCParties; i++)
-        {
-            tapes[i] = new byte[2 * engine.andSizeBytes];
-        }
-        pos = 0;
-        nTapes = engine.numMPCParties;
-    }
+        internal byte[][] tapes;
+        internal int pos;
+        int nTapes;
 
-    protected internal void SetAuxBits(byte[] input)
-    {
-        int last = engine.numMPCParties - 1;
-        int pos = 0;
-        int n = engine.stateSizeBits;
+        private PicnicEngine engine;
 
-        for(int j = 0; j < engine.numRounds; j++)
+        internal Tape(PicnicEngine engine)
         {
-            for(int i = 0; i < n; i++)
+            this.engine = engine;
+            tapes = new byte[engine.numMPCParties][]; //[2 * engine.andSizeBytes];
+            for (int i = 0; i < engine.numMPCParties; i++)
             {
-                Utils.SetBit(this.tapes[last], n + n*2*j  + i, Utils.GetBit(input, pos++));
+                tapes[i] = new byte[2 * engine.andSizeBytes];
             }
+            pos = 0;
+            nTapes = engine.numMPCParties;
         }
-    }
-
-    /* Input is the tapes for one parallel repitition; i.e., tapes[t]
-     * Updates the random tapes of all players with the mask values for the output of
-     * AND gates, and computes the N-th party's share such that the AND gate invariant
-     * holds on the mask values.
-     */
-    protected internal void ComputeAuxTape(byte[] inputs)
-    {
-        uint[] roundKey = new uint[PicnicEngine.LOWMC_MAX_WORDS];
-        uint[] x = new uint[PicnicEngine.LOWMC_MAX_WORDS];
-        uint[] y = new uint[PicnicEngine.LOWMC_MAX_WORDS];
-        uint[] key = new uint[PicnicEngine.LOWMC_MAX_WORDS];
-        uint[] key0 = new uint[PicnicEngine.LOWMC_MAX_WORDS];
 
-        key0[engine.stateSizeWords - 1] = 0;
-        TapesToParityBits(key0, engine.stateSizeBits);
-
-//        System.out.print("key0: ");
-//        for (int i = 0; i < key0.Length; i++)
-//        {System.out.printf("%08x ", key0[i]);}System.out.Println();
-
-        // key = key0 x KMatrix[0]^(-1)
-        KMatricesWithPointer current = LowmcConstants.Instance.KMatrixInv(engine, 0);
-        engine.matrix_mul(key, key0, current.GetData(), current.GetMatrixPointer());
-
-//        System.out.print("key: ");
-//        for (int i = 0; i < key0.Length; i++)
-//        {System.out.printf("%08x ", key[i]);}System.out.Println();
+        internal void SetAuxBits(byte[] input)
+        {
+            int last = engine.numMPCParties - 1;
+            int pos = 0;
+            int n = engine.stateSizeBits;
 
+            for(int j = 0; j < engine.numRounds; j++)
+            {
+                for(int i = 0; i < n; i++)
+                {
+                    Utils.SetBit(this.tapes[last], n + n*2*j  + i, Utils.GetBit(input, pos++));
+                }
+            }
+        }
 
-        if(inputs != null)
+        /* Input is the tapes for one parallel repitition; i.e., tapes[t]
+         * Updates the random tapes of all players with the mask values for the output of
+         * AND gates, and computes the N-th party's share such that the AND gate invariant
+         * holds on the mask values.
+         */
+        internal void ComputeAuxTape(byte[] inputs)
         {
-            Pack.UInt32_To_LE(Arrays.CopyOf(key, engine.stateSizeWords), inputs, 0);
-        }
+            uint[] roundKey = new uint[PicnicEngine.LOWMC_MAX_WORDS];
+            uint[] x = new uint[PicnicEngine.LOWMC_MAX_WORDS];
+            uint[] y = new uint[PicnicEngine.LOWMC_MAX_WORDS];
+            uint[] key = new uint[PicnicEngine.LOWMC_MAX_WORDS];
+            uint[] key0 = new uint[PicnicEngine.LOWMC_MAX_WORDS];
 
+            key0[engine.stateSizeWords - 1] = 0;
+            TapesToParityBits(key0, engine.stateSizeBits);
 
-        for (int r = engine.numRounds; r > 0; r--)
-        {
-            current = LowmcConstants.Instance.KMatrix(engine, r);
-            engine.matrix_mul(roundKey, key, current.GetData(), current.GetMatrixPointer());    // roundKey = key * KMatrix(r)
+    //        System.out.print("key0: ");
+    //        for (int i = 0; i < key0.Length; i++)
+    //        {System.out.printf("%08x ", key0[i]);}System.out.Println();
+
+            // key = key0 x KMatrix[0]^(-1)
+            KMatricesWithPointer current = LowmcConstants.Instance.KMatrixInv(engine, 0);
+            engine.matrix_mul(key, key0, current.GetData(), current.GetMatrixPointer());
 
-            engine.xor_array(x, x, roundKey, 0, engine.stateSizeWords);
+    //        System.out.print("key: ");
+    //        for (int i = 0; i < key0.Length; i++)
+    //        {System.out.printf("%08x ", key[i]);}System.out.Println();
 
-            current = LowmcConstants.Instance.LMatrixInv(engine, r-1);
-            engine.matrix_mul(y, x, current.GetData(), current.GetMatrixPointer());
 
-            if(r == 1)
+            if(inputs != null)
             {
-                // Use key as input
-                System.Array.Copy(key0, 0, x, 0, key0.Length);
+                Pack.UInt32_To_LE(Arrays.CopyOf(key, engine.stateSizeWords), inputs, 0);
             }
-            else
+
+
+            for (int r = engine.numRounds; r > 0; r--)
             {
-                this.pos = engine.stateSizeBits * 2 * (r - 1);
-                // Read input mask shares from tapes
-                TapesToParityBits(x, engine.stateSizeBits);
+                current = LowmcConstants.Instance.KMatrix(engine, r);
+                engine.matrix_mul(roundKey, key, current.GetData(), current.GetMatrixPointer());    // roundKey = key * KMatrix(r)
+
+                engine.xor_array(x, x, roundKey, 0, engine.stateSizeWords);
+
+                current = LowmcConstants.Instance.LMatrixInv(engine, r-1);
+                engine.matrix_mul(y, x, current.GetData(), current.GetMatrixPointer());
+
+                if(r == 1)
+                {
+                    // Use key as input
+                    System.Array.Copy(key0, 0, x, 0, key0.Length);
+                }
+                else
+                {
+                    this.pos = engine.stateSizeBits * 2 * (r - 1);
+                    // Read input mask shares from tapes
+                    TapesToParityBits(x, engine.stateSizeBits);
+                }
+
+                this.pos = engine.stateSizeBits * 2 * (r - 1) + engine.stateSizeBits;
+                engine.aux_mpc_sbox(x, y, this);
             }
 
-            this.pos = engine.stateSizeBits * 2 * (r - 1) + engine.stateSizeBits;
-            engine.aux_mpc_sbox(x, y, this);
+            // Reset the random tape counter so that the online execution uses the
+            // same random bits as when computing the aux shares
+            this.pos = 0;
         }
 
-        // Reset the random tape counter so that the online execution uses the
-        // same random bits as when computing the aux shares
-        this.pos = 0;
-    }
-
-    private void TapesToParityBits(uint[] output, int outputBitLen)
-    {
-        for (int i = 0; i < outputBitLen; i++)
+        private void TapesToParityBits(uint[] output, int outputBitLen)
         {
-            Utils.SetBitInWordArray(output, i, Utils.Parity16(TapesToWord()));
+            for (int i = 0; i < outputBitLen; i++)
+            {
+                Utils.SetBitInWordArray(output, i, Utils.Parity16(TapesToWord()));
+            }
         }
-    }
-
-    protected internal uint TapesToWord()
-    {
-        byte[] shares = new byte[4];
 
-        for (int i = 0; i < 16; i++)
+        internal uint TapesToWord()
         {
-            byte bit = Utils.GetBit(this.tapes[i], this.pos);
-            Utils.SetBit(shares, i, bit);
+            byte[] shares = new byte[4];
+
+            for (int i = 0; i < 16; i++)
+            {
+                byte bit = Utils.GetBit(this.tapes[i], this.pos);
+                Utils.SetBit(shares, i, bit);
+            }
+            this.pos++;
+            return Pack.LE_To_UInt32(shares, 0);
         }
-        this.pos++;
-        return Pack.LE_To_UInt32(shares, 0);
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/picnic/Tree.cs b/crypto/src/pqc/crypto/picnic/Tree.cs
index 50f844a52..80b2f87ba 100644
--- a/crypto/src/pqc/crypto/picnic/Tree.cs
+++ b/crypto/src/pqc/crypto/picnic/Tree.cs
@@ -1,12 +1,11 @@
 using System;
+
 using Org.BouncyCastle.Crypto.Utilities;
-using Org.BouncyCastle.Pqc.Crypto.Picnic;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 {
-
-    public class Tree
+    internal sealed class Tree
     {
         private static int MAX_SEED_SIZE_BYTES = 32;
         private uint MAX_AUX_BYTES;
@@ -22,17 +21,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 
         private PicnicEngine engine;
 
-        protected internal byte[][] GetLeaves()
+        internal byte[][] GetLeaves()
         {
             return this.nodes;
         }
 
-        protected internal uint GetLeavesOffset()
+        internal uint GetLeavesOffset()
         {
             return this.numNodes - this.numLeaves;
         }
 
-        public Tree(PicnicEngine engine, uint numLeaves, int dataSize)
+        internal Tree(PicnicEngine engine, uint numLeaves, int dataSize)
         {
             this.engine = engine;
             MAX_AUX_BYTES = ((PicnicEngine.LOWMC_MAX_AND_GATES + PicnicEngine.LOWMC_MAX_KEY_BITS) / 8 + 1);
@@ -69,7 +68,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 
         /* Create a Merkle tree by hashing up all nodes.
          * leafData must have Length this.numNodes, but some may be NULL. */
-        protected internal void BuildMerkleTree(byte[][] leafData, byte[] salt)
+        internal void BuildMerkleTree(byte[][] leafData, byte[] salt)
         {
             uint firstLeaf = this.numNodes - this.numLeaves;
 
@@ -92,7 +91,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         }
 
         /* verifyMerkleTree: verify for each leaf that is set */
-        protected internal int VerifyMerkleTree(byte[][] leafData, byte[] salt)
+        internal int VerifyMerkleTree(byte[][] leafData, byte[] salt)
         {
             uint firstLeaf = this.numNodes - this.numLeaves;
 
@@ -131,7 +130,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return 0;
         }
 
-        protected internal int ReconstructSeeds(uint[] hideList, uint hideListSize,
+        internal int ReconstructSeeds(uint[] hideList, uint hideListSize,
             byte[] input, uint inputLen, byte[] salt, uint repIndex)
         {
             int ret = 0;
@@ -163,7 +162,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         }
 
         /* Serialze the missing nodes that the verifier will require to check commitments for non-missing leaves */
-        protected internal byte[] OpenMerkleTree(uint[] missingLeaves, uint missingLeavesSize, int[] outputSizeBytes)
+        internal byte[] OpenMerkleTree(uint[] missingLeaves, uint missingLeavesSize, int[] outputSizeBytes)
         {
             uint[] revealedSize = new uint[1];
             uint[] revealed = this.GetRevealedMerkleNodes(missingLeaves, missingLeavesSize, revealedSize);
@@ -293,7 +292,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
         }
 
 
-        protected internal uint RevealSeedsSize(uint[] hideList, uint hideListSize)
+        internal uint RevealSeedsSize(uint[] hideList, uint hideListSize)
         {
             uint[] numNodesRevealed = new uint[1];
             numNodesRevealed[0] = 0;
@@ -301,7 +300,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return numNodesRevealed[0] * (uint)engine.seedSizeBytes;
         }
 
-        protected internal int RevealSeeds(uint[] hideList, uint hideListSize, byte[] output, int outputSize)
+        internal int RevealSeeds(uint[] hideList, uint hideListSize, byte[] output, int outputSize)
         {
 //        byte[] outputBase = Arrays.clone(output);
             uint[] revealedSize = new uint[1];
@@ -330,7 +329,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return output.Length - outLen;
         }
 
-        protected internal uint OpenMerkleTreeSize(uint[] missingLeaves, uint missingLeavesSize)
+        internal uint OpenMerkleTreeSize(uint[] missingLeaves, uint missingLeavesSize)
         {
             uint[] revealedSize = new uint[1];
             uint[] revealed = this.GetRevealedMerkleNodes(missingLeaves, missingLeavesSize, revealedSize);
@@ -454,15 +453,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             this.haveNode[parent] = true;
         }
 
-
-        protected internal byte[] GetLeaf(uint leafIndex)
+        internal byte[] GetLeaf(uint leafIndex)
         {
             uint firstLeaf = this.numNodes - this.numLeaves;
             return this.nodes[firstLeaf + leafIndex];
         }
 
         /* addMerkleNodes: deserialize and add the data for nodes provided by the committer */
-        protected internal int AddMerkleNodes(uint[] missingLeaves, uint missingLeavesSize, byte[] input, uint inputSize)
+        internal int AddMerkleNodes(uint[] missingLeaves, uint missingLeavesSize, byte[] input, uint inputSize)
         {
 //        if (inputSize > INT_MAX) {
 //            return -1;
@@ -495,7 +493,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
             return 0;
         }
 
-        protected internal void GenerateSeeds(byte[] rootSeed, byte[] salt, uint repIndex)
+        internal void GenerateSeeds(byte[] rootSeed, byte[] salt, uint repIndex)
         {
             this.nodes[0] = rootSeed;
             this.haveNode[0] = true;
@@ -578,6 +576,5 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 
             return this.exists[i] == 1;
         }
-
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/picnic/View.cs b/crypto/src/pqc/crypto/picnic/View.cs
index d72afa9d4..cac47631a 100644
--- a/crypto/src/pqc/crypto/picnic/View.cs
+++ b/crypto/src/pqc/crypto/picnic/View.cs
@@ -1,18 +1,16 @@
-using Org.BouncyCastle.Pqc.Crypto.Picnic;
-
 namespace Org.BouncyCastle.Pqc.Crypto.Picnic
 {
-    public class View
+    internal class View
     {
         internal uint[] inputShare;
         internal byte[] communicatedBits;
         internal uint[] outputShare;
 
-        public View(PicnicEngine engine)
+        internal View(PicnicEngine engine)
         {
             inputShare = new uint[engine.stateSizeBytes];
             communicatedBits = new byte[engine.andSizeBytes];
             outputShare = new uint[engine.stateSizeBytes];
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/sike/Fpx.cs b/crypto/src/pqc/crypto/sike/Fpx.cs
index 9ba332753..140b36afb 100644
--- a/crypto/src/pqc/crypto/sike/Fpx.cs
+++ b/crypto/src/pqc/crypto/sike/Fpx.cs
@@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Cyclotomic squaring on elements of norm 1, using a^(p+1) = 1.
-        protected internal void sqr_Fp2_cycl(ulong[][] a, ulong[] one)
+        internal void sqr_Fp2_cycl(ulong[][] a, ulong[] one)
         {
             ulong[] t0 = new ulong[engine.param.NWORDS_FIELD];
 
@@ -44,7 +44,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         // n-way simultaneous inversion using Montgomery's trick.
         // SECURITY NOTE: This function does not run in constant time.
         // Also, vec and out CANNOT be the same variable!
-        protected internal void mont_n_way_inv(ulong[][][] vec, uint n, ulong[][][] output)
+        internal void mont_n_way_inv(ulong[][][] vec, uint n, ulong[][][] output)
         {
             ulong[][] t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
             int i;
@@ -68,7 +68,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
 
         // Copy a field element, c = a.
-        protected internal void fpcopy(ulong[] a, long aOffset, ulong[] c)
+        internal void fpcopy(ulong[] a, long aOffset, ulong[] c)
         {
             for (uint i = 0; i < engine.param.NWORDS_FIELD; i++)
             {
@@ -77,21 +77,21 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // GF(p^2) addition without correction, c = a+b in GF(p^2).
-        protected internal void mp2_add(ulong[][] a, ulong[][] b, ulong[][] c)
+        internal void mp2_add(ulong[][] a, ulong[][] b, ulong[][] c)
         {
             mp_add(a[0], b[0], c[0], engine.param.NWORDS_FIELD);
             mp_add(a[1], b[1], c[1], engine.param.NWORDS_FIELD);
         }
 
         // Modular correction, a = a in GF(p^2).
-        protected internal void fp2correction(ulong[][] a)
+        internal void fp2correction(ulong[][] a)
         {
             fpcorrectionPRIME(a[0]);
             fpcorrectionPRIME(a[1]);
         }
 
         // Multiprecision addition, c = a+b, where lng(a) = lng(b) = nwords. Returns the carry bit.
-        protected internal ulong mp_add(ulong[] a, ulong[] b, ulong[] c, uint nwords)
+        internal ulong mp_add(ulong[] a, ulong[] b, ulong[] c, uint nwords)
         {
             ulong carry = 0;
 
@@ -169,7 +169,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // Is x = 0? return 1 (TRUE) if condition is true, 0 (FALSE) otherwise.
         // SECURITY NOTE: This function does not run in constant-time.
-        protected internal bool is_felm_zero(ulong[] x)
+        internal bool is_felm_zero(ulong[] x)
         {
             for (uint i = 0; i < engine.param.NWORDS_FIELD; i++)
             {
@@ -205,7 +205,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // Test if a is a square in GF(p^2) and return 1 if true, 0 otherwise
         // If a is a quadratic residue, s will be assigned with a partially computed square root of a
-        protected internal bool is_sqr_fp2(ulong[][] a, ulong[] s)
+        internal bool is_sqr_fp2(ulong[][] a, ulong[] s)
         {
             uint i;
             ulong[] a0 = new ulong[engine.param.NWORDS_FIELD],
@@ -365,7 +365,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // GF(p^2) inversion using Montgomery arithmetic, a = (a0-i*a1)/(a0^2+a1^2)
         // This uses the binary GCD for inversion in fp and is NOT constant time!!!
-        protected internal void fp2inv_mont_bingcd(ulong[][] a)
+        internal void fp2inv_mont_bingcd(ulong[][] a)
         {
             ulong[][] t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
 
@@ -381,7 +381,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // GF(p^2) division by two, c = a/2  in GF(p^2).
-        protected internal void fp2div2(ulong[][] a, ulong[][] c)
+        internal void fp2div2(ulong[][] a, ulong[][] c)
         {
             //todo/org : make fp class and change this to generic fpdiv2
             fpdiv2_PRIME(a[0], c[0]);
@@ -606,7 +606,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             Debug.Assert(t == 0);
         }
 
-        protected internal static bool subarrayEquals(ulong[] a, ulong[] b, uint length)
+        internal static bool subarrayEquals(ulong[] a, ulong[] b, uint length)
         {
     //        if(a.Length < length || b.Length < length)
     //            return false;
@@ -619,7 +619,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             return true;
         }
 
-        protected internal static bool subarrayEquals(ulong[][] a, ulong[][] b, uint length)
+        internal static bool subarrayEquals(ulong[][] a, ulong[][] b, uint length)
         {
             int nwords_feild = b[0].Length;
     //        if(a[0].Length < length || b[0].Length < length)
@@ -633,7 +633,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             return true;
         }
 
-        protected internal static bool subarrayEquals(ulong[][] a, ulong[][] b, uint bOffset, uint length)
+        internal static bool subarrayEquals(ulong[][] a, ulong[][] b, uint bOffset, uint length)
         {
             int nwords_feild = b[0].Length;
     //        if(a[0].Length*2 < length || b[0].Length*2 < length)
@@ -647,7 +647,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             return true;
         }
 
-        protected internal static bool subarrayEquals(ulong[][] a, ulong[] b, uint bOffset, uint length)
+        internal static bool subarrayEquals(ulong[][] a, ulong[] b, uint bOffset, uint length)
         {
             int nwords_field = a[0].Length;
     //        if(a[0].Length < length || b.Length < length)
@@ -662,9 +662,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Computes square roots of elements in (Fp2)^2 using Hamburg's trick.
-        protected internal void sqrt_Fp2(ulong[][] u, ulong[][] y)
+        internal void sqrt_Fp2(ulong[][] u, ulong[][] y)
         {
-
             ulong[] t0 = new ulong[engine.param.NWORDS_FIELD],
                    t1 = new ulong[engine.param.NWORDS_FIELD],
                    t2 = new ulong[engine.param.NWORDS_FIELD],
@@ -714,7 +713,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         // GF(p^2) squaring using Montgomery arithmetic, c = a^2 in GF(p^2).
         // Inputs: a = a0+a1*i, where a0, a1 are in [0, 2*p-1]
         // Output: c = c0+c1*i, where c0, c1 are in [0, 2*p-1]
-        protected internal void fp2sqr_mont(ulong[][] a, ulong[][] c)
+        internal void fp2sqr_mont(ulong[][] a, ulong[][] c)
         {
             ulong[] t1 = new ulong[engine.param.NWORDS_FIELD],
                     t2 = new ulong[engine.param.NWORDS_FIELD],
@@ -731,7 +730,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         // Modular addition, c = a+b mod PRIME.
         // Inputs: a, b in [0, 2*PRIME-1]
         // Output: c in [0, 2*PRIME-1]
-        protected internal void fpaddPRIME(ulong[] a, ulong[] b, ulong[] c)
+        internal void fpaddPRIME(ulong[] a, ulong[] b, ulong[] c)
         {
             ulong i, carry = 0;
             ulong mask;
@@ -769,7 +768,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Cyclotomic cubing on elements of norm 1, using a^(p+1) = 1.
-        protected internal void cube_Fp2_cycl(ulong[][] a, ulong[] one)
+        internal void cube_Fp2_cycl(ulong[][] a, ulong[] one)
         {
             ulong[] t0 = new ulong[engine.param.NWORDS_FIELD];
 
@@ -786,7 +785,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         // Modular subtraction, c = a-b mod PRIME.
         // Inputs: a, b in [0, 2*PRIME-1]
         // Output: c in [0, 2*PRIME-1]
-        protected internal void fpsubPRIME(ulong[] a, ulong[] b, uint bOffset, ulong[] c)
+        internal void fpsubPRIME(ulong[] a, ulong[] b, uint bOffset, ulong[] c)
         {
             ulong i, borrow = 0;
             ulong mask;
@@ -811,7 +810,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             }
         }
 
-        protected internal void fpsubPRIME(ulong[] a, uint aOffset, ulong[] b, ulong[] c)
+        internal void fpsubPRIME(ulong[] a, uint aOffset, ulong[] b, ulong[] c)
         {
             ulong i, borrow = 0;
             ulong mask;
@@ -836,7 +835,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             }
         }
 
-        protected internal void fpsubPRIME(ulong[] a, ulong[] b, ulong[] c)
+        internal void fpsubPRIME(ulong[] a, ulong[] b, ulong[] c)
         {
             ulong i, borrow = 0;
             ulong mask;
@@ -864,7 +863,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         // todo/org : move to fp_generic
         // Modular negation, a = -a mod PRIME.
         // Input/output: a in [0, 2*PRIME-1]
-        protected internal void fpnegPRIME(ulong[] a)
+        internal void fpnegPRIME(ulong[] a)
         {
             ulong i, borrow = 0;
 
@@ -881,7 +880,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         // todo/org : move to fp_generic
         // Conversion of a GF(p^2) element from Montgomery representation to standard representation,
         // c_i = ma_i*R^(-1) = a_i in GF(p^2).
-        protected internal void from_fp2mont(ulong[][] ma, ulong[][] c)
+        internal void from_fp2mont(ulong[][] ma, ulong[][] c)
         {
             from_mont(ma[0], c[0]);
             from_mont(ma[1], c[1]);
@@ -889,7 +888,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // todo/org : move to fp_generic
         // Conversion of GF(p^2) element from Montgomery to standard representation, and encoding by removing leading 0 bytes
-        protected internal void fp2_encode(ulong[][] x, byte[] enc, uint encOffset)
+        internal void fp2_encode(ulong[][] x, byte[] enc, uint encOffset)
         {
             ulong[][] t = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
 
@@ -899,7 +898,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Parse byte sequence back uinto GF(p^2) element, and conversion to Montgomery representation
-        protected internal void fp2_decode(byte[] x, ulong[][] dec, uint xOffset)
+        internal void fp2_decode(byte[] x, ulong[][] dec, uint xOffset)
         {
             decode_to_digits(x, xOffset, dec[0], engine.param.FP2_ENCODED_BYTES / 2, engine.param.NWORDS_FIELD);
             decode_to_digits(x,xOffset + (engine.param.FP2_ENCODED_BYTES/2), dec[1], engine.param.FP2_ENCODED_BYTES / 2, engine.param.NWORDS_FIELD);
@@ -907,7 +906,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Conversion of elements in Z_r to Montgomery representation, where the order r is up to NBITS_ORDER bits.
-        protected internal void to_Montgomery_mod_order(ulong[] a, ulong[] mc, ulong[] order, ulong[] Montgomery_rprime, ulong[] Montgomery_Rprime)
+        internal void to_Montgomery_mod_order(ulong[] a, ulong[] mc, ulong[] order, ulong[] Montgomery_rprime, ulong[] Montgomery_Rprime)
         {
             Montgomery_multiply_mod_order(a, Montgomery_Rprime, mc, order, Montgomery_rprime);
         }
@@ -916,7 +915,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         // ma, mb and mc are assumed to be in Montgomery representation.
         // The Montgomery constant r' = -r^(-1) mod 2^(log_2(r)) is the value "Montgomery_rprime", where r is the order.
         // Assume log_2(r) is a multiple of RADIX bits
-        protected internal void Montgomery_multiply_mod_order(ulong[] ma, ulong[] mb, ulong[] mc, ulong[] order, ulong[] Montgomery_rprime)
+        internal void Montgomery_multiply_mod_order(ulong[] ma, ulong[] mb, ulong[] mc, ulong[] order, ulong[] Montgomery_rprime)
         {
             ulong i, cout = 0, bout = 0;
             ulong mask;
@@ -949,7 +948,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         // Inversion of an odd uinteger modulo an even uinteger of the form 2^m.
         // Algorithm 3: Explicit Quadratic Modular inverse modulo 2^m from Dumas'12: http://arxiv.org/pdf/1209.6626.pdf
         // If the input is invalid (even), the function outputs c = a.
-        protected internal void inv_mod_orderA(ulong[] a, ulong[] c)
+        internal void inv_mod_orderA(ulong[] a, ulong[] c)
         { 
             uint i, f, s = 0;
             ulong[] am1 = new ulong[engine.param.NWORDS_ORDER],
@@ -999,7 +998,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // Multiprecision comba multiply, c = a*b, where lng(a) = lng(b) = nwords.
         // NOTE: a and c CANNOT be the same variable!
-        protected internal void multiply(ulong[] a, ulong[] b, ulong[] c, uint nwords)
+        internal void multiply(ulong[] a, ulong[] b, ulong[] c, uint nwords)
         {
             ulong i, j;
             ulong t = 0, u = 0, v = 0;
@@ -1187,7 +1186,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Montgomery inversion modulo order, c = a^(-1)*R mod order.
-        protected internal void Montgomery_inversion_mod_order_bingcd(ulong[] a, ulong[] c, ulong[] order, ulong[] Montgomery_rprime, ulong[] Montgomery_Rprime)
+        internal void Montgomery_inversion_mod_order_bingcd(ulong[] a, ulong[] c, ulong[] order, ulong[] Montgomery_rprime, ulong[] Montgomery_Rprime)
         {
             ulong[] x = new ulong[engine.param.NWORDS_ORDER],
                    t = new ulong[engine.param.NWORDS_ORDER];
@@ -1211,7 +1210,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Conversion of elements in Z_r from Montgomery to standard representation, where the order is up to NBITS_ORDER bits.
-        protected internal void from_Montgomery_mod_order(ulong[] ma, ulong[] c, ulong[] order, ulong[] Montgomery_rprime)
+        internal void from_Montgomery_mod_order(ulong[] ma, ulong[] c, ulong[] order, ulong[] Montgomery_rprime)
         {
             ulong[] one = new ulong[engine.param.NWORDS_ORDER];
             one[0] = 1;
@@ -1221,7 +1220,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // Computes the input modulo 3
         // The input is assumed to be NWORDS_ORDER ulong
-        protected internal uint mod3(ulong[] a)
+        internal uint mod3(ulong[] a)
         {
             ulong result = 0;
             for (int i = 0; i < engine.param.NWORDS_ORDER; ++i)
@@ -1234,7 +1233,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // Conversion of a GF(p^2) element to Montgomery representation,
         // mc_i = a_i*R^2*R^(-1) = a_i*R in GF(p^2).
-        protected internal void to_fp2mont(ulong[][] a, ulong[][] mc)
+        internal void to_fp2mont(ulong[][] a, ulong[][] mc)
         {
             to_mont(a[0], mc[0]);
             to_mont(a[1], mc[1]);
@@ -1251,7 +1250,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // todo/org : move to fp_generic
         // Modular correction to reduce field element a in [0, 2*PRIME-1] to [0, PRIME-1].
-        protected internal void fpcorrectionPRIME(ulong[] a)
+        internal void fpcorrectionPRIME(ulong[] a)
         {
             ulong i, borrow = 0;
             ulong mask;
@@ -1276,7 +1275,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             }
         }
 
-        protected internal byte cmp_f2elm(ulong[][] x, ulong[][] y)
+        internal byte cmp_f2elm(ulong[][] x, ulong[][] y)
         { // Comparison of two GF(p^2) elements in constant time. 
             // Is x != y? return -1 if condition is true, 0 otherwise.
             ulong[][] a = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -1298,7 +1297,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
 
         // Encoding digits to bytes according to endianness
-        protected internal void encode_to_bytes(ulong[] x, byte[] enc, uint encOffset, uint nbytes)
+        internal void encode_to_bytes(ulong[] x, byte[] enc, uint encOffset, uint nbytes)
         {
             byte[] test = new byte[((nbytes*4+7)&~7)];
             Pack.UInt64_To_LE(x, test, 0);
@@ -1306,7 +1305,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Decoding bytes to digits according to endianness
-        protected internal void decode_to_digits(byte[] x, uint xOffset, ulong[] dec, uint nbytes, uint ndigits)
+        internal void decode_to_digits(byte[] x, uint xOffset, ulong[] dec, uint nbytes, uint ndigits)
         {
             // x -> dec
             dec[ndigits - 1] = 0;
@@ -1317,7 +1316,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // r = a - b*i where v = a + b*i
-        protected internal void fp2_conj(ulong[][] v, ulong[][] r)
+        internal void fp2_conj(ulong[][] v, ulong[][] r)
         {
             fpcopy(v[0], 0, r[0]);
             fpcopy(v[1], 0, r[1]);
@@ -1368,28 +1367,28 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Copy a GF(p^2) element, c = a.
-        protected internal void fp2copy(ulong[][] a, ulong[][] c)
+        internal void fp2copy(ulong[][] a, ulong[][] c)
         {
             fpcopy(a[0], 0, c[0]);
             fpcopy(a[1], 0, c[1]);
         }
 
         // Copy a GF(p^2) element, c = a.
-        protected internal void fp2copy(ulong[][] a, uint aOffset, ulong[][] c)
+        internal void fp2copy(ulong[][] a, uint aOffset, ulong[][] c)
         {
             fpcopy(a[0 + aOffset], 0, c[0]);
             fpcopy(a[1 + aOffset], 0, c[1]);
         }
 
         // Copy a GF(p^2) element, c = a.
-        protected internal void fp2copy(ulong[] a, uint aOffset, ulong[][] c)
+        internal void fp2copy(ulong[] a, uint aOffset, ulong[][] c)
         {
             fpcopy(a, aOffset, c[0]);
             fpcopy(a, aOffset + engine.param.NWORDS_FIELD, c[1]);
         }
 
         // Zero a field element, a = 0.
-        protected internal void fpzero(ulong[] a)
+        internal void fpzero(ulong[] a)
         {
             uint i;
 
@@ -1400,7 +1399,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // GF(p^2) subtraction with correction with 2*p, c = a-b+2p in GF(p^2).
-        protected internal void mp2_sub_p2(ulong[][] a, ulong[][] b, ulong[][] c)
+        internal void mp2_sub_p2(ulong[][] a, ulong[][] b, ulong[][] c)
         {
             //todo/org : make fp class and change this to generic mp_sub_p2
             mp_subPRIME_p2(a[0], b[0], c[0]);
@@ -1408,7 +1407,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Multiprecision comba multiply, c = a*b, where lng(a) = lng(b) = nwords.
-        protected internal void mp_mul(ulong[] a, ulong[] b, ulong[] c, uint nwords)
+        internal void mp_mul(ulong[] a, ulong[] b, ulong[] c, uint nwords)
         {
             ulong i, j;
             ulong t = 0, u = 0, v = 0;
@@ -1461,7 +1460,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Multiprecision comba multiply, c = a*b, where lng(a) = lng(b) = nwords.
-        protected internal void mp_mul(ulong[] a, uint aOffset, ulong[] b, ulong[] c, uint nwords)
+        internal void mp_mul(ulong[] a, uint aOffset, ulong[] b, ulong[] c, uint nwords)
         {
             ulong i, j;
             ulong t = 0, u = 0, v = 0;
@@ -1516,7 +1515,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         // GF(p^2) multiplication using Montgomery arithmetic, c = a*b in GF(p^2).
         // Inputs: a = a0+a1*i and b = b0+b1*i, where a0, a1, b0, b1 are in [0, 2*p-1]
         // Output: c = c0+c1*i, where c0, c1 are in [0, 2*p-1]
-        protected internal void fp2mul_mont(ulong[][] a, ulong[][] b, ulong[][] c)
+        internal void fp2mul_mont(ulong[][] a, ulong[][] b, ulong[][] c)
         {
             ulong[] t1 = new ulong[engine.param.NWORDS_FIELD],
                    t2 = new ulong[engine.param.NWORDS_FIELD];
@@ -1535,7 +1534,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             rdc_mont(tt1, c[0]);                             // c[0] = a0*b0 - a1*b1
         }
 
-        protected internal void fp2mul_mont(ulong[][] a, ulong[][] b, uint bOffset, ulong[][] c)
+        internal void fp2mul_mont(ulong[][] a, ulong[][] b, uint bOffset, ulong[][] c)
         {
 
     //        System.out.pruint("b: ");
@@ -1559,7 +1558,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             rdc_mont(tt1, c[0]);                             // c[0] = a0*b0 - a1*b1
         }
 
-        protected internal void fp2mul_mont(ulong[][] a, ulong[] b, uint bOffset, ulong[][] c)
+        internal void fp2mul_mont(ulong[][] a, ulong[] b, uint bOffset, ulong[][] c)
         {
             ulong[] t1 = new ulong[engine.param.NWORDS_FIELD],
                     t2 = new ulong[engine.param.NWORDS_FIELD];
@@ -1587,7 +1586,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Multiprecision subtraction, c = a-b, where lng(a) = lng(b) = nwords. Returns the borrow bit.
-        protected internal ulong mp_sub(ulong[] a, ulong[] b, ulong[] c, uint nwords)
+        internal ulong mp_sub(ulong[] a, ulong[] b, ulong[] c, uint nwords)
         {
             ulong i, borrow = 0;
         
@@ -1606,7 +1605,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // Is x < y? return 1 (TRUE) if condition is true, 0 (FALSE) otherwise.
         // SECURITY NOTE: This function does not run in constant-time.
-        protected internal bool is_orderelm_lt(ulong[] x, ulong[] y)
+        internal bool is_orderelm_lt(ulong[] x, ulong[] y)
         {
             for (int i = (int)engine.param.NWORDS_ORDER-1; i >= 0; i--)
             {
@@ -1637,7 +1636,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Multiprecision squaring, c = a^2 mod p.
-        protected internal void fpsqr_mont(ulong[] ma, ulong[] mc)
+        internal void fpsqr_mont(ulong[] ma, ulong[] mc)
         {
             ulong[] temp = new ulong[2*engine.param.NWORDS_FIELD];
 
@@ -1658,7 +1657,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // GF(p^2) inversion using Montgomery arithmetic, a = (a0-i*a1)/(a0^2+a1^2).
-        protected internal void fp2inv_mont(ulong[][] a)
+        internal void fp2inv_mont(ulong[][] a)
         {
             ulong[][] t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
 
@@ -1673,7 +1672,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // Computes a = 3*a
         // The input is assumed to be OBOB_BITS-2 bits ulong and stored in SECRETKEY_B_BYTES
-        protected internal void mul3(byte[] a)
+        internal void mul3(byte[] a)
         {
             ulong[] temp1 = new ulong[engine.param.NWORDS_ORDER],
                    temp2 = new ulong[engine.param.NWORDS_ORDER];
@@ -1686,7 +1685,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // Compare two byte arrays in constant time.
         // Returns 0 if the byte arrays are equal, -1 otherwise.
-        protected internal byte ct_compare(byte[] a, byte[] b, uint len)
+        internal byte ct_compare(byte[] a, byte[] b, uint len)
         {
             byte r = 0;
 
@@ -1699,7 +1698,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         // Conditional move in constant time.
         // If selector = -1 then load r with a, else if selector = 0 then keep r.
-        protected internal void ct_cmov(byte[] r, byte[] a, uint len, byte selector)
+        internal void ct_cmov(byte[] r, byte[] a, uint len, byte selector)
         {
             for (uint i = 0; i < len; i++)
             {
@@ -1708,7 +1707,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Copy wordsize digits, c = a, where lng(a) = nwords.
-        protected internal void copy_words(ulong[] a, ulong[] c, uint nwords)
+        internal void copy_words(ulong[] a, ulong[] c, uint nwords)
         {
             uint i;
             for (i = 0; i < nwords; i++)
@@ -1718,7 +1717,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // c = (2^k)*a
-        protected internal void fp2shl(ulong[][] a, uint k, ulong[][] c)
+        internal void fp2shl(ulong[][] a, uint k, ulong[][] c)
         {
             fp2copy(a, c);
             for (uint j = 0; j < k; j++)
@@ -1728,7 +1727,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Copy wordsize digits, c = a, where lng(a) = nwords.
-        protected internal void copy_words(PointProj a, PointProj c)
+        internal void copy_words(PointProj a, PointProj c)
         {
             uint i;
             for (i = 0; i < engine.param.NWORDS_FIELD; i++)
@@ -1759,7 +1758,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
 
         // GF(p^2) addition, c = a+b in GF(p^2).
-        protected internal void fp2add(ulong[][] a, ulong[][] b, ulong[][] c)
+        internal void fp2add(ulong[][] a, ulong[][] b, ulong[][] c)
         {
             //todo/org : make fp class and change this to generic function
             fpaddPRIME(a[0], b[0], c[0]);
@@ -1767,7 +1766,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // GF(p^2) subtraction, c = a-b in GF(p^2).
-        protected internal void fp2sub(ulong[][] a, ulong[][] b, ulong[][] c)
+        internal void fp2sub(ulong[][] a, ulong[][] b, ulong[][] c)
         {
             //todo/org : make fp class and change this to generic function
             fpsubPRIME(a[0], b[0], c[0]);
@@ -1783,7 +1782,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Multiprecision multiplication, c = a*b mod p.
-        protected internal void fpmul_mont(ulong[] ma, ulong[] mb, ulong[] mc)
+        internal void fpmul_mont(ulong[] ma, ulong[] mb, ulong[] mc)
         {
             ulong[] temp = new ulong[2*engine.param.NWORDS_FIELD];
 
@@ -1792,7 +1791,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
         }
 
         // Multiprecision multiplication, c = a*b mod p.
-        protected internal void fpmul_mont(ulong[] ma, uint maOffset, ulong[] mb, ulong[] mc)
+        internal void fpmul_mont(ulong[] ma, uint maOffset, ulong[] mb, ulong[] mc)
         {
             ulong[] temp = new ulong[2*engine.param.NWORDS_FIELD];
 
@@ -2233,5 +2232,4 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             }
         }
     }
-
-    }
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/sike/Isogeny.cs b/crypto/src/pqc/crypto/sike/Isogeny.cs
index 2d0ef1473..be9e3ba4d 100644
--- a/crypto/src/pqc/crypto/sike/Isogeny.cs
+++ b/crypto/src/pqc/crypto/sike/Isogeny.cs
@@ -12,7 +12,7 @@ internal sealed class Isogeny
     // Doubling of a Montgomery point in projective coordinates (X:Z) over affine curve coefficient A. 
     // Input: projective Montgomery x-coordinates P = (X1:Z1), where x1=X1/Z1 and Montgomery curve constants (A+2)/4.
     // Output: projective Montgomery x-coordinates Q = 2*P = (X2:Z2). 
-    protected internal void Double(PointProj P, PointProj Q, ulong[][] A24, uint k)
+    internal void Double(PointProj P, PointProj Q, ulong[][] A24, uint k)
     {
         ulong[][] temp = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             a = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -37,7 +37,7 @@ internal sealed class Isogeny
         }
     }
 
-    protected internal void CompleteMPoint(ulong[][] A, PointProj P, PointProjFull R)
+    internal void CompleteMPoint(ulong[][] A, PointProj P, PointProjFull R)
     { // Given an xz-only representation on a montgomery curve, compute its affine representation
         ulong[][] zero = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             one = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -81,10 +81,9 @@ internal sealed class Isogeny
 
     internal void Ladder(PointProj P, ulong[] m, ulong[][] A, uint order_bits, PointProj R)
     {
-        PointProj R0 = new PointProj(engine.param.NWORDS_FIELD),
-                  R1 = new PointProj(engine.param.NWORDS_FIELD);
+        var R0 = new PointProj(engine.param.NWORDS_FIELD);
+        var R1 = new PointProj(engine.param.NWORDS_FIELD);
         ulong[][] A24 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
-        uint bit = 0;
         ulong mask;
         int j, swap, prevbit = 0;
         
@@ -96,7 +95,7 @@ internal sealed class Isogeny
         engine.fpx.fp2div2(A24, A24);  // A24 = (A+2)/4          
 
         j = (int)order_bits - 1;
-        bit = (uint) ((m[j >> (int)Internal.LOG2RADIX] >> (int)(j & (Internal.RADIX-1))) & 1);
+        uint bit = (uint) ((m[j >> (int)Internal.LOG2RADIX] >> (int)(j & (Internal.RADIX-1))) & 1);
         while (bit == 0)
         {
             j--;
@@ -106,7 +105,7 @@ internal sealed class Isogeny
         // R0 <- P, R1 <- 2P
         engine.fpx.fp2copy(P.X, R0.X);
         engine.fpx.fp2copy(P.Z, R0.Z);
-        xDBL_e(P, R1, A24, 1);
+        XDblE(P, R1, A24, 1);
 
         // Main loop
         for (int i = (int)j - 1;  i >= 0; i--) 
@@ -116,12 +115,12 @@ internal sealed class Isogeny
             prevbit = (int) bit;
             mask = (ulong) (0 - swap);
 
-            swap_points(R0, R1, mask);
-            xDBLADD_proj(R0, R1, P.X, P.Z, A24);
+            SwapPoints(R0, R1, mask);
+            XDblAddProj(R0, R1, P.X, P.Z, A24);
         }
         swap = 0 ^ prevbit;
         mask = (ulong) (0 - swap);
-        swap_points(R0, R1, mask);
+        SwapPoints(R0, R1, mask);
 
         engine.fpx.fp2copy(R0.X, R.X);
         engine.fpx.fp2copy(R0.Z, R.Z);
@@ -130,7 +129,7 @@ internal sealed class Isogeny
     // Simultaneous doubling and differential addition.
     // Input: projective Montgomery points P=(XP:ZP) and Q=(XQ:ZQ) such that xP=XP/ZP and xQ=XQ/ZQ, affine difference xPQ=x(P-Q) and Montgomery curve constant A24=(A+2)/4.
     // Output: projective Montgomery points P <- 2*P = (X2P:Z2P) such that x(2P)=X2P/Z2P, and Q <- P+Q = (XQP:ZQP) such that = x(Q+P)=XQP/ZQP.
-    private void xDBLADD_proj(PointProj P, PointProj Q, ulong[][] XPQ, ulong[][] ZPQ, ulong[][] A24)
+    private void XDblAddProj(PointProj P, PointProj Q, ulong[][] XPQ, ulong[][] ZPQ, ulong[][] A24)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -162,7 +161,7 @@ internal sealed class Isogeny
     // Doubling of a Montgomery point in projective coordinates (X:Z) over affine curve coefficient A.
     // Input: projective Montgomery x-coordinates P = (X1:Z1), where x1=X1/Z1 and Montgomery curve constants (A+2)/4.
     // Output: projective Montgomery x-coordinates Q = 2*P = (X2:Z2).
-    private void xDBL_e(PointProj P, PointProj Q, ulong[][] A24, int e)
+    private void XDblE(PointProj P, PointProj Q, ulong[][] A24, int e)
     {
         ulong[][] temp = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             a = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -192,14 +191,14 @@ internal sealed class Isogeny
     // Computes [3^e](X:Z) on Montgomery curve with projective constant via e repeated triplings. e triplings in E costs k*(5M + 6S + 9A)
     // Input: projective Montgomery x-coordinates P = (X:Z), where x=X/Z, Montgomery curve constant A2 = A/2 and the number of triplings e.
     // Output: projective Montgomery x-coordinates Q <- [3^e]P.
-    internal void xTPLe_fast(PointProj P, PointProj Q, ulong[][] A2, uint e)
+    internal void XTplEFast(PointProj P, PointProj Q, ulong[][] A2, uint e)
     {
-        PointProj T = new PointProj(engine.param.NWORDS_FIELD);
+        var T = new PointProj(engine.param.NWORDS_FIELD);
 
         engine.fpx.copy_words(P, T);
         for (int j = 0; j < e; j++)
         {
-            xTPL_fast(T, T, A2);
+            XTplFast(T, T, A2);
         }
         engine.fpx.copy_words(T, Q);
     }
@@ -207,7 +206,7 @@ internal sealed class Isogeny
     // Montgomery curve (E: y^2 = x^3 + A*x^2 + x) x-only tripling at a cost 5M + 6S + 9A = 27p + 61a.
     // Input : projective Montgomery x-coordinates P = (X:Z), where x=X/Z and Montgomery curve constant A/2.
     // Output: projective Montgomery x-coordinates Q = 3*P = (X3:Z3).
-    private void xTPL_fast(PointProj P, PointProj Q, ulong[][] A2)
+    private void XTplFast(PointProj P, PointProj Q, ulong[][] A2)
     {
         ulong[][] t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t2 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -238,10 +237,10 @@ internal sealed class Isogeny
     }
 
 
-    protected internal void LADDER3PT(ulong[][] xP, ulong[][] xQ, ulong[][] xPQ, ulong[] m, uint AliceOrBob, PointProj R, ulong[][] A)
+    internal void LADDER3PT(ulong[][] xP, ulong[][] xQ, ulong[][] xPQ, ulong[] m, uint AliceOrBob, PointProj R, ulong[][] A)
     {
-        PointProj R0 = new PointProj(engine.param.NWORDS_FIELD),
-                  R2 = new PointProj(engine.param.NWORDS_FIELD);
+        var R0 = new PointProj(engine.param.NWORDS_FIELD);
+        var R2 = new PointProj(engine.param.NWORDS_FIELD);
         ulong[][] A24 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
         ulong mask;
         uint i, nbits, bit, swap, prevbit = 0;
@@ -277,17 +276,17 @@ internal sealed class Isogeny
             swap = bit ^ prevbit;
             prevbit = bit;
             mask = 0 - (ulong) swap;
-            swap_points(R, R2, mask);
-            xDBLADD(R0, R2, R.X, A24);
+            SwapPoints(R, R2, mask);
+            XDblAdd(R0, R2, R.X, A24);
             engine.fpx.fp2mul_mont(R2.X, R.Z, R2.X);
         }
         swap = 0 ^ prevbit;
         mask = 0 - (ulong)swap;
-        swap_points(R, R2, mask);
+        SwapPoints(R, R2, mask);
     }
 
     // Complete point on A = 0 curve
-    protected internal void CompletePoint(PointProj P, PointProjFull R)
+    internal void CompletePoint(PointProj P, PointProjFull R)
     {
         ulong[][] xz = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             s2 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -318,13 +317,11 @@ internal sealed class Isogeny
 
     // Swap points.
     // If option = 0 then P <- P and Q <- Q, else if option = 0xFF...FF then P <- Q and Q <- P
-    protected internal void swap_points(PointProj P, PointProj Q, ulong option)
+    internal void SwapPoints(PointProj P, PointProj Q, ulong option)
     {
         //todo/org : put this in the PointProj class
         ulong temp;
-        int i;
-
-        for (i = 0; i < engine.param.NWORDS_FIELD; i++)
+        for (int i = 0; i < engine.param.NWORDS_FIELD; i++)
         {
             temp = option & (P.X[0][i] ^ Q.X[0][i]);
             P.X[0][i] = temp ^ P.X[0][i];
@@ -344,7 +341,7 @@ internal sealed class Isogeny
     // Simultaneous doubling and differential addition.
     // Input: projective Montgomery points P=(XP:ZP) and Q=(XQ:ZQ) such that xP=XP/ZP and xQ=XQ/ZQ, affine difference xPQ=x(P-Q) and Montgomery curve constant A24=(A+2)/4.
     // Output: projective Montgomery points P <- 2*P = (X2P:Z2P) such that x(2P)=X2P/Z2P, and Q <- P+Q = (XQP:ZQP) such that = x(Q+P)=XQP/ZQP.
-    protected internal void xDBLADD(PointProj P, PointProj Q, ulong[][] xPQ, ulong[][] A24)
+    internal void XDblAdd(PointProj P, PointProj Q, ulong[][] xPQ, ulong[][] A24)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -373,21 +370,21 @@ internal sealed class Isogeny
     // Computes [2^e](X:Z) on Montgomery curve with projective constant via e repeated doublings.
     // Input: projective Montgomery x-coordinates P = (XP:ZP), such that xP=XP/ZP and Montgomery curve constants A+2C and 4C.
     // Output: projective Montgomery x-coordinates Q <- (2^e)*P.
-    protected internal void xDBLe(PointProj P, PointProj Q, ulong[][] A24plus, ulong[][] C24, uint e)
+    internal void XDblE(PointProj P, PointProj Q, ulong[][] A24plus, ulong[][] C24, uint e)
     {
         int i;
         engine.fpx.copy_words(P, Q);
 
         for (i = 0; i < e; i++)
         {
-            xDBL(Q, Q, A24plus, C24);
+            XDbl(Q, Q, A24plus, C24);
         }
     }
 
     // Doubling of a Montgomery point in projective coordinates (X:Z).
     // Input: projective Montgomery x-coordinates P = (X1:Z1), where x1=X1/Z1 and Montgomery curve constants A+2C and 4C.
     // Output: projective Montgomery x-coordinates Q = 2*P = (X2:Z2).
-    protected void xDBL(PointProj P, PointProj Q, ulong[][] A24plus, ulong[][] C24)
+    internal void XDbl(PointProj P, PointProj Q, ulong[][] A24plus, ulong[][] C24)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
@@ -407,7 +404,7 @@ internal sealed class Isogeny
     // Tripling of a Montgomery point in projective coordinates (X:Z).
     // Input: projective Montgomery x-coordinates P = (X:Z), where x=X/Z and Montgomery curve constants A24plus = A+2C and A24minus = A-2C.
     // Output: projective Montgomery x-coordinates Q = 3*P = (X3:Z3).
-    private void xTPL(PointProj P, PointProj Q, ulong[][] A24minus, ulong[][] A24plus)
+    private void XTpl(PointProj P, PointProj Q, ulong[][] A24minus, ulong[][] A24plus)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -444,20 +441,20 @@ internal sealed class Isogeny
     // Computes [3^e](X:Z) on Montgomery curve with projective constant via e repeated triplings.
     // Input: projective Montgomery x-coordinates P = (XP:ZP), such that xP=XP/ZP and Montgomery curve constants A24plus = A+2C and A24minus = A-2C.
     // Output: projective Montgomery x-coordinates Q <- (3^e)*P.
-    protected internal void xTPLe(PointProj P, PointProj Q, ulong[][] A24minus, ulong[][] A24plus, uint e)
+    internal void XTplE(PointProj P, PointProj Q, ulong[][] A24minus, ulong[][] A24plus, uint e)
     {
         int i;
         engine.fpx.copy_words(P, Q);
         for (i = 0; i < e; i++)
         {
-            xTPL(Q, Q, A24minus, A24plus);
+            XTpl(Q, Q, A24minus, A24plus);
         }
     }
 
     // Given the x-coordinates of P, Q, and R, returns the value A corresponding to the Montgomery curve E_A: y^2=x^3+A*x^2+x such that R=Q-P on E_A.
     // Input:  the x-coordinates xP, xQ, and xR of the points P, Q and R.
     // Output: the coefficient A corresponding to the curve E_A: y^2=x^3+A*x^2+x.
-    protected internal void get_A(ulong[][] xP, ulong[][] xQ, ulong[][] xR, ulong[][] A)
+    internal void GetA(ulong[][] xP, ulong[][] xQ, ulong[][] xR, ulong[][] A)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -482,7 +479,7 @@ internal sealed class Isogeny
     // Computes the j-invariant of a Montgomery curve with projective constant.
     // Input: A,C in GF(p^2).
     // Output: j=256*(A^2-3*C^2)^3/(C^4*(A^2-4*C^2)), which is the j-invariant of the Montgomery curve B*y^2=x^3+(A/C)*x^2+x or (equivalently) j-invariant of B'*y^2=C*x^3+A*x^2+C*x.
-    protected internal void j_inv(ulong[][] A, ulong[][] C, ulong[][] jinv)
+    internal void JInv(ulong[][] A, ulong[][] C, ulong[][] jinv)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
@@ -510,7 +507,7 @@ internal sealed class Isogeny
     // Computes the corresponding 3-isogeny of a projective Montgomery point (X3:Z3) of order 3.
     // Input:  projective point of order three P = (X3:Z3).
     // Output: the 3-isogenous Montgomery curve with projective coefficient A/C.
-    protected internal void get_3_isog(PointProj P, ulong[][] A24minus, ulong[][] A24plus, ulong[][][] coeff)
+    internal void Get3Isog(PointProj P, ulong[][] A24minus, ulong[][] A24plus, ulong[][][] coeff)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -540,7 +537,7 @@ internal sealed class Isogeny
     // a point P with 2 coefficients in coeff (computed in the function get_3_Isog()).
     // Inputs: projective points P = (X3:Z3) and Q = (X:Z).
     // Output: the projective point Q <- phi(Q) = (X3:Z3).
-    protected internal void eval_3_isog(PointProj Q, ulong[][][] coeff)
+    internal void Eval3Isog(PointProj Q, ulong[][][] coeff)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -561,7 +558,7 @@ internal sealed class Isogeny
     // 3-way simultaneous inversion
     // Input:  z1,z2,z3
     // Output: 1/z1,1/z2,1/z3 (override inputs).
-    protected internal void inv_3_way(ulong[][] z1, ulong[][] z2, ulong[][] z3)
+    internal void Inv3Way(ulong[][] z1, ulong[][] z2, ulong[][] z3)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -581,7 +578,7 @@ internal sealed class Isogeny
     // Computes the corresponding 2-isogeny of a projective Montgomery point (X2:Z2) of order 2.
     // Input:  projective point of order two P = (X2:Z2).
     // Output: the 2-isogenous Montgomery curve with projective coefficients A/C.
-    protected internal void get_2_isog(PointProj P, ulong[][] A, ulong[][] C)
+    internal void Get2Isog(PointProj P, ulong[][] A, ulong[][] C)
     {
         engine.fpx.fp2sqr_mont(P.X, A);                    // A = X2^2
         engine.fpx.fp2sqr_mont(P.Z, C);                    // C = Z2^2
@@ -591,7 +588,7 @@ internal sealed class Isogeny
     // Evaluates the isogeny at the point (X:Z) in the domain of the isogeny, given a 2-isogeny phi.
     // Inputs: the projective point P = (X:Z) and the 2-isogeny kernel projetive point Q = (X2:Z2).
     // Output: the projective point P = phi(P) = (X:Z) in the codomain. 
-    protected internal void eval_2_isog(PointProj P, PointProj Q)
+    internal void Eval2Isog(PointProj P, PointProj Q)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -614,7 +611,7 @@ internal sealed class Isogeny
     // Input:  projective point of order four P = (X4:Z4).
     // Output: the 4-isogenous Montgomery curve with projective coefficients A+2C/4C and the 3 coefficients
     //         that are used to evaluate the isogeny at a point in eval_4_Isog().
-    protected internal void get_4_isog(PointProj P, ulong[][] A24plus, ulong[][] C24, ulong[][][] coeff)
+    internal void Get4Isog(PointProj P, ulong[][] A24plus, ulong[][] C24, ulong[][][] coeff)
     {
         engine.fpx.mp2_sub_p2(P.X, P.Z, coeff[1]);         // coeff[1] = X4-Z4
         engine.fpx.mp2_add(P.X, P.Z, coeff[2]);            // coeff[2] = X4+Z4
@@ -631,7 +628,7 @@ internal sealed class Isogeny
     // by the 3 coefficients in coeff (computed in the function get_4_Isog()).
     // Inputs: the coefficients defining the isogeny, and the projective point P = (X:Z).
     // Output: the projective point P = phi(P) = (X:Z) in the codomain.
-    protected internal void eval_4_isog(PointProj P, ulong[][][] coeff)
+    internal void Eval4Isog(PointProj P, ulong[][][] coeff)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
@@ -651,9 +648,5 @@ internal sealed class Isogeny
         engine.fpx.fp2mul_mont(P.X, t1, P.X);              // Xfinal
         engine.fpx.fp2mul_mont(P.Z, t0, P.Z);              // Zfinal
     }
-    
-    
-
 }
-
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/sike/SIDH.cs b/crypto/src/pqc/crypto/sike/SIDH.cs
index c1d1714f6..8246291c6 100644
--- a/crypto/src/pqc/crypto/sike/SIDH.cs
+++ b/crypto/src/pqc/crypto/sike/SIDH.cs
@@ -4,13 +4,13 @@ internal sealed class Sidh
 {
     private readonly SikeEngine engine;
 
-    public Sidh(SikeEngine engine)
+    internal Sidh(SikeEngine engine)
     {
         this.engine = engine;
     }
 
     // Initialization of basis points
-    protected void init_basis(ulong[] gen, ulong[][] XP, ulong[][] XQ, ulong[][] XR)
+    internal void init_basis(ulong[] gen, ulong[][] XP, ulong[][] XQ, ulong[][] XR)
     {
        engine.fpx.fpcopy(gen, 0, XP[0]);
        engine.fpx.fpcopy(gen, engine.param.NWORDS_FIELD, XP[1]);
@@ -23,7 +23,7 @@ internal sealed class Sidh
     // Bob's ephemeral public key generation
     // Input:  a private key PrivateKeyB in the range [0, 2^Floor(Log(2,oB)) - 1].
     // Output: the public key PublicKeyB consisting of 3 elements in GF(p^2) which are encoded by removing leading 0 bytes.
-    protected internal void EphemeralKeyGeneration_B(byte[] sk, byte[] pk)
+    internal void EphemeralKeyGeneration_B(byte[] sk, byte[] pk)
     {
         PointProj R = new PointProj(engine.param.NWORDS_FIELD),
                 phiP = new PointProj(engine.param.NWORDS_FIELD),
@@ -71,29 +71,29 @@ internal sealed class Sidh
                 engine.fpx.fp2copy(R.Z, pts[npts].Z);
                 pts_index[npts++] = index;
                 m = engine.param.strat_Bob[ii++];
-                engine.isogeny.xTPLe(R, R, A24minus, A24plus, m);
+                engine.isogeny.XTplE(R, R, A24minus, A24plus, m);
                 index += m;
             }
-            engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
+            engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
 
             for (i = 0; i < npts; i++)
             {
-                engine.isogeny.eval_3_isog(pts[i], coeff);
+                engine.isogeny.Eval3Isog(pts[i], coeff);
             }
-            engine.isogeny.eval_3_isog(phiP, coeff);
-            engine.isogeny.eval_3_isog(phiQ, coeff);
-            engine.isogeny.eval_3_isog(phiR, coeff);
+            engine.isogeny.Eval3Isog(phiP, coeff);
+            engine.isogeny.Eval3Isog(phiQ, coeff);
+            engine.isogeny.Eval3Isog(phiR, coeff);
 
             engine.fpx.fp2copy(pts[npts - 1].X, R.X);
             engine.fpx.fp2copy(pts[npts - 1].Z, R.Z);
             index = pts_index[npts - 1];
             npts -= 1;
         }
-        engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
-        engine.isogeny.eval_3_isog(phiP, coeff);
-        engine.isogeny.eval_3_isog(phiQ, coeff);
-        engine.isogeny.eval_3_isog(phiR, coeff);
-        engine.isogeny.inv_3_way(phiP.Z, phiQ.Z, phiR.Z);
+        engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
+        engine.isogeny.Eval3Isog(phiP, coeff);
+        engine.isogeny.Eval3Isog(phiQ, coeff);
+        engine.isogeny.Eval3Isog(phiR, coeff);
+        engine.isogeny.Inv3Way(phiP.Z, phiQ.Z, phiR.Z);
 
         engine.fpx.fp2mul_mont(phiP.X, phiP.Z, phiP.X);
         engine.fpx.fp2mul_mont(phiQ.X, phiQ.Z, phiQ.X);
@@ -108,7 +108,7 @@ internal sealed class Sidh
     // Alice's ephemeral public key generation
     // Input:  a private key PrivateKeyA in the range [0, 2^eA - 1].
     // Output: the public key PublicKeyA consisting of 3 elements in GF(p^2) which are encoded by removing leading 0 bytes.
-    protected internal void EphemeralKeyGeneration_A(byte[] ephemeralsk, byte[] ct)
+    internal void EphemeralKeyGeneration_A(byte[] ephemeralsk, byte[] ct)
     {
         PointProj R = new PointProj(engine.param.NWORDS_FIELD),
                 phiP = new PointProj(engine.param.NWORDS_FIELD),
@@ -149,12 +149,12 @@ internal sealed class Sidh
         if (engine.param.OALICE_BITS % 2 == 1)
         {
             PointProj S = new PointProj(engine.param.NWORDS_FIELD);
-            engine.isogeny.xDBLe(R, S, A24plus, C24, (engine.param.OALICE_BITS - 1));
-            engine.isogeny.get_2_isog(S, A24plus, C24);
-            engine.isogeny.eval_2_isog(phiP, S);
-            engine.isogeny.eval_2_isog(phiQ, S);
-            engine.isogeny.eval_2_isog(phiR, S);
-            engine.isogeny.eval_2_isog(R, S);
+            engine.isogeny.XDblE(R, S, A24plus, C24, (engine.param.OALICE_BITS - 1));
+            engine.isogeny.Get2Isog(S, A24plus, C24);
+            engine.isogeny.Eval2Isog(phiP, S);
+            engine.isogeny.Eval2Isog(phiQ, S);
+            engine.isogeny.Eval2Isog(phiR, S);
+            engine.isogeny.Eval2Isog(R, S);
         }
 
         // Traverse tree
@@ -168,18 +168,18 @@ internal sealed class Sidh
                 engine.fpx.fp2copy(R.Z, pts[npts].Z);
                 pts_index[npts++] = index;
                 m = engine.param.strat_Alice[ii++];
-                engine.isogeny.xDBLe(R, R, A24plus, C24, 2*m);
+                engine.isogeny.XDblE(R, R, A24plus, C24, 2*m);
                 index += m;
             }
-            engine.isogeny.get_4_isog(R, A24plus, C24, coeff);
+            engine.isogeny.Get4Isog(R, A24plus, C24, coeff);
 
             for (i = 0; i < npts; i++)
             {
-                engine.isogeny.eval_4_isog(pts[i], coeff);
+                engine.isogeny.Eval4Isog(pts[i], coeff);
             }
-            engine.isogeny.eval_4_isog(phiP, coeff);
-            engine.isogeny.eval_4_isog(phiQ, coeff);
-            engine.isogeny.eval_4_isog(phiR, coeff);
+            engine.isogeny.Eval4Isog(phiP, coeff);
+            engine.isogeny.Eval4Isog(phiQ, coeff);
+            engine.isogeny.Eval4Isog(phiR, coeff);
 
             engine.fpx.fp2copy(pts[npts-1].X, R.X);
             engine.fpx.fp2copy(pts[npts-1].Z, R.Z);
@@ -187,12 +187,12 @@ internal sealed class Sidh
             npts -= 1;
         }
 
-        engine.isogeny.get_4_isog(R, A24plus, C24, coeff);
-        engine.isogeny.eval_4_isog(phiP, coeff);
-        engine.isogeny.eval_4_isog(phiQ, coeff);
-        engine.isogeny.eval_4_isog(phiR, coeff);
+        engine.isogeny.Get4Isog(R, A24plus, C24, coeff);
+        engine.isogeny.Eval4Isog(phiP, coeff);
+        engine.isogeny.Eval4Isog(phiQ, coeff);
+        engine.isogeny.Eval4Isog(phiR, coeff);
 
-        engine.isogeny.inv_3_way(phiP.Z, phiQ.Z, phiR.Z);
+        engine.isogeny.Inv3Way(phiP.Z, phiQ.Z, phiR.Z);
         engine.fpx.fp2mul_mont(phiP.X, phiP.Z, phiP.X);
         engine.fpx.fp2mul_mont(phiQ.X, phiQ.Z, phiQ.X);
         engine.fpx.fp2mul_mont(phiR.X, phiR.Z, phiR.X);
@@ -209,7 +209,7 @@ internal sealed class Sidh
     // Inputs: Alice's PrivateKeyA is an integer in the range [0, oA-1].
     //         Bob's PublicKeyB consists of 3 elements in GF(p^2) encoded by removing leading 0 bytes.
     // Output: a shared secret SharedSecretA that consists of one element in GF(p^2) encoded by removing leading 0 bytes.
-    protected internal void EphemeralSecretAgreement_A(byte[] ephemeralsk, byte[] pk, byte[] jinvariant)
+    internal void EphemeralSecretAgreement_A(byte[] ephemeralsk, byte[] pk, byte[] jinvariant)
     {
         PointProj R = new PointProj(engine.param.NWORDS_FIELD);
         PointProj[] pts = new PointProj[engine.param.MAX_INT_POINTS_ALICE];
@@ -230,7 +230,7 @@ internal sealed class Sidh
         engine.fpx.fp2_decode(pk, PKB[2], 2*engine.param.FP2_ENCODED_BYTES);
 
         // Initialize constants: A24plus = A+2C, C24 = 4C, where C=1
-        engine.isogeny.get_A(PKB[0], PKB[1], PKB[2], A);
+        engine.isogeny.GetA(PKB[0], PKB[1], PKB[2], A);
         engine.fpx.mp_add(engine.param.Montgomery_one, engine.param.Montgomery_one, C24[0], engine.param.NWORDS_FIELD);
         engine.fpx.mp2_add(A, C24, A24plus);
         engine.fpx.mp_add(C24[0], C24[0], C24[0], engine.param.NWORDS_FIELD);
@@ -243,9 +243,9 @@ internal sealed class Sidh
         {
             PointProj S = new PointProj(engine.param.NWORDS_FIELD);
 
-            engine.isogeny.xDBLe(R, S, A24plus, C24, engine.param.OALICE_BITS - 1);
-            engine.isogeny.get_2_isog(S, A24plus, C24);
-            engine.isogeny.eval_2_isog(R, S);
+            engine.isogeny.XDblE(R, S, A24plus, C24, engine.param.OALICE_BITS - 1);
+            engine.isogeny.Get2Isog(S, A24plus, C24);
+            engine.isogeny.Eval2Isog(R, S);
         }
 
         // Traverse tree
@@ -259,14 +259,14 @@ internal sealed class Sidh
                 engine.fpx.fp2copy(R.Z, pts[npts].Z);
                 pts_index[npts++] = index;
                 m = engine.param.strat_Alice[ii++];
-                engine.isogeny.xDBLe(R, R, A24plus, C24, 2*m);
+                engine.isogeny.XDblE(R, R, A24plus, C24, 2*m);
                 index += m;
             }
-            engine.isogeny.get_4_isog(R, A24plus, C24, coeff);
+            engine.isogeny.Get4Isog(R, A24plus, C24, coeff);
 
             for (i = 0; i < npts; i++)
             {
-                engine.isogeny.eval_4_isog(pts[i], coeff);
+                engine.isogeny.Eval4Isog(pts[i], coeff);
             }
 
             engine.fpx.fp2copy(pts[npts-1].X, R.X);
@@ -275,11 +275,11 @@ internal sealed class Sidh
             npts -= 1;
         }
 
-        engine.isogeny.get_4_isog(R, A24plus, C24, coeff);
+        engine.isogeny.Get4Isog(R, A24plus, C24, coeff);
         engine.fpx.mp2_add(A24plus, A24plus, A24plus);
         engine.fpx.fp2sub(A24plus, C24, A24plus);
         engine.fpx.fp2add(A24plus, A24plus, A24plus);
-        engine.isogeny.j_inv(A24plus, C24, jinv);
+        engine.isogeny.JInv(A24plus, C24, jinv);
         engine.fpx.fp2_encode(jinv, jinvariant, 0);    // Format shared secret
     }
 
@@ -288,7 +288,7 @@ internal sealed class Sidh
     // Inputs: Bob's PrivateKeyB is an integer in the range [0, 2^Floor(Log(2,oB)) - 1].
     //         Alice's PublicKeyA consists of 3 elements in GF(p^2) encoded by removing leading 0 bytes.
     // Output: a shared secret SharedSecretB that consists of one element in GF(p^2) encoded by removing leading 0 bytes.
-    protected internal void EphemeralSecretAgreement_B(byte[] sk, byte[] ct, byte[] jinvariant_)
+    internal void EphemeralSecretAgreement_B(byte[] sk, byte[] ct, byte[] jinvariant_)
     {
         PointProj R = new PointProj(engine.param.NWORDS_FIELD);
         PointProj[] pts = new PointProj[engine.param.MAX_INT_POINTS_BOB];
@@ -309,7 +309,7 @@ internal sealed class Sidh
         engine.fpx.fp2_decode(ct, PKB[2], 2*engine.param.FP2_ENCODED_BYTES);
 
         // Initialize constants: A24plus = A+2C, A24minus = A-2C, where C=1
-        engine.isogeny.get_A(PKB[0], PKB[1], PKB[2], A);
+        engine.isogeny.GetA(PKB[0], PKB[1], PKB[2], A);
         engine.fpx.mp_add(engine.param.Montgomery_one, engine.param.Montgomery_one, A24minus[0], engine.param.NWORDS_FIELD);
         engine.fpx.mp2_add(A, A24minus, A24plus);
         engine.fpx.mp2_sub_p2(A, A24minus, A24minus);
@@ -329,13 +329,13 @@ internal sealed class Sidh
                 engine.fpx.fp2copy(R.Z, pts[npts].Z);
                 pts_index[npts++] = index;
                 m = engine.param.strat_Bob[ii++];
-                engine.isogeny.xTPLe(R, R, A24minus, A24plus, m);
+                engine.isogeny.XTplE(R, R, A24minus, A24plus, m);
                 index += m;
             }
-            engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
+            engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
 
             for (i = 0; i < npts; i++) {
-                engine.isogeny.eval_3_isog(pts[i], coeff);
+                engine.isogeny.Eval3Isog(pts[i], coeff);
             }
 
             engine.fpx.fp2copy(pts[npts-1].X, R.X);
@@ -344,14 +344,12 @@ internal sealed class Sidh
             npts -= 1;
         }
 
-        engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
+        engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
         engine.fpx.fp2add(A24plus, A24minus, A);
         engine.fpx.fp2add(A, A, A);
         engine.fpx.fp2sub(A24plus, A24minus, A24plus);
-        engine.isogeny.j_inv(A, A24plus, jinv);
+        engine.isogeny.JInv(A, A24plus, jinv);
         engine.fpx.fp2_encode(jinv, jinvariant_, 0);    // Format shared secret
     }
-
 }
-
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/sike/SIDH_Compressed.cs b/crypto/src/pqc/crypto/sike/SIDH_Compressed.cs
index ca140aa50..cf69d0f82 100644
--- a/crypto/src/pqc/crypto/sike/SIDH_Compressed.cs
+++ b/crypto/src/pqc/crypto/sike/SIDH_Compressed.cs
@@ -9,12 +9,12 @@ internal sealed class SidhCompressed
 {
     private readonly SikeEngine engine;
 
-    public SidhCompressed(SikeEngine engine)
+    internal SidhCompressed(SikeEngine engine)
     {
         this.engine = engine;
     }
 
-    protected void init_basis(ulong[] gen, ulong[][] XP, ulong[][] XQ, ulong[][] XR)
+    internal void init_basis(ulong[] gen, ulong[][] XP, ulong[][] XQ, ulong[][] XR)
     { // Initialization of basis points
 
         engine.fpx.fpcopy(gen, 0, XP[0]);
@@ -25,8 +25,7 @@ internal sealed class SidhCompressed
         engine.fpx.fpcopy(gen, 5*engine.param.NWORDS_FIELD, XR[1]);
     }
 
-
-    protected internal void FormatPrivKey_B(byte[] skB)
+    internal void FormatPrivKey_B(byte[] skB)
     {
         skB[engine.param.SECRETKEY_B_BYTES-2] &= (byte)engine.param.MASK3_BOB;
         skB[engine.param.SECRETKEY_B_BYTES-1] &= (byte)engine.param.MASK2_BOB;    // Clear necessary bits so that 3*ephemeralsk is still less than Bob_order
@@ -35,7 +34,7 @@ internal sealed class SidhCompressed
 
     // Generation of Alice's secret key
     // Outputs random value in [0, 2^eA - 1]
-    protected void random_mod_order_A(byte[] random_digits, SecureRandom random)
+    internal void random_mod_order_A(byte[] random_digits, SecureRandom random)
     {
         byte[] temp = new byte[engine.param.SECRETKEY_A_BYTES];
         random.NextBytes(temp);
@@ -46,7 +45,7 @@ internal sealed class SidhCompressed
 
     // Generation of Bob's secret key
     // Outputs random value in [0, 2^Floor(Log(2, oB)) - 1]
-    protected void random_mod_order_B(byte[] random_digits, SecureRandom random)
+    internal void random_mod_order_B(byte[] random_digits, SecureRandom random)
     {
         byte[] temp = new byte[engine.param.SECRETKEY_B_BYTES];
         random.NextBytes(temp);
@@ -55,7 +54,7 @@ internal sealed class SidhCompressed
     }
 
     // Project 3-pouint ladder
-    protected void Ladder3pt_dual(PointProj[] Rs, ulong[] m, uint AliceOrBob, PointProj R, ulong[][] A24)
+    internal void Ladder3pt_dual(PointProj[] Rs, ulong[] m, uint AliceOrBob, PointProj R, ulong[][] A24)
     {
         PointProj R0 = new PointProj(engine.param.NWORDS_FIELD),
                   R2 = new PointProj(engine.param.NWORDS_FIELD);
@@ -85,18 +84,16 @@ internal sealed class SidhCompressed
             prevbit = bit;
             mask = 0 - (ulong)swap;
 
-            engine.isogeny.swap_points(R, R2, mask);
-            engine.isogeny.xDBLADD(R0, R2, R.X, A24);
+            engine.isogeny.SwapPoints(R, R2, mask);
+            engine.isogeny.XDblAdd(R0, R2, R.X, A24);
             engine.fpx.fp2mul_mont(R2.X, R.Z, R2.X);
         }
         swap = 0 ^ prevbit;
         mask = 0 - (ulong)swap;
-        engine.isogeny.swap_points(R, R2, mask);
+        engine.isogeny.SwapPoints(R, R2, mask);
     }
 
-
-
-    protected void Elligator2(ulong[][] a24, uint[] r, uint rIndex, ulong[][] x, byte[] bit, uint bitOffset, uint COMPorDEC)
+    internal void Elligator2(ulong[][] a24, uint[] r, uint rIndex, ulong[][] x, byte[] bit, uint bitOffset, uint COMPorDEC)
     { // Generate an x-coordinate of a pouint on curve with (affine) coefficient a24 
         // Use the counter r
         uint i;
@@ -189,8 +186,7 @@ internal sealed class SidhCompressed
 //        {System.out.printf("%016x ", x[di][dj] );}System.out.Println();}
     }
 
-
-    protected void make_positive(ulong[][] x)
+    internal void make_positive(ulong[][] x)
     {
         uint nbytes = engine.param.NWORDS_FIELD;
         ulong[] zero = new ulong[engine.param.NWORDS_FIELD];
@@ -213,8 +209,7 @@ internal sealed class SidhCompressed
         engine.fpx.to_fp2mont(x, x);
     }
 
-
-    protected void BiQuad_affine(ulong[][] a24, ulong[][] x0, ulong[][] x1, PointProj R)
+    internal void BiQuad_affine(ulong[][] a24, ulong[][] x0, ulong[][] x1, PointProj R)
     {
         ulong[][] Ap2 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             aa = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -259,8 +254,7 @@ internal sealed class SidhCompressed
         engine.fpx.fp2add(aa, aa, R.Z);
     }
 
-
-    protected void get_4_isog_dual(PointProj P, ulong[][] A24, ulong[][] C24, ulong[][][] coeff)
+    internal void get_4_isog_dual(PointProj P, ulong[][] A24, ulong[][] C24, ulong[][][] coeff)
     {
         engine.fpx.fp2sub(P.X, P.Z, coeff[1]);
         engine.fpx.fp2add(P.X, P.Z, coeff[2]);
@@ -275,7 +269,7 @@ internal sealed class SidhCompressed
 
 //    if (engine.param.OALICE_BITS % 2 == 1)
 
-    protected void eval_dual_2_isog(ulong[][] X2, ulong[][] Z2, PointProj P)
+    internal void eval_dual_2_isog(ulong[][] X2, ulong[][] Z2, PointProj P)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
 
@@ -288,7 +282,7 @@ internal sealed class SidhCompressed
         engine.fpx.fp2mul_mont(Z2, t0, P.X);
     }
 
-    protected void eval_final_dual_2_isog(PointProj P)
+    internal void eval_final_dual_2_isog(PointProj P)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
@@ -306,7 +300,7 @@ internal sealed class SidhCompressed
     }
 
 
-    protected void eval_dual_4_isog_shared(ulong[][] X4pZ4, ulong[][] X42, ulong[][] Z42, ulong[][][] coeff, uint coeffOffset)
+    internal void eval_dual_4_isog_shared(ulong[][] X4pZ4, ulong[][] X42, ulong[][] Z42, ulong[][][] coeff, uint coeffOffset)
     {
 //        System.out.print("coeff0: ");
 //        for (uint di = 0; di < 2; di++){
@@ -340,8 +334,7 @@ internal sealed class SidhCompressed
 //                System.out.printf("%016x ", coeff[2 + coeffOffset][di][dj]);}System.out.Println();}
     }
 
-
-    protected void eval_dual_4_isog(ulong[][] A24, ulong[][] C24, ulong[][][] coeff, uint coeffOffset, PointProj P)
+    internal void eval_dual_4_isog(ulong[][] A24, ulong[][] C24, ulong[][][] coeff, uint coeffOffset, PointProj P)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -365,8 +358,7 @@ internal sealed class SidhCompressed
         engine.fpx.fp2mul_mont(coeff[2 + coeffOffset], P.Z, P.Z);
     }
 
-
-    protected void eval_full_dual_4_isog(ulong[][][][] As, PointProj P)
+    internal void eval_full_dual_4_isog(ulong[][][][] As, PointProj P)
     {
         //todo : check
         // First all 4-isogenies
@@ -384,8 +376,7 @@ internal sealed class SidhCompressed
         eval_final_dual_2_isog(P);    // to A = 0
     }
 
-
-    protected void TripleAndParabola_proj(PointProjFull R, ulong[][] l1x, ulong[][] l1z)
+    internal void TripleAndParabola_proj(PointProjFull R, ulong[][] l1x, ulong[][] l1z)
     {
         engine.fpx.fp2sqr_mont(R.X, l1z);
         engine.fpx.fp2add(l1z, l1z, l1x);
@@ -394,8 +385,7 @@ internal sealed class SidhCompressed
         engine.fpx.fp2add(R.Y, R.Y, l1z);
     }
 
-
-    protected void Tate3_proj(PointProjFull P, PointProjFull Q, ulong[][] gX, ulong[][] gZ)
+    internal void Tate3_proj(PointProjFull P, PointProjFull Q, ulong[][] gX, ulong[][] gZ)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             l1x = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
@@ -408,8 +398,7 @@ internal sealed class SidhCompressed
         engine.fpx.fp2add(gX, t0, gX);
     }
 
-
-    protected void FinalExpo3(ulong[][] gX, ulong[][] gZ)
+    internal void FinalExpo3(ulong[][] gX, ulong[][] gZ)
     {
         uint i;
 
@@ -432,8 +421,7 @@ internal sealed class SidhCompressed
         }
     }
 
-
-    protected void FinalExpo3_2way(ulong[][][] gX, ulong[][][] gZ)
+    internal void FinalExpo3_2way(ulong[][][] gX, ulong[][][] gZ)
     {
         uint i, j;
 
@@ -463,7 +451,6 @@ internal sealed class SidhCompressed
         }
     }
 
-
     private bool FirstPoint_dual(PointProj P, PointProjFull R, byte[] ind)
     {
         PointProjFull R3 = new PointProjFull(engine.param.NWORDS_FIELD),
@@ -580,7 +567,6 @@ internal sealed class SidhCompressed
         return true;
     }
 
-
     private bool SecondPoint_dual(PointProj P, PointProjFull R, byte[] ind)
     {
         PointProjFull RS3 = new PointProjFull(engine.param.NWORDS_FIELD);
@@ -612,8 +598,7 @@ internal sealed class SidhCompressed
         }
     }
 
-
-    protected void FirstPoint3n(ulong[][] a24, ulong[][][][] As, ulong[][] x, PointProjFull R, uint[] r, byte[] ind, byte[] bitEll)
+    internal void FirstPoint3n(ulong[][] a24, ulong[][][][] As, ulong[][] x, PointProjFull R, uint[] r, byte[] ind, byte[] bitEll)
     {
         bool b = false;
         PointProj P = new PointProj(engine.param.NWORDS_FIELD);
@@ -668,8 +653,7 @@ internal sealed class SidhCompressed
         }
     }
 
-
-    protected void SecondPoint3n(ulong[][] a24, ulong[][][][] As, ulong[][] x, PointProjFull R, uint[] r, byte[] ind, byte[] bitEll)
+    internal void SecondPoint3n(ulong[][] a24, ulong[][][][] As, ulong[][] x, PointProjFull R, uint[] r, byte[] ind, byte[] bitEll)
     {
         bool b = false;
         PointProj P = new PointProj(engine.param.NWORDS_FIELD);
@@ -724,8 +708,7 @@ internal sealed class SidhCompressed
         }
     }
 
-
-    protected void makeDiff(PointProjFull R, PointProjFull S, PointProj D)
+    internal void makeDiff(PointProjFull R, PointProjFull S, PointProj D)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -749,8 +732,7 @@ internal sealed class SidhCompressed
         }
     }
 
-
-    protected void BuildOrdinary3nBasis_dual(ulong[][] a24, ulong[][][][] As, PointProjFull[] R, uint[] r, uint[] bitsEll, uint bitsEllOffset)
+    internal void BuildOrdinary3nBasis_dual(ulong[][] a24, ulong[][][][] As, PointProjFull[] R, uint[] r, uint[] bitsEll, uint bitsEllOffset)
     {
         PointProj D = new PointProj(engine.param.NWORDS_FIELD);
 
@@ -809,8 +791,7 @@ internal sealed class SidhCompressed
         makeDiff(R[0], R[1], D);
     }
 
-
-    protected void FullIsogeny_A_dual(byte[] PrivateKeyA, ulong[][][][] As, ulong[][] a24, uint sike)
+    internal void FullIsogeny_A_dual(byte[] PrivateKeyA, ulong[][][][] As, ulong[][] a24, uint sike)
     {
         // Input:  a private key PrivateKeyA in the range [0, 2^eA - 1]. 
         // Output: the public key PublicKeyA consisting of 3 elements in GF(p^2) which are encoded by removing leading 0 bytes.
@@ -925,9 +906,9 @@ internal sealed class SidhCompressed
         {
             PointProj S = new PointProj(engine.param.NWORDS_FIELD);
 
-            engine.isogeny.xDBLe(R, S, A24, C24, engine.param.OALICE_BITS - 1);
-            engine.isogeny.get_2_isog(S, A24, C24);
-            engine.isogeny.eval_2_isog(R, S);
+            engine.isogeny.XDblE(R, S, A24, C24, engine.param.OALICE_BITS - 1);
+            engine.isogeny.Get2Isog(S, A24, C24);
+            engine.isogeny.Eval2Isog(R, S);
             engine.fpx.fp2copy(S.X, As[engine.param.MAX_Alice][2]);
             engine.fpx.fp2copy(S.Z, As[engine.param.MAX_Alice][3]);
         }
@@ -945,7 +926,7 @@ internal sealed class SidhCompressed
                 engine.fpx.fp2copy(R.Z, pts[npts].Z);
                 pts_index[npts++] = index;
                 m = engine.param.strat_Alice[ii++];
-                engine.isogeny.xDBLe(R, R, A24, C24, 2*m);
+                engine.isogeny.XDblE(R, R, A24, C24, 2*m);
                 index += m;
             }
 
@@ -976,7 +957,7 @@ internal sealed class SidhCompressed
             get_4_isog_dual(R, A24, C24, coeff);
             for (i = 0; i < npts; i++)
             {
-                engine.isogeny.eval_4_isog(pts[i], coeff);
+                engine.isogeny.Eval4Isog(pts[i], coeff);
 
 //                System.out.print(i + "ptsX: ");
 //                for (uint di = 0; di < 2; di++){
@@ -1043,8 +1024,7 @@ internal sealed class SidhCompressed
 //                System.out.printf("%016x ", a24[di][dj]);}System.out.Println();}
     }
 
-
-    protected void Dlogs3_dual(ulong[][][] f, int[] D, ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1)
+    internal void Dlogs3_dual(ulong[][][] f, int[] D, ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1)
     {
         solve_dlog(f[0], D, d0, 3);
         solve_dlog(f[2], D, c0, 3);
@@ -1054,8 +1034,7 @@ internal sealed class SidhCompressed
         engine.fpx.mp_sub(engine.param.Bob_order, c1, c1, engine.param.NWORDS_ORDER);
     }
 
-
-    protected void BuildOrdinary3nBasis_Decomp_dual(ulong[][] A24, PointProj[] Rs, uint[] r, uint[] bitsEll, uint bitsEllIndex)
+    internal void BuildOrdinary3nBasis_Decomp_dual(ulong[][] A24, PointProj[] Rs, uint[] r, uint[] bitsEll, uint bitsEllIndex)
     {
         byte[] bitEll = new byte[2];
 
@@ -1071,8 +1050,7 @@ internal sealed class SidhCompressed
         BiQuad_affine(A24, Rs[0].X, Rs[1].X, Rs[2]);
     }
 
-
-    protected void PKADecompression_dual(byte[] SecretKeyB, byte[] CompressedPKA, PointProj R, ulong[][] A)
+    internal void PKADecompression_dual(byte[] SecretKeyB, byte[] CompressedPKA, PointProj R, ulong[][] A)
     {
         byte bit;
         uint[] rs = new uint[3];
@@ -1120,7 +1098,7 @@ internal sealed class SidhCompressed
         engine.fpx.fpcopy(engine.param.Montgomery_one, 0, Rs[0].Z[0]);
         engine.fpx.fpcopy(engine.param.Montgomery_one, 0, Rs[1].Z[0]);
 
-        engine.isogeny.swap_points(Rs[0], Rs[1], ((ulong) -bit));//todo check
+        engine.isogeny.SwapPoints(Rs[0], Rs[1], ((ulong) -bit));//todo check
         engine.fpx.decode_to_digits(SecretKeyB, 0, SKin, engine.param.SECRETKEY_B_BYTES, engine.param.NWORDS_ORDER);
         engine.fpx.to_Montgomery_mod_order(SKin, t1, engine.param.Bob_order, engine.param.Montgomery_RB2, engine.param.Montgomery_RB1);    // Converting to Montgomery representation
         engine.fpx.decode_to_digits(CompressedPKA, 0, temp, engine.param.ORDER_B_ENCODED_BYTES, engine.param.NWORDS_ORDER);
@@ -1155,8 +1133,7 @@ internal sealed class SidhCompressed
         engine.isogeny.Double(R, R, A24, engine.param.OALICE_BITS);    // x, z := Double(A24, x, 1, eA);
     }
 
-
-    protected void Compress_PKA_dual(ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1, ulong[][] a24, uint[] rs, byte[] CompressedPKA)
+    internal void Compress_PKA_dual(ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1, ulong[][] a24, uint[] rs, byte[] CompressedPKA)
     {
         uint bit;
         ulong[] temp = new ulong[engine.param.NWORDS_ORDER],
@@ -1231,7 +1208,7 @@ internal sealed class SidhCompressed
 
     // Alice's ephemeral public key generation using compression -- SIKE protocol
     // Output: PrivateKeyA[MSG_BYTES + engine.param.SECRETKEY_A_BYTES] <- x(K_A) where K_A = PA + sk_A*Q_A
-    protected internal uint EphemeralKeyGeneration_A_extended(byte[] PrivateKeyA, byte[] CompressedPKA)
+    internal uint EphemeralKeyGeneration_A_extended(byte[] PrivateKeyA, byte[] CompressedPKA)
     {
         uint[] rs = new uint[3];
         int[] D = new int[engine.param.DLEN_3];
@@ -1363,14 +1340,14 @@ internal sealed class SidhCompressed
                 engine.fpx.fp2copy(R.Z, pts[npts].Z);
                 pts_index[npts++] = index;
                 m = engine.param.strat_Bob[ii++];
-                engine.isogeny.xTPLe(R, R, A24minus, A24plus, m);
+                engine.isogeny.XTplE(R, R, A24minus, A24plus, m);
                 index += m;
             }
-            engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
+            engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
 
             for (i = 0; i < npts; i++)
             {
-                engine.isogeny.eval_3_isog(pts[i], coeff);
+                engine.isogeny.Eval3Isog(pts[i], coeff);
             }
 
             engine.fpx.fp2copy(pts[npts-1].X, R.X);
@@ -1379,18 +1356,17 @@ internal sealed class SidhCompressed
             npts -= 1;
         }
 
-        engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
+        engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
         engine.fpx.fp2add(A24plus, A24minus, A);
         engine.fpx.fp2add(A, A, A);
         engine.fpx.fp2sub(A24plus, A24minus, A24plus);
-        engine.isogeny.j_inv(A, A24plus, jinv);
+        engine.isogeny.JInv(A, A24plus, jinv);
         engine.fpx.fp2_encode(jinv, SharedSecretB, 0);    // Format shared secret
 
         return 0;
     }
 
-
-    protected void BuildEntangledXonly(ulong[][] A, PointProj[] R, byte[] qnr, byte[] ind)
+    internal void BuildEntangledXonly(ulong[][] A, PointProj[] R, byte[] qnr, byte[] ind)
     {
         ulong[] s = new ulong[engine.param.NWORDS_FIELD];
         ulong[][] t_ptr,
@@ -1456,8 +1432,7 @@ internal sealed class SidhCompressed
         engine.fpx.fp2mul_mont(t, r, R[2].X);
     }
 
-
-    protected void RecoverY(ulong[][] A, PointProj[] xs, PointProjFull[] Rs)
+    internal void RecoverY(ulong[][] A, PointProj[] xs, PointProjFull[] Rs)
     {
         ulong[][] t0 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             t1 = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -1497,8 +1472,7 @@ internal sealed class SidhCompressed
         engine.fpx.fp2mul_mont(Rs[1].Y, Rs[1].Z, Rs[1].Y);
     }
 
-
-    protected void BuildOrdinary2nBasis_dual(ulong[][] A, ulong[][][][] Ds, PointProjFull[] Rs, byte[] qnr, byte[] ind)
+    internal void BuildOrdinary2nBasis_dual(ulong[][] A, ulong[][][][] Ds, PointProjFull[] Rs, byte[] qnr, byte[] ind)
     {
         uint i;
         ulong[] t0 = new ulong[engine.param.NWORDS_FIELD];
@@ -1545,9 +1519,9 @@ internal sealed class SidhCompressed
         // Move them back to A = 6 
         for(i = 0; i < engine.param.MAX_Bob; i++)
         {
-            engine.isogeny.eval_3_isog(xs[0], Ds[engine.param.MAX_Bob-1-i]);
-            engine.isogeny.eval_3_isog(xs[1], Ds[engine.param.MAX_Bob-1-i]);
-            engine.isogeny.eval_3_isog(xs[2], Ds[engine.param.MAX_Bob-1-i]);
+            engine.isogeny.Eval3Isog(xs[0], Ds[engine.param.MAX_Bob-1-i]);
+            engine.isogeny.Eval3Isog(xs[1], Ds[engine.param.MAX_Bob-1-i]);
+            engine.isogeny.Eval3Isog(xs[2], Ds[engine.param.MAX_Bob-1-i]);
         }
 
         // Recover y-coordinates with a single sqrt on A = 6
@@ -1563,7 +1537,7 @@ internal sealed class SidhCompressed
     // Bob's ephemeral public key generation
     // Input:  a private key PrivateKeyB in the range [0, 2^Floor(Log(2,oB)) - 1].
     // Output: the public key PublicKeyB consisting of 3 elements in GF(p^2) which are encoded by removing leading 0 bytes.
-    protected void FullIsogeny_B_dual(byte[] PrivateKeyB, ulong[][][][] Ds, ulong[][] A)
+    internal void FullIsogeny_B_dual(byte[] PrivateKeyB, ulong[][][][] Ds, ulong[][] A)
     {
         PointProj R = new PointProj(engine.param.NWORDS_FIELD),
         Q3 = new PointProj(engine.param.NWORDS_FIELD);
@@ -1621,15 +1595,15 @@ internal sealed class SidhCompressed
                 engine.fpx.fp2copy(R.Z, pts[npts].Z);
                 pts_index[npts++] = index;
                 m = engine.param.strat_Bob[ii++];
-                engine.isogeny.xTPLe(R, R, A24minus, A24plus, m);
+                engine.isogeny.XTplE(R, R, A24minus, A24plus, m);
                 index += m;
             }
-            engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
+            engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
             for (i = 0; i < npts; i++)
             {
-                engine.isogeny.eval_3_isog(pts[i], coeff);
+                engine.isogeny.Eval3Isog(pts[i], coeff);
             }
-            engine.isogeny.eval_3_isog(Q3, coeff);    // Kernel of dual 
+            engine.isogeny.Eval3Isog(Q3, coeff);    // Kernel of dual 
             engine.fpx.fp2sub(Q3.X,Q3.Z,Ds[row-1][0]);
             engine.fpx.fp2add(Q3.X,Q3.Z,Ds[row-1][1]);
 
@@ -1638,8 +1612,8 @@ internal sealed class SidhCompressed
             index = pts_index[npts-1];
             npts -= 1;
         }
-        engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
-        engine.isogeny.eval_3_isog(Q3, coeff);    // Kernel of dual 
+        engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
+        engine.isogeny.Eval3Isog(Q3, coeff);    // Kernel of dual 
         engine.fpx.fp2sub(Q3.X, Q3.Z, Ds[engine.param.MAX_Bob-1][0]);
         engine.fpx.fp2add(Q3.X, Q3.Z, Ds[engine.param.MAX_Bob-1][1]);
 
@@ -1650,8 +1624,7 @@ internal sealed class SidhCompressed
         engine.fpx.fp2add(A, A, A);    // A = 2*(A24plus+A24mins)/(A24plus-A24minus) 
     }
 
-
-    protected void Dlogs2_dual(ulong[][][] f, int[] D, ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1)
+    internal void Dlogs2_dual(ulong[][][] f, int[] D, ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1)
     {
         solve_dlog(f[0], D, d0, 2);
         solve_dlog(f[2], D, c0, 2);
@@ -1661,8 +1634,7 @@ internal sealed class SidhCompressed
         engine.fpx.mp_sub(engine.param.Alice_order, c1, c1, engine.param.NWORDS_ORDER);
     }
 
-
-    protected void BuildEntangledXonly_Decomp(ulong[][] A, PointProj[] R, uint qnr, uint ind)
+    internal void BuildEntangledXonly_Decomp(ulong[][] A, PointProj[] R, uint qnr, uint ind)
     {
         ulong[][] t_ptr,
             r = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -1714,7 +1686,7 @@ internal sealed class SidhCompressed
     }
 
     // Bob's PK decompression -- SIKE protocol
-    protected void PKBDecompression_extended(byte[] SecretKeyA, uint SecretKeyAOffset, byte[] CompressedPKB, PointProj R, ulong[][] A, byte[] tphiBKA_t, uint tphiBKA_tOffset)
+    internal void PKBDecompression_extended(byte[] SecretKeyA, uint SecretKeyAOffset, byte[] CompressedPKB, PointProj R, ulong[][] A, byte[] tphiBKA_t, uint tphiBKA_tOffset)
     { 
         ulong mask = unchecked((ulong) -1L);
         uint qnr, ind;
@@ -1807,12 +1779,12 @@ internal sealed class SidhCompressed
             engine.fpx.inv_mod_orderA(tmp2, inv);
             engine.fpx.multiply(inv, tmp1, scal, engine.param.NWORDS_ORDER);
             scal[engine.param.NWORDS_ORDER-1] &= (ulong)mask;
-            engine.isogeny.swap_points(Rs[0], Rs[1], unchecked((ulong)-1L));//check
+            engine.isogeny.SwapPoints(Rs[0], Rs[1], unchecked((ulong)-1L));//check
             Ladder3pt_dual(Rs, scal, engine.param.ALICE, R, A24);
         }
 
         engine.fpx.fp2div2(A,Adiv2);
-        engine.isogeny.xTPLe_fast(R, R, Adiv2, engine.param.OBOB_EXPON);
+        engine.isogeny.XTplEFast(R, R, Adiv2, engine.param.OBOB_EXPON);
 
         engine.fpx.fp2_encode(R.X, tphiBKA_t, tphiBKA_tOffset);
         engine.fpx.fp2_encode(R.Z, tphiBKA_t, tphiBKA_tOffset + engine.param.FP2_ENCODED_BYTES);
@@ -1820,7 +1792,7 @@ internal sealed class SidhCompressed
     }
 
     // Bob's PK compression -- SIKE protocol
-    protected void Compress_PKB_dual_extended(ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1, ulong[][] A, byte[] qnr, byte[] ind, byte[] CompressedPKB)
+    internal void Compress_PKB_dual_extended(ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1, ulong[][] A, byte[] qnr, byte[] ind, byte[] CompressedPKB)
     {
         ulong mask = unchecked((ulong) -1L);
         ulong[] tmp = new ulong[2*engine.param.NWORDS_ORDER],
@@ -1876,7 +1848,7 @@ internal sealed class SidhCompressed
     }
 
     // Bob's PK decompression -- SIDH protocol
-    protected void PKBDecompression(byte[] SecretKeyA, uint SecretKeyAOffset, byte[] CompressedPKB, PointProj R, ulong[][] A)
+    internal void PKBDecompression(byte[] SecretKeyA, uint SecretKeyAOffset, byte[] CompressedPKB, PointProj R, ulong[][] A)
     {
         ulong mask = unchecked((ulong) -1L);
         uint bit,qnr,ind;
@@ -1909,7 +1881,7 @@ internal sealed class SidhCompressed
         engine.fpx.fp2div2(A24, A24);
 
         engine.fpx.decode_to_digits(SecretKeyA, SecretKeyAOffset, SKin, engine.param.SECRETKEY_A_BYTES, engine.param.NWORDS_ORDER);
-        engine.isogeny.swap_points(Rs[0], Rs[1], 0-(ulong)bit);
+        engine.isogeny.SwapPoints(Rs[0], Rs[1], 0-(ulong)bit);
         if (bit == 0)
         {
             engine.fpx.decode_to_digits(CompressedPKB, engine.param.ORDER_A_ENCODED_BYTES, comp_temp, engine.param.ORDER_A_ENCODED_BYTES, engine.param.NWORDS_ORDER);
@@ -1941,11 +1913,11 @@ internal sealed class SidhCompressed
             Ladder3pt_dual(Rs, vone, engine.param.ALICE, R, A24);
         }
         engine.fpx.fp2div2(A, A24);
-        engine.isogeny.xTPLe_fast(R, R, A24, engine.param.OBOB_EXPON);
+        engine.isogeny.XTplEFast(R, R, A24, engine.param.OBOB_EXPON);
     }
 
     // Bob's PK compression -- SIDH protocol
-    protected void Compress_PKB_dual(ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1, ulong[][] A, byte[] qnr, byte[] ind, byte[] CompressedPKB)
+    internal void Compress_PKB_dual(ulong[] d0, ulong[] c0, ulong[] d1, ulong[] c1, ulong[][] A, byte[] qnr, byte[] ind, byte[] CompressedPKB)
     {
         
         ulong[] tmp = new ulong[2*engine.param.NWORDS_ORDER],
@@ -1990,7 +1962,7 @@ internal sealed class SidhCompressed
     }
 
     // Bob's ephemeral public key generation using compression -- SIKE protocol
-    protected internal uint EphemeralKeyGeneration_B_extended(byte[] PrivateKeyB, byte[] CompressedPKB, uint sike)
+    internal uint EphemeralKeyGeneration_B_extended(byte[] PrivateKeyB, byte[] CompressedPKB, uint sike)
     {
         byte[] qnr = new byte[1], ind = new byte[1];
         int[] D = new int[engine.param.DLEN_2];
@@ -2114,13 +2086,13 @@ internal sealed class SidhCompressed
     }
 
     // Bob's ephemeral public key generation using compression -- SIDH protocol
-    protected uint EphemeralKeyGeneration_B(byte[] PrivateKeyB, byte[] CompressedPKB)
+    internal uint EphemeralKeyGeneration_B(byte[] PrivateKeyB, byte[] CompressedPKB)
     {
         return EphemeralKeyGeneration_B_extended(PrivateKeyB, CompressedPKB, 0);
     }
 
     // Alice's ephemeral shared secret computation using compression -- SIKE protocol
-    protected internal uint EphemeralSecretAgreement_A_extended(byte[] PrivateKeyA, uint PrivateKeyAOffset, byte[] PKB, byte[] SharedSecretA, uint sike)
+    internal uint EphemeralSecretAgreement_A_extended(byte[] PrivateKeyA, uint PrivateKeyAOffset, byte[] PKB, byte[] SharedSecretA, uint sike)
     {
         uint i, ii = 0, row, m, index = 0, npts = 0;
         uint[] pts_index = new uint[engine.param.MAX_INT_POINTS_ALICE];
@@ -2153,9 +2125,9 @@ internal sealed class SidhCompressed
         {
             PointProj S = new PointProj(engine.param.NWORDS_FIELD);
 
-            engine.isogeny.xDBLe(R, S, A24plus, C24, (engine.param.OALICE_BITS - 1));
-            engine.isogeny.get_2_isog(S, A24plus, C24);
-            engine.isogeny.eval_2_isog(R, S);
+            engine.isogeny.XDblE(R, S, A24plus, C24, (engine.param.OALICE_BITS - 1));
+            engine.isogeny.Get2Isog(S, A24plus, C24);
+            engine.isogeny.Eval2Isog(R, S);
         }
 
         // Traverse tree
@@ -2169,14 +2141,14 @@ internal sealed class SidhCompressed
                 engine.fpx.fp2copy(R.Z, pts[npts].Z);
                 pts_index[npts++] = index;
                 m = engine.param.strat_Alice[ii++];
-                engine.isogeny.xDBLe(R, R, A24plus, C24, 2*m);
+                engine.isogeny.XDblE(R, R, A24plus, C24, 2*m);
                 index += m;
             }
-            engine.isogeny.get_4_isog(R, A24plus, C24, coeff);
+            engine.isogeny.Get4Isog(R, A24plus, C24, coeff);
 
             for (i = 0; i < npts; i++)
             {
-                engine.isogeny.eval_4_isog(pts[i], coeff);
+                engine.isogeny.Eval4Isog(pts[i], coeff);
             }
 
             engine.fpx.fp2copy(pts[npts-1].X, R.X);
@@ -2185,11 +2157,11 @@ internal sealed class SidhCompressed
             npts -= 1;
         }
 
-        engine.isogeny.get_4_isog(R, A24plus, C24, coeff);
+        engine.isogeny.Get4Isog(R, A24plus, C24, coeff);
         engine.fpx.fp2add(A24plus, A24plus, A24plus);
         engine.fpx.fp2sub(A24plus, C24, A24plus);
         engine.fpx.fp2add(A24plus, A24plus, A24plus);
-        engine.isogeny.j_inv(A24plus, C24, jinv);
+        engine.isogeny.JInv(A24plus, C24, jinv);
         engine.fpx.fp2_encode(jinv, SharedSecretA, 0);    // Format shared secret
 
         return 0;
@@ -2200,13 +2172,12 @@ internal sealed class SidhCompressed
     // Inputs: Alice's PrivateKeyA is an even integer in the range [2, oA-2], where oA = 2^engine.param.OALICE_BITS.
     //         Bob's decompressed data consists of point_R in (X:Z) coordinates and the curve parameter param_A in GF(p^2).
     // Output: a shared secret SharedSecretA that consists of one element in GF(p^2).
-    uint EphemeralSecretAgreement_A(byte[] PrivateKeyA, uint PrivateKeyAOffset, byte[] PKB, byte[] SharedSecretA)
+    private uint EphemeralSecretAgreement_A(byte[] PrivateKeyA, uint PrivateKeyAOffset, byte[] PKB, byte[] SharedSecretA)
     {
         return EphemeralSecretAgreement_A_extended(PrivateKeyA, PrivateKeyAOffset, PKB, SharedSecretA, 0);
     }
 
-
-    protected internal byte validate_ciphertext(byte[] ephemeralsk_, byte[] CompressedPKB, byte[] xKA, uint xKAOffset, byte[] tphiBKA_t, uint tphiBKA_tOffset)
+    internal byte validate_ciphertext(byte[] ephemeralsk_, byte[] CompressedPKB, byte[] xKA, uint xKAOffset, byte[] tphiBKA_t, uint tphiBKA_tOffset)
     { // If ct validation passes returns 0, otherwise returns -1.
         PointProj[] phis = new PointProj[3],
                     pts = new PointProj[engine.param.MAX_INT_POINTS_BOB];
@@ -2264,23 +2235,23 @@ internal sealed class SidhCompressed
                 engine.fpx.fp2copy(R.Z, pts[npts].Z);
                 pts_index[npts++] = index;
                 m = engine.param.strat_Bob[ii++];
-                engine.isogeny.xTPLe(R, R, A24minus, A24plus, m);
+                engine.isogeny.XTplE(R, R, A24minus, A24plus, m);
                 index += m;
             }
-            engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
+            engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
             for (i = 0; i < npts; i++)
             {
-                engine.isogeny.eval_3_isog(pts[i], coeff);
+                engine.isogeny.Eval3Isog(pts[i], coeff);
             }
-            engine.isogeny.eval_3_isog(phis[0], coeff);
+            engine.isogeny.Eval3Isog(phis[0], coeff);
 
             engine.fpx.fp2copy(pts[npts-1].X, R.X);
             engine.fpx.fp2copy(pts[npts-1].Z, R.Z);
             index = pts_index[npts-1];
             npts -= 1;
         }
-        engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
-        engine.isogeny.eval_3_isog(phis[0], coeff);  // phis[0] <- phiB(PA + skA*QA)
+        engine.isogeny.Get3Isog(R, A24minus, A24plus, coeff);
+        engine.isogeny.Eval3Isog(phis[0], coeff);  // phis[0] <- phiB(PA + skA*QA)
 
         engine.fpx.fp2_decode(CompressedPKB, A, 4*engine.param.ORDER_A_ENCODED_BYTES);
 
@@ -2296,13 +2267,11 @@ internal sealed class SidhCompressed
         return (engine.fpx.cmp_f2elm(comp1, comp2));
     }
 
-
-
     /// DLOG
 
     // Computes the discrete log of input r = g^d where g = e(P,Q)^ell^e, and P,Q are torsion generators in the initial curve
     // Return the integer d
-    void solve_dlog(ulong[][] r, int[] D, ulong[] d, uint ell)
+    internal void solve_dlog(ulong[][] r, int[] D, ulong[] d, uint ell)
     {
         if (ell == 2)
         {
@@ -2449,12 +2418,10 @@ internal sealed class SidhCompressed
         }
     }
 
-
-
     // Traverse a Pohlig-Hellman optimal strategy to solve a discrete log in a group of order ell^e
     // Leaves are used to recover the digits which are numbers from 0 to ell^w-1 except by the last leaf that gives a digit between 0 and ell^(e mod w)
     // Assume w does not divide the exponent e
-    void Traverse_w_notdiv_e_fullsigned(ulong[][] r, uint j, uint k, uint z, uint[] P, ulong[] CT1, ulong[] CT2,
+    internal void Traverse_w_notdiv_e_fullsigned(ulong[][] r, uint j, uint k, uint z, uint[] P, ulong[] CT1, ulong[] CT2,
                                         int[] D, uint Dlen, uint ell, uint ellw, uint ell_emodw, uint w, uint e)
     {
         ulong[][] rp = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
@@ -2628,12 +2595,10 @@ internal sealed class SidhCompressed
         }
     }
 
-
-
     // Traverse a Pohlig-Hellman optimal strategy to solve a discrete log in a group of order ell^e
     // The leaves of the tree will be used to recover the signed digits which are numbers from +/-{0,1... Ceil((ell^w-1)/2)}
     // Assume the integer w divides the exponent e
-    void Traverse_w_div_e_fullsigned(ulong[][] r, uint j, uint k, uint z, uint[] P, ulong[] CT, int[] D, uint Dlen, uint ellw, uint w)
+    internal void Traverse_w_div_e_fullsigned(ulong[][] r, uint j, uint k, uint z, uint[] P, ulong[] CT, int[] D, uint Dlen, uint ellw, uint w)
     {
         ulong[][] rp = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD),
             alpha = SikeUtilities.InitArray(2, engine.param.NWORDS_FIELD);
@@ -3158,9 +3123,5 @@ internal sealed class SidhCompressed
         }
         engine.fpx.fp2copy(temp, fout);
     }
-
-
-
 }
-
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/sike/SIKEEngine.cs b/crypto/src/pqc/crypto/sike/SIKEEngine.cs
index 6a825fe44..12f468340 100644
--- a/crypto/src/pqc/crypto/sike/SIKEEngine.cs
+++ b/crypto/src/pqc/crypto/sike/SIKEEngine.cs
@@ -4,43 +4,42 @@ using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Sike
 {
-internal sealed class SikeEngine
-{
-    //private readonly SecureRandom random;
-
-    protected internal Internal param;
-    protected internal Isogeny isogeny;
-    protected internal Fpx fpx;
-    private Sidh sidh;
-    private SidhCompressed sidhCompressed;
-    private bool isCompressed;
-
-    public uint GetDefaultSessionKeySize()
+    internal sealed class SikeEngine
     {
-        return param.MSG_BYTES * 8;
-    }
+        internal Internal param;
+        internal Isogeny isogeny;
+        internal Fpx fpx;
+        private Sidh sidh;
+        private SidhCompressed sidhCompressed;
+        private bool isCompressed;
+
+        internal uint GetDefaultSessionKeySize()
+        {
+            return param.MSG_BYTES * 8;
+        }
 
-    public int GetCipherTextSize()
-    {
-        return param.CRYPTO_CIPHERTEXTBYTES;
-    }
+        internal int GetCipherTextSize()
+        {
+            return param.CRYPTO_CIPHERTEXTBYTES;
+        }
 
-    public uint GetPrivateKeySize()
-    {
-        return param.CRYPTO_SECRETKEYBYTES;
-    }
+        internal uint GetPrivateKeySize()
+        {
+            return param.CRYPTO_SECRETKEYBYTES;
+        }
 
-    public uint GetPublicKeySize()
-    {
-        return param.CRYPTO_PUBLICKEYBYTES;
-    }
-    public SikeEngine(int ver, bool isCompressed, SecureRandom random)
-    {
-        //this.random = random;
-        this.isCompressed = isCompressed;
-        //todo switch for different parameters
-        switch(ver)
+        internal uint GetPublicKeySize()
         {
+            return param.CRYPTO_PUBLICKEYBYTES;
+        }
+
+        internal SikeEngine(int ver, bool isCompressed, SecureRandom random)
+        {
+            //this.random = random;
+            this.isCompressed = isCompressed;
+            //todo switch for different parameters
+            switch(ver)
+            {
             case 434:
                 param = new P434(isCompressed);
                 break;
@@ -55,239 +54,236 @@ internal sealed class SikeEngine
                 break;
             default:
                 break;
-                
+            }
+            fpx = new Fpx(this);
+            isogeny = new Isogeny(this);
+            if(isCompressed)
+            {
+                sidhCompressed = new SidhCompressed(this);
+            }
+            sidh = new Sidh(this);
         }
-        fpx = new Fpx(this);
-        isogeny = new Isogeny(this);
-        if(isCompressed)
+
+        // SIKE's key generation
+        // Outputs: secret key sk (CRYPTO_SECRETKEYBYTES = MSG_BYTES + SECRETKEY_B_BYTES + CRYPTO_PUBLICKEYBYTES bytes)
+        //          public key pk (CRYPTO_PUBLICKEYBYTES bytes)
+        internal int crypto_kem_keypair(byte[] pk, byte[] sk, SecureRandom random)
         {
-            sidhCompressed = new SidhCompressed(this);
-        }
-        sidh = new Sidh(this);
-    }
+            random.NextBytes(sk, 0, (int)param.MSG_BYTES);
 
-    // SIKE's key generation
-    // Outputs: secret key sk (CRYPTO_SECRETKEYBYTES = MSG_BYTES + SECRETKEY_B_BYTES + CRYPTO_PUBLICKEYBYTES bytes)
-    //          public key pk (CRYPTO_PUBLICKEYBYTES bytes)
-    public int crypto_kem_keypair(byte[] pk, byte[] sk, SecureRandom random)
-    {
-        random.NextBytes(sk, 0, (int)param.MSG_BYTES);
+            if (isCompressed)
+            {
+                // Generation of Alice's secret key
+                // Outputs random value in [0, 2^eA - 1]
 
-        if (isCompressed)
-        {
-            // Generation of Alice's secret key
-            // Outputs random value in [0, 2^eA - 1]
+                random.NextBytes(sk, (int)param.MSG_BYTES, (int)param.SECRETKEY_A_BYTES);
+                sk[param.MSG_BYTES] &= 0xFE;                                                    // Make private scalar even
+                sk[param.MSG_BYTES + param.SECRETKEY_A_BYTES - 1] &= (byte)param.MASK_ALICE;    // Masking last
 
-            random.NextBytes(sk, (int)param.MSG_BYTES, (int)param.SECRETKEY_A_BYTES);
-            sk[param.MSG_BYTES] &= 0xFE;                                                    // Make private scalar even
-            sk[param.MSG_BYTES + param.SECRETKEY_A_BYTES - 1] &= (byte)param.MASK_ALICE;    // Masking last
+                sidhCompressed.EphemeralKeyGeneration_A_extended(sk, pk);
 
-            sidhCompressed.EphemeralKeyGeneration_A_extended(sk, pk);
+                // Append public key pk to secret key sk
+                System.Array.Copy(pk, 0, sk, param.MSG_BYTES + param.SECRETKEY_A_BYTES, param.CRYPTO_PUBLICKEYBYTES);
+            }
+            else
+            {
+                // Generation of Bob's secret key
+                // Outputs random value in [0, 2^Floor(Log(2, oB)) - 1]
+                // todo/org: SIDH.random_mod_order_B(sk, random);
 
-            // Append public key pk to secret key sk
-            System.Array.Copy(pk, 0, sk, param.MSG_BYTES + param.SECRETKEY_A_BYTES, param.CRYPTO_PUBLICKEYBYTES);
-        }
-        else
-        {
-            // Generation of Bob's secret key
-            // Outputs random value in [0, 2^Floor(Log(2, oB)) - 1]
-            // todo/org: SIDH.random_mod_order_B(sk, random);
+                random.NextBytes(sk, (int)param.MSG_BYTES, (int)param.SECRETKEY_B_BYTES);
+                sk[param.MSG_BYTES + param.SECRETKEY_B_BYTES - 1] &= (byte)param.MASK_BOB;
 
-            random.NextBytes(sk, (int)param.MSG_BYTES, (int)param.SECRETKEY_B_BYTES);
-            sk[param.MSG_BYTES + param.SECRETKEY_B_BYTES - 1] &= (byte)param.MASK_BOB;
+                sidh.EphemeralKeyGeneration_B(sk, pk);
 
-            sidh.EphemeralKeyGeneration_B(sk, pk);
+                // Append public key pk to secret key sk
+                System.Array.Copy(pk, 0, sk, param.MSG_BYTES + param.SECRETKEY_B_BYTES, param.CRYPTO_PUBLICKEYBYTES);
 
-            // Append public key pk to secret key sk
-            System.Array.Copy(pk, 0, sk, param.MSG_BYTES + param.SECRETKEY_B_BYTES, param.CRYPTO_PUBLICKEYBYTES);
+            }
 
+            return 0;
         }
 
-        return 0;
-    }
-
-    // SIKE's encapsulation
-    // Input:   public key pk         (CRYPTO_PUBLICKEYBYTES bytes)
-    // Outputs: shared secret ss      (CRYPTO_BYTES bytes)
-    //          ciphertext message ct (CRYPTO_CIPHERTEXTBYTES = CRYPTO_PUBLICKEYBYTES + MSG_BYTES bytes)
-    public int crypto_kem_enc(byte[] ct, byte[] ss, byte[] pk, SecureRandom random)
-    {
-        if(isCompressed)
+        // SIKE's encapsulation
+        // Input:   public key pk         (CRYPTO_PUBLICKEYBYTES bytes)
+        // Outputs: shared secret ss      (CRYPTO_BYTES bytes)
+        //          ciphertext message ct (CRYPTO_CIPHERTEXTBYTES = CRYPTO_PUBLICKEYBYTES + MSG_BYTES bytes)
+        internal int crypto_kem_enc(byte[] ct, byte[] ss, byte[] pk, SecureRandom random)
         {
-            byte[] ephemeralsk = new byte[param.SECRETKEY_B_BYTES];
-            byte[] jinvariant = new byte[param.FP2_ENCODED_BYTES];
-            byte[] h = new byte[param.MSG_BYTES];
-            byte[] temp = new byte[param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES];
+            if(isCompressed)
+            {
+                byte[] ephemeralsk = new byte[param.SECRETKEY_B_BYTES];
+                byte[] jinvariant = new byte[param.FP2_ENCODED_BYTES];
+                byte[] h = new byte[param.MSG_BYTES];
+                byte[] temp = new byte[param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES];
 
-            // Generate ephemeralsk <- G(m||pk) mod oB
-            random.NextBytes(temp, 0, (int)param.MSG_BYTES);
-            System.Array.Copy(pk, 0, temp, param.MSG_BYTES, param.CRYPTO_PUBLICKEYBYTES);
+                // Generate ephemeralsk <- G(m||pk) mod oB
+                random.NextBytes(temp, 0, (int)param.MSG_BYTES);
+                System.Array.Copy(pk, 0, temp, param.MSG_BYTES, param.CRYPTO_PUBLICKEYBYTES);
 
-            IXof digest = new ShakeDigest(256);
-            digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_PUBLICKEYBYTES + param.MSG_BYTES));
-            digest.OutputFinal(ephemeralsk, 0, (int) param.SECRETKEY_B_BYTES);
+                IXof digest = new ShakeDigest(256);
+                digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_PUBLICKEYBYTES + param.MSG_BYTES));
+                digest.OutputFinal(ephemeralsk, 0, (int) param.SECRETKEY_B_BYTES);
 
-            sidhCompressed.FormatPrivKey_B(ephemeralsk);
+                sidhCompressed.FormatPrivKey_B(ephemeralsk);
 
-//            System.out.println("ephemeralsk: " + Hex.toHexstring(ephemeralsk));
+    //            System.out.println("ephemeralsk: " + Hex.toHexstring(ephemeralsk));
 
 
-            // Encrypt
-            sidhCompressed.EphemeralKeyGeneration_B_extended(ephemeralsk, ct, 1);
+                // Encrypt
+                sidhCompressed.EphemeralKeyGeneration_B_extended(ephemeralsk, ct, 1);
 
-//            System.out.println("ct: " + Hex.toHexstring(ct));
-//            System.out.println("pk: " + Hex.toHexstring(pk));
+    //            System.out.println("ct: " + Hex.toHexstring(ct));
+    //            System.out.println("pk: " + Hex.toHexstring(pk));
 
-            sidhCompressed.EphemeralSecretAgreement_B(ephemeralsk, pk, jinvariant);
+                sidhCompressed.EphemeralSecretAgreement_B(ephemeralsk, pk, jinvariant);
 
-//            System.out.println("jinv: " + Hex.toHexstring(jinvariant));
+    //            System.out.println("jinv: " + Hex.toHexstring(jinvariant));
 
-            digest.BlockUpdate(jinvariant, 0, (int) param.FP2_ENCODED_BYTES);
-            digest.OutputFinal(h, 0, (int) param.MSG_BYTES);
+                digest.BlockUpdate(jinvariant, 0, (int) param.FP2_ENCODED_BYTES);
+                digest.OutputFinal(h, 0, (int) param.MSG_BYTES);
 
-//            System.out.println("h: " + Hex.toHexstring(h));
-//            System.out.println("temp: " + Hex.toHexstring(temp));
+    //            System.out.println("h: " + Hex.toHexstring(h));
+    //            System.out.println("temp: " + Hex.toHexstring(temp));
 
-            for (int i = 0; i < param.MSG_BYTES; i++)
-            {
-                ct[i + param.PARTIALLY_COMPRESSED_CHUNK_CT] = (byte) (temp[i] ^ h[i]);
-            }
+                for (int i = 0; i < param.MSG_BYTES; i++)
+                {
+                    ct[i + param.PARTIALLY_COMPRESSED_CHUNK_CT] = (byte) (temp[i] ^ h[i]);
+                }
 
-            // Generate shared secret ss <- H(m||ct)
-            System.Array.Copy(ct, 0, temp, param.MSG_BYTES, param.CRYPTO_CIPHERTEXTBYTES);
+                // Generate shared secret ss <- H(m||ct)
+                System.Array.Copy(ct, 0, temp, param.MSG_BYTES, param.CRYPTO_CIPHERTEXTBYTES);
 
-            digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES));
-            digest.OutputFinal(ss, 0, (int) param.CRYPTO_BYTES);
-            return 0;
-        }
-        else
-        {
-            byte[] ephemeralsk = new byte[param.SECRETKEY_A_BYTES];
-            byte[] jinvariant = new byte[param.FP2_ENCODED_BYTES];
-            byte[] h = new byte[param.MSG_BYTES];
-            byte[] temp = new byte[param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES];
+                digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES));
+                digest.OutputFinal(ss, 0, (int) param.CRYPTO_BYTES);
+                return 0;
+            }
+            else
+            {
+                byte[] ephemeralsk = new byte[param.SECRETKEY_A_BYTES];
+                byte[] jinvariant = new byte[param.FP2_ENCODED_BYTES];
+                byte[] h = new byte[param.MSG_BYTES];
+                byte[] temp = new byte[param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES];
 
-            // Generate ephemeralsk <- G(m||pk) mod oA
-            random.NextBytes(temp, 0, (int)param.MSG_BYTES);
-            System.Array.Copy(pk, 0, temp, param.MSG_BYTES, param.CRYPTO_PUBLICKEYBYTES);
+                // Generate ephemeralsk <- G(m||pk) mod oA
+                random.NextBytes(temp, 0, (int)param.MSG_BYTES);
+                System.Array.Copy(pk, 0, temp, param.MSG_BYTES, param.CRYPTO_PUBLICKEYBYTES);
 
-            IXof digest = new ShakeDigest(256);
-            digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_PUBLICKEYBYTES + param.MSG_BYTES));
-            digest.OutputFinal(ephemeralsk, 0, (int) param.SECRETKEY_A_BYTES);
-            ephemeralsk[param.SECRETKEY_A_BYTES - 1] &= (byte) param.MASK_ALICE;
+                IXof digest = new ShakeDigest(256);
+                digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_PUBLICKEYBYTES + param.MSG_BYTES));
+                digest.OutputFinal(ephemeralsk, 0, (int) param.SECRETKEY_A_BYTES);
+                ephemeralsk[param.SECRETKEY_A_BYTES - 1] &= (byte) param.MASK_ALICE;
 
-            // Encrypt
-            sidh.EphemeralKeyGeneration_A(ephemeralsk, ct);
-            sidh.EphemeralSecretAgreement_A(ephemeralsk, pk, jinvariant);
+                // Encrypt
+                sidh.EphemeralKeyGeneration_A(ephemeralsk, ct);
+                sidh.EphemeralSecretAgreement_A(ephemeralsk, pk, jinvariant);
 
-            digest.BlockUpdate(jinvariant, 0, (int) param.FP2_ENCODED_BYTES);
-            digest.OutputFinal(h, 0, (int) param.MSG_BYTES);
+                digest.BlockUpdate(jinvariant, 0, (int) param.FP2_ENCODED_BYTES);
+                digest.OutputFinal(h, 0, (int) param.MSG_BYTES);
 
-            for (int i = 0; i < param.MSG_BYTES; i++)
-            {
-                ct[i + param.CRYPTO_PUBLICKEYBYTES] = (byte) (temp[i] ^ h[i]);
-            }
+                for (int i = 0; i < param.MSG_BYTES; i++)
+                {
+                    ct[i + param.CRYPTO_PUBLICKEYBYTES] = (byte) (temp[i] ^ h[i]);
+                }
 
-            // Generate shared secret ss <- H(m||ct)
-            System.Array.Copy(ct, 0, temp, param.MSG_BYTES, param.CRYPTO_CIPHERTEXTBYTES);
+                // Generate shared secret ss <- H(m||ct)
+                System.Array.Copy(ct, 0, temp, param.MSG_BYTES, param.CRYPTO_CIPHERTEXTBYTES);
 
-            digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES));
-            digest.OutputFinal(ss, 0, (int) param.CRYPTO_BYTES);
+                digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES));
+                digest.OutputFinal(ss, 0, (int) param.CRYPTO_BYTES);
 
-            return 0;
+                return 0;
+            }
         }
-    }
 
-    // SIKE's decapsulation
-    // Input:   secret key sk         (CRYPTO_SECRETKEYBYTES = MSG_BYTES + SECRETKEY_B_BYTES + CRYPTO_PUBLICKEYBYTES bytes)
-    //          ciphertext message ct (CRYPTO_CIPHERTEXTBYTES = CRYPTO_PUBLICKEYBYTES + MSG_BYTES bytes)
-    // Outputs: shared secret ss      (CRYPTO_BYTES bytes)
-    public int crypto_kem_dec(byte[] ss, byte[] ct, byte[] sk)
-    {
-        if (isCompressed)
+        // SIKE's decapsulation
+        // Input:   secret key sk         (CRYPTO_SECRETKEYBYTES = MSG_BYTES + SECRETKEY_B_BYTES + CRYPTO_PUBLICKEYBYTES bytes)
+        //          ciphertext message ct (CRYPTO_CIPHERTEXTBYTES = CRYPTO_PUBLICKEYBYTES + MSG_BYTES bytes)
+        // Outputs: shared secret ss      (CRYPTO_BYTES bytes)
+        internal int crypto_kem_dec(byte[] ss, byte[] ct, byte[] sk)
         {
-            byte[] ephemeralsk_ = new byte[param.SECRETKEY_B_BYTES];
-            byte[] jinvariant_ = new byte[param.FP2_ENCODED_BYTES + 2*param.FP2_ENCODED_BYTES + param.SECRETKEY_A_BYTES],
-                   h_ = new byte[param.MSG_BYTES];
-            byte[] temp = new byte[param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES];
-            byte[] tphiBKA_t = jinvariant_;//jinvariant_[param.FP2_ENCODED_BYTES];
+            if (isCompressed)
+            {
+                byte[] ephemeralsk_ = new byte[param.SECRETKEY_B_BYTES];
+                byte[] jinvariant_ = new byte[param.FP2_ENCODED_BYTES + 2*param.FP2_ENCODED_BYTES + param.SECRETKEY_A_BYTES],
+                       h_ = new byte[param.MSG_BYTES];
+                byte[] temp = new byte[param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES];
+                byte[] tphiBKA_t = jinvariant_;//jinvariant_[param.FP2_ENCODED_BYTES];
 
-            // Decrypt
-            sidhCompressed.EphemeralSecretAgreement_A_extended(sk, param.MSG_BYTES, ct, jinvariant_, 1);
+                // Decrypt
+                sidhCompressed.EphemeralSecretAgreement_A_extended(sk, param.MSG_BYTES, ct, jinvariant_, 1);
 
-            IXof digest = new ShakeDigest(256);
-            digest.BlockUpdate(jinvariant_, 0, (int) param.FP2_ENCODED_BYTES);
-            digest.OutputFinal(h_, 0, (int) param.MSG_BYTES);
+                IXof digest = new ShakeDigest(256);
+                digest.BlockUpdate(jinvariant_, 0, (int) param.FP2_ENCODED_BYTES);
+                digest.OutputFinal(h_, 0, (int) param.MSG_BYTES);
 
-//            System.out.println("h_: " + Hex.toHexstring(h_));
+    //            System.out.println("h_: " + Hex.toHexstring(h_));
 
-            for (int i = 0; i < param.MSG_BYTES; i++)
-            {
-                temp[i] = (byte) (ct[i + param.PARTIALLY_COMPRESSED_CHUNK_CT] ^ h_[i]);
-            }
+                for (int i = 0; i < param.MSG_BYTES; i++)
+                {
+                    temp[i] = (byte) (ct[i + param.PARTIALLY_COMPRESSED_CHUNK_CT] ^ h_[i]);
+                }
 
-            // Generate ephemeralsk_ <- G(m||pk) mod oB
-            System.Array.Copy(sk, param.MSG_BYTES + param.SECRETKEY_A_BYTES, temp, param.MSG_BYTES, param.CRYPTO_PUBLICKEYBYTES);
+                // Generate ephemeralsk_ <- G(m||pk) mod oB
+                System.Array.Copy(sk, param.MSG_BYTES + param.SECRETKEY_A_BYTES, temp, param.MSG_BYTES, param.CRYPTO_PUBLICKEYBYTES);
 
-            digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_PUBLICKEYBYTES + param.MSG_BYTES));
-            digest.OutputFinal(ephemeralsk_, 0, (int) param.SECRETKEY_B_BYTES);
-            sidhCompressed.FormatPrivKey_B(ephemeralsk_);
+                digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_PUBLICKEYBYTES + param.MSG_BYTES));
+                digest.OutputFinal(ephemeralsk_, 0, (int) param.SECRETKEY_B_BYTES);
+                sidhCompressed.FormatPrivKey_B(ephemeralsk_);
 
-            // Generate shared secret ss <- H(m||ct), or output ss <- H(s||ct) in case of ct verification failure
-            // No need to recompress, just check if x(phi(P) + t*phi(Q)) == x((a0 + t*a1)*R1 + (b0 + t*b1)*R2)
-            byte selector = sidhCompressed.validate_ciphertext(ephemeralsk_, ct, sk, param.MSG_BYTES + param.SECRETKEY_A_BYTES + param.CRYPTO_PUBLICKEYBYTES, tphiBKA_t, param.FP2_ENCODED_BYTES);
-            // If ct validation passes (selector = 0) then do ss = H(m||ct), otherwise (selector = -1) load s to do ss = H(s||ct)
-            fpx.ct_cmov(temp, sk, param.MSG_BYTES, selector);
+                // Generate shared secret ss <- H(m||ct), or output ss <- H(s||ct) in case of ct verification failure
+                // No need to recompress, just check if x(phi(P) + t*phi(Q)) == x((a0 + t*a1)*R1 + (b0 + t*b1)*R2)
+                byte selector = sidhCompressed.validate_ciphertext(ephemeralsk_, ct, sk, param.MSG_BYTES + param.SECRETKEY_A_BYTES + param.CRYPTO_PUBLICKEYBYTES, tphiBKA_t, param.FP2_ENCODED_BYTES);
+                // If ct validation passes (selector = 0) then do ss = H(m||ct), otherwise (selector = -1) load s to do ss = H(s||ct)
+                fpx.ct_cmov(temp, sk, param.MSG_BYTES, selector);
 
-            System.Array.Copy(ct, 0, temp, param.MSG_BYTES, param.CRYPTO_CIPHERTEXTBYTES);
-            digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES));
-            digest.OutputFinal(ss, 0, (int) param.CRYPTO_BYTES);
+                System.Array.Copy(ct, 0, temp, param.MSG_BYTES, param.CRYPTO_CIPHERTEXTBYTES);
+                digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES));
+                digest.OutputFinal(ss, 0, (int) param.CRYPTO_BYTES);
 
-            return 0;
-        }
-        else
-        {
-            byte[] ephemeralsk_ = new byte[param.SECRETKEY_A_BYTES];
-            byte[] jinvariant_ = new byte[param.FP2_ENCODED_BYTES];
-            byte[] h_ = new byte[param.MSG_BYTES];
-            byte[] c0_ = new byte[param.CRYPTO_PUBLICKEYBYTES];
-            byte[] temp = new byte[param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES];
-
-            // Decrypt
-            // int EphemeralSecretAgreement_B(PrivateKeyB, PublicKeyA, SharedSecretB)
-            sidh.EphemeralSecretAgreement_B(sk, ct, jinvariant_);
-
-            IXof digest = new ShakeDigest(256);
-            digest.BlockUpdate(jinvariant_, 0, (int) param.FP2_ENCODED_BYTES);
-            digest.OutputFinal(h_, 0, (int) param.MSG_BYTES);
-            for (int i = 0; i < param.MSG_BYTES; i++)
-            {
-                temp[i] = (byte) (ct[i + param.CRYPTO_PUBLICKEYBYTES] ^ h_[i]);
+                return 0;
             }
+            else
+            {
+                byte[] ephemeralsk_ = new byte[param.SECRETKEY_A_BYTES];
+                byte[] jinvariant_ = new byte[param.FP2_ENCODED_BYTES];
+                byte[] h_ = new byte[param.MSG_BYTES];
+                byte[] c0_ = new byte[param.CRYPTO_PUBLICKEYBYTES];
+                byte[] temp = new byte[param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES];
 
-            // Generate ephemeralsk_ <- G(m||pk) mod oA
-            System.Array.Copy(sk, param.MSG_BYTES + param.SECRETKEY_B_BYTES, temp, param.MSG_BYTES, param.CRYPTO_PUBLICKEYBYTES);
+                // Decrypt
+                // int EphemeralSecretAgreement_B(PrivateKeyB, PublicKeyA, SharedSecretB)
+                sidh.EphemeralSecretAgreement_B(sk, ct, jinvariant_);
 
-            digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_PUBLICKEYBYTES + param.MSG_BYTES));
-            digest.OutputFinal(ephemeralsk_, 0, (int) param.SECRETKEY_A_BYTES);
-            ephemeralsk_[param.SECRETKEY_A_BYTES - 1] &= (byte) param.MASK_ALICE;
+                IXof digest = new ShakeDigest(256);
+                digest.BlockUpdate(jinvariant_, 0, (int) param.FP2_ENCODED_BYTES);
+                digest.OutputFinal(h_, 0, (int) param.MSG_BYTES);
+                for (int i = 0; i < param.MSG_BYTES; i++)
+                {
+                    temp[i] = (byte) (ct[i + param.CRYPTO_PUBLICKEYBYTES] ^ h_[i]);
+                }
 
+                // Generate ephemeralsk_ <- G(m||pk) mod oA
+                System.Array.Copy(sk, param.MSG_BYTES + param.SECRETKEY_B_BYTES, temp, param.MSG_BYTES, param.CRYPTO_PUBLICKEYBYTES);
 
-            // Generate shared secret ss <- H(m||ct), or output ss <- H(s||ct) in case of ct verification failure
-            sidh.EphemeralKeyGeneration_A(ephemeralsk_, c0_);
+                digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_PUBLICKEYBYTES + param.MSG_BYTES));
+                digest.OutputFinal(ephemeralsk_, 0, (int) param.SECRETKEY_A_BYTES);
+                ephemeralsk_[param.SECRETKEY_A_BYTES - 1] &= (byte) param.MASK_ALICE;
 
-            // If selector = 0 then do ss = H(m||ct), else if selector = -1 load s to do ss = H(s||ct)
-            byte selector = fpx.ct_compare(c0_, ct, param.CRYPTO_PUBLICKEYBYTES);
-            fpx.ct_cmov(temp, sk, param.MSG_BYTES, selector);
 
-            System.Array.Copy(ct, 0, temp, param.MSG_BYTES, param.CRYPTO_CIPHERTEXTBYTES);
-            digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES));
-            digest.OutputFinal(ss, 0, (int) param.CRYPTO_BYTES);
+                // Generate shared secret ss <- H(m||ct), or output ss <- H(s||ct) in case of ct verification failure
+                sidh.EphemeralKeyGeneration_A(ephemeralsk_, c0_);
 
-            return 0;
+                // If selector = 0 then do ss = H(m||ct), else if selector = -1 load s to do ss = H(s||ct)
+                byte selector = fpx.ct_compare(c0_, ct, param.CRYPTO_PUBLICKEYBYTES);
+                fpx.ct_cmov(temp, sk, param.MSG_BYTES, selector);
+
+                System.Array.Copy(ct, 0, temp, param.MSG_BYTES, param.CRYPTO_CIPHERTEXTBYTES);
+                digest.BlockUpdate(temp, 0, (int) (param.CRYPTO_CIPHERTEXTBYTES + param.MSG_BYTES));
+                digest.OutputFinal(ss, 0, (int) param.CRYPTO_BYTES);
+
+                return 0;
+            }
         }
     }
-
 }
-
-}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/sike/SIKEKEMExtractor.cs b/crypto/src/pqc/crypto/sike/SIKEKEMExtractor.cs
index 3c523ba8c..879f1d8ef 100644
--- a/crypto/src/pqc/crypto/sike/SIKEKEMExtractor.cs
+++ b/crypto/src/pqc/crypto/sike/SIKEKEMExtractor.cs
@@ -4,6 +4,7 @@ using Org.BouncyCastle.Crypto;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Sike
 {
+    [Obsolete("Will be removed")]
     public sealed class SikeKemExtractor
         : IEncapsulatedSecretExtractor
     {
@@ -26,10 +27,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
 
         public byte[] ExtractSecret(byte[] encapsulation)
         {
-            return ExtractSecret(encapsulation, engine.GetDefaultSessionKeySize());
+            return ExtractSecret(encapsulation, (int)engine.GetDefaultSessionKeySize());
         }
 
-        public byte[] ExtractSecret(byte[] encapsulation, uint sessionKeySizeInBits)
+        public byte[] ExtractSecret(byte[] encapsulation, int sessionKeySizeInBits)
         {
             Console.Error.WriteLine("WARNING: the SIKE algorithm is only for research purposes, insecure");
             byte[] session_key = new byte[sessionKeySizeInBits / 8];
diff --git a/crypto/src/pqc/crypto/sike/SIKEKEMGenerator.cs b/crypto/src/pqc/crypto/sike/SIKEKEMGenerator.cs
index 76689496f..5e4bd41eb 100644
--- a/crypto/src/pqc/crypto/sike/SIKEKEMGenerator.cs
+++ b/crypto/src/pqc/crypto/sike/SIKEKEMGenerator.cs
@@ -6,6 +6,7 @@ using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Sike
 {
+    [Obsolete("Will be removed")]
     public sealed class SikeKemGenerator
         : IEncapsulatedSecretGenerator
     {
@@ -22,10 +23,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Sike
             SikePublicKeyParameters key = (SikePublicKeyParameters)recipientKey;
             SikeEngine engine = key.Parameters.Engine;
 
-            return GenerateEncapsulated(recipientKey, engine.GetDefaultSessionKeySize());
+            return GenerateEncapsulated(recipientKey, (int)engine.GetDefaultSessionKeySize());
         }
 
-        public ISecretWithEncapsulation GenerateEncapsulated(AsymmetricKeyParameter recipientKey, uint sessionKeySizeInBits)
+        public ISecretWithEncapsulation GenerateEncapsulated(AsymmetricKeyParameter recipientKey,
+            int sessionKeySizeInBits)
         {
             Console.Error.WriteLine("WARNING: the SIKE algorithm is only for research purposes, insecure");
             SikePublicKeyParameters key = (SikePublicKeyParameters)recipientKey;
diff --git a/crypto/src/pqc/crypto/sike/SIKEKeyGenerationParameters.cs b/crypto/src/pqc/crypto/sike/SIKEKeyGenerationParameters.cs
index 353587637..f595032eb 100644
--- a/crypto/src/pqc/crypto/sike/SIKEKeyGenerationParameters.cs
+++ b/crypto/src/pqc/crypto/sike/SIKEKeyGenerationParameters.cs
@@ -1,8 +1,11 @@
+using System;
+
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Sike
 {
+    [Obsolete("Will be removed")]
     public sealed class SikeKeyGenerationParameters
         : KeyGenerationParameters
     {
diff --git a/crypto/src/pqc/crypto/sike/SIKEKeyPairGenerator.cs b/crypto/src/pqc/crypto/sike/SIKEKeyPairGenerator.cs
index 3ba67faa9..20def8a32 100644
--- a/crypto/src/pqc/crypto/sike/SIKEKeyPairGenerator.cs
+++ b/crypto/src/pqc/crypto/sike/SIKEKeyPairGenerator.cs
@@ -1,8 +1,11 @@
+using System;
+
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Sike
 {
+    [Obsolete("Will be removed")]
     public sealed class SikeKeyPairGenerator
         : IAsymmetricCipherKeyPairGenerator
     {
diff --git a/crypto/src/pqc/crypto/sike/SIKEKeyParameters.cs b/crypto/src/pqc/crypto/sike/SIKEKeyParameters.cs
index 5d515eb1d..759c8dd5d 100644
--- a/crypto/src/pqc/crypto/sike/SIKEKeyParameters.cs
+++ b/crypto/src/pqc/crypto/sike/SIKEKeyParameters.cs
@@ -1,7 +1,10 @@
+using System;
+
 using Org.BouncyCastle.Crypto;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Sike
 {
+    [Obsolete("Will be removed")]
     public abstract class SikeKeyParameters
         : AsymmetricKeyParameter
     {
diff --git a/crypto/src/pqc/crypto/sike/SIKEParameters.cs b/crypto/src/pqc/crypto/sike/SIKEParameters.cs
index 3aa332341..d18797067 100644
--- a/crypto/src/pqc/crypto/sike/SIKEParameters.cs
+++ b/crypto/src/pqc/crypto/sike/SIKEParameters.cs
@@ -1,5 +1,8 @@
+using System;
+
 namespace Org.BouncyCastle.Pqc.Crypto.Sike
 {
+    [Obsolete("Will be removed")]
     public sealed class SikeParameters
     {
         public static readonly SikeParameters sikep434 = new SikeParameters(434, false, "sikep434");
diff --git a/crypto/src/pqc/crypto/sike/SIKEPrivateKeyParameters.cs b/crypto/src/pqc/crypto/sike/SIKEPrivateKeyParameters.cs
index 0666ffb72..ee7116b68 100644
--- a/crypto/src/pqc/crypto/sike/SIKEPrivateKeyParameters.cs
+++ b/crypto/src/pqc/crypto/sike/SIKEPrivateKeyParameters.cs
@@ -1,7 +1,10 @@
+using System;
+
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Sike
 {
+    [Obsolete("Will be removed")]
     public sealed class SikePrivateKeyParameters
         : SikeKeyParameters
     {
diff --git a/crypto/src/pqc/crypto/sike/SIKEPublicKeyParameters.cs b/crypto/src/pqc/crypto/sike/SIKEPublicKeyParameters.cs
index b567e979c..3300ed438 100644
--- a/crypto/src/pqc/crypto/sike/SIKEPublicKeyParameters.cs
+++ b/crypto/src/pqc/crypto/sike/SIKEPublicKeyParameters.cs
@@ -1,7 +1,10 @@
+using System;
+
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Sike
 {
+    [Obsolete("Will be removed")]
     public sealed class SikePublicKeyParameters
         : SikeKeyParameters
     {
diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusParameters.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusParameters.cs
index e8a95fd2f..8cde7cf7f 100644
--- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusParameters.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusParameters.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 
 using Org.BouncyCastle.Crypto.Utilities;
@@ -265,9 +266,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
          * @param id the oid of interest.
          * @return the parameter set.
          */
-        public static SphincsPlusParameters GetParams(uint id)
+        public static SphincsPlusParameters GetParams(int id)
         {
-            return oidToParams[id];
+            return oidToParams[Convert.ToUInt32(id)];
         }
 
         /**
@@ -276,14 +277,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
          * @param params the parameters of interest.
          * @return the OID for the parameter set.
          */
-        public static uint GetID(SphincsPlusParameters parameters)
+        public static int GetID(SphincsPlusParameters parameters)
         {
-            return paramsToOid[parameters];
+            return Convert.ToInt32(paramsToOid[parameters]);
         }
 
         public byte[] GetEncoded()
         {
-            return Pack.UInt32_To_BE(GetID(this));
+            return Pack.UInt32_To_BE((uint)GetID(this));
         }
     }
 
diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPrivateKeyParameters.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPrivateKeyParameters.cs
index ed5195da2..55757b927 100644
--- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPrivateKeyParameters.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPrivateKeyParameters.cs
@@ -53,13 +53,13 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 
         public byte[] GetEncoded()
         {
-            var id = Pack.UInt32_To_BE(SphincsPlusParameters.GetID(Parameters));
+            var id = Pack.UInt32_To_BE((uint)SphincsPlusParameters.GetID(Parameters));
             return Arrays.ConcatenateAll(id, m_sk.seed, m_sk.prf, m_pk.seed, m_pk.root);
         }
 
         public byte[] GetEncodedPublicKey()
         {
-            var id = Pack.UInt32_To_BE(SphincsPlusParameters.GetID(Parameters));
+            var id = Pack.UInt32_To_BE((uint)SphincsPlusParameters.GetID(Parameters));
             return Arrays.ConcatenateAll(id, m_pk.seed, m_pk.root);
         }
     }
diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPublicKeyParameters.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPublicKeyParameters.cs
index 96e9324cc..b34843998 100644
--- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPublicKeyParameters.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusPublicKeyParameters.cs
@@ -38,7 +38,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 
         public byte[] GetEncoded()
         {
-            var id = Pack.UInt32_To_BE(SphincsPlusParameters.GetID(Parameters));
+            var id = Pack.UInt32_To_BE((uint)SphincsPlusParameters.GetID(Parameters));
             return Arrays.ConcatenateAll(id, m_pk.seed, m_pk.root);
         }
     }
diff --git a/crypto/src/pqc/crypto/utils/PqcUtilities.cs b/crypto/src/pqc/crypto/utils/PqcUtilities.cs
index 1f1da5e74..67e58fd28 100644
--- a/crypto/src/pqc/crypto/utils/PqcUtilities.cs
+++ b/crypto/src/pqc/crypto/utils/PqcUtilities.cs
@@ -15,7 +15,7 @@ using Org.BouncyCastle.Pqc.Crypto.SphincsPlus;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Utilities
 {
-    public class PqcUtilities
+    internal class PqcUtilities
     {
         private readonly static Dictionary<CmceParameters, DerObjectIdentifier> mcElieceOids = new Dictionary<CmceParameters, DerObjectIdentifier>();
         private readonly static Dictionary<DerObjectIdentifier, CmceParameters> mcElieceParams = new Dictionary<DerObjectIdentifier, CmceParameters>();
@@ -25,10 +25,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
 
         private readonly static Dictionary<PicnicParameters, DerObjectIdentifier> picnicOids = new Dictionary<PicnicParameters, DerObjectIdentifier>();
         private readonly static Dictionary<DerObjectIdentifier, PicnicParameters> picnicParams = new Dictionary<DerObjectIdentifier, PicnicParameters>();
-        
+
+#pragma warning disable CS0618 // Type or member is obsolete
         private readonly static Dictionary<SikeParameters, DerObjectIdentifier> sikeOids = new Dictionary<SikeParameters, DerObjectIdentifier>();
         private readonly static Dictionary<DerObjectIdentifier, SikeParameters> sikeParams = new Dictionary<DerObjectIdentifier, SikeParameters>();
-     
+#pragma warning restore CS0618 // Type or member is obsolete
+
         private readonly static Dictionary<KyberParameters, DerObjectIdentifier> kyberOids = new Dictionary<KyberParameters, DerObjectIdentifier>();
         private readonly static Dictionary<DerObjectIdentifier, KyberParameters> kyberParams = new Dictionary<DerObjectIdentifier, KyberParameters>();
 
@@ -132,7 +134,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
             picnicParams[BCObjectIdentifiers.picnicl1full] = PicnicParameters.picnicl1full;
             picnicParams[BCObjectIdentifiers.picnicl3full] = PicnicParameters.picnicl3full;
             picnicParams[BCObjectIdentifiers.picnicl5full] = PicnicParameters.picnicl5full;
-            
+
+#pragma warning disable CS0618 // Type or member is obsolete
             sikeParams[BCObjectIdentifiers.sikep434] = SikeParameters.sikep434;
             sikeParams[BCObjectIdentifiers.sikep503] = SikeParameters.sikep503;
             sikeParams[BCObjectIdentifiers.sikep610] = SikeParameters.sikep610;
@@ -141,7 +144,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
             sikeParams[BCObjectIdentifiers.sikep503_compressed] = SikeParameters.sikep503_compressed;
             sikeParams[BCObjectIdentifiers.sikep610_compressed] = SikeParameters.sikep610_compressed;
             sikeParams[BCObjectIdentifiers.sikep751_compressed] = SikeParameters.sikep751_compressed;
-            
+
             sikeOids[SikeParameters.sikep434] = BCObjectIdentifiers.sikep434;
             sikeOids[SikeParameters.sikep503] = BCObjectIdentifiers.sikep503;
             sikeOids[SikeParameters.sikep610] = BCObjectIdentifiers.sikep610;
@@ -150,7 +153,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
             sikeOids[SikeParameters.sikep503_compressed] = BCObjectIdentifiers.sikep503_compressed;
             sikeOids[SikeParameters.sikep610_compressed] = BCObjectIdentifiers.sikep610_compressed;
             sikeOids[SikeParameters.sikep751_compressed] = BCObjectIdentifiers.sikep751_compressed;
-            
+#pragma warning restore CS0618 // Type or member is obsolete
+
             kyberOids[KyberParameters.kyber512] = BCObjectIdentifiers.kyber512;
             kyberOids[KyberParameters.kyber768] = BCObjectIdentifiers.kyber768;
             kyberOids[KyberParameters.kyber1024] = BCObjectIdentifiers.kyber1024;
@@ -203,7 +207,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
             hqcOids[HqcParameters.hqc256] = BCObjectIdentifiers.hqc256;
         }
 
-        public static DerObjectIdentifier McElieceOidLookup(CmceParameters parameters)
+        internal static DerObjectIdentifier McElieceOidLookup(CmceParameters parameters)
         {
             return mcElieceOids[parameters];
         }
@@ -248,17 +252,13 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
 
         internal static DerObjectIdentifier SphincsPlusOidLookup(SphincsPlusParameters parameters)
         {
-            uint pId = SphincsPlusParameters.GetID(parameters);
+            int pId = SphincsPlusParameters.GetID(parameters);
 
             if ((pId & 0x020000) == 0x020000)
-            {
                 return BCObjectIdentifiers.sphincsPlus_shake_256;
-            }
 
             if ((pId & 0x05) == 0x05 || (pId & 0x06) == 0x06)
-            {
                 return BCObjectIdentifiers.sphincsPlus_sha_512;
-            }
 
             return BCObjectIdentifiers.sphincsPlus_sha_256;
         }
@@ -272,6 +272,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
         {
             return picnicParams[oid];
         }
+
+#pragma warning disable CS0618 // Type or member is obsolete
         internal static DerObjectIdentifier SikeOidLookup(SikeParameters parameters)
         {
             return sikeOids[parameters];
@@ -281,6 +283,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
         {
             return sikeParams[oid];
         }
+#pragma warning restore CS0618 // Type or member is obsolete
 
         internal static DerObjectIdentifier BikeOidLookup(BikeParameters parameters)
         {
diff --git a/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs b/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs
index 7db65dbfb..6ad9bdb0c 100644
--- a/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs
+++ b/crypto/src/pqc/crypto/utils/PrivateKeyFactory.cs
@@ -81,7 +81,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
             if (algOID.On(BCObjectIdentifiers.sphincsPlus))
             {
                 byte[] keyEnc = Asn1OctetString.GetInstance(keyInfo.ParsePrivateKey()).GetOctets();
-                SphincsPlusParameters spParams = SphincsPlusParameters.GetParams((uint)BigInteger.ValueOf(Pack.BE_To_UInt32(keyEnc, 0)).IntValue);
+                SphincsPlusParameters spParams = SphincsPlusParameters.GetParams(BigInteger.ValueOf(Pack.BE_To_UInt32(keyEnc, 0)).IntValue);
 
                 return new SphincsPlusPrivateKeyParameters(spParams, Arrays.CopyOfRange(keyEnc, 4, keyEnc.Length));
             }
@@ -99,6 +99,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
 
                 return new PicnicPrivateKeyParameters(picnicParams, keyEnc);
             }
+#pragma warning disable CS0618 // Type or member is obsolete
             if (algOID.On(BCObjectIdentifiers.pqc_kem_sike))
             {
                 byte[] keyEnc = Asn1OctetString.GetInstance(keyInfo.ParsePrivateKey()).GetOctets();
@@ -106,6 +107,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
 
                 return new SikePrivateKeyParameters(sikeParams, keyEnc);
             }
+#pragma warning restore CS0618 // Type or member is obsolete
             if (algOID.On(BCObjectIdentifiers.pqc_kem_bike))
             {
                 byte[] keyEnc = Asn1OctetString.GetInstance(keyInfo.ParsePrivateKey()).GetOctets();
diff --git a/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs
index 806eae8b7..53b34b3f4 100644
--- a/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs
+++ b/crypto/src/pqc/crypto/utils/PrivateKeyInfoFactory.cs
@@ -93,6 +93,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
                     PqcUtilities.PicnicOidLookup(picnicPrivateKeyParameters.Parameters));
                 return new PrivateKeyInfo(algorithmIdentifier, new DerOctetString(encoding), attributes);
             }
+#pragma warning disable CS0618 // Type or member is obsolete
             if (privateKey is SikePrivateKeyParameters sikePrivateKeyParameters)
             {
                 byte[] encoding = sikePrivateKeyParameters.GetEncoded();
@@ -101,14 +102,15 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
                     PqcUtilities.SikeOidLookup(sikePrivateKeyParameters.Parameters));
                 return new PrivateKeyInfo(algorithmIdentifier, new DerOctetString(encoding), attributes);
             }
+#pragma warning restore CS0618 // Type or member is obsolete
             if (privateKey is FalconPrivateKeyParameters falconPrivateKeyParameters)
             {
                 Asn1EncodableVector v = new Asn1EncodableVector();
 
                 v.Add(new DerInteger(1));
-                v.Add(new DerOctetString(falconPrivateKeyParameters.GetSpolyf()));
+                v.Add(new DerOctetString(falconPrivateKeyParameters.GetSpolyLittleF()));
                 v.Add(new DerOctetString(falconPrivateKeyParameters.GetG()));
-                v.Add(new DerOctetString(falconPrivateKeyParameters.GetSpolyF()));
+                v.Add(new DerOctetString(falconPrivateKeyParameters.GetSpolyBigF()));
 
                 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(
                     PqcUtilities.FalconOidLookup(falconPrivateKeyParameters.Parameters));
diff --git a/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs b/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs
index 5d55a73aa..9eea279b1 100644
--- a/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs
+++ b/crypto/src/pqc/crypto/utils/PublicKeyFactory.cs
@@ -82,7 +82,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
             Converters[BCObjectIdentifiers.picnicl1full] = new PicnicConverter();
             Converters[BCObjectIdentifiers.picnicl3full] = new PicnicConverter();
             Converters[BCObjectIdentifiers.picnicl5full] = new PicnicConverter();
-            
+
+#pragma warning disable CS0618 // Type or member is obsolete
             Converters[BCObjectIdentifiers.sikep434] = new SikeConverter();
             Converters[BCObjectIdentifiers.sikep503] = new SikeConverter();
             Converters[BCObjectIdentifiers.sikep610] = new SikeConverter();
@@ -91,7 +92,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
             Converters[BCObjectIdentifiers.sikep503_compressed] = new SikeConverter();
             Converters[BCObjectIdentifiers.sikep610_compressed] = new SikeConverter();
             Converters[BCObjectIdentifiers.sikep751_compressed] = new SikeConverter();
-            
+#pragma warning restore CS0618 // Type or member is obsolete
+
             Converters[BCObjectIdentifiers.dilithium2] = new DilithiumConverter();
             Converters[BCObjectIdentifiers.dilithium3] = new DilithiumConverter();
             Converters[BCObjectIdentifiers.dilithium5] = new DilithiumConverter();
@@ -197,11 +199,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
         {
             internal override AsymmetricKeyParameter GetPublicKeyParameters(SubjectPublicKeyInfo keyInfo, object defaultParams)
             {
-            byte[] keyEnc = DerOctetString.GetInstance(keyInfo.ParsePublicKey()).GetOctets();
+                byte[] keyEnc = Asn1OctetString.GetInstance(keyInfo.ParsePublicKey()).GetOctets();
 
-            SphincsPlusParameters spParams = SphincsPlusParameters.GetParams((uint)BigInteger.ValueOf(Pack.BE_To_UInt32(keyEnc, 0)).IntValue);
+                SphincsPlusParameters spParams = SphincsPlusParameters.GetParams((int)Pack.BE_To_UInt32(keyEnc, 0));
 
-            return new SphincsPlusPublicKeyParameters(spParams, Arrays.CopyOfRange(keyEnc, 4, keyEnc.Length));
+                return new SphincsPlusPublicKeyParameters(spParams, Arrays.CopyOfRange(keyEnc, 4, keyEnc.Length));
             }
         }
         
@@ -223,7 +225,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
         {
             internal override AsymmetricKeyParameter GetPublicKeyParameters(SubjectPublicKeyInfo keyInfo, object defaultParams)
             {
-                byte[] keyEnc = DerOctetString.GetInstance(
+                byte[] keyEnc = Asn1OctetString.GetInstance(
                     DerSequence.GetInstance(keyInfo.ParsePublicKey())[0]).GetOctets();
 
                 SaberParameters saberParams = PqcUtilities.SaberParamsLookup(keyInfo.AlgorithmID.Algorithm);
@@ -237,19 +239,20 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
         {
             internal override AsymmetricKeyParameter GetPublicKeyParameters(SubjectPublicKeyInfo keyInfo, object defaultParams)
             {
-                byte[] keyEnc = DerOctetString.GetInstance(keyInfo.ParsePublicKey()).GetOctets();
+                byte[] keyEnc = Asn1OctetString.GetInstance(keyInfo.ParsePublicKey()).GetOctets();
 
                 PicnicParameters picnicParams = PqcUtilities.PicnicParamsLookup(keyInfo.AlgorithmID.Algorithm);
 
                 return new PicnicPublicKeyParameters(picnicParams, keyEnc);
             }
         }
+        [Obsolete("Will be removed")]
         private class SikeConverter
             : SubjectPublicKeyInfoConverter
         {
             internal override AsymmetricKeyParameter GetPublicKeyParameters(SubjectPublicKeyInfo keyInfo, object defaultParams)
             {
-                byte[] keyEnc = DerOctetString.GetInstance(keyInfo.ParsePublicKey()).GetOctets();
+                byte[] keyEnc = Asn1OctetString.GetInstance(keyInfo.ParsePublicKey()).GetOctets();
 
                 SikeParameters sikeParams = PqcUtilities.SikeParamsLookup(keyInfo.AlgorithmID.Algorithm);
 
diff --git a/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs
index 39d437320..b88834e50 100644
--- a/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs
+++ b/crypto/src/pqc/crypto/utils/SubjectPublicKeyInfoFactory.cs
@@ -89,6 +89,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
                     PqcUtilities.PicnicOidLookup(picnicPublicKeyParameters.Parameters));
                 return new SubjectPublicKeyInfo(algorithmIdentifier, new DerOctetString(encoding));
             }
+#pragma warning disable CS0618 // Type or member is obsolete
             if (publicKey is SikePublicKeyParameters sikePublicKeyParameters)
             {
                 byte[] encoding = sikePublicKeyParameters.GetEncoded();
@@ -97,6 +98,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
                     PqcUtilities.SikeOidLookup(sikePublicKeyParameters.Parameters));
                 return new SubjectPublicKeyInfo(algorithmIdentifier, new DerOctetString(encoding));
             }
+#pragma warning restore CS0618 // Type or member is obsolete
             if (publicKey is FalconPublicKeyParameters falconPublicKeyParameters)
             {
                 byte[] encoding = falconPublicKeyParameters.GetEncoded();
diff --git a/crypto/src/security/DotNetUtilities.cs b/crypto/src/security/DotNetUtilities.cs
index 3a7c5f0cb..08853e45e 100644
--- a/crypto/src/security/DotNetUtilities.cs
+++ b/crypto/src/security/DotNetUtilities.cs
@@ -5,9 +5,12 @@ using System.Runtime.Versioning;
 using System.Security.Cryptography;
 using SystemX509 = System.Security.Cryptography.X509Certificates;
 
+using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Pkcs;
 using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Utilities;
@@ -50,23 +53,11 @@ namespace Org.BouncyCastle.Security
 
         public static AsymmetricCipherKeyPair GetDsaKeyPair(DSAParameters dp)
         {
-            DsaValidationParameters validationParameters = (dp.Seed != null)
-                ? new DsaValidationParameters(dp.Seed, dp.Counter)
-                : null;
-
-            DsaParameters parameters = new DsaParameters(
-                new BigInteger(1, dp.P),
-                new BigInteger(1, dp.Q),
-                new BigInteger(1, dp.G),
-                validationParameters);
-
-            DsaPublicKeyParameters pubKey = new DsaPublicKeyParameters(
-                new BigInteger(1, dp.Y),
-                parameters);
+            DsaPublicKeyParameters pubKey = GetDsaPublicKey(dp);
 
             DsaPrivateKeyParameters privKey = new DsaPrivateKeyParameters(
                 new BigInteger(1, dp.X),
-                parameters);
+                pubKey.Parameters);
 
             return new AsymmetricCipherKeyPair(pubKey, privKey);
         }
@@ -93,6 +84,62 @@ namespace Org.BouncyCastle.Security
                 parameters);
         }
 
+#if NETCOREAPP1_0_OR_GREATER || NET47_OR_GREATER || NETSTANDARD1_6_OR_GREATER
+        public static AsymmetricCipherKeyPair GetECDsaKeyPair(ECDsa ecDsa)
+        {
+            return GetECKeyPair("ECDSA", ecDsa.ExportParameters(true));
+        }
+
+        public static ECPublicKeyParameters GetECDsaPublicKey(ECDsa ecDsa)
+        {
+            return GetECPublicKey("ECDSA", ecDsa.ExportParameters(false));
+        }
+
+        public static AsymmetricCipherKeyPair GetECKeyPair(string algorithm, ECParameters ec)
+        {
+            ECPublicKeyParameters pubKey = GetECPublicKey(algorithm, ec);
+
+            ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(
+                pubKey.AlgorithmName,
+                new BigInteger(1, ec.D),
+                pubKey.Parameters);
+
+            return new AsymmetricCipherKeyPair(pubKey, privKey);
+        }
+
+        public static ECPublicKeyParameters GetECPublicKey(string algorithm, ECParameters ec)
+        {
+            X9ECParameters x9 = GetX9ECParameters(ec.Curve);
+            if (x9 == null)
+                throw new NotSupportedException("Unrecognized curve");
+
+            return new ECPublicKeyParameters(
+                algorithm,
+                GetECPoint(x9.Curve, ec.Q),
+                new ECDomainParameters(x9));
+        }
+
+        private static Math.EC.ECPoint GetECPoint(Math.EC.ECCurve curve, ECPoint point)
+        {
+            return curve.CreatePoint(new BigInteger(1, point.X), new BigInteger(1, point.Y));
+        }
+
+        private static X9ECParameters GetX9ECParameters(ECCurve curve)
+        {
+            if (!curve.IsNamed)
+                throw new NotSupportedException("Only named curves are supported");
+
+            Oid oid = curve.Oid;
+            if (oid != null)
+            {
+                string oidValue = oid.Value;
+                if (oidValue != null)
+                    return ECKeyPairGenerator.FindECCurveByOid(new DerObjectIdentifier(oidValue));
+            }
+            return null;
+        }
+#endif
+
         public static AsymmetricCipherKeyPair GetRsaKeyPair(RSA rsa)
         {
             return GetRsaKeyPair(rsa.ExportParameters(true));
@@ -100,17 +147,11 @@ namespace Org.BouncyCastle.Security
 
         public static AsymmetricCipherKeyPair GetRsaKeyPair(RSAParameters rp)
         {
-            BigInteger modulus = new BigInteger(1, rp.Modulus);
-            BigInteger pubExp = new BigInteger(1, rp.Exponent);
-
-            RsaKeyParameters pubKey = new RsaKeyParameters(
-                false,
-                modulus,
-                pubExp);
+            RsaKeyParameters pubKey = GetRsaPublicKey(rp);
 
             RsaPrivateCrtKeyParameters privKey = new RsaPrivateCrtKeyParameters(
-                modulus,
-                pubExp,
+                pubKey.Modulus,
+                pubKey.Exponent,
                 new BigInteger(1, rp.D),
                 new BigInteger(1, rp.P),
                 new BigInteger(1, rp.Q),
@@ -137,17 +178,18 @@ namespace Org.BouncyCastle.Security
 
         public static AsymmetricCipherKeyPair GetKeyPair(AsymmetricAlgorithm privateKey)
         {
-            if (privateKey is DSA)
-            {
-                return GetDsaKeyPair((DSA)privateKey);
-            }
+            if (privateKey is DSA dsa)
+                return GetDsaKeyPair(dsa);
 
-            if (privateKey is RSA)
-            {
-                return GetRsaKeyPair((RSA)privateKey);
-            }
+#if NETCOREAPP1_0_OR_GREATER || NET47_OR_GREATER || NETSTANDARD1_6_OR_GREATER
+            if (privateKey is ECDsa ecDsa)
+                return GetECDsaKeyPair(ecDsa);
+#endif
 
-            throw new ArgumentException("Unsupported algorithm specified", "privateKey");
+            if (privateKey is RSA rsa)
+                return GetRsaKeyPair(rsa);
+
+            throw new ArgumentException("Unsupported algorithm specified", nameof(privateKey));
         }
 
 #if NET5_0_OR_GREATER
@@ -244,6 +286,18 @@ namespace Org.BouncyCastle.Security
             return BigIntegers.AsUnsignedByteArray(size, n);
         }
 
+        // TODO Why do we use CspParameters instead of just RSA.Create in methods below?
+//        private static RSA CreateRSA(RSAParameters rp)
+//        {
+//#if NETCOREAPP2_0_OR_GREATER || NET472_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+//            return RSA.Create(rp);
+//#else
+//            var rsa = RSA.Create();
+//            rsa.ImportParameters(rp);
+//            return rsa;
+//#endif
+//        }
+
 #if NET5_0_OR_GREATER
         [SupportedOSPlatform("windows")]
 #endif
diff --git a/crypto/src/security/JksStore.cs b/crypto/src/security/JksStore.cs
index 30b21fad2..9f4aced96 100644
--- a/crypto/src/security/JksStore.cs
+++ b/crypto/src/security/JksStore.cs
@@ -10,6 +10,7 @@ using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.IO;
 using Org.BouncyCastle.Pkcs;
 using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
 using Org.BouncyCastle.Utilities.Date;
 using Org.BouncyCastle.Utilities.IO;
 using Org.BouncyCastle.X509;
@@ -216,9 +217,7 @@ namespace Org.BouncyCastle.Security
             {
                 var aliases = new HashSet<string>(m_certificateEntries.Keys);
                 aliases.UnionWith(m_keyEntries.Keys);
-                // FIXME
-                //return CollectionUtilities.Proxy(aliases);
-                return aliases;
+                return CollectionUtilities.Proxy(aliases);
             }
         }
 
diff --git a/crypto/src/security/PrivateKeyFactory.cs b/crypto/src/security/PrivateKeyFactory.cs
index d9d855c45..e66224a67 100644
--- a/crypto/src/security/PrivateKeyFactory.cs
+++ b/crypto/src/security/PrivateKeyFactory.cs
@@ -2,8 +2,10 @@ using System;
 using System.IO;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cryptlib;
 using Org.BouncyCastle.Asn1.CryptoPro;
 using Org.BouncyCastle.Asn1.EdEC;
+using Org.BouncyCastle.Asn1.Gnu;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
 using Org.BouncyCastle.Asn1.Rosstandart;
@@ -128,29 +130,99 @@ namespace Org.BouncyCastle.Security
                 ECDomainParameters dParams = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed());
                 return new ECPrivateKeyParameters(d, dParams);
             }
-            else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x2001))
+            else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x2001) ||
+                     algOid.Equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512) ||
+                     algOid.Equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256))
             {
-                Gost3410PublicKeyAlgParameters gostParams = Gost3410PublicKeyAlgParameters.GetInstance(
-                    algID.Parameters.ToAsn1Object());
+                Asn1Object p = algID.Parameters.ToAsn1Object();
+                Gost3410PublicKeyAlgParameters gostParams = Gost3410PublicKeyAlgParameters.GetInstance(p);
 
-                X9ECParameters ecP = ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet);
+                ECGost3410Parameters ecSpec;
+                BigInteger d;
 
-                if (ecP == null)
-                    throw new ArgumentException("Unrecognized curve OID for GostR3410x2001 private key");
+                if (p is Asn1Sequence seq && (seq.Count == 2 || seq.Count == 3))
+                {
+                    X9ECParameters ecP = ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet);
+                    if (ecP == null)
+                        throw new ArgumentException("Unrecognized curve OID for GostR3410x2001 private key");
 
-                Asn1Object privKey = keyInfo.ParsePrivateKey();
-                ECPrivateKeyStructure ec;
+                    ecSpec = new ECGost3410Parameters(
+                        new ECNamedDomainParameters(gostParams.PublicKeyParamSet, ecP),
+                        gostParams.PublicKeyParamSet,
+                        gostParams.DigestParamSet,
+                        gostParams.EncryptionParamSet);
 
-                if (privKey is DerInteger)
-                {
-                    ec = new ECPrivateKeyStructure(ecP.N.BitLength, ((DerInteger)privKey).PositiveValue);
+                    Asn1OctetString privEnc = keyInfo.PrivateKeyData;
+                    if (privEnc.GetOctets().Length == 32 || privEnc.GetOctets().Length == 64)
+                    {
+                        d = new BigInteger(1, Arrays.Reverse(privEnc.GetOctets()));
+                    }
+                    else
+                    {
+                        Asn1Object privKey = keyInfo.ParsePrivateKey();
+                        if (privKey is DerInteger derInteger)
+                        {
+                            d = derInteger.PositiveValue;
+                        }
+                        else
+                        {
+                            byte[] dVal = Arrays.Reverse(Asn1OctetString.GetInstance(privKey).GetOctets());
+                            d = new BigInteger(1, dVal);
+                        }
+                    }
                 }
                 else
                 {
-                    ec = ECPrivateKeyStructure.GetInstance(privKey);
+                    X962Parameters x962Parameters = X962Parameters.GetInstance(p);
+
+                    if (x962Parameters.IsNamedCurve)
+                    {
+                        DerObjectIdentifier oid = DerObjectIdentifier.GetInstance(x962Parameters.Parameters);
+                        X9ECParameters ecP = ECNamedCurveTable.GetByOid(oid);
+                        if (ecP == null)
+                            throw new ArgumentException("Unrecognized curve OID for GostR3410x2001 private key");
+
+                        ecSpec = new ECGost3410Parameters(
+                            new ECNamedDomainParameters(oid, ecP),
+                            gostParams.PublicKeyParamSet,
+                            gostParams.DigestParamSet,
+                            gostParams.EncryptionParamSet);
+                    }
+                    else if (x962Parameters.IsImplicitlyCA)
+                    {
+                        ecSpec = null;
+                    }
+                    else
+                    {
+                        X9ECParameters ecP = X9ECParameters.GetInstance(x962Parameters.Parameters);
+
+                        ecSpec = new ECGost3410Parameters(
+                            new ECNamedDomainParameters(algOid, ecP),
+                            gostParams.PublicKeyParamSet,
+                            gostParams.DigestParamSet,
+                            gostParams.EncryptionParamSet);
+                    }
+
+                    Asn1Object privKey = keyInfo.ParsePrivateKey();
+                    if (privKey is DerInteger derD)
+                    {
+                        d = derD.Value;
+                    }
+                    else
+                    {
+                        ECPrivateKeyStructure ec = ECPrivateKeyStructure.GetInstance(privKey);
+
+                        d = ec.GetKey();
+                    }
                 }
 
-                return new ECPrivateKeyParameters("ECGOST3410", ec.GetKey(), gostParams.PublicKeyParamSet);
+                return new ECPrivateKeyParameters(
+                    d,
+                    new ECGost3410Parameters(
+                        ecSpec,
+                        gostParams.PublicKeyParamSet,
+                        gostParams.DigestParamSet,
+                        gostParams.EncryptionParamSet));
             }
             else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x94))
             {
@@ -170,7 +242,8 @@ namespace Org.BouncyCastle.Security
 
                 return new Gost3410PrivateKeyParameters(x, gostParams.PublicKeyParamSet);
             }
-            else if (algOid.Equals(EdECObjectIdentifiers.id_X25519))
+            else if (algOid.Equals(EdECObjectIdentifiers.id_X25519)
+                || algOid.Equals(CryptlibObjectIdentifiers.curvey25519))
             {
                 return new X25519PrivateKeyParameters(GetRawKey(keyInfo));
             }
@@ -178,7 +251,8 @@ namespace Org.BouncyCastle.Security
             {
                 return new X448PrivateKeyParameters(GetRawKey(keyInfo));
             }
-            else if (algOid.Equals(EdECObjectIdentifiers.id_Ed25519))
+            else if (algOid.Equals(EdECObjectIdentifiers.id_Ed25519)
+                || algOid.Equals(GnuObjectIdentifiers.Ed25519))
             {
                 return new Ed25519PrivateKeyParameters(GetRawKey(keyInfo));
             }
diff --git a/crypto/src/security/PublicKeyFactory.cs b/crypto/src/security/PublicKeyFactory.cs
index e0c7ce950..8c3e9db99 100644
--- a/crypto/src/security/PublicKeyFactory.cs
+++ b/crypto/src/security/PublicKeyFactory.cs
@@ -2,8 +2,10 @@ using System;
 using System.IO;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cryptlib;
 using Org.BouncyCastle.Asn1.CryptoPro;
 using Org.BouncyCastle.Asn1.EdEC;
+using Org.BouncyCastle.Asn1.Gnu;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
 using Org.BouncyCastle.Asn1.Rosstandart;
@@ -211,7 +213,8 @@ namespace Org.BouncyCastle.Security
 
                 return new Gost3410PublicKeyParameters(y, algParams.PublicKeyParamSet);
             }
-            else if (algOid.Equals(EdECObjectIdentifiers.id_X25519))
+            else if (algOid.Equals(EdECObjectIdentifiers.id_X25519)
+                || algOid.Equals(CryptlibObjectIdentifiers.curvey25519))
             {
                 return new X25519PublicKeyParameters(GetRawKey(keyInfo));
             }
@@ -219,7 +222,8 @@ namespace Org.BouncyCastle.Security
             {
                 return new X448PublicKeyParameters(GetRawKey(keyInfo));
             }
-            else if (algOid.Equals(EdECObjectIdentifiers.id_Ed25519))
+            else if (algOid.Equals(EdECObjectIdentifiers.id_Ed25519)
+                || algOid.Equals(GnuObjectIdentifiers.Ed25519))
             {
                 return new Ed25519PublicKeyParameters(GetRawKey(keyInfo));
             }
diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs
index e6210dad7..6500cdf13 100644
--- a/crypto/src/security/SignerUtilities.cs
+++ b/crypto/src/security/SignerUtilities.cs
@@ -379,14 +379,18 @@ namespace Org.BouncyCastle.Security
 
             AlgorithmMap["GOST-3410-2012-256"] = "ECGOST3410-2012-256";
             AlgorithmMap["GOST3411WITHECGOST3410-2012-256"] = "ECGOST3410-2012-256";
+            AlgorithmMap["GOST3411-2012-256WITHECGOST3410"] = "ECGOST3410-2012-256";
             AlgorithmMap["GOST3411-2012-256WITHECGOST3410-2012-256"] = "ECGOST3410-2012-256";
+            AlgorithmMap["GOST3411-2012-256/ECGOST3410"] = "ECGOST3410-2012-256";
             AlgorithmMap["GOST3411-2012-256/ECGOST3410-2012-256"] = "ECGOST3410-2012-256";
             AlgorithmMap[RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256.Id] =
                 "ECGOST3410-2012-256";
 
             AlgorithmMap["GOST-3410-2012-512"] = "ECGOST3410-2012-512";
             AlgorithmMap["GOST3411WITHECGOST3410-2012-512"] = "ECGOST3410-2012-512";
+            AlgorithmMap["GOST3411-2012-512WITHECGOST3410"] = "ECGOST3410-2012-512";
             AlgorithmMap["GOST3411-2012-512WITHECGOST3410-2012-512"] = "ECGOST3410-2012-512";
+            AlgorithmMap["GOST3411-2012-512/ECGOST3410"] = "ECGOST3410-2012-512";
             AlgorithmMap["GOST3411-2012-512/ECGOST3410-2012-512"] = "ECGOST3410-2012-512";
             AlgorithmMap[RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512.Id] =
                 "ECGOST3410-2012-512";
diff --git a/crypto/src/tls/AbstractTlsPeer.cs b/crypto/src/tls/AbstractTlsPeer.cs
index 4e1b28e58..6d7c88f1b 100644
--- a/crypto/src/tls/AbstractTlsPeer.cs
+++ b/crypto/src/tls/AbstractTlsPeer.cs
@@ -157,5 +157,7 @@ namespace Org.BouncyCastle.Tls
         {
             return HeartbeatMode.peer_not_allowed_to_send;
         }
+
+        public virtual bool IgnoreCorruptDtlsRecords => false;
     }
 }
diff --git a/crypto/src/tls/CombinedHash.cs b/crypto/src/tls/CombinedHash.cs
index 71151d2a5..360b9d426 100644
--- a/crypto/src/tls/CombinedHash.cs
+++ b/crypto/src/tls/CombinedHash.cs
@@ -43,6 +43,14 @@ namespace Org.BouncyCastle.Tls
             m_sha1.Update(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            m_md5.Update(input);
+            m_sha1.Update(input);
+        }
+#endif
+
         public virtual byte[] CalculateHash()
         {
             if (null != m_context && TlsUtilities.IsSsl(m_context))
diff --git a/crypto/src/tls/DatagramReceiver.cs b/crypto/src/tls/DatagramReceiver.cs
index 5ab605ac4..a689515f6 100644
--- a/crypto/src/tls/DatagramReceiver.cs
+++ b/crypto/src/tls/DatagramReceiver.cs
@@ -10,5 +10,10 @@ namespace Org.BouncyCastle.Tls
 
         /// <exception cref="IOException"/>
         int Receive(byte[] buf, int off, int len, int waitMillis);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <exception cref="IOException"/>
+        int Receive(Span<byte> buffer, int waitMillis);
+#endif
     }
 }
diff --git a/crypto/src/tls/DatagramSender.cs b/crypto/src/tls/DatagramSender.cs
index bf14c18fe..c2a987b51 100644
--- a/crypto/src/tls/DatagramSender.cs
+++ b/crypto/src/tls/DatagramSender.cs
@@ -10,5 +10,10 @@ namespace Org.BouncyCastle.Tls
 
         /// <exception cref="IOException"/>
         void Send(byte[] buf, int off, int len);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <exception cref="IOException"/>
+        void Send(ReadOnlySpan<byte> buffer);
+#endif
     }
 }
diff --git a/crypto/src/tls/DeferredHash.cs b/crypto/src/tls/DeferredHash.cs
index 82f7899a5..e6397ab1e 100644
--- a/crypto/src/tls/DeferredHash.cs
+++ b/crypto/src/tls/DeferredHash.cs
@@ -176,6 +176,22 @@ namespace Org.BouncyCastle.Tls
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            if (m_buf != null)
+            {
+                m_buf.Write(input);
+                return;
+            }
+
+            foreach (TlsHash hash in m_hashes.Values)
+            {
+                hash.Update(input);
+            }
+        }
+#endif
+
         public byte[] CalculateHash()
         {
             throw new InvalidOperationException("Use 'ForkPrfHash' to get a definite hash");
diff --git a/crypto/src/tls/DtlsClientProtocol.cs b/crypto/src/tls/DtlsClientProtocol.cs
index b8c09617a..0a4a711ae 100644
--- a/crypto/src/tls/DtlsClientProtocol.cs
+++ b/crypto/src/tls/DtlsClientProtocol.cs
@@ -173,7 +173,7 @@ namespace Org.BouncyCastle.Tls
                 recordLayer.InitHeartbeat(state.heartbeat,
                     HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
 
-                return new DtlsTransport(recordLayer);
+                return new DtlsTransport(recordLayer, state.client.IgnoreCorruptDtlsRecords);
             }
 
             InvalidateSession(state);
@@ -392,7 +392,7 @@ namespace Org.BouncyCastle.Tls
 
             recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
 
-            return new DtlsTransport(recordLayer);
+            return new DtlsTransport(recordLayer, state.client.IgnoreCorruptDtlsRecords);
         }
 
         /// <exception cref="IOException"/>
diff --git a/crypto/src/tls/DtlsRecordLayer.cs b/crypto/src/tls/DtlsRecordLayer.cs
index b93253146..bab6892b7 100644
--- a/crypto/src/tls/DtlsRecordLayer.cs
+++ b/crypto/src/tls/DtlsRecordLayer.cs
@@ -1,8 +1,6 @@
 using System;
 using System.IO;
-#if !PORTABLE || DOTNET
 using System.Net.Sockets;
-#endif
 
 using Org.BouncyCastle.Tls.Crypto;
 using Org.BouncyCastle.Utilities;
@@ -244,6 +242,9 @@ namespace Org.BouncyCastle.Tls
         /// <exception cref="IOException"/>
         public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Receive(buf.AsSpan(off, len), waitMillis);
+#else
             long currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
 
             Timeout timeout = Timeout.ForWaitMillis(waitMillis, currentTimeMillis);
@@ -307,11 +308,85 @@ namespace Org.BouncyCastle.Tls
             }
 
             return -1;
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <exception cref="IOException"/>
+        public virtual int Receive(Span<byte> buffer, int waitMillis)
+        {
+            long currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
+
+            Timeout timeout = Timeout.ForWaitMillis(waitMillis, currentTimeMillis);
+            byte[] record = null;
+
+            while (waitMillis >= 0)
+            {
+                if (null != m_retransmitTimeout && m_retransmitTimeout.RemainingMillis(currentTimeMillis) < 1)
+                {
+                    m_retransmit = null;
+                    m_retransmitEpoch = null;
+                    m_retransmitTimeout = null;
+                }
+
+                if (Timeout.HasExpired(m_heartbeatTimeout, currentTimeMillis))
+                {
+                    if (null != m_heartbeatInFlight)
+                        throw new TlsTimeoutException("Heartbeat timed out");
+
+                    this.m_heartbeatInFlight = HeartbeatMessage.Create(m_context,
+                        HeartbeatMessageType.heartbeat_request, m_heartbeat.GeneratePayload());
+                    this.m_heartbeatTimeout = new Timeout(m_heartbeat.TimeoutMillis, currentTimeMillis);
+
+                    this.m_heartbeatResendMillis = DtlsReliableHandshake.INITIAL_RESEND_MILLIS;
+                    this.m_heartbeatResendTimeout = new Timeout(m_heartbeatResendMillis, currentTimeMillis);
+
+                    SendHeartbeatMessage(m_heartbeatInFlight);
+                }
+                else if (Timeout.HasExpired(m_heartbeatResendTimeout, currentTimeMillis))
+                {
+                    this.m_heartbeatResendMillis = DtlsReliableHandshake.BackOff(m_heartbeatResendMillis);
+                    this.m_heartbeatResendTimeout = new Timeout(m_heartbeatResendMillis, currentTimeMillis);
+
+                    SendHeartbeatMessage(m_heartbeatInFlight);
+                }
+
+                waitMillis = Timeout.ConstrainWaitMillis(waitMillis, m_heartbeatTimeout, currentTimeMillis);
+                waitMillis = Timeout.ConstrainWaitMillis(waitMillis, m_heartbeatResendTimeout, currentTimeMillis);
+
+                // NOTE: Guard against bad logic giving a negative value 
+                if (waitMillis < 0)
+                {
+                    waitMillis = 1;
+                }
+
+                int receiveLimit = System.Math.Min(buffer.Length, GetReceiveLimit()) + RECORD_HEADER_LENGTH;
+                if (null == record || record.Length < receiveLimit)
+                {
+                    record = new byte[receiveLimit];
+                }
+
+                int received = ReceiveRecord(record, 0, receiveLimit, waitMillis);
+                int processed = ProcessRecord(received, record, buffer);
+                if (processed >= 0)
+                {
+                    return processed;
+                }
+
+                currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
+                waitMillis = Timeout.GetWaitMillis(timeout, currentTimeMillis);
+            }
+
+            return -1;
+        }
+#endif
+
         /// <exception cref="IOException"/>
         public virtual void Send(byte[] buf, int off, int len)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Send(buf.AsSpan(off, len));
+#else
             short contentType = ContentType.application_data;
 
             if (m_inHandshake || m_writeEpoch == m_retransmitEpoch)
@@ -340,7 +415,7 @@ namespace Org.BouncyCastle.Tls
                     // Implicitly send change_cipher_spec and change to pending cipher state
 
                     // TODO Send change_cipher_spec and finished records in single datagram?
-                    byte[] data = new byte[]{ 1 };
+                    byte[] data = new byte[1]{ 1 };
                     SendRecord(ContentType.change_cipher_spec, data, 0, data.Length);
 
                     this.m_writeEpoch = nextEpoch;
@@ -348,7 +423,51 @@ namespace Org.BouncyCastle.Tls
             }
 
             SendRecord(contentType, buf, off, len);
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <exception cref="IOException"/>
+        public virtual void Send(ReadOnlySpan<byte> buffer)
+        {
+            short contentType = ContentType.application_data;
+
+            if (m_inHandshake || m_writeEpoch == m_retransmitEpoch)
+            {
+                contentType = ContentType.handshake;
+
+                short handshakeType = TlsUtilities.ReadUint8(buffer);
+                if (handshakeType == HandshakeType.finished)
+                {
+                    DtlsEpoch nextEpoch = null;
+                    if (m_inHandshake)
+                    {
+                        nextEpoch = m_pendingEpoch;
+                    }
+                    else if (m_writeEpoch == m_retransmitEpoch)
+                    {
+                        nextEpoch = m_currentEpoch;
+                    }
+
+                    if (nextEpoch == null)
+                    {
+                        // TODO
+                        throw new InvalidOperationException();
+                    }
+
+                    // Implicitly send change_cipher_spec and change to pending cipher state
+
+                    // TODO Send change_cipher_spec and finished records in single datagram?
+                    ReadOnlySpan<byte> data = stackalloc byte[1]{ 1 };
+                    SendRecord(ContentType.change_cipher_spec, data);
+
+                    this.m_writeEpoch = nextEpoch;
+                }
+            }
+
+            SendRecord(contentType, buffer);
         }
+#endif
 
         /// <exception cref="IOException"/>
         public virtual void Close()
@@ -434,11 +553,13 @@ namespace Org.BouncyCastle.Tls
         {
             m_peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause);
 
-            byte[] error = new byte[2];
-            error[0] = (byte)alertLevel;
-            error[1] = (byte)alertDescription;
-
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            ReadOnlySpan<byte> error = stackalloc byte[2]{ (byte)alertLevel, (byte)alertDescription };
+            SendRecord(ContentType.alert, error);
+#else
+            byte[] error = new byte[2]{ (byte)alertLevel, (byte)alertDescription };
             SendRecord(ContentType.alert, error, 0, 2);
+#endif
         }
 
         /// <exception cref="IOException"/>
@@ -452,7 +573,6 @@ namespace Org.BouncyCastle.Tls
             {
                 return -1;
             }
-#if !PORTABLE || DOTNET
             catch (SocketException e)
             {
                 if (TlsUtilities.IsTimeout(e))
@@ -460,7 +580,6 @@ namespace Org.BouncyCastle.Tls
 
                 throw e;
             }
-#endif
             // TODO[tls-port] Can we support interrupted IO on .NET?
             //catch (InterruptedIOException e)
             //{
@@ -471,7 +590,11 @@ namespace Org.BouncyCastle.Tls
 
         // TODO Include 'currentTimeMillis' as an argument, use with Timeout, resetHeartbeat
         /// <exception cref="IOException"/>
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int ProcessRecord(int received, byte[] record, Span<byte> buffer)
+#else
         private int ProcessRecord(int received, byte[] record, byte[] buf, int off)
+#endif
         {
             // NOTE: received < 0 (timeout) is covered by this first case
             if (received < RECORD_HEADER_LENGTH)
@@ -704,7 +827,11 @@ namespace Org.BouncyCastle.Tls
                 this.m_retransmitTimeout = null;
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            decoded.buf.AsSpan(decoded.off, decoded.len).CopyTo(buffer);
+#else
             Array.Copy(decoded.buf, decoded.off, buf, off, decoded.len);
+#endif
             return decoded.len;
         }
 
@@ -716,9 +843,7 @@ namespace Org.BouncyCastle.Tls
                 int length = 0;
                 if (m_recordQueue.Available >= RECORD_HEADER_LENGTH)
                 {
-                    byte[] lengthBytes = new byte[2];
-                    m_recordQueue.Read(lengthBytes, 0, 2, 11);
-                    length = TlsUtilities.ReadUint16(lengthBytes, 0);
+                    length = m_recordQueue.ReadUint16(11);
                 }
 
                 int received = System.Math.Min(m_recordQueue.Available, RECORD_HEADER_LENGTH + length);
@@ -758,9 +883,16 @@ namespace Org.BouncyCastle.Tls
         {
             MemoryStream output = new MemoryStream();
             heartbeatMessage.Encode(output);
-            byte[] buf = output.ToArray();
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            if (!output.TryGetBuffer(out var buffer))
+                throw new InvalidOperationException();
+
+            SendRecord(ContentType.heartbeat, buffer);
+#else
+            byte[] buf = output.ToArray();
             SendRecord(ContentType.heartbeat, buf, 0, buf.Length);
+#endif
         }
 
         /*
@@ -770,12 +902,20 @@ namespace Org.BouncyCastle.Tls
          * be possible reordering of records (which might surprise a reliable transport implementation).
          */
         /// <exception cref="IOException"/>
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void SendRecord(short contentType, ReadOnlySpan<byte> buffer)
+#else
         private void SendRecord(short contentType, byte[] buf, int off, int len)
+#endif
         {
             // Never send anything until a valid ClientHello has been received
             if (m_writeVersion == null)
                 return;
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            int len = buffer.Length;
+#endif
+
             if (len > m_plaintextLimit)
                 throw new TlsFatalAlert(AlertDescription.internal_error);
 
@@ -793,8 +933,13 @@ namespace Org.BouncyCastle.Tls
                 long macSequenceNumber = GetMacSequenceNumber(recordEpoch, recordSequenceNumber);
                 ProtocolVersion recordVersion = m_writeVersion;
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                TlsEncodeResult encoded = m_writeEpoch.Cipher.EncodePlaintext(macSequenceNumber, contentType,
+                    recordVersion, RECORD_HEADER_LENGTH, buffer);
+#else
                 TlsEncodeResult encoded = m_writeEpoch.Cipher.EncodePlaintext(macSequenceNumber, contentType,
                     recordVersion, RECORD_HEADER_LENGTH, buf, off, len);
+#endif
 
                 int ciphertextLength = encoded.len - RECORD_HEADER_LENGTH;
                 TlsUtilities.CheckUint16(ciphertextLength);
diff --git a/crypto/src/tls/DtlsServerProtocol.cs b/crypto/src/tls/DtlsServerProtocol.cs
index b42f97b64..5edd5595e 100644
--- a/crypto/src/tls/DtlsServerProtocol.cs
+++ b/crypto/src/tls/DtlsServerProtocol.cs
@@ -381,7 +381,7 @@ namespace Org.BouncyCastle.Tls
 
             recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
 
-            return new DtlsTransport(recordLayer);
+            return new DtlsTransport(recordLayer, state.server.IgnoreCorruptDtlsRecords);
         }
 
         /// <exception cref="IOException"/>
diff --git a/crypto/src/tls/DtlsTransport.cs b/crypto/src/tls/DtlsTransport.cs
index a41cb7866..033e0af0b 100644
--- a/crypto/src/tls/DtlsTransport.cs
+++ b/crypto/src/tls/DtlsTransport.cs
@@ -1,8 +1,6 @@
 using System;
 using System.IO;
-#if !PORTABLE || DOTNET
 using System.Net.Sockets;
-#endif
 
 namespace Org.BouncyCastle.Tls
 {
@@ -10,10 +8,12 @@ namespace Org.BouncyCastle.Tls
         : DatagramTransport
     {
         private readonly DtlsRecordLayer m_recordLayer;
+        private readonly bool m_ignoreCorruptRecords;
 
-        internal DtlsTransport(DtlsRecordLayer recordLayer)
+        internal DtlsTransport(DtlsRecordLayer recordLayer, bool ignoreCorruptRecords)
         {
-            this.m_recordLayer = recordLayer;
+            m_recordLayer = recordLayer;
+            m_ignoreCorruptRecords = ignoreCorruptRecords;
         }
 
         /// <exception cref="IOException"/>
@@ -37,6 +37,10 @@ namespace Org.BouncyCastle.Tls
                 throw new ArgumentException("invalid offset: " + off, "off");
             if (len < 0 || len > buf.Length - off)
                 throw new ArgumentException("invalid length: " + len, "len");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Receive(buf.AsSpan(off, len), waitMillis);
+#else
             if (waitMillis < 0)
                 throw new ArgumentException("cannot be negative", "waitMillis");
 
@@ -46,6 +50,9 @@ namespace Org.BouncyCastle.Tls
             }
             catch (TlsFatalAlert fatalAlert)
             {
+                if (m_ignoreCorruptRecords && AlertDescription.bad_record_mac == fatalAlert.AlertDescription)
+                    return -1;
+
                 m_recordLayer.Fail(fatalAlert.AlertDescription);
                 throw fatalAlert;
             }
@@ -53,7 +60,6 @@ namespace Org.BouncyCastle.Tls
             {
                 throw e;
             }
-#if !PORTABLE || DOTNET
             catch (SocketException e)
             {
                 if (TlsUtilities.IsTimeout(e))
@@ -62,7 +68,55 @@ namespace Org.BouncyCastle.Tls
                 m_recordLayer.Fail(AlertDescription.internal_error);
                 throw new TlsFatalAlert(AlertDescription.internal_error, e);
             }
+            // TODO[tls-port] Can we support interrupted IO on .NET?
+            //catch (InterruptedIOException e)
+            //{
+            //    throw e;
+            //}
+            catch (IOException e)
+            {
+                m_recordLayer.Fail(AlertDescription.internal_error);
+                throw e;
+            }
+            catch (Exception e)
+            {
+                m_recordLayer.Fail(AlertDescription.internal_error);
+                throw new TlsFatalAlert(AlertDescription.internal_error, e);
+            }
 #endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <exception cref="IOException"/>
+        public virtual int Receive(Span<byte> buffer, int waitMillis)
+        {
+            if (waitMillis < 0)
+                throw new ArgumentException("cannot be negative", nameof(waitMillis));
+
+            try
+            {
+                return m_recordLayer.Receive(buffer, waitMillis);
+            }
+            catch (TlsFatalAlert fatalAlert)
+            {
+                if (m_ignoreCorruptRecords && AlertDescription.bad_record_mac == fatalAlert.AlertDescription)
+                    return -1;
+
+                m_recordLayer.Fail(fatalAlert.AlertDescription);
+                throw fatalAlert;
+            }
+            catch (TlsTimeoutException e)
+            {
+                throw e;
+            }
+            catch (SocketException e)
+            {
+                if (TlsUtilities.IsTimeout(e))
+                    throw e;
+
+                m_recordLayer.Fail(AlertDescription.internal_error);
+                throw new TlsFatalAlert(AlertDescription.internal_error, e);
+            }
             // TODO[tls-port] Can we support interrupted IO on .NET?
             //catch (InterruptedIOException e)
             //{
@@ -79,6 +133,7 @@ namespace Org.BouncyCastle.Tls
                 throw new TlsFatalAlert(AlertDescription.internal_error, e);
             }
         }
+#endif
 
         /// <exception cref="IOException"/>
         public virtual void Send(byte[] buf, int off, int len)
@@ -90,6 +145,9 @@ namespace Org.BouncyCastle.Tls
             if (len < 0 || len > buf.Length - off)
                 throw new ArgumentException("invalid length: " + len, "len");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Send(buf.AsSpan(off, len));
+#else
             try
             {
                 m_recordLayer.Send(buf, off, len);
@@ -103,7 +161,6 @@ namespace Org.BouncyCastle.Tls
             {
                 throw e;
             }
-#if !PORTABLE || DOTNET
             catch (SocketException e)
             {
                 if (TlsUtilities.IsTimeout(e))
@@ -112,7 +169,48 @@ namespace Org.BouncyCastle.Tls
                 m_recordLayer.Fail(AlertDescription.internal_error);
                 throw new TlsFatalAlert(AlertDescription.internal_error, e);
             }
+            // TODO[tls-port] Can we support interrupted IO on .NET?
+            //catch (InterruptedIOException e)
+            //{
+            //    throw e;
+            //}
+            catch (IOException e)
+            {
+                m_recordLayer.Fail(AlertDescription.internal_error);
+                throw e;
+            }
+            catch (Exception e)
+            {
+                m_recordLayer.Fail(AlertDescription.internal_error);
+                throw new TlsFatalAlert(AlertDescription.internal_error, e);
+            }
 #endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void Send(ReadOnlySpan<byte> buffer)
+        {
+            try
+            {
+                m_recordLayer.Send(buffer);
+            }
+            catch (TlsFatalAlert fatalAlert)
+            {
+                m_recordLayer.Fail(fatalAlert.AlertDescription);
+                throw fatalAlert;
+            }
+            catch (TlsTimeoutException e)
+            {
+                throw e;
+            }
+            catch (SocketException e)
+            {
+                if (TlsUtilities.IsTimeout(e))
+                    throw e;
+
+                m_recordLayer.Fail(AlertDescription.internal_error);
+                throw new TlsFatalAlert(AlertDescription.internal_error, e);
+            }
             // TODO[tls-port] Can we support interrupted IO on .NET?
             //catch (InterruptedIOException e)
             //{
@@ -129,6 +227,7 @@ namespace Org.BouncyCastle.Tls
                 throw new TlsFatalAlert(AlertDescription.internal_error, e);
             }
         }
+#endif
 
         /// <exception cref="IOException"/>
         public virtual void Close()
diff --git a/crypto/src/tls/RecordStream.cs b/crypto/src/tls/RecordStream.cs
index a97d34698..a5926d05b 100644
--- a/crypto/src/tls/RecordStream.cs
+++ b/crypto/src/tls/RecordStream.cs
@@ -258,6 +258,9 @@ namespace Org.BouncyCastle.Tls
         /// <exception cref="IOException"/>
         internal void WriteRecord(short contentType, byte[] plaintext, int plaintextOffset, int plaintextLength)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            WriteRecord(contentType, plaintext.AsSpan(plaintextOffset, plaintextLength));
+#else
             // Never send anything until a valid ClientHello has been received
             if (m_writeVersion == null)
                 return;
@@ -298,8 +301,56 @@ namespace Org.BouncyCastle.Tls
             //}
 
             m_output.Flush();
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <exception cref="IOException"/>
+        internal void WriteRecord(short contentType, ReadOnlySpan<byte> plaintext)
+        {
+            // Never send anything until a valid ClientHello has been received
+            if (m_writeVersion == null)
+                return;
+
+            /*
+             * RFC 5246 6.2.1 The length should not exceed 2^14.
+             */
+            CheckLength(plaintext.Length, m_plaintextLimit, AlertDescription.internal_error);
+
+            /*
+             * RFC 5246 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+             * or ChangeCipherSpec content types.
+             */
+            if (plaintext.Length < 1 && contentType != ContentType.application_data)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            long seqNo = m_writeSeqNo.NextValue(AlertDescription.internal_error);
+            ProtocolVersion recordVersion = m_writeVersion;
+
+            TlsEncodeResult encoded = m_writeCipher.EncodePlaintext(seqNo, contentType, recordVersion,
+                RecordFormat.FragmentOffset, plaintext);
+
+            int ciphertextLength = encoded.len - RecordFormat.FragmentOffset;
+            TlsUtilities.CheckUint16(ciphertextLength);
+
+            TlsUtilities.WriteUint8(encoded.recordType, encoded.buf, encoded.off + RecordFormat.TypeOffset);
+            TlsUtilities.WriteVersion(recordVersion, encoded.buf, encoded.off + RecordFormat.VersionOffset);
+            TlsUtilities.WriteUint16(ciphertextLength, encoded.buf, encoded.off + RecordFormat.LengthOffset);
+
+            // TODO[tls-port] Can we support interrupted IO on .NET?
+            //try
+            //{
+                m_output.Write(encoded.buf, encoded.off, encoded.len);
+            //}
+            //catch (InterruptedIOException e)
+            //{
+            //    throw new TlsFatalAlert(AlertDescription.internal_error, e);
+            //}
+
+            m_output.Flush();
+        }
+#endif
+
         /// <exception cref="IOException"/>
         internal void Close()
         {
diff --git a/crypto/src/tls/TlsPeer.cs b/crypto/src/tls/TlsPeer.cs
index ef2837135..04d66d38f 100644
--- a/crypto/src/tls/TlsPeer.cs
+++ b/crypto/src/tls/TlsPeer.cs
@@ -119,5 +119,12 @@ namespace Org.BouncyCastle.Tls
         /// </remarks>
         /// <returns>the <see cref="HeartbeatMode"/> value.</returns>
         short GetHeartbeatPolicy();
+
+        /// <summary>Indicates whether a DTLS connection should ignore corrupt records (bad_record_mac) instead of
+        /// failing the connection.</summary>
+        /// <remarks>Called only once at the start of a connection and applies throughout.</remarks>
+        /// <returns>The value <c>true</c> to ignore corrupt DTLS records, or <c>false</c> to fail the connection.
+        /// </returns>
+        bool IgnoreCorruptDtlsRecords { get; }
     }
 }
diff --git a/crypto/src/tls/TlsProtocol.cs b/crypto/src/tls/TlsProtocol.cs
index 3461e9b58..437a51447 100644
--- a/crypto/src/tls/TlsProtocol.cs
+++ b/crypto/src/tls/TlsProtocol.cs
@@ -707,6 +707,9 @@ namespace Org.BouncyCastle.Tls
         {
             Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return ReadApplicationData(buffer.AsSpan(offset, count));
+#else
             if (!m_appDataReady)
                 throw new InvalidOperationException("Cannot read application data until initial handshake completed.");
 
@@ -733,8 +736,42 @@ namespace Org.BouncyCastle.Tls
                 m_applicationDataQueue.RemoveData(buffer, offset, count, 0);
             }
             return count;
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ReadApplicationData(Span<byte> buffer)
+        {
+            if (!m_appDataReady)
+                throw new InvalidOperationException("Cannot read application data until initial handshake completed.");
+
+            while (m_applicationDataQueue.Available < 1)
+            {
+                if (this.m_closed)
+                {
+                    if (this.m_failedWithError)
+                        throw new IOException("Cannot read application data on failed TLS connection");
+
+                    return 0;
+                }
+
+                /*
+                 * NOTE: Only called more than once when empty records are received, so no special
+                 * InterruptedIOException handling is necessary.
+                 */
+                SafeReadRecord();
+            }
+
+            int count = buffer.Length;
+            if (count > 0)
+            {
+                count = System.Math.Min(count, m_applicationDataQueue.Available);
+                m_applicationDataQueue.RemoveData(buffer[..count], 0);
+            }
+            return count;
+        }
+#endif
+
         /// <exception cref="IOException"/>
         protected virtual RecordPreview SafePreviewRecordHeader(byte[] recordHeader)
         {
@@ -850,6 +887,32 @@ namespace Org.BouncyCastle.Tls
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <exception cref="IOException"/>
+        protected virtual void SafeWriteRecord(short type, ReadOnlySpan<byte> buffer)
+        {
+            try
+            {
+                m_recordStream.WriteRecord(type, buffer);
+            }
+            catch (TlsFatalAlert e)
+            {
+                HandleException(e.AlertDescription, "Failed to write record", e);
+                throw e;
+            }
+            catch (IOException e)
+            {
+                HandleException(AlertDescription.internal_error, "Failed to write record", e);
+                throw e;
+            }
+            catch (Exception e)
+            {
+                HandleException(AlertDescription.internal_error, "Failed to write record", e);
+                throw new TlsFatalAlert(AlertDescription.internal_error, e);
+            }
+        }
+#endif
+
         /// <summary>Write some application data.</summary>
         /// <remarks>
         /// Fragmentation is handled internally. Usable in both blocking/non-blocking modes.<br/><br/>
@@ -869,6 +932,9 @@ namespace Org.BouncyCastle.Tls
         {
             Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            WriteApplicationData(buffer.AsSpan(offset, count));
+#else
             if (!m_appDataReady)
                 throw new InvalidOperationException(
                     "Cannot write application data until initial handshake completed.");
@@ -938,7 +1004,82 @@ namespace Org.BouncyCastle.Tls
                     count -= toWrite;
                 }
             }
+#endif
+        }
+
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void WriteApplicationData(ReadOnlySpan<byte> buffer)
+        {
+            if (!m_appDataReady)
+                throw new InvalidOperationException(
+                    "Cannot write application data until initial handshake completed.");
+
+            lock (m_recordWriteLock)
+            {
+                while (!buffer.IsEmpty)
+                {
+                    if (m_closed)
+                        throw new IOException("Cannot write application data on closed/failed TLS connection");
+
+                    /*
+                     * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are
+                     * potentially useful as a traffic analysis countermeasure.
+                     * 
+                     * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting.
+                     */
+                    if (m_appDataSplitEnabled)
+                    {
+                        /*
+                         * Protect against known IV attack!
+                         * 
+                         * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
+                         */
+                        switch (m_appDataSplitMode)
+                        {
+                        case ADS_MODE_0_N_FIRSTONLY:
+                        {
+                            this.m_appDataSplitEnabled = false;
+                            SafeWriteRecord(ContentType.application_data, TlsUtilities.EmptyBytes, 0, 0);
+                            break;
+                        }
+                        case ADS_MODE_0_N:
+                        {
+                            SafeWriteRecord(ContentType.application_data, TlsUtilities.EmptyBytes, 0, 0);
+                            break;
+                        }
+                        case ADS_MODE_1_Nsub1:
+                        default:
+                        {
+                            if (buffer.Length > 1)
+                            {
+                                SafeWriteRecord(ContentType.application_data, buffer[..1]);
+                                buffer = buffer[1..];
+                            }
+                            break;
+                        }
+                        }
+                    }
+                    else if (m_keyUpdateEnabled)
+                    {
+                        if (m_keyUpdatePendingSend)
+                        {
+                            Send13KeyUpdate(false);
+                        }
+                        else if (m_recordStream.NeedsKeyUpdate())
+                        {
+                            Send13KeyUpdate(true);
+                        }
+                    }
+
+                    // Fragment data according to the current fragment limit.
+                    int toWrite = System.Math.Min(buffer.Length, m_recordStream.PlaintextLimit);
+                    SafeWriteRecord(ContentType.application_data, buffer[..toWrite]);
+                    buffer = buffer[toWrite..];
+                }
+            }
         }
+#endif
 
         public virtual int AppDataSplitMode
         {
diff --git a/crypto/src/tls/TlsStream.cs b/crypto/src/tls/TlsStream.cs
index 01b990799..5c07da2bf 100644
--- a/crypto/src/tls/TlsStream.cs
+++ b/crypto/src/tls/TlsStream.cs
@@ -58,6 +58,13 @@ namespace Org.BouncyCastle.Tls
             return m_handler.ReadApplicationData(buffer, offset, count);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            return m_handler.ReadApplicationData(buffer);
+        }
+#endif
+
         public override int ReadByte()
         {
             byte[] buf = new byte[1];
@@ -80,6 +87,13 @@ namespace Org.BouncyCastle.Tls
             m_handler.WriteApplicationData(buffer, offset, count);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            m_handler.WriteApplicationData(buffer);
+        }
+#endif
+
         public override void WriteByte(byte value)
         {
             m_handler.WriteApplicationData(new byte[]{ value }, 0, 1);
diff --git a/crypto/src/tls/TlsUtilities.cs b/crypto/src/tls/TlsUtilities.cs
index f12198082..463928ba6 100644
--- a/crypto/src/tls/TlsUtilities.cs
+++ b/crypto/src/tls/TlsUtilities.cs
@@ -747,9 +747,16 @@ namespace Org.BouncyCastle.Tls
 
         public static short ReadUint8(byte[] buf, int offset)
         {
-            return (short)(buf[offset] & 0xff);
+            return (short)buf[offset];
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static short ReadUint8(ReadOnlySpan<byte> buffer)
+        {
+            return (short)buffer[0];
+        }
+#endif
+
         public static int ReadUint16(Stream input)
         {
             int i1 = input.ReadByte();
diff --git a/crypto/src/tls/crypto/TlsCipher.cs b/crypto/src/tls/crypto/TlsCipher.cs
index 4c2147bf7..53a8141fd 100644
--- a/crypto/src/tls/crypto/TlsCipher.cs
+++ b/crypto/src/tls/crypto/TlsCipher.cs
@@ -38,6 +38,11 @@ namespace Org.BouncyCastle.Tls.Crypto
         TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
             int headerAllocation, byte[] plaintext, int offset, int len);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+            int headerAllocation, ReadOnlySpan<byte> plaintext);
+#endif
+
         /// <summary>Decode the passed in ciphertext using the current bulk cipher.</summary>
         /// <param name="seqNo">sequence number of the message represented by ciphertext.</param>
         /// <param name="recordType">content type used in the record for this message.</param>
diff --git a/crypto/src/tls/crypto/TlsHash.cs b/crypto/src/tls/crypto/TlsHash.cs
index 4732fc280..6fbaeceb9 100644
--- a/crypto/src/tls/crypto/TlsHash.cs
+++ b/crypto/src/tls/crypto/TlsHash.cs
@@ -11,6 +11,10 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <param name="length">the length of the input data.</param>
         void Update(byte[] input, int inOff, int length);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void Update(ReadOnlySpan<byte> input);
+#endif
+
         /// <summary>Return calculated hash for any input passed in.</summary>
         /// <returns>the hash value.</returns>
         byte[] CalculateHash();
diff --git a/crypto/src/tls/crypto/TlsHashSink.cs b/crypto/src/tls/crypto/TlsHashSink.cs
index a1681b0c8..3401eb60e 100644
--- a/crypto/src/tls/crypto/TlsHashSink.cs
+++ b/crypto/src/tls/crypto/TlsHashSink.cs
@@ -29,6 +29,16 @@ namespace Org.BouncyCastle.Tls.Crypto
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            if (!buffer.IsEmpty)
+            {
+                m_hash.Update(buffer);
+            }
+        }
+#endif
+
         public override void WriteByte(byte value)
         {
             m_hash.Update(new byte[]{ value }, 0, 1);
diff --git a/crypto/src/tls/crypto/TlsMac.cs b/crypto/src/tls/crypto/TlsMac.cs
index a898a9bcc..511e29d10 100644
--- a/crypto/src/tls/crypto/TlsMac.cs
+++ b/crypto/src/tls/crypto/TlsMac.cs
@@ -21,6 +21,10 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <param name="length">the length of the input data.</param>
         void Update(byte[] input, int inOff, int length);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void Update(ReadOnlySpan<byte> input);
+#endif
+
         /// <summary>Return calculated MAC for any input passed in.</summary>
         /// <returns>the MAC value.</returns>
         byte[] CalculateMac();
diff --git a/crypto/src/tls/crypto/TlsMacSink.cs b/crypto/src/tls/crypto/TlsMacSink.cs
index e7d5c70d7..fbb2e5893 100644
--- a/crypto/src/tls/crypto/TlsMacSink.cs
+++ b/crypto/src/tls/crypto/TlsMacSink.cs
@@ -29,6 +29,16 @@ namespace Org.BouncyCastle.Tls.Crypto
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            if (!buffer.IsEmpty)
+            {
+                m_mac.Update(buffer);
+            }
+        }
+#endif
+
         public override void WriteByte(byte value)
         {
             m_mac.Update(new byte[]{ value }, 0, 1);
diff --git a/crypto/src/tls/crypto/TlsNullNullCipher.cs b/crypto/src/tls/crypto/TlsNullNullCipher.cs
index 082dff358..13fe092f7 100644
--- a/crypto/src/tls/crypto/TlsNullNullCipher.cs
+++ b/crypto/src/tls/crypto/TlsNullNullCipher.cs
@@ -31,6 +31,16 @@ namespace Org.BouncyCastle.Tls.Crypto
             return new TlsEncodeResult(result, 0, result.Length, contentType);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+            int headerAllocation, ReadOnlySpan<byte> plaintext)
+        {
+            byte[] result = new byte[headerAllocation + plaintext.Length];
+            plaintext.CopyTo(result.AsSpan(headerAllocation));
+            return new TlsEncodeResult(result, 0, result.Length, contentType);
+        }
+#endif
+
         public TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
             byte[] ciphertext, int offset, int len)
         {
diff --git a/crypto/src/tls/crypto/impl/TlsAeadCipher.cs b/crypto/src/tls/crypto/impl/TlsAeadCipher.cs
index 046e6883f..594981210 100644
--- a/crypto/src/tls/crypto/impl/TlsAeadCipher.cs
+++ b/crypto/src/tls/crypto/impl/TlsAeadCipher.cs
@@ -161,6 +161,10 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
         public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
             int headerAllocation, byte[] plaintext, int plaintextOffset, int plaintextLength)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return EncodePlaintext(seqNo, contentType, recordVersion, headerAllocation,
+                plaintext.AsSpan(plaintextOffset, plaintextLength));
+#else
             byte[] nonce = new byte[m_encryptNonce.Length + m_record_iv_length];
 
             switch (m_nonceMode)
@@ -229,7 +233,83 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
             }
 
             return new TlsEncodeResult(output, 0, output.Length, recordType);
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+            int headerAllocation, ReadOnlySpan<byte> plaintext)
+        {
+            byte[] nonce = new byte[m_encryptNonce.Length + m_record_iv_length];
+
+            switch (m_nonceMode)
+            {
+            case NONCE_RFC5288:
+                Array.Copy(m_encryptNonce, 0, nonce, 0, m_encryptNonce.Length);
+                // RFC 5288/6655: The nonce_explicit MAY be the 64-bit sequence number.
+                TlsUtilities.WriteUint64(seqNo, nonce, m_encryptNonce.Length);
+                break;
+            case NONCE_RFC7905:
+                TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
+                for (int i = 0; i < m_encryptNonce.Length; ++i)
+                {
+                    nonce[i] ^= m_encryptNonce[i];
+                }
+                break;
+            default:
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+
+            int extraLength = m_isTlsV13 ? 1 : 0;
+
+            // TODO[tls13] If we support adding padding to TLSInnerPlaintext, this will need review
+            int encryptionLength = m_encryptCipher.GetOutputSize(plaintext.Length + extraLength);
+            int ciphertextLength = m_record_iv_length + encryptionLength;
+
+            byte[] output = new byte[headerAllocation + ciphertextLength];
+            int outputPos = headerAllocation;
+
+            if (m_record_iv_length != 0)
+            {
+                Array.Copy(nonce, nonce.Length - m_record_iv_length, output, outputPos, m_record_iv_length);
+                outputPos += m_record_iv_length;
+            }
+
+            short recordType = m_isTlsV13 ? ContentType.application_data : contentType;
+
+            byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
+                plaintext.Length);
+
+            try
+            {
+                plaintext.CopyTo(output.AsSpan(outputPos));
+                if (m_isTlsV13)
+                {
+                    output[outputPos + plaintext.Length] = (byte)contentType;
+                }
+
+                m_encryptCipher.Init(nonce, m_macSize, additionalData);
+                outputPos += m_encryptCipher.DoFinal(output, outputPos, plaintext.Length + extraLength, output,
+                    outputPos);
+            }
+            catch (IOException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new TlsFatalAlert(AlertDescription.internal_error, e);
+            }
+
+            if (outputPos != output.Length)
+            {
+                // NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+
+            return new TlsEncodeResult(output, 0, output.Length, recordType);
         }
+#endif
 
         public virtual TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
             byte[] ciphertext, int ciphertextOffset, int ciphertextLength)
@@ -271,12 +351,21 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
                 outputPos = m_decryptCipher.DoFinal(ciphertext, encryptionOffset, encryptionLength, ciphertext,
                     encryptionOffset);
             }
+            catch (TlsFatalAlert fatalAlert)
+            {
+                if (AlertDescription.bad_record_mac == fatalAlert.AlertDescription)
+                {
+                    m_decryptCipher.Reset();
+                }
+                throw fatalAlert;
+            }
             catch (IOException e)
             {
                 throw e;
             }
             catch (Exception e)
             {
+                m_decryptCipher.Reset();
                 throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
             }
 
diff --git a/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs b/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs
index 4c69c0b72..0cd2923c2 100644
--- a/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs
+++ b/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs
@@ -41,5 +41,7 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
         /// <returns>the amount of data written to output.</returns>
         /// <exception cref="IOException">in case of failure.</exception>
         int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset);
+
+        void Reset();
     }
 }
diff --git a/crypto/src/tls/crypto/impl/TlsBlockCipher.cs b/crypto/src/tls/crypto/impl/TlsBlockCipher.cs
index ed9d68649..64a73bfea 100644
--- a/crypto/src/tls/crypto/impl/TlsBlockCipher.cs
+++ b/crypto/src/tls/crypto/impl/TlsBlockCipher.cs
@@ -199,6 +199,9 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
         public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
             int headerAllocation, byte[] plaintext, int offset, int len)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return EncodePlaintext(seqNo, contentType, recordVersion, headerAllocation, plaintext.AsSpan(offset, len));
+#else
             int blockSize = m_encryptCipher.GetBlockSize();
             int macSize = m_writeMac.Size;
 
@@ -264,7 +267,80 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
                 throw new TlsFatalAlert(AlertDescription.internal_error);
 
             return new TlsEncodeResult(outBuf, 0, outBuf.Length, contentType);
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+            int headerAllocation, ReadOnlySpan<byte> plaintext)
+        {
+            int blockSize = m_encryptCipher.GetBlockSize();
+            int macSize = m_writeMac.Size;
+
+            int enc_input_length = plaintext.Length;
+            if (!m_encryptThenMac)
+            {
+                enc_input_length += macSize;
+            }
+
+            int padding_length = blockSize - (enc_input_length % blockSize);
+            if (m_useExtraPadding)
+            {
+                // Add a random number of extra blocks worth of padding
+                int maxExtraPadBlocks = (256 - padding_length) / blockSize;
+                int actualExtraPadBlocks = ChooseExtraPadBlocks(maxExtraPadBlocks);
+                padding_length += actualExtraPadBlocks * blockSize;
+            }
+
+            int totalSize = plaintext.Length + macSize + padding_length;
+            if (m_useExplicitIV)
+            {
+                totalSize += blockSize;
+            }
+
+            byte[] outBuf = new byte[headerAllocation + totalSize];
+            int outOff = headerAllocation;
+
+            if (m_useExplicitIV)
+            {
+                // Technically the explicit IV will be the encryption of this nonce
+                byte[] explicitIV = m_cryptoParams.NonceGenerator.GenerateNonce(blockSize);
+                Array.Copy(explicitIV, 0, outBuf, outOff, blockSize);
+                outOff += blockSize;
+            }
+
+            plaintext.CopyTo(outBuf.AsSpan(outOff));
+            outOff += plaintext.Length;
+
+            if (!m_encryptThenMac)
+            {
+                byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, plaintext);
+                mac.CopyTo(outBuf.AsSpan(outOff));
+                outOff += mac.Length;
+            }
+
+            byte padByte = (byte)(padding_length - 1);
+            for (int i = 0; i < padding_length; ++i)
+            {
+                outBuf[outOff++] = padByte;
+            }
+
+            m_encryptCipher.DoFinal(outBuf, headerAllocation, outOff - headerAllocation, outBuf, headerAllocation);
+
+            if (m_encryptThenMac)
+            {
+                byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, outBuf, headerAllocation,
+                    outOff - headerAllocation);
+                Array.Copy(mac, 0, outBuf, outOff, mac.Length);
+                outOff += mac.Length;
+            }
+
+            if (outOff != outBuf.Length)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            return new TlsEncodeResult(outBuf, 0, outBuf.Length, contentType);
         }
+#endif
 
         public virtual TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
             byte[] ciphertext, int offset, int len)
diff --git a/crypto/src/tls/crypto/impl/TlsNullCipher.cs b/crypto/src/tls/crypto/impl/TlsNullCipher.cs
index 5b6b5663a..9bb08110a 100644
--- a/crypto/src/tls/crypto/impl/TlsNullCipher.cs
+++ b/crypto/src/tls/crypto/impl/TlsNullCipher.cs
@@ -81,6 +81,18 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
             return new TlsEncodeResult(ciphertext, 0, ciphertext.Length, contentType);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
+            int headerAllocation, ReadOnlySpan<byte> plaintext)
+        {
+            byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, plaintext);
+            byte[] ciphertext = new byte[headerAllocation + plaintext.Length + mac.Length];
+            plaintext.CopyTo(ciphertext.AsSpan(headerAllocation));
+            mac.CopyTo(ciphertext.AsSpan(headerAllocation + plaintext.Length));
+            return new TlsEncodeResult(ciphertext, 0, ciphertext.Length, contentType);
+        }
+#endif
+
         public virtual TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
             byte[] ciphertext, int offset, int len)
         {
diff --git a/crypto/src/tls/crypto/impl/TlsSuiteHmac.cs b/crypto/src/tls/crypto/impl/TlsSuiteHmac.cs
index 9f43f4382..b4edde760 100644
--- a/crypto/src/tls/crypto/impl/TlsSuiteHmac.cs
+++ b/crypto/src/tls/crypto/impl/TlsSuiteHmac.cs
@@ -55,6 +55,9 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
 
         public virtual byte[] CalculateMac(long seqNo, short type, byte[] msg, int msgOff, int msgLen)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return CalculateMac(seqNo, type, msg.AsSpan(msgOff, msgLen));
+#else
             ProtocolVersion serverVersion = m_cryptoParams.ServerVersion;
             bool isSsl = serverVersion.IsSsl;
 
@@ -71,8 +74,31 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
             m_mac.Update(msg, msgOff, msgLen);
 
             return Truncate(m_mac.CalculateMac());
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual byte[] CalculateMac(long seqNo, short type, ReadOnlySpan<byte> message)
+        {
+            ProtocolVersion serverVersion = m_cryptoParams.ServerVersion;
+            bool isSsl = serverVersion.IsSsl;
+
+            byte[] macHeader = new byte[isSsl ? 11 : 13];
+            TlsUtilities.WriteUint64(seqNo, macHeader, 0);
+            TlsUtilities.WriteUint8(type, macHeader, 8);
+            if (!isSsl)
+            {
+                TlsUtilities.WriteVersion(serverVersion, macHeader, 9);
+            }
+            TlsUtilities.WriteUint16(message.Length, macHeader, macHeader.Length - 2);
+
+            m_mac.Update(macHeader);
+            m_mac.Update(message);
+
+            return Truncate(m_mac.CalculateMac());
+        }
+#endif
+
         public virtual byte[] CalculateMacConstantTime(long seqNo, short type, byte[] msg, int msgOff, int msgLen,
             int fullLength, byte[] dummyData)
         {
diff --git a/crypto/src/tls/crypto/impl/TlsSuiteMac.cs b/crypto/src/tls/crypto/impl/TlsSuiteMac.cs
index 6e4942928..1a28eba81 100644
--- a/crypto/src/tls/crypto/impl/TlsSuiteMac.cs
+++ b/crypto/src/tls/crypto/impl/TlsSuiteMac.cs
@@ -18,6 +18,10 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
         /// <returns>A new byte array containing the MAC value.</returns>
         byte[] CalculateMac(long seqNo, short type, byte[] message, int offset, int length);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        byte[] CalculateMac(long seqNo, short type, ReadOnlySpan<byte> message);
+#endif
+
         /// <summary>Constant time calculation of the MAC for some given data with a given expected length.</summary>
         /// <param name="seqNo">The sequence number of the record.</param>
         /// <param name="type">The content type of the message.</param>
diff --git a/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs b/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs
index 6b87c100a..06a09bbb1 100644
--- a/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs
@@ -96,6 +96,12 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             }
         }
 
+        public void Reset()
+        {
+            m_cipher.Reset();
+            m_mac.Reset();
+        }
+
         public void SetKey(byte[] key, int keyOff, int keyLen)
         {
             KeyParameter cipherKey = new KeyParameter(key, keyOff, keyLen);
@@ -106,16 +112,23 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
         public void SetKey(ReadOnlySpan<byte> key)
         {
             KeyParameter cipherKey = new KeyParameter(key);
-            m_cipher.Init(m_isEncrypting, new ParametersWithIV(cipherKey, Zeroes[..12]));
+            m_cipher.Init(m_isEncrypting, new ParametersWithIV(cipherKey, Zeroes.AsSpan(0, 12)));
         }
 #endif
 
         private void InitMac()
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> firstBlock = stackalloc byte[64];
+            m_cipher.ProcessBytes(firstBlock, firstBlock);
+            m_mac.Init(new KeyParameter(firstBlock[..32]));
+            firstBlock.Fill(0x00);
+#else
             byte[] firstBlock = new byte[64];
             m_cipher.ProcessBytes(firstBlock, 0, 64, firstBlock, 0);
             m_mac.Init(new KeyParameter(firstBlock, 0, 32));
             Array.Clear(firstBlock, 0, firstBlock.Length);
+#endif
         }
 
         private void UpdateMac(byte[] buf, int off, int len)
diff --git a/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs b/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
index f26a50d46..a0378e334 100644
--- a/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
@@ -66,6 +66,13 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             m_digest.BlockUpdate(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            m_digest.BlockUpdate(input);
+        }
+#endif
+
         public virtual byte[] CalculateMac()
         {
             byte[] result = new byte[m_digest.GetDigestSize()];
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
index 0b2781326..4965c92bd 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
@@ -57,5 +57,10 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
 
             return len;
         }
+
+        public void Reset()
+        {
+            m_cipher.Reset();
+        }
     }
 }
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs b/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs
index 0b35831f3..0ad2576cb 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs
@@ -28,6 +28,13 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             m_digest.BlockUpdate(data, offSet, length);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            m_digest.BlockUpdate(input);
+        }
+#endif
+
         public byte[] CalculateHash()
         {
             byte[] rv = new byte[m_digest.GetDigestSize()];
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs b/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
index 7a2318a31..dbe7f4c69 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
@@ -32,6 +32,13 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             m_hmac.BlockUpdate(input, inOff, length);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            m_hmac.BlockUpdate(input);
+        }
+#endif
+
         public byte[] CalculateMac()
         {
             byte[] rv = new byte[m_hmac.GetMacSize()];
diff --git a/crypto/src/tsp/TSPUtil.cs b/crypto/src/tsp/TSPUtil.cs
index a9402ac6d..1ba289ae6 100644
--- a/crypto/src/tsp/TSPUtil.cs
+++ b/crypto/src/tsp/TSPUtil.cs
@@ -145,7 +145,7 @@ namespace Org.BouncyCastle.Tsp
 				ExtendedKeyUsage extKey = ExtendedKeyUsage.GetInstance(
 					Asn1Object.FromByteArray(ext.GetOctets()));
 
-				if (!extKey.HasKeyPurposeId(KeyPurposeID.IdKPTimeStamping) || extKey.Count != 1)
+				if (!extKey.HasKeyPurposeId(KeyPurposeID.id_kp_timeStamping) || extKey.Count != 1)
 					throw new TspValidationException("ExtendedKeyUsage not solely time stamping.");
 			}
 			catch (IOException)
diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index 936e510be..bd2a4faea 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -6,7 +6,7 @@ using Org.BouncyCastle.Math;
 namespace Org.BouncyCastle.Utilities
 {
     /// <summary> General array utilities.</summary>
-    public abstract class Arrays
+    public static class Arrays
     {
         public static readonly byte[] EmptyBytes = new byte[0];
         public static readonly int[] EmptyInts = new int[0];
@@ -884,6 +884,17 @@ namespace Org.BouncyCastle.Utilities
             return result;
         }
 
+        public static T[] Prepend<T>(T[] a, T b)
+        {
+            if (a == null)
+                return new T[1]{ b };
+
+            T[] result = new T[1 + a.Length];
+            result[0] = b;
+            a.CopyTo(result, 1);
+            return result;
+        }
+
         public static byte[] Reverse(byte[] a)
         {
             if (a == null)
diff --git a/crypto/src/util/io/BaseInputStream.cs b/crypto/src/util/io/BaseInputStream.cs
index ebe256632..eaaf9556d 100644
--- a/crypto/src/util/io/BaseInputStream.cs
+++ b/crypto/src/util/io/BaseInputStream.cs
@@ -45,5 +45,9 @@ namespace Org.BouncyCastle.Utilities.IO
         public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
         public sealed override void SetLength(long value) { throw new NotSupportedException(); }
         public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer) { throw new NotSupportedException(); }
+#endif
     }
 }
diff --git a/crypto/src/util/io/BaseOutputStream.cs b/crypto/src/util/io/BaseOutputStream.cs
index dad9b19a4..0fc8e9681 100644
--- a/crypto/src/util/io/BaseOutputStream.cs
+++ b/crypto/src/util/io/BaseOutputStream.cs
@@ -21,6 +21,9 @@ namespace Org.BouncyCastle.Utilities.IO
             set { throw new NotSupportedException(); }
         }
         public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public sealed override int Read(Span<byte> buffer) { throw new NotSupportedException(); }
+#endif
         public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
         public sealed override void SetLength(long value) { throw new NotSupportedException(); }
 
diff --git a/crypto/src/util/io/FilterStream.cs b/crypto/src/util/io/FilterStream.cs
index d9bcbb8ef..38077edd2 100644
--- a/crypto/src/util/io/FilterStream.cs
+++ b/crypto/src/util/io/FilterStream.cs
@@ -10,10 +10,7 @@ namespace Org.BouncyCastle.Utilities.IO
 
         public FilterStream(Stream s)
         {
-            if (s == null)
-                throw new ArgumentNullException(nameof(s));
-
-            this.s = s;
+            this.s = s ?? throw new ArgumentNullException(nameof(s));
         }
         public override bool CanRead
         {
@@ -27,6 +24,12 @@ namespace Org.BouncyCastle.Utilities.IO
         {
             get { return s.CanWrite; }
         }
+#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void CopyTo(Stream destination, int bufferSize)
+        {
+            s.CopyTo(destination, bufferSize);
+        }
+#endif
         public override void Flush()
         {
             s.Flush();
@@ -44,6 +47,12 @@ namespace Org.BouncyCastle.Utilities.IO
         {
             return s.Read(buffer, offset, count);
         }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            return s.Read(buffer);
+        }
+#endif
         public override int ReadByte()
         {
             return s.ReadByte();
@@ -60,6 +69,12 @@ namespace Org.BouncyCastle.Utilities.IO
         {
             s.Write(buffer, offset, count);
         }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            s.Write(buffer);
+        }
+#endif
         public override void WriteByte(byte value)
         {
             s.WriteByte(value);