summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Hook <dgh@cryptoworkshop.com>2023-08-14 13:30:36 +1000
committerDavid Hook <dgh@cryptoworkshop.com>2023-08-14 13:30:36 +1000
commitf3dac948c7e6214663e58dc966bd10cb31689411 (patch)
tree60742057ef07d02d80162165b849a15a842503cb
parenttest vector updates to Kyber and Dilithium (diff)
parentMicrosoft.NET.Test.Sdk 17.7.0 (diff)
downloadBouncyCastle.NET-ed25519-f3dac948c7e6214663e58dc966bd10cb31689411.tar.xz
Merge remote-tracking branch 'refs/remotes/origin/master'
-rw-r--r--crypto/src/asn1/Asn1RelativeOid.cs21
-rw-r--r--crypto/src/asn1/DerObjectIdentifier.cs20
-rw-r--r--crypto/src/asn1/cmp/CmpObjectIdentifiers.cs68
-rw-r--r--crypto/src/asn1/cmp/KemBMParameter.cs76
-rw-r--r--crypto/src/asn1/cmp/KemCiphertextInfo.cs64
-rw-r--r--crypto/src/asn1/cmp/KemOtherInfo.cs150
-rw-r--r--crypto/src/asn1/x9/X9ECParametersHolder.cs39
-rw-r--r--crypto/src/crypto/agreement/DHStandardGroups.cs4
-rw-r--r--crypto/src/crypto/generators/DHParametersHelper.cs18
-rw-r--r--crypto/src/crypto/parameters/ECDomainParameters.cs16
-rw-r--r--crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs25
-rw-r--r--crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs25
-rw-r--r--crypto/src/math/BigInteger.cs219
-rw-r--r--crypto/src/math/ec/abc/Tnaf.cs4
-rw-r--r--crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs6
-rw-r--r--crypto/test/BouncyCastle.Crypto.Tests.csproj2
-rw-r--r--crypto/test/src/pqc/crypto/test/HSSTest.cs40
17 files changed, 544 insertions, 253 deletions
diff --git a/crypto/src/asn1/Asn1RelativeOid.cs b/crypto/src/asn1/Asn1RelativeOid.cs
index b7df4b75a..1d2ecb3df 100644
--- a/crypto/src/asn1/Asn1RelativeOid.cs
+++ b/crypto/src/asn1/Asn1RelativeOid.cs
@@ -137,8 +137,11 @@ namespace Org.BouncyCastle.Asn1
             return new PrimitiveDerEncoding(tagClass, tagNo, GetContents());
         }
 
-        private void DoOutput(MemoryStream bOut)
+        private byte[] GetContents() => Objects.EnsureSingletonInitialized(ref contents, identifier, CreateContents);
+
+        private static byte[] CreateContents(string identifier)
         {
+            MemoryStream bOut = new MemoryStream();
             OidTokenizer tok = new OidTokenizer(identifier);
             while (tok.HasMoreTokens)
             {
@@ -152,21 +155,7 @@ namespace Org.BouncyCastle.Asn1
                     WriteField(bOut, new BigInteger(token));
                 }
             }
-        }
-
-        private byte[] GetContents()
-        {
-            lock (this)
-            {
-                if (contents == null)
-                {
-                    MemoryStream bOut = new MemoryStream();
-                    DoOutput(bOut);
-                    contents = bOut.ToArray();
-                }
-
-                return contents;
-            }
+            return bOut.ToArray();
         }
 
         internal static Asn1RelativeOid CreatePrimitive(byte[] contents, bool clone)
diff --git a/crypto/src/asn1/DerObjectIdentifier.cs b/crypto/src/asn1/DerObjectIdentifier.cs
index 91e427d0b..12e8ea0a8 100644
--- a/crypto/src/asn1/DerObjectIdentifier.cs
+++ b/crypto/src/asn1/DerObjectIdentifier.cs
@@ -168,8 +168,11 @@ namespace Org.BouncyCastle.Asn1
             return new PrimitiveDerEncoding(tagClass, tagNo, GetContents());
         }
 
-        private void DoOutput(MemoryStream bOut)
+        private byte[] GetContents() => Objects.EnsureSingletonInitialized(ref contents, identifier, CreateContents);
+
+        private static byte[] CreateContents(string identifier)
         {
+            MemoryStream bOut = new MemoryStream();
             OidTokenizer tok = new OidTokenizer(identifier);
 
             string token = tok.NextToken();
@@ -197,21 +200,8 @@ namespace Org.BouncyCastle.Asn1
                     Asn1RelativeOid.WriteField(bOut, new BigInteger(token));
                 }
             }
-        }
-
-        private byte[] GetContents()
-        {
-            lock (this)
-            {
-                if (contents == null)
-                {
-                    MemoryStream bOut = new MemoryStream();
-                    DoOutput(bOut);
-                    contents = bOut.ToArray();
-                }
 
-                return contents;
-            }
+            return bOut.ToArray();
         }
 
         internal static DerObjectIdentifier CreatePrimitive(byte[] contents, bool clone)
diff --git a/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs b/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
index 1b3227c47..63fb29cf4 100644
--- a/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
+++ b/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
@@ -58,108 +58,110 @@ namespace Org.BouncyCastle.Asn1.Cmp
         // and
         //   id-it   OBJECT IDENTIFIER ::= {id-pkix 4}
 
-        /** RFC 4120: it-id: PKIX.4 = 1.3.6.1.5.5.7.4 */
-
+        /** RFC 4120: id-it: PKIX.4 = 1.3.6.1.5.5.7.4 */
+        public static readonly DerObjectIdentifier id_it = new DerObjectIdentifier("1.3.6.1.5.5.7.4");
 
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.1
          */
-        public static readonly DerObjectIdentifier it_caProtEncCert = new DerObjectIdentifier("1.3.6.1.5.5.7.4.1");
+        public static readonly DerObjectIdentifier it_caProtEncCert = id_it.Branch("1");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.2
          */
-        public static readonly DerObjectIdentifier it_signKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.2");
+        public static readonly DerObjectIdentifier it_signKeyPairTypes = id_it.Branch("2");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.3
          */
-        public static readonly DerObjectIdentifier it_encKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.3");
+        public static readonly DerObjectIdentifier it_encKeyPairTypes = id_it.Branch("3");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.4
          */
-        public static readonly DerObjectIdentifier it_preferredSymAlg = new DerObjectIdentifier("1.3.6.1.5.5.7.4.4");
+        public static readonly DerObjectIdentifier it_preferredSymAlg = id_it.Branch("4");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.5
          */
-        public static readonly DerObjectIdentifier it_caKeyUpdateInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.4.5");
+        public static readonly DerObjectIdentifier it_caKeyUpdateInfo = id_it.Branch("5");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.6
          */
-        public static readonly DerObjectIdentifier it_currentCRL = new DerObjectIdentifier("1.3.6.1.5.5.7.4.6");
+        public static readonly DerObjectIdentifier it_currentCRL = id_it.Branch("6");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.7
          */
-        public static readonly DerObjectIdentifier it_unsupportedOIDs = new DerObjectIdentifier("1.3.6.1.5.5.7.4.7");
+        public static readonly DerObjectIdentifier it_unsupportedOIDs = id_it.Branch("7");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.10
          */
-        public static readonly DerObjectIdentifier it_keyPairParamReq = new DerObjectIdentifier("1.3.6.1.5.5.7.4.10");
+        public static readonly DerObjectIdentifier it_keyPairParamReq = id_it.Branch("10");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.11
          */
-        public static readonly DerObjectIdentifier it_keyPairParamRep = new DerObjectIdentifier("1.3.6.1.5.5.7.4.11");
+        public static readonly DerObjectIdentifier it_keyPairParamRep = id_it.Branch("11");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.12
          */
-        public static readonly DerObjectIdentifier it_revPassphrase = new DerObjectIdentifier("1.3.6.1.5.5.7.4.12");
+        public static readonly DerObjectIdentifier it_revPassphrase = id_it.Branch("12");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.13
          */
-        public static readonly DerObjectIdentifier it_implicitConfirm = new DerObjectIdentifier("1.3.6.1.5.5.7.4.13");
+        public static readonly DerObjectIdentifier it_implicitConfirm = id_it.Branch("13");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.14
          */
-        public static readonly DerObjectIdentifier it_confirmWaitTime = new DerObjectIdentifier("1.3.6.1.5.5.7.4.14");
+        public static readonly DerObjectIdentifier it_confirmWaitTime = id_it.Branch("14");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.15
          */
-        public static readonly DerObjectIdentifier it_origPKIMessage = new DerObjectIdentifier("1.3.6.1.5.5.7.4.15");
+        public static readonly DerObjectIdentifier it_origPKIMessage = id_it.Branch("15");
         /**
          * RFC 4120: 1.3.6.1.5.5.7.4.16
          */
-        public static readonly DerObjectIdentifier it_suppLangTags = new DerObjectIdentifier("1.3.6.1.5.5.7.4.16");
+        public static readonly DerObjectIdentifier it_suppLangTags = id_it.Branch("16");
 
         /**
          * Update 16, RFC 4210
          * {id-it 17}
          */
-        public static readonly DerObjectIdentifier id_it_caCerts = new DerObjectIdentifier("1.3.6.1.5.5.7.4.17");
+        public static readonly DerObjectIdentifier id_it_caCerts = id_it.Branch("17");
 
 
         /**
          * Update 16, RFC 4210
          * GenRep:    {id-it 18}, RootCaKeyUpdateContent
          */
-        public static readonly DerObjectIdentifier id_it_rootCaKeyUpdate = new DerObjectIdentifier("1.3.6.1.5.5.7.4.18");
+        public static readonly DerObjectIdentifier id_it_rootCaKeyUpdate = id_it.Branch("18");
 
 
         /**
          * Update 16, RFC 4210
          * {id-it 19}
          */
-        public static readonly DerObjectIdentifier id_it_certReqTemplate = new DerObjectIdentifier("1.3.6.1.5.5.7.4.19");
+        public static readonly DerObjectIdentifier id_it_certReqTemplate = id_it.Branch("19");
 
 
         /**
          * Update 16, RFC 4210
          * GenMsg:    {id-it 20}, RootCaCertValue
          */
-        public static readonly DerObjectIdentifier id_it_rootCaCert = new DerObjectIdentifier("1.3.6.1.5.5.7.4.20");
+        public static readonly DerObjectIdentifier id_it_rootCaCert = id_it.Branch("20");
 
         /**
          * Update-16 to RFC 4210
          * id-it-certProfile  OBJECT IDENTIFIER ::= {id-it 21}
          */
-        public static readonly DerObjectIdentifier id_it_certProfile = new DerObjectIdentifier("1.3.6.1.5.5.7.4.21");
-
-        public static readonly DerObjectIdentifier id_it_crlStatusList = new DerObjectIdentifier("1.3.6.1.5.5.7.4.22");
+        public static readonly DerObjectIdentifier id_it_certProfile = id_it.Branch("21");
 
-        public static readonly DerObjectIdentifier id_it_crls = new DerObjectIdentifier("1.3.6.1.5.5.7.4.23");
+        public static readonly DerObjectIdentifier id_it_crlStatusList = id_it.Branch("22");
 
-        // Not yet formally defined.
-
-        //public static readonly DerObjectIdentifier id_it_crlStatusList = null;
-        //public static readonly DerObjectIdentifier id_it_crls = null;
+        public static readonly DerObjectIdentifier id_it_crls = id_it.Branch("23");
 
+        // TODO Update once OID allocated.
+#if false
+        /**
+         * id-it-KemCiphertextInfo OBJECT IDENTIFIER ::= { id-it TBD1 }
+         */
+        public static readonly DerObjectIdentifier id_it_KemCiphertextInfo = id_it.Branch("TBD1");
+#endif
 
         // RFC 4211
 
@@ -254,5 +256,13 @@ namespace Org.BouncyCastle.Asn1.Cmp
          * mechanisms(5) pkix(7) pkip(5) regCtrl(1) 12 }
          */
         public static readonly DerObjectIdentifier id_regCtrl_rsaKeyLen = id_pkip.Branch("1.12");
-	}
+
+        // TODO Update once OID allocated.
+#if false
+        /**
+         * id-KemBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 TBD4}
+         */
+        public static readonly DerObjectIdentifier id_KemBasedMac = new DerObjectIdentifier("1.2.840.113533.7.66.TBD4");
+#endif
+    }
 }
diff --git a/crypto/src/asn1/cmp/KemBMParameter.cs b/crypto/src/asn1/cmp/KemBMParameter.cs
new file mode 100644
index 000000000..846233054
--- /dev/null
+++ b/crypto/src/asn1/cmp/KemBMParameter.cs
@@ -0,0 +1,76 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * <pre>
+     *  KemBMParameter ::= SEQUENCE {
+     *      kdf              AlgorithmIdentifier{KEY-DERIVATION, {...}},
+     *      len              INTEGER (1..MAX),
+     *      mac              AlgorithmIdentifier{MAC-ALGORITHM, {...}}
+     *   }
+     * </pre>
+     */
+    public class KemBMParameter
+        : Asn1Encodable
+    {
+        public static KemBMParameter GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+            if (obj is KemBMParameter kemBMParameter)
+                return kemBMParameter;
+            return new KemBMParameter(Asn1Sequence.GetInstance(obj));
+        }
+
+        public static KemBMParameter GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) =>
+            new KemBMParameter(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
+
+        private readonly AlgorithmIdentifier m_kdf;
+        private readonly DerInteger m_len;
+        private readonly AlgorithmIdentifier m_mac;
+
+        private KemBMParameter(Asn1Sequence seq)
+        {
+            if (seq.Count != 3)
+                throw new ArgumentException("sequence size should 3", nameof(seq));
+
+            m_kdf = AlgorithmIdentifier.GetInstance(seq[0]);
+            m_len = DerInteger.GetInstance(seq[1]);
+            m_mac = AlgorithmIdentifier.GetInstance(seq[2]);
+        }
+
+        public KemBMParameter(AlgorithmIdentifier kdf, DerInteger len, AlgorithmIdentifier mac)
+        {
+            m_kdf = kdf;
+            m_len = len;
+            m_mac = mac;
+        }
+
+        public KemBMParameter(AlgorithmIdentifier kdf, long len, AlgorithmIdentifier mac)
+            : this(kdf, new DerInteger(len), mac)
+        {
+        }
+
+        public virtual AlgorithmIdentifier Kdf => m_kdf;
+
+        public virtual DerInteger Len => m_len;
+
+        public virtual AlgorithmIdentifier Mac => m_mac;
+
+        /**
+         * <pre>
+         *  KemBMParameter ::= SEQUENCE {
+         *      kdf              AlgorithmIdentifier{KEY-DERIVATION, {...}},
+         *      len              INTEGER (1..MAX),
+         *      mac              AlgorithmIdentifier{MAC-ALGORITHM, {...}}
+         *    }
+         * </pre>
+         *
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object() => new DerSequence(m_kdf, m_len, m_mac);
+    }
+}
diff --git a/crypto/src/asn1/cmp/KemCiphertextInfo.cs b/crypto/src/asn1/cmp/KemCiphertextInfo.cs
new file mode 100644
index 000000000..7a6c3b25e
--- /dev/null
+++ b/crypto/src/asn1/cmp/KemCiphertextInfo.cs
@@ -0,0 +1,64 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * <pre>
+     *    KemCiphertextInfo ::= SEQUENCE {
+     *      kem              AlgorithmIdentifier{KEM-ALGORITHM, {...}},
+     *      ct               OCTET STRING
+     *    }
+     * </pre>
+     */
+    public class KemCiphertextInfo
+        : Asn1Encodable
+    {
+        public static KemCiphertextInfo GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+            if (obj is KemCiphertextInfo kemCiphertextInfo)
+                return kemCiphertextInfo;
+            return new KemCiphertextInfo(Asn1Sequence.GetInstance(obj));
+        }
+
+        public static KemCiphertextInfo GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) =>
+            new KemCiphertextInfo(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
+
+        private readonly AlgorithmIdentifier m_kem;
+        private readonly Asn1OctetString m_ct;
+
+        private KemCiphertextInfo(Asn1Sequence seq)
+        {
+            if (seq.Count != 2)
+                throw new ArgumentException("sequence size should 2", nameof(seq));
+
+            m_kem = AlgorithmIdentifier.GetInstance(seq[0]);
+            m_ct = Asn1OctetString.GetInstance(seq[1]);
+        }
+
+        public KemCiphertextInfo(AlgorithmIdentifier kem, Asn1OctetString ct)
+        {
+            m_kem = kem;
+            m_ct = ct;
+        }
+
+        public virtual AlgorithmIdentifier Kem => m_kem;
+
+        public virtual Asn1OctetString Ct => m_ct;
+
+        /**
+         * <pre>
+         *    KemCiphertextInfo ::= SEQUENCE {
+         *      kem              AlgorithmIdentifier{KEM-ALGORITHM, {...}},
+         *      ct               OCTET STRING
+         *    }
+         * </pre>
+         *
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object() => new DerSequence(m_kem, m_ct);
+    }
+}
diff --git a/crypto/src/asn1/cmp/KemOtherInfo.cs b/crypto/src/asn1/cmp/KemOtherInfo.cs
new file mode 100644
index 000000000..3185495fc
--- /dev/null
+++ b/crypto/src/asn1/cmp/KemOtherInfo.cs
@@ -0,0 +1,150 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /*
+     * <pre>
+     * KemOtherInfo ::= SEQUENCE {
+     *   staticString      PKIFreeText,  -- MUST be "CMP-KEM"
+     *   transactionID [0] OCTET STRING     OPTIONAL,
+     *   senderNonce   [1] OCTET STRING     OPTIONAL,
+     *   recipNonce    [2] OCTET STRING     OPTIONAL,
+     *   len               INTEGER (1..MAX),
+     *   mac               AlgorithmIdentifier{MAC-ALGORITHM, {...}}
+     *   ct                OCTET STRING
+     * }
+     * </pre>
+     */
+    public class KemOtherInfo
+        : Asn1Encodable
+    {
+        public static KemOtherInfo GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+            if (obj is KemOtherInfo kemOtherInfo)
+                return kemOtherInfo;
+            return new KemOtherInfo(Asn1Sequence.GetInstance(obj));
+        }
+
+        public static KemOtherInfo GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) =>
+            new KemOtherInfo(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
+
+        private static readonly PkiFreeText DEFAULT_staticString = new PkiFreeText("CMP-KEM");
+
+        private readonly PkiFreeText m_staticString;
+        private readonly Asn1OctetString m_transactionID;
+        private readonly Asn1OctetString m_senderNonce;
+        private readonly Asn1OctetString m_recipNonce;
+        private readonly DerInteger m_len;
+        private readonly AlgorithmIdentifier m_mac;
+        private readonly Asn1OctetString m_ct;
+
+        public KemOtherInfo(Asn1OctetString transactionID, Asn1OctetString senderNonce, Asn1OctetString recipNonce,
+            DerInteger len, AlgorithmIdentifier mac, Asn1OctetString ct)
+        {
+            m_staticString = DEFAULT_staticString;
+            m_transactionID = transactionID;
+            m_senderNonce = senderNonce;
+            m_recipNonce = recipNonce;
+            m_len = len;
+            m_mac = mac;
+            m_ct = ct;
+        }
+
+        public KemOtherInfo(Asn1OctetString transactionID, Asn1OctetString senderNonce, Asn1OctetString recipNonce,
+            long len, AlgorithmIdentifier mac, Asn1OctetString ct)
+            : this(transactionID, senderNonce, recipNonce, new DerInteger(len), mac, ct)
+        {
+        }
+
+        private KemOtherInfo(Asn1Sequence seq)
+        {
+            if (seq.Count < 4 || seq.Count > 7)
+                throw new ArgumentException("sequence size should be between 4 and 7 inclusive", nameof(seq));
+
+            int seqPos = 0;
+
+            m_staticString = PkiFreeText.GetInstance(seq[seqPos]);
+            if (!DEFAULT_staticString.Equals(m_staticString))
+                throw new ArgumentException("staticString field should be " + DEFAULT_staticString);
+
+            Asn1TaggedObject tagged = seq[++seqPos] as Asn1TaggedObject;
+
+            if (tagged != null &&
+                Asn1Utilities.TryGetContextBaseUniversal(tagged, 0, true, Asn1Tags.OctetString, out var transactionID))
+            {
+                m_transactionID = (Asn1OctetString)transactionID;
+                tagged = seq[++seqPos] as Asn1TaggedObject;
+            }
+
+            if (tagged != null &&
+                Asn1Utilities.TryGetContextBaseUniversal(tagged, 1, true, Asn1Tags.OctetString, out var senderNonce))
+            {
+                m_senderNonce = (Asn1OctetString)senderNonce;
+                tagged = seq[++seqPos] as Asn1TaggedObject;
+            }
+
+            if (tagged != null &&
+                Asn1Utilities.TryGetContextBaseUniversal(tagged, 2, true, Asn1Tags.OctetString, out var recipNonce))
+            {
+                m_recipNonce = (Asn1OctetString)recipNonce;
+                tagged = seq[++seqPos] as Asn1TaggedObject;
+            }
+
+            if (tagged != null)
+                throw new ArgumentException("unknown tag: " + Asn1Utilities.GetTagText(tagged));
+
+            m_len = DerInteger.GetInstance(seq[seqPos]);
+            m_mac = AlgorithmIdentifier.GetInstance(seq[++seqPos]);
+            m_ct = Asn1OctetString.GetInstance(seq[++seqPos]);
+
+            if (++seqPos != seq.Count)
+                throw new ArgumentException("unexpected data at end of sequence", nameof(seq));
+        }
+
+        public virtual Asn1OctetString TransactionID => m_transactionID;
+
+        public virtual Asn1OctetString SenderNonce => m_senderNonce;
+
+        public virtual Asn1OctetString RecipNonce => m_recipNonce;
+
+        public virtual DerInteger Len => m_len;
+
+        public virtual AlgorithmIdentifier Mac => m_mac;
+
+        public virtual Asn1OctetString Ct => m_ct;
+
+        /**
+         * <pre>
+         * KemOtherInfo ::= SEQUENCE {
+         *   staticString      PKIFreeText,   -- MUST be "CMP-KEM"
+         *   transactionID [0] OCTET STRING     OPTIONAL,
+         *   senderNonce   [1] OCTET STRING     OPTIONAL,
+         *   recipNonce    [2] OCTET STRING     OPTIONAL,
+         *   len               INTEGER (1..MAX),
+         *   mac               AlgorithmIdentifier{MAC-ALGORITHM, {...}}
+         *   ct                OCTET STRING
+         * }
+         * </pre>
+         *
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(7);
+
+            v.Add(m_staticString);
+            v.AddOptionalTagged(true, 0, m_transactionID);
+            v.AddOptionalTagged(true, 1, m_senderNonce);
+            v.AddOptionalTagged(true, 2, m_recipNonce);
+            v.Add(m_len);
+            v.Add(m_mac);
+            v.Add(m_ct);
+
+            return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/x9/X9ECParametersHolder.cs b/crypto/src/asn1/x9/X9ECParametersHolder.cs
index ea72cc6ac..535dad9f7 100644
--- a/crypto/src/asn1/x9/X9ECParametersHolder.cs
+++ b/crypto/src/asn1/x9/X9ECParametersHolder.cs
@@ -1,4 +1,5 @@
 using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.X9
 {
@@ -7,42 +8,12 @@ namespace Org.BouncyCastle.Asn1.X9
         private ECCurve m_curve;
         private X9ECParameters m_parameters;
 
-        public ECCurve Curve
-        {
-            get
-            {
-                lock (this)
-                {
-                    if (m_curve == null)
-                    {
-                        m_curve = CreateCurve();
-                    }
+        public ECCurve Curve => Objects.EnsureSingletonInitialized(ref m_curve, this, self => self.CreateCurve());
 
-                    return m_curve;
-                }
-            }
-        }
+        public X9ECParameters Parameters =>
+            Objects.EnsureSingletonInitialized(ref m_parameters, this, self => self.CreateParameters());
 
-        public X9ECParameters Parameters
-		{
-			get
-			{
-                lock (this)
-                {
-                    if (m_parameters == null)
-                    {
-                        m_parameters = CreateParameters();
-                    }
-
-                    return m_parameters;
-                }
-            }
-        }
-
-        protected virtual ECCurve CreateCurve()
-        {
-            return CreateParameters().Curve;
-        }
+        protected virtual ECCurve CreateCurve() => Parameters.Curve;
 
         protected abstract X9ECParameters CreateParameters();
 	}
diff --git a/crypto/src/crypto/agreement/DHStandardGroups.cs b/crypto/src/crypto/agreement/DHStandardGroups.cs
index e334489c8..08afb801c 100644
--- a/crypto/src/crypto/agreement/DHStandardGroups.cs
+++ b/crypto/src/crypto/agreement/DHStandardGroups.cs
@@ -9,8 +9,6 @@ namespace Org.BouncyCastle.Crypto.Agreement
     /// <summary>Standard Diffie-Hellman groups from various IETF specifications.</summary>
     public class DHStandardGroups
     {
-        private static readonly BigInteger Two = BigInteger.ValueOf(2);
-
         private static BigInteger FromHex(string hex)
         {
             return new BigInteger(1, Hex.DecodeStrict(hex));
@@ -30,7 +28,7 @@ namespace Org.BouncyCastle.Crypto.Agreement
         {
             // NOTE: A group using a safe prime (i.e. q = (p-1)/2), and generator g = 2
             BigInteger p = FromHex(hexP);
-            return new DHParameters(p, Two, p.ShiftRight(1), l);
+            return new DHParameters(p, BigInteger.Two, p.ShiftRight(1), l);
         }
 
         /*
diff --git a/crypto/src/crypto/generators/DHParametersHelper.cs b/crypto/src/crypto/generators/DHParametersHelper.cs
index 385690430..a05918944 100644
--- a/crypto/src/crypto/generators/DHParametersHelper.cs
+++ b/crypto/src/crypto/generators/DHParametersHelper.cs
@@ -9,21 +9,9 @@ namespace Org.BouncyCastle.Crypto.Generators
 {
     internal class DHParametersHelper
     {
-        private static readonly BigInteger Six = BigInteger.ValueOf(6);
-
         private static readonly int[][] primeLists = BigInteger.primeLists;
         private static readonly int[] primeProducts = BigInteger.primeProducts;
-        private static readonly BigInteger[] BigPrimeProducts = ConstructBigPrimeProducts(primeProducts);
-
-        private static BigInteger[] ConstructBigPrimeProducts(int[] primeProducts)
-        {
-            BigInteger[] bpp = new BigInteger[primeProducts.Length];
-            for (int i = 0; i < bpp.Length; ++i)
-            {
-                bpp[i] = BigInteger.ValueOf(primeProducts[i]);
-            }
-            return bpp;
-        }
+        private static readonly BigInteger[] BigPrimeProducts = Array.ConvertAll(primeProducts, BigInteger.ValueOf);
 
         /*
          * Finds a pair of prime BigInteger's {p, q: p = 2q + 1}
@@ -83,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Generators
                             int qRem = test % prime;
                             if (qRem == 0 || qRem == (prime >> 1))
                             {
-                                q = q.Add(Six);
+                                q = q.Add(BigInteger.Six);
                                 goto retry;
                             }
                         }
@@ -146,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Generators
             {
                 BigInteger h = BigIntegers.CreateRandomInRange(BigInteger.Two, pMinusTwo, random);
 
-                g = h.ModPow(BigInteger.Two, p);
+                g = h.Square().Mod(p);
             }
             while (g.Equals(BigInteger.One));
 
diff --git a/crypto/src/crypto/parameters/ECDomainParameters.cs b/crypto/src/crypto/parameters/ECDomainParameters.cs
index b5ca183de..8bd58d018 100644
--- a/crypto/src/crypto/parameters/ECDomainParameters.cs
+++ b/crypto/src/crypto/parameters/ECDomainParameters.cs
@@ -81,20 +81,8 @@ namespace Org.BouncyCastle.Crypto.Parameters
             get { return h; }
         }
 
-        public BigInteger HInv
-        {
-            get
-            {
-                lock (this)
-                {
-                    if (hInv == null)
-                    {
-                        hInv = BigIntegers.ModOddInverseVar(n, h);
-                    }
-                    return hInv;
-                }
-            }
-        }
+        public BigInteger HInv =>
+            Objects.EnsureSingletonInitialized(ref hInv, this, self => BigIntegers.ModOddInverseVar(self.n, self.h));
 
         public byte[] GetSeed()
         {
diff --git a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs
index 3a760afc1..4d2746065 100644
--- a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs
+++ b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs
@@ -76,22 +76,8 @@ namespace Org.BouncyCastle.Crypto.Parameters
         internal ReadOnlyMemory<byte> DataMemory => data;
 #endif
 
-        public Ed25519PublicKeyParameters GeneratePublicKey()
-        {
-            lock (data)
-            {
-                if (null == cachedPublicKey)
-                {
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-                    cachedPublicKey = new Ed25519PublicKeyParameters(Ed25519.GeneratePublicKey(data));
-#else
-                    cachedPublicKey = new Ed25519PublicKeyParameters(Ed25519.GeneratePublicKey(data, 0));
-#endif
-                }
-
-                return cachedPublicKey;
-            }
-        }
+        public Ed25519PublicKeyParameters GeneratePublicKey() =>
+            Objects.EnsureSingletonInitialized(ref cachedPublicKey, data, CreatePublicKey);
 
         public void Sign(Ed25519.Algorithm algorithm, byte[] ctx, byte[] msg, int msgOff, int msgLen,
             byte[] sig, int sigOff)
@@ -140,6 +126,13 @@ namespace Org.BouncyCastle.Crypto.Parameters
             }
         }
 
+        private static Ed25519PublicKeyParameters CreatePublicKey(byte[] data) =>
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            new Ed25519PublicKeyParameters(Ed25519.GeneratePublicKey(data));
+#else
+            new Ed25519PublicKeyParameters(Ed25519.GeneratePublicKey(data, 0));
+#endif
+
         private static byte[] Validate(byte[] buf)
         {
             if (buf.Length != KeySize)
diff --git a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs
index 544dbf32d..664716ae9 100644
--- a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs
+++ b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs
@@ -76,22 +76,8 @@ namespace Org.BouncyCastle.Crypto.Parameters
         internal ReadOnlyMemory<byte> DataMemory => data;
 #endif
 
-        public Ed448PublicKeyParameters GeneratePublicKey()
-        {
-            lock (data)
-            {
-                if (null == cachedPublicKey)
-                {
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-                    cachedPublicKey = new Ed448PublicKeyParameters(Ed448.GeneratePublicKey(data));
-#else
-                    cachedPublicKey = new Ed448PublicKeyParameters(Ed448.GeneratePublicKey(data, 0));
-#endif
-                }
-
-                return cachedPublicKey;
-            }
-        }
+        public Ed448PublicKeyParameters GeneratePublicKey() =>
+            Objects.EnsureSingletonInitialized(ref cachedPublicKey, data, CreatePublicKey);
 
         public void Sign(Ed448.Algorithm algorithm, byte[] ctx, byte[] msg, int msgOff, int msgLen,
             byte[] sig, int sigOff)
@@ -132,6 +118,13 @@ namespace Org.BouncyCastle.Crypto.Parameters
             }
         }
 
+        private static Ed448PublicKeyParameters CreatePublicKey(byte[] data) =>
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            new Ed448PublicKeyParameters(Ed448.GeneratePublicKey(data));
+#else
+            new Ed448PublicKeyParameters(Ed448.GeneratePublicKey(data, 0));
+#endif
+
         private static byte[] Validate(byte[] buf)
         {
             if (buf.Length != KeySize)
diff --git a/crypto/src/math/BigInteger.cs b/crypto/src/math/BigInteger.cs
index 7da886c4f..42b5b5089 100644
--- a/crypto/src/math/BigInteger.cs
+++ b/crypto/src/math/BigInteger.cs
@@ -139,6 +139,8 @@ namespace Org.BouncyCastle.Math
         public static readonly BigInteger Two;
         public static readonly BigInteger Three;
         public static readonly BigInteger Four;
+        public static readonly BigInteger Five;
+        public static readonly BigInteger Six;
         public static readonly BigInteger Ten;
 
 #if !NETCOREAPP3_0_OR_GREATER
@@ -181,27 +183,34 @@ namespace Org.BouncyCastle.Math
         static BigInteger()
         {
             Zero = new BigInteger(0, ZeroMagnitude, false);
-            Zero.nBits = 0; Zero.nBitLength = 0;
+            Zero.nBits = 0;
+            Zero.nBitLength = 0;
 
             SMALL_CONSTANTS[0] = Zero;
             for (uint i = 1; i < SMALL_CONSTANTS.Length; ++i)
             {
-                SMALL_CONSTANTS[i] = CreateUValueOf(i);
+                var sc = CreateUValueOf(i);
+                sc.nBits = Integers.PopCount(i);
+                sc.nBitLength = BitLen(i);
+
+                SMALL_CONSTANTS[i] = sc;
             }
 
             One = SMALL_CONSTANTS[1];
             Two = SMALL_CONSTANTS[2];
             Three = SMALL_CONSTANTS[3];
             Four = SMALL_CONSTANTS[4];
+            Five = SMALL_CONSTANTS[5];
+            Six = SMALL_CONSTANTS[6];
             Ten = SMALL_CONSTANTS[10];
 
-            radix2 = ValueOf(2);
+            radix2 = Two;
             radix2E = radix2.Pow(chunk2);
 
             radix8 = ValueOf(8);
             radix8E = radix8.Pow(chunk8);
 
-            radix10 = ValueOf(10);
+            radix10 = Ten;
             radix10E = radix10.Pow(chunk10);
 
             radix16 = ValueOf(16);
@@ -1171,7 +1180,7 @@ namespace Org.BouncyCastle.Math
                 ?  1
                 :  sign == 0
                 ?  0
-                :  sign * CompareNoLeadingZeroes(0, magnitude, 0, other.magnitude);
+                :  sign * CompareNoLeadingZeros(0, magnitude, 0, other.magnitude);
         }
 
         /**
@@ -1190,10 +1199,10 @@ namespace Org.BouncyCastle.Math
                 yIndx++;
             }
 
-            return CompareNoLeadingZeroes(xIndx, x, yIndx, y);
+            return CompareNoLeadingZeros(xIndx, x, yIndx, y);
         }
 
-        private static int CompareNoLeadingZeroes(int xIndx, uint[] x, int yIndx, uint[] y)
+        private static int CompareNoLeadingZeros(int xIndx, uint[] x, int yIndx, uint[] y)
         {
             int diff = (x.Length - y.Length) - (xIndx - yIndx);
 
@@ -1234,7 +1243,7 @@ namespace Org.BouncyCastle.Math
 
             Debug.Assert(yStart < y.Length);
 
-            int xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y);
+            int xyCmp = CompareNoLeadingZeros(xStart, x, yStart, y);
             uint[] count;
 
             if (xyCmp > 0)
@@ -1271,7 +1280,7 @@ namespace Org.BouncyCastle.Math
                 for (;;)
                 {
                     if (cBitLength < xBitLength
-                        || CompareNoLeadingZeroes(xStart, x, cStart, c) >= 0)
+                        || CompareNoLeadingZeros(xStart, x, cStart, c) >= 0)
                     {
                         Subtract(xStart, x, cStart, c);
                         AddMagnitudes(count, iCount);
@@ -1289,7 +1298,7 @@ namespace Org.BouncyCastle.Math
                             if (xBitLength < yBitLength)
                                 return count;
 
-                            xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y);
+                            xyCmp = CompareNoLeadingZeros(xStart, x, yStart, y);
 
                             if (xyCmp <= 0)
                                 break;
@@ -1623,6 +1632,8 @@ namespace Org.BouncyCastle.Math
             BigInteger montRadix = One.ShiftLeft(32 * n.magnitude.Length).Remainder(n);
             BigInteger minusMontRadix = n.Subtract(montRadix);
 
+            uint[] yAccum = new uint[n.magnitude.Length + 1];
+
             do
             {
                 BigInteger a;
@@ -1633,7 +1644,7 @@ namespace Org.BouncyCastle.Math
                 while (a.sign == 0 || a.CompareTo(n) >= 0
                     || a.IsEqualMagnitude(montRadix) || a.IsEqualMagnitude(minusMontRadix));
 
-                BigInteger y = ModPowMonty(a, r, n, false);
+                BigInteger y = ModPowMonty(yAccum, a, r, n, false);
 
                 if (!y.Equals(montRadix))
                 {
@@ -1643,7 +1654,7 @@ namespace Org.BouncyCastle.Math
                         if (++j == s)
                             return false;
 
-                        y = ModPowMonty(y, Two, n, false);
+                        y = ModSquareMonty(yAccum, y, n);
 
                         if (y.Equals(montRadix))
                             return false;
@@ -1725,12 +1736,12 @@ namespace Org.BouncyCastle.Math
 //				for (;;)
 //				{
 //					// While F is even, do F=F/u, C=C*u, k=k+1.
-//					int zeroes = F.GetLowestSetBit();
-//					if (zeroes > 0)
+//					int zeros = F.GetLowestSetBit();
+//					if (zeros > 0)
 //					{
-//						F = F.ShiftRight(zeroes);
-//						C = C.ShiftLeft(zeroes);
-//						k += zeroes;
+//						F = F.ShiftRight(zeros);
+//						C = C.ShiftLeft(zeros);
+//						k += zeros;
 //					}
 //
 //					// If F = 1, then return B,k.
@@ -1891,7 +1902,8 @@ namespace Org.BouncyCastle.Math
                 }
                 else
                 {
-                    result = ModPowMonty(result, e, m, true);
+                    uint[] yAccum = new uint[m.magnitude.Length + 1];
+                    result = ModPowMonty(yAccum, result, e, m, true);
                 }
             }
 
@@ -1925,17 +1937,17 @@ namespace Org.BouncyCastle.Math
                 oddPowers[i] = ReduceBarrett(oddPowers[i - 1].Multiply(b2), m, mr, yu);
             }
 
-            int[] windowList = GetWindowList(e.magnitude, extraBits);
+            uint[] windowList = GetWindowList(e.magnitude, extraBits);
             Debug.Assert(windowList.Length > 0);
 
-            int window = windowList[0];
-            int mult = window & 0xFF, lastZeroes = window >> 8;
+            uint window = windowList[0];
+            uint mult = window & 0xFFU, lastZeros = window >> 8;
 
             BigInteger y;
             if (mult == 1)
             {
                 y = b2;
-                --lastZeroes;
+                --lastZeros;
             }
             else
             {
@@ -1943,11 +1955,11 @@ namespace Org.BouncyCastle.Math
             }
 
             int windowPos = 1;
-            while ((window = windowList[windowPos++]) != -1)
+            while ((window = windowList[windowPos++]) != uint.MaxValue)
             {
                 mult = window & 0xFF;
 
-                int bits = lastZeroes + BitLen((byte)mult);
+                int bits = (int)lastZeros + BitLen((byte)mult);
                 for (int j = 0; j < bits; ++j)
                 {
                     y = ReduceBarrett(y.Square(), m, mr, yu);
@@ -1955,10 +1967,10 @@ namespace Org.BouncyCastle.Math
 
                 y = ReduceBarrett(y.Multiply(oddPowers[mult >> 1]), m, mr, yu);
 
-                lastZeroes = window >> 8;
+                lastZeros = window >> 8;
             }
 
-            for (int i = 0; i < lastZeroes; ++i)
+            for (int i = 0; i < lastZeros; ++i)
             {
                 y = ReduceBarrett(y.Square(), m, mr, yu);
             }
@@ -1999,7 +2011,7 @@ namespace Org.BouncyCastle.Math
             return x;
         }
 
-        private static BigInteger ModPowMonty(BigInteger b, BigInteger e, BigInteger m, bool convert)
+        private static BigInteger ModPowMonty(uint[] yAccum, BigInteger b, BigInteger e, BigInteger m, bool convert)
         {
             int n = m.magnitude.Length;
             int powR = 32 * n;
@@ -2012,7 +2024,7 @@ namespace Org.BouncyCastle.Math
                 b = b.ShiftLeft(powR).Remainder(m);
             }
 
-            uint[] yAccum = new uint[n + 1];
+            Debug.Assert(yAccum.Length == n + 1);
 
             uint[] zVal = b.magnitude;
             Debug.Assert(zVal.Length <= n);
@@ -2050,17 +2062,17 @@ namespace Org.BouncyCastle.Math
                 MultiplyMonty(yAccum, oddPowers[i], zSquared, m.magnitude, mDash, smallMontyModulus);
             }
 
-            int[] windowList = GetWindowList(e.magnitude, extraBits);
+            uint[] windowList = GetWindowList(e.magnitude, extraBits);
             Debug.Assert(windowList.Length > 1);
 
-            int window = windowList[0];
-            int mult = window & 0xFF, lastZeroes = window >> 8;
+            uint window = windowList[0];
+            uint mult = window & 0xFF, lastZeros = window >> 8;
 
             uint[] yVal;
             if (mult == 1)
             {
                 yVal = zSquared;
-                --lastZeroes;
+                --lastZeros;
             }
             else
             {
@@ -2068,11 +2080,11 @@ namespace Org.BouncyCastle.Math
             }
 
             int windowPos = 1;
-            while ((window = windowList[windowPos++]) != -1)
+            while ((window = windowList[windowPos++]) != uint.MaxValue)
             {
                 mult = window & 0xFF;
 
-                int bits = lastZeroes + BitLen((byte)mult);
+                int bits = (int)lastZeros + BitLen((byte)mult);
                 for (int j = 0; j < bits; ++j)
                 {
                     SquareMonty(yAccum, yVal, m.magnitude, mDash, smallMontyModulus);
@@ -2080,10 +2092,10 @@ namespace Org.BouncyCastle.Math
 
                 MultiplyMonty(yAccum, yVal, oddPowers[mult >> 1], m.magnitude, mDash, smallMontyModulus);
 
-                lastZeroes = window >> 8;
+                lastZeros = window >> 8;
             }
 
-            for (int i = 0; i < lastZeroes; ++i)
+            for (int i = 0; i < lastZeros; ++i)
             {
                 SquareMonty(yAccum, yVal, m.magnitude, mDash, smallMontyModulus);
             }
@@ -2101,22 +2113,49 @@ namespace Org.BouncyCastle.Math
             return new BigInteger(1, yVal, true);
         }
 
-        private static int[] GetWindowList(uint[] mag, int extraBits)
+        private static BigInteger ModSquareMonty(uint[] yAccum, BigInteger b, BigInteger m)
+        {
+            int n = m.magnitude.Length;
+            int powR = 32 * n;
+            bool smallMontyModulus = m.BitLength + 2 <= powR;
+            uint mDash = m.GetMQuote();
+
+            Debug.Assert(yAccum.Length == n + 1);
+
+            uint[] zVal = b.magnitude;
+            Debug.Assert(zVal.Length <= n);
+
+            uint[] yVal = new uint[n];
+            zVal.CopyTo(yVal, n - zVal.Length);
+
+            SquareMonty(yAccum, yVal, m.magnitude, mDash, smallMontyModulus);
+
+            if (smallMontyModulus && CompareTo(0, yVal, 0, m.magnitude) >= 0)
+            {
+                Subtract(0, yVal, 0, m.magnitude);
+            }
+
+            return new BigInteger(1, yVal, true);
+        }
+
+        private static uint[] GetWindowList(uint[] mag, int extraBits)
         {
-            int v = (int)mag[0];
-            Debug.Assert(v != 0);
+            uint v = mag[0];
+            Debug.Assert(v != 0U);
 
-            int leadingBits = BitLen((uint)v);
+            int leadingBits = BitLen(v);
+            int totalBits = ((mag.Length - 1) << 5) + leadingBits;
 
-            int resultSize = (((mag.Length - 1) << 5) + leadingBits) / (1 + extraBits) + 2;
-            int[] result = new int[resultSize];
+            int resultSize = (totalBits + extraBits) / (1 + extraBits) + 1;
+            uint[] result = new uint[resultSize];
             int resultPos = 0;
 
             int bitPos = 33 - leadingBits;
             v <<= bitPos;
 
-            int mult = 1, multLimit = 1 << extraBits;
-            int zeroes = 0;
+            uint mult = 1U;
+            uint multLimit = 1U << extraBits;
+            uint zeros = 0U;
 
             int i = 0;
             for (;;)
@@ -2125,17 +2164,17 @@ namespace Org.BouncyCastle.Math
                 {
                     if (mult < multLimit)
                     {
-                        mult = (mult << 1) | (int)((uint)v >> 31);
+                        mult = (mult << 1) | (v >> 31);
                     }
-                    else if (v < 0)
+                    else if ((int)v < 0)
                     {
-                        result[resultPos++] = CreateWindowEntry(mult, zeroes);
-                        mult = 1;
-                        zeroes = 0;
+                        result[resultPos++] = CreateWindowEntry(mult, zeros);
+                        mult = 1U;
+                        zeros = 0U;
                     }
                     else
                     {
-                        ++zeroes;
+                        ++zeros;
                     }
 
                     v <<= 1;
@@ -2143,35 +2182,35 @@ namespace Org.BouncyCastle.Math
 
                 if (++i == mag.Length)
                 {
-                    result[resultPos++] = CreateWindowEntry(mult, zeroes);
+                    result[resultPos++] = CreateWindowEntry(mult, zeros);
                     break;
                 }
 
-                v = (int)mag[i];
+                v = mag[i];
                 bitPos = 0;
             }
 
-            result[resultPos] = -1;
+            result[resultPos] = uint.MaxValue; // Sentinel value
             return result;
         }
 
-        private static int CreateWindowEntry(int mult, int zeroes)
+        private static uint CreateWindowEntry(uint mult, uint zeros)
         {
             Debug.Assert(mult > 0);
 
 #if NETCOREAPP3_0_OR_GREATER
             int tz = BitOperations.TrailingZeroCount(mult);
             mult >>= tz;
-            zeroes += tz;
+            zeros += (uint)tz;
 #else
-            while ((mult & 1) == 0)
+            while ((mult & 1U) == 0U)
             {
                 mult >>= 1;
-                ++zeroes;
+                ++zeros;
             }
 #endif
 
-            return mult | (zeroes << 8);
+            return mult | (zeros << 8);
         }
 
         /**
@@ -2682,7 +2721,7 @@ namespace Org.BouncyCastle.Math
 
             Debug.Assert(yStart < y.Length);
 
-            int xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y);
+            int xyCmp = CompareNoLeadingZeros(xStart, x, yStart, y);
 
             if (xyCmp > 0)
             {
@@ -2709,7 +2748,7 @@ namespace Org.BouncyCastle.Math
                 for (;;)
                 {
                     if (cBitLength < xBitLength
-                        || CompareNoLeadingZeroes(xStart, x, cStart, c) >= 0)
+                        || CompareNoLeadingZeros(xStart, x, cStart, c) >= 0)
                     {
                         Subtract(xStart, x, cStart, c);
 
@@ -2726,7 +2765,7 @@ namespace Org.BouncyCastle.Math
                             if (xBitLength < yBitLength)
                                 return x;
 
-                            xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y);
+                            xyCmp = CompareNoLeadingZeros(xStart, x, yStart, y);
 
                             if (xyCmp <= 0)
                                 break;
@@ -2799,7 +2838,7 @@ namespace Org.BouncyCastle.Math
                 }
             }
 
-            if (CompareNoLeadingZeroes(0, magnitude, 0, n.magnitude) < 0)
+            if (CompareNoLeadingZeros(0, magnitude, 0, n.magnitude) < 0)
                 return this;
 
             uint[] result;
@@ -3094,7 +3133,7 @@ namespace Org.BouncyCastle.Math
             if (this.sign != n.sign)
                 return Add(n.Negate());
 
-            int compare = CompareNoLeadingZeroes(0, magnitude, 0, n.magnitude);
+            int compare = CompareNoLeadingZeros(0, magnitude, 0, n.magnitude);
             if (compare == 0)
                 return Zero;
 
@@ -3607,47 +3646,55 @@ namespace Org.BouncyCastle.Math
             sb.Append(s);
         }
 
+        private static BigInteger CreateUValueOf(uint value)
+        {
+            if (value == 0)
+                return Zero;
+
+            return new BigInteger(1, new uint[]{ value }, false);
+        }
+
         private static BigInteger CreateUValueOf(ulong value)
         {
             uint msw = (uint)(value >> 32);
             uint lsw = (uint)value;
 
-            if (msw != 0)
-                return new BigInteger(1, new uint[]{ msw, lsw }, false);
-
-            if (lsw != 0)
-            {
-                BigInteger n = new BigInteger(1, new uint[]{ lsw }, false);
-                // Check for a power of two
-                if ((lsw & -lsw) == lsw)
-                {
-                    n.nBits = 1;
-                }
-                return n;
-            }
+            if (msw == 0)
+                return CreateUValueOf(lsw);
 
-            return Zero;
+            return new BigInteger(1, new uint[]{ msw, lsw }, false);
         }
 
-        private static BigInteger CreateValueOf(long value)
+        public static BigInteger ValueOf(int value)
         {
-            if (value < 0)
+            if (value >= 0)
             {
-                if (value == long.MinValue)
-                    return CreateValueOf(~value).Not();
+                if (value < SMALL_CONSTANTS.Length)
+                    return SMALL_CONSTANTS[value];
 
-                return CreateValueOf(-value).Negate();
+                return CreateUValueOf((uint)value);
             }
 
-            return CreateUValueOf((ulong)value);
+            if (value == int.MinValue)
+                return CreateUValueOf((uint)~value).Not();
+
+            return ValueOf(-value).Negate();
         }
 
         public static BigInteger ValueOf(long value)
         {
-            if (value >= 0 && value < SMALL_CONSTANTS.Length)
-                return SMALL_CONSTANTS[value];
+            if (value >= 0L)
+            {
+                if (value < SMALL_CONSTANTS.Length)
+                    return SMALL_CONSTANTS[value];
+
+                return CreateUValueOf((ulong)value);
+            }
+
+            if (value == long.MinValue)
+                return CreateUValueOf((ulong)~value).Not();
 
-            return CreateValueOf(value);
+            return ValueOf(-value).Negate();
         }
 
         public int GetLowestSetBit()
diff --git a/crypto/src/math/ec/abc/Tnaf.cs b/crypto/src/math/ec/abc/Tnaf.cs
index 88a4eeb96..d8e9b6ae0 100644
--- a/crypto/src/math/ec/abc/Tnaf.cs
+++ b/crypto/src/math/ec/abc/Tnaf.cs
@@ -500,12 +500,12 @@ namespace Org.BouncyCastle.Math.EC.Abc
             {
                 if (mu == 1)
                 {
-                    return BigInteger.ValueOf(6);
+                    return BigInteger.Six;
                 }
                 else
                 {
                     // mu == -1
-                    return BigInteger.ValueOf(10);
+                    return BigInteger.Ten;
                 }
             }
             else
diff --git a/crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs b/crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs
index 85b781228..01bac93b8 100644
--- a/crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs
+++ b/crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs
@@ -96,7 +96,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             }
 
             LmsSignedPubKey[] signedPubKeys = signature.GetSignedPubKeys();
-            LmsPublicKeyParameters key = signedPubKeys[signedPubKeys.Length - 1].GetPublicKey();
+            LmsPublicKeyParameters key = LmsPublicKey;
+            if (signedPubKeys.Length != 0)
+            {
+                key = signedPubKeys[signedPubKeys.Length - 1].GetPublicKey();
+            }
 
             return key.GenerateOtsContext(signature.Signature).WithSignedPublicKeys(signedPubKeys);
         }
diff --git a/crypto/test/BouncyCastle.Crypto.Tests.csproj b/crypto/test/BouncyCastle.Crypto.Tests.csproj
index 0264491a3..08fcae206 100644
--- a/crypto/test/BouncyCastle.Crypto.Tests.csproj
+++ b/crypto/test/BouncyCastle.Crypto.Tests.csproj
@@ -30,7 +30,7 @@
     <EmbeddedResource Include="data\**\*.*" Exclude="**\README.txt" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
diff --git a/crypto/test/src/pqc/crypto/test/HSSTest.cs b/crypto/test/src/pqc/crypto/test/HSSTest.cs
index 29b5e1f5a..45cabb906 100644
--- a/crypto/test/src/pqc/crypto/test/HSSTest.cs
+++ b/crypto/test/src/pqc/crypto/test/HSSTest.cs
@@ -13,16 +13,46 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests
     public class HSSTest
     {
         [Test]
+        public void TestOneLevelKeyGenAndSign()
+        {
+            byte[] msg = Strings.ToByteArray("Hello, world!");
+            IAsymmetricCipherKeyPairGenerator kpGen = new HssKeyPairGenerator();
+
+            var lmsParameters = new LmsParameters[]
+            {
+                new LmsParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4)
+            };
+            kpGen.Init(new HssKeyGenerationParameters(lmsParameters, new SecureRandom()));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            HssSigner signer = new HssSigner();
+
+            signer.Init(true, kp.Private);
+
+            byte[] sig = signer.GenerateSignature(msg);
+
+            signer.Init(false, kp.Public);
+
+            Assert.True(signer.VerifySignature(msg, sig));
+
+            HssPublicKeyParameters hssPubKey = (HssPublicKeyParameters)kp.Public;
+
+            hssPubKey.GenerateLmsContext(sig);
+        }
+
+        [Test]
 		public void TestKeyGenAndSign()
         {
             byte[] msg = Strings.ToByteArray("Hello, world!");
             IAsymmetricCipherKeyPairGenerator kpGen = new HssKeyPairGenerator();
 
-            kpGen.Init(new HssKeyGenerationParameters(
-                new LmsParameters[]{
-                    new LmsParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4),
-                    new LmsParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4)
-                }, new SecureRandom()));
+            var lmsParameters = new LmsParameters[]
+            {
+                new LmsParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4),
+                new LmsParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4)
+            };
+            kpGen.Init(new HssKeyGenerationParameters(lmsParameters, new SecureRandom()));
 
             AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();