summary refs log tree commit diff
path: root/crypto/src/pqc
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-12-13 01:23:50 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-12-13 01:23:50 +0700
commitfb78aa65513a5ccf287c0851ed2aa60074975ff3 (patch)
tree97fc93c7cfb93995c2947c9a4937ea727caf4139 /crypto/src/pqc
parentUpdate safegcd implementation (diff)
downloadBouncyCastle.NET-ed25519-fb78aa65513a5ccf287c0851ed2aa60074975ff3.tar.xz
LMS updates
Diffstat (limited to 'crypto/src/pqc')
-rw-r--r--crypto/src/pqc/crypto/lms/HSS.cs35
-rw-r--r--crypto/src/pqc/crypto/lms/HSSPrivateKeyParameters.cs251
-rw-r--r--crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs51
-rw-r--r--crypto/src/pqc/crypto/lms/HSSSignature.cs44
-rw-r--r--crypto/src/pqc/crypto/lms/HSSSigner.cs2
-rw-r--r--crypto/src/pqc/crypto/lms/LMOtsParameters.cs43
-rw-r--r--crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs27
-rw-r--r--crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs33
-rw-r--r--crypto/src/pqc/crypto/lms/LMOtsSignature.cs17
-rw-r--r--crypto/src/pqc/crypto/lms/LMS.cs7
-rw-r--r--crypto/src/pqc/crypto/lms/LMSContext.cs3
-rw-r--r--crypto/src/pqc/crypto/lms/LMSException.cs1
-rw-r--r--crypto/src/pqc/crypto/lms/LMSKeyPairGenerator.cs15
-rw-r--r--crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs115
-rw-r--r--crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs74
-rw-r--r--crypto/src/pqc/crypto/lms/LMSSignature.cs121
-rw-r--r--crypto/src/pqc/crypto/lms/LMSSignedPubKey.cs56
-rw-r--r--crypto/src/pqc/crypto/lms/LMSigParameters.cs53
-rw-r--r--crypto/src/pqc/crypto/lms/LM_OTS.cs66
-rw-r--r--crypto/src/pqc/crypto/lms/LmsUtils.cs89
-rw-r--r--crypto/src/pqc/crypto/lms/SeedDerive.cs22
-rw-r--r--crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs2
-rw-r--r--crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs2
23 files changed, 598 insertions, 531 deletions
diff --git a/crypto/src/pqc/crypto/lms/HSS.cs b/crypto/src/pqc/crypto/lms/HSS.cs
index 4634088c7..75d8a0558 100644
--- a/crypto/src/pqc/crypto/lms/HSS.cs
+++ b/crypto/src/pqc/crypto/lms/HSS.cs
@@ -1,8 +1,11 @@
 using System;
 using System.Collections.Generic;
 
+using Org.BouncyCastle.Security;
+
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public static class Hss
     {
         public static HssPrivateKeyParameters GenerateHssKeyPair(HssKeyGenerationParameters parameters)
@@ -13,11 +16,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             LmsPrivateKeyParameters[] keys = new LmsPrivateKeyParameters[parameters.Depth];
             LmsSignature[] sig = new LmsSignature[parameters.Depth - 1];
 
-            byte[] rootSeed = new byte[32];
-            parameters.Random.NextBytes(rootSeed);
-
-            byte[] I = new byte[16];
-            parameters.Random.NextBytes(I);
+            byte[] rootSeed = SecureRandom.GetNextBytes(parameters.Random,
+                parameters.GetLmsParameters(0).LMSigParameters.M);
+            byte[] I = SecureRandom.GetNextBytes(parameters.Random, 16);
 
             //
             // Set the HSS key up with a valid root LMSPrivateKeyParameters and placeholders for the remaining LMS keys.
@@ -85,7 +86,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             {
                 RangeTestKeys(keyPair);
                 keyPair.IncIndex();
-                keyPair.GetKeys()[keyPair.L - 1].IncIndex();
+                keyPair.GetKeys()[keyPair.Level - 1].IncIndex();
             }
         }
 
@@ -95,18 +96,18 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             {
                 if (keyPair.GetIndex() >= keyPair.IndexLimit)
                 {
+                    // TODO ExhaustedPrivateKeyException
                     throw new Exception(
-                        "hss private key" +
-                            ((keyPair.IsShard()) ? " shard" : "") +
-                            " is exhausted");
+                        "hss private key" + (keyPair.IsShard() ? " shard" : "") + " is exhausted");
                 }
 
-                int L = keyPair.L;
+                int L = keyPair.Level;
                 int d = L;
                 var prv = keyPair.GetKeys();
                 while (prv[d - 1].GetIndex() == 1 << prv[d - 1].GetSigParameters().H)
                 {
                     if (--d == 0)
+                        // TODO ExhaustedPrivateKeyException
                         throw new Exception("hss private key" + (keyPair.IsShard() ? " shard" : "") +
                             " is exhausted the maximum limit for this HSS private key");
                 }
@@ -118,12 +119,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             }
         }
 
-
         public static HssSignature GenerateSignature(HssPrivateKeyParameters keyPair, byte[] message)
         {
             LmsSignedPubKey[] signed_pub_key;
             LmsPrivateKeyParameters nextKey;
-            int L = keyPair.L;
+            int L = keyPair.Level;
 
             lock (keyPair)
             {
@@ -163,8 +163,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public static bool VerifySignature(HssPublicKeyParameters publicKey, HssSignature signature, byte[] message)
         {
-            int Nspk = signature.GetLMinus1();
-            if (Nspk + 1 != publicKey.L)
+            int Nspk = signature.LMinus1;
+            if (Nspk + 1 != publicKey.Level)
                 return false;
 
             LmsSignature[] sigList = new LmsSignature[Nspk + 1];
@@ -172,8 +172,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             for (int i = 0; i < Nspk; i++)
             {
-                sigList[i] = signature.GetSignedPubKeys()[i].GetSignature();
-                pubList[i] = signature.GetSignedPubKeys()[i].GetPublicKey();
+                sigList[i] = signature.SignedPubKeys[i].Signature;
+                pubList[i] = signature.SignedPubKeys[i].PublicKey;
             }
             sigList[Nspk] = signature.Signature;
 
@@ -184,9 +184,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 LmsSignature sig = sigList[i];
                 byte[] msg = pubList[i].ToByteArray();
                 if (!Lms.VerifySignature(key, sig, msg))
-                {
                     return false;
-                }
+
                 try
                 {
                     key = pubList[i];
diff --git a/crypto/src/pqc/crypto/lms/HSSPrivateKeyParameters.cs b/crypto/src/pqc/crypto/lms/HSSPrivateKeyParameters.cs
index 676da3db4..cda020b82 100644
--- a/crypto/src/pqc/crypto/lms/HSSPrivateKeyParameters.cs
+++ b/crypto/src/pqc/crypto/lms/HSSPrivateKeyParameters.cs
@@ -11,25 +11,25 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
     public class HssPrivateKeyParameters
         : LmsKeyParameters, ILmsContextBasedSigner
     {
-        private int l;
-        private bool isShard;
-        private IList<LmsPrivateKeyParameters> keys;
-        private IList<LmsSignature> sig;
-        private long indexLimit;
-        private long index = 0;
+        private readonly int m_level;
+        private readonly bool m_isShard;
+        private IList<LmsPrivateKeyParameters> m_keys;
+        private IList<LmsSignature> m_sig;
+        private readonly long m_indexLimit;
+        private long m_index = 0;
 
-        private HssPublicKeyParameters publicKey;
+        private HssPublicKeyParameters m_publicKey;
 
         public HssPrivateKeyParameters(int l, IList<LmsPrivateKeyParameters> keys, IList<LmsSignature> sig, long index,
             long indexLimit)
-    	    :base(true)
+    	    : base(true)
         {
-            this.l = l;
-            this.keys = new List<LmsPrivateKeyParameters>(keys);
-            this.sig = new List<LmsSignature>(sig);
-            this.index = index;
-            this.indexLimit = indexLimit;
-            this.isShard = false;
+            m_level = l;
+            m_isShard = false;
+            m_keys = new List<LmsPrivateKeyParameters>(keys);
+            m_sig = new List<LmsSignature>(sig);
+            m_index = index;
+            m_indexLimit = indexLimit;
 
             //
             // Correct Intermediate LMS values will be constructed during reset to index.
@@ -42,22 +42,18 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
     	    :base(true)
         {
 
-            this.l = l;
-            // this.keys =  new UnmodifiableListProxy(keys);
-            // this.sig =  new UnmodifiableListProxy(sig);
-            this.keys = new List<LmsPrivateKeyParameters>(keys);
-            this.sig = new List<LmsSignature>(sig);
-            this.index = index;
-            this.indexLimit = indexLimit;
-            this.isShard = isShard;
+            m_level = l;
+            m_isShard = isShard;
+            m_keys = new List<LmsPrivateKeyParameters>(keys);
+            m_sig = new List<LmsSignature>(sig);
+            m_index = index;
+            m_indexLimit = indexLimit;
         }
 
         public static HssPrivateKeyParameters GetInstance(byte[] privEnc, byte[] pubEnc)
         {
             HssPrivateKeyParameters pKey = GetInstance(privEnc);
-
-            pKey.publicKey = HssPublicKeyParameters.GetInstance(pubEnc);
-
+            pKey.m_publicKey = HssPublicKeyParameters.GetInstance(pubEnc);
             return pKey;
         }
 
@@ -107,39 +103,38 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return new HssPrivateKeyParameters(d, keys, signatures, index, maxIndex, limited);
         }
 
-        public int L => l;
+        [Obsolete("Use 'Level' instead")]
+        public int L => m_level;
+
+        public int Level => m_level;
 
         public long GetIndex()
         {
-            lock (this)
-                return index;
+            lock (this) return m_index;
         }
 
         public LmsParameters[] GetLmsParameters()
         {
             lock (this)
             {
-                int len = keys.Count;
+                int len = m_keys.Count;
 
-                LmsParameters[] parms = new LmsParameters[len];
+                LmsParameters[] parameters = new LmsParameters[len];
 
                 for (int i = 0; i < len; i++)
                 {
-                    LmsPrivateKeyParameters lmsPrivateKey = keys[i];
+                    LmsPrivateKeyParameters lmsPrivateKey = m_keys[i];
 
-                    parms[i] = new LmsParameters(lmsPrivateKey.GetSigParameters(), lmsPrivateKey.GetOtsParameters());
+                    parameters[i] = new LmsParameters(lmsPrivateKey.GetSigParameters(), lmsPrivateKey.GetOtsParameters());
                 }
 
-                return parms;
+                return parameters;
             }
         }
 
         internal void IncIndex()
         {
-            lock (this)
-            {
-                index++;
-            }
+            lock (this) m_index++;
         }
 
         private static HssPrivateKeyParameters MakeCopy(HssPrivateKeyParameters privateKeyParameters)
@@ -151,27 +146,18 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         {
             lock (this)
             {
-                keys = new List<LmsPrivateKeyParameters>(newKeys);
-                sig = new List<LmsSignature>(newSig);
+                m_keys = new List<LmsPrivateKeyParameters>(newKeys);
+                m_sig = new List<LmsSignature>(newSig);
             }
         }
 
-        public bool IsShard()
-        {
-            return isShard;
-        }
+        public bool IsShard() => m_isShard;
 
-        public long IndexLimit => indexLimit;
+        public long IndexLimit => m_indexLimit;
 
-        public long GetUsagesRemaining()
-        {
-            return indexLimit - index;
-        }
+        public long GetUsagesRemaining() => m_indexLimit - m_index;
 
-        LmsPrivateKeyParameters GetRootKey()
-        {
-            return keys[0];
-        }
+        internal LmsPrivateKeyParameters GetRootKey() => m_keys[0];
 
         /**
          * Return a key that can be used usageCount times.
@@ -189,19 +175,19 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 if (GetUsagesRemaining() < usageCount)
                     throw new ArgumentException("usageCount exceeds usages remaining in current leaf");
 
-                long maxIndexForShard = index + usageCount;
-                long shardStartIndex = index;
+                long maxIndexForShard = m_index + usageCount;
+                long shardStartIndex = m_index;
 
                 //
                 // Move this key's index along
                 //
-                index += usageCount;
+                m_index += usageCount;
 
                 var keys = new List<LmsPrivateKeyParameters>(this.GetKeys());
                 var sig = new List<LmsSignature>(this.GetSig());
 
                 HssPrivateKeyParameters shard = MakeCopy(
-                    new HssPrivateKeyParameters(l, keys, sig, shardStartIndex, maxIndexForShard, true));
+                    new HssPrivateKeyParameters(m_level, keys, sig, shardStartIndex, maxIndexForShard, true));
 
                 ResetKeyToIndex();
 
@@ -211,12 +197,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public IList<LmsPrivateKeyParameters> GetKeys()
         {
-            lock (this) return keys;
+            lock (this) return m_keys;
         }
 
         internal IList<LmsSignature> GetSig()
         {
-            lock (this) return sig;
+            lock (this) return m_sig;
         }
 
         /**
@@ -252,9 +238,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             //
             // We need to replace the root key to a new q value.
             //
-            if (keys[0].GetIndex() - 1 != qTreePath[0])
+            if (m_keys[0].GetIndex() - 1 != qTreePath[0])
             {
-                keys[0] = Lms.GenerateKeys(
+                m_keys[0] = Lms.GenerateKeys(
                     originalRootKey.GetSigParameters(),
                     originalRootKey.GetOtsParameters(),
                     (int)qTreePath[0], originalRootKey.GetI(), originalRootKey.GetMasterSecret());
@@ -263,21 +249,22 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             for (int i = 1; i < qTreePath.Length; i++)
             {
-                LmsPrivateKeyParameters intermediateKey = keys[i - 1];
+                LmsPrivateKeyParameters intermediateKey = m_keys[i - 1];
+                int n = intermediateKey.GetOtsParameters().N;
 
                 byte[] childI = new byte[16];
-                byte[] childSeed = new byte[32];
+                byte[] childSeed = new byte[n];
                 SeedDerive derive = new SeedDerive(
                     intermediateKey.GetI(),
                     intermediateKey.GetMasterSecret(),
-                    DigestUtilities.GetDigest(intermediateKey.GetOtsParameters().DigestOid))
+                    LmsUtilities.GetDigest(intermediateKey.GetOtsParameters()))
                 {
                     Q = (int)qTreePath[i - 1],
                     J = ~1,
                 };
 
                 derive.DeriveSeed(true, childSeed, 0);
-                byte[] postImage = new byte[32];
+                byte[] postImage = new byte[n];
                 derive.DeriveSeed(false, postImage, 0);
                 Array.Copy(postImage, 0, childI, 0, childI.Length);
 
@@ -287,22 +274,22 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 // For the end key its value will match so no correction is required.
                 //
                 bool lmsQMatch = (i < qTreePath.Length - 1)
-                    ? qTreePath[i] == keys[i].GetIndex() - 1
-                    : qTreePath[i] == keys[i].GetIndex();
+                    ? qTreePath[i] == m_keys[i].GetIndex() - 1
+                    : qTreePath[i] == m_keys[i].GetIndex();
 
                 //
                 // Equality is I and seed being equal and the lmsQMath.
                 // I and seed are derived from this nodes parent and will change if the parent q, I, seed changes.
                 //
-                bool seedEquals = Arrays.AreEqual(childI, keys[i].GetI())
-                    && Arrays.AreEqual(childSeed, keys[i].GetMasterSecret());
+                bool seedEquals = Arrays.AreEqual(childI, m_keys[i].GetI())
+                    && Arrays.AreEqual(childSeed, m_keys[i].GetMasterSecret());
 
                 if (!seedEquals)
                 {
                     //
                     // This means the parent has changed.
                     //
-                    keys[i] = Lms.GenerateKeys(
+                    m_keys[i] = Lms.GenerateKeys(
                         originalKeys[i].GetSigParameters(),
                         originalKeys[i].GetOtsParameters(),
                         (int)qTreePath[i], childI, childSeed);
@@ -310,7 +297,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                     //
                     // Ensure post increment occurs on parent and the new public key is signed.
                     //
-                    sig[i - 1] = Lms.GenerateSign((LmsPrivateKeyParameters)keys[i - 1], ((LmsPrivateKeyParameters)keys[i]).GetPublicKey().ToByteArray());
+                    m_sig[i - 1] = Lms.GenerateSign((LmsPrivateKeyParameters)m_keys[i - 1], ((LmsPrivateKeyParameters)m_keys[i]).GetPublicKey().ToByteArray());
                     changed = true;
                 }
                 else if (!lmsQMatch)
@@ -319,7 +306,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                     // Q is different so we can generate a new private key but it will have the same public
                     // key so we do not need to sign it again.
                     //
-                    keys[i] = Lms.GenerateKeys(
+                    m_keys[i] = Lms.GenerateKeys(
                         originalKeys[i].GetSigParameters(),
                         originalKeys[i].GetOtsParameters(),
                         (int)qTreePath[i], childI, childSeed);
@@ -330,92 +317,60 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             if (changed)
             {
                 // We mutate the HSS key here!
-                UpdateHierarchy(keys, sig);
+                UpdateHierarchy(m_keys, m_sig);
             }
         }
 
         public HssPublicKeyParameters GetPublicKey()
         {
             lock (this)
-                return new HssPublicKeyParameters(l, GetRootKey().GetPublicKey());
+                return new HssPublicKeyParameters(m_level, GetRootKey().GetPublicKey());
         }
 
         internal void ReplaceConsumedKey(int d)
         {
-            SeedDerive deriver = keys[d - 1].GetCurrentOtsKey().GetDerivationFunction();
+            LMOtsPrivateKey currentOtsKey = m_keys[d - 1].GetCurrentOtsKey();
+            int n = currentOtsKey.Parameters.N;
+
+            SeedDerive deriver = currentOtsKey.GetDerivationFunction();
             deriver.J = ~1;
-            byte[] childRootSeed = new byte[32];
+            byte[] childRootSeed = new byte[n];
             deriver.DeriveSeed(true, childRootSeed, 0);
-            byte[] postImage = new byte[32];
+            byte[] postImage = new byte[n];
             deriver.DeriveSeed(false, postImage, 0);
             byte[] childI = new byte[16];
             Array.Copy(postImage, 0, childI, 0, childI.Length);
 
-            var newKeys = new List<LmsPrivateKeyParameters>(keys);
+            var newKeys = new List<LmsPrivateKeyParameters>(m_keys);
 
             //
             // We need the parameters from the LMS key we are replacing.
             //
-            LmsPrivateKeyParameters oldPk = keys[d];
+            LmsPrivateKeyParameters oldPk = m_keys[d];
 
             newKeys[d] = Lms.GenerateKeys(oldPk.GetSigParameters(), oldPk.GetOtsParameters(), 0, childI, childRootSeed);
 
-            var newSig = new List<LmsSignature>(sig);
+            var newSig = new List<LmsSignature>(m_sig);
 
             newSig[d - 1] = Lms.GenerateSign(newKeys[d - 1], newKeys[d].GetPublicKey().ToByteArray());
 
-            this.keys = new List<LmsPrivateKeyParameters>(newKeys);
-            this.sig = new List<LmsSignature>(newSig);
+            this.m_keys = new List<LmsPrivateKeyParameters>(newKeys);
+            this.m_sig = new List<LmsSignature>(newSig);
         }
 
-        public override bool Equals(Object o)
+        public override bool Equals(object obj)
         {
-            if (this == o)
-            {
+            if (this == obj)
                 return true;
-            }
-            if (o == null || GetType() != o.GetType())
-            {
-                return false;
-            }
-
-            HssPrivateKeyParameters that = (HssPrivateKeyParameters)o;
-
-            if (l != that.l)
-            {
-                return false;
-            }
-            if (isShard != that.isShard)
-            {
-                return false;
-            }
-            if (indexLimit != that.indexLimit)
-            {
-                return false;
-            }
-            if (index != that.index)
-            {
-                return false;
-            }
-            if (!CompareLists(keys, that.keys))
-            {
-                return false;
-            }
-            return CompareLists(sig, that.sig);
-        }
 
-        private bool CompareLists<T>(IList<T> arr1, IList<T> arr2)
-        {
-            for (int i=0; i<arr1.Count && i<arr2.Count; i++)
-            {
-                if (!Object.Equals(arr1[i], arr2[i]))
-                {
-                    return false;
-                }
-            }
-            return true;
+            return obj is HssPrivateKeyParameters that
+                && this.m_level == that.m_level
+                && this.m_isShard == that.m_isShard
+                && this.m_indexLimit == that.m_indexLimit
+                && this.m_index == that.m_index
+                && CompareLists(this.m_keys, that.m_keys)
+                && CompareLists(this.m_sig, that.m_sig);
         }
-        
 
         public override byte[] GetEncoded()
         {
@@ -427,17 +382,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
                 Composer composer = Composer.Compose()
                     .U32Str(0) // Version.
-                    .U32Str(l)
-                    .U64Str(index)
-                    .U64Str(indexLimit)
-                    .Boolean(isShard); // Depth
+                    .U32Str(m_level)
+                    .U64Str(m_index)
+                    .U64Str(m_indexLimit)
+                    .Boolean(m_isShard); // Depth
 
-                foreach (LmsPrivateKeyParameters key in keys)
+                foreach (LmsPrivateKeyParameters key in m_keys)
                 {
                     composer.Bytes(key);
                 }
 
-                foreach (LmsSignature s in sig)
+                foreach (LmsSignature s in m_sig)
                 {
                     composer.Bytes(s);
                 }
@@ -448,12 +403,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public override int GetHashCode()
         {
-            int result = l;
-            result = 31 * result + (isShard ? 1 : 0);
-            result = 31 * result + keys.GetHashCode();
-            result = 31 * result + sig.GetHashCode();
-            result = 31 * result + (int)(indexLimit ^ (indexLimit >> 32));
-            result = 31 * result + (int)(index ^ (index >> 32));
+            int result = m_level;
+            result = 31 * result + m_isShard.GetHashCode();
+            result = 31 * result + m_keys.GetHashCode();
+            result = 31 * result + m_sig.GetHashCode();
+            result = 31 * result + m_indexLimit.GetHashCode();
+            result = 31 * result + m_index.GetHashCode();
             return result;
         }
 
@@ -466,7 +421,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         {
             LmsSignedPubKey[] signed_pub_key;
             LmsPrivateKeyParameters nextKey;
-            int L = this.L;
+            int level = Level;
 
             lock (this)
             {
@@ -475,12 +430,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 var keys = this.GetKeys();
                 var sig = this.GetSig();
 
-                nextKey = this.GetKeys()[L - 1];
+                nextKey = this.GetKeys()[level - 1];
 
-                // Step 2. Stand in for sig[L-1]
+                // Step 2. Stand in for sig[level-1]
                 int i = 0;
-                signed_pub_key = new LmsSignedPubKey[L - 1];
-                while (i < L - 1)
+                signed_pub_key = new LmsSignedPubKey[level - 1];
+                while (i < level - 1)
                 {
                     signed_pub_key[i] = new LmsSignedPubKey(sig[i], keys[i + 1].GetPublicKey());
                     ++i;
@@ -499,12 +454,22 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         {
             try
             {
-                return Hss.GenerateSignature(L, context).GetEncoded();
+                return Hss.GenerateSignature(Level, context).GetEncoded();
             }
             catch (IOException e)
             {
                 throw new Exception($"unable to encode signature: {e.Message}", e);
             }
         }
+
+        private static bool CompareLists<T>(IList<T> arr1, IList<T> arr2)
+        {
+            for (int i = 0; i < arr1.Count && i < arr2.Count; i++)
+            {
+                if (!Object.Equals(arr1[i], arr2[i]))
+                    return false;
+            }
+            return true;
+        }
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs b/crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs
index d3bc697d9..9766efdd1 100644
--- a/crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs
+++ b/crypto/src/pqc/crypto/lms/HSSPublicKeyParameters.cs
@@ -1,6 +1,5 @@
 using System;
 using System.IO;
-using System.Text;
 
 using Org.BouncyCastle.Utilities.IO;
 
@@ -9,14 +8,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
     public sealed class HssPublicKeyParameters
         : LmsKeyParameters, ILmsContextBasedVerifier
     {
-        private readonly int m_l;
+        private readonly int m_level; // hierarchical level
         private readonly LmsPublicKeyParameters m_lmsPublicKey;
 
         public HssPublicKeyParameters(int l, LmsPublicKeyParameters lmsPublicKey)
-    	    :base(false)
+    	    : base(false)
         {
-            m_l = l;
-            m_lmsPublicKey = lmsPublicKey;
+            m_level = l;
+            m_lmsPublicKey = lmsPublicKey ?? throw new ArgumentNullException(nameof(lmsPublicKey));
         }
 
         public static HssPublicKeyParameters GetInstance(object src)
@@ -43,33 +42,35 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return new HssPublicKeyParameters(L, lmsPublicKey);
         }
 
-        public int L => m_l;
+        [Obsolete("Use 'Level' instead")]
+        public int L => m_level;
+
+        public int Level => m_level;
 
         public LmsPublicKeyParameters LmsPublicKey => m_lmsPublicKey;
 
-        public override bool Equals(Object o)
+        // TODO[api] Fix parameter name
+        public override bool Equals(object o)
         {
             if (this == o)
                 return true;
-            if (o == null || GetType() != o.GetType())
-                return false;
-
-            HssPublicKeyParameters publicKey = (HssPublicKeyParameters)o;
 
-            return m_l == publicKey.m_l
-                && m_lmsPublicKey.Equals(publicKey.m_lmsPublicKey);
+            return o is HssPublicKeyParameters that
+                && this.m_level == that.m_level
+                && this.m_lmsPublicKey.Equals(that.m_lmsPublicKey);
         }
 
         public override int GetHashCode()
         {
-            int result = m_l;
+            int result = m_level;
             result = 31 * result + m_lmsPublicKey.GetHashCode();
             return result;
         }
 
         public override byte[] GetEncoded()
         {
-            return Composer.Compose().U32Str(m_l)
+            return Composer.Compose()
+                .U32Str(m_level)
                 .Bytes(m_lmsPublicKey.GetEncoded())
                 .Build();
         }
@@ -79,18 +80,18 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             HssSignature signature;
             try
             {
-                signature = HssSignature.GetInstance(sigEnc, L);
+                signature = HssSignature.GetInstance(sigEnc, Level);
             }
             catch (IOException e)
             {
                 throw new Exception($"cannot parse signature: {e.Message}");
             }
 
-            LmsSignedPubKey[] signedPubKeys = signature.GetSignedPubKeys();
+            LmsSignedPubKey[] signedPubKeys = signature.SignedPubKeys;
             LmsPublicKeyParameters key = LmsPublicKey;
             if (signedPubKeys.Length != 0)
             {
-                key = signedPubKeys[signedPubKeys.Length - 1].GetPublicKey();
+                key = signedPubKeys[signedPubKeys.Length - 1].PublicKey;
             }
 
             return key.GenerateOtsContext(signature.Signature).WithSignedPublicKeys(signedPubKeys);
@@ -100,7 +101,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         {
             LmsSignedPubKey[] sigKeys = context.SignedPubKeys;
 
-            if (sigKeys.Length != L - 1)
+            if (sigKeys.Length != Level - 1)
                 return false;
 
             LmsPublicKeyParameters key = LmsPublicKey;
@@ -108,16 +109,18 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             for (int i = 0; i < sigKeys.Length; i++)
             {
-                LmsSignature sig = sigKeys[i].GetSignature();
-                byte[] msg = sigKeys[i].GetPublicKey().ToByteArray();
-                if (!Lms.VerifySignature(key, sig, msg))
+                LmsSignature sig = sigKeys[i].Signature;
+                LmsPublicKeyParameters nextKey = sigKeys[i].PublicKey;
+
+                if (!Lms.VerifySignature(key, sig, nextKey.ToByteArray()))
                 {
                     failed = true;
                 }
-                key = sigKeys[i].GetPublicKey();
+
+                key = nextKey;
             }
 
             return !failed & key.Verify(context);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/lms/HSSSignature.cs b/crypto/src/pqc/crypto/lms/HSSSignature.cs
index bbf0c6f0f..946d0ef89 100644
--- a/crypto/src/pqc/crypto/lms/HSSSignature.cs
+++ b/crypto/src/pqc/crypto/lms/HSSSignature.cs
@@ -7,18 +7,19 @@ using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public sealed class HssSignature
         : IEncodable
     {
         private readonly int m_lMinus1;
-        private readonly LmsSignedPubKey[] m_signedPubKey;
+        private readonly LmsSignedPubKey[] m_signedPubKeys;
         private readonly LmsSignature m_signature;
 
         // TODO[api] signedPubKeys
         public HssSignature(int lMinus1, LmsSignedPubKey[] signedPubKey, LmsSignature signature)
         {
             m_lMinus1 = lMinus1;
-            m_signedPubKey = signedPubKey;
+            m_signedPubKeys = signedPubKey;
             m_signature = signature;
         }
 
@@ -75,46 +76,37 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             }
         }
 
+        [Obsolete("Use 'LMinus1' instead")]
         public int GetLMinus1()
         {
             return m_lMinus1;
         }
 
-        // FIXME
-        public LmsSignedPubKey[] GetSignedPubKeys()
-        {
-            return m_signedPubKey;
-        }
+        public int LMinus1 => m_lMinus1;
+
+        public LmsSignedPubKey[] GetSignedPubKeys() => (LmsSignedPubKey[])m_signedPubKeys?.Clone();
+
+        internal LmsSignedPubKey[] SignedPubKeys => m_signedPubKeys;
 
         public LmsSignature Signature => m_signature;
 
+        // TODO[api] Fix parameter name
         public override bool Equals(object other)
         {
             if (this == other)
                 return true;
-            if (!(other is HssSignature that))
-                return false;
-
-            if (this.m_lMinus1 != that.m_lMinus1)
-                return false;
-
-            if (this.m_signedPubKey.Length != that.m_signedPubKey.Length)
-                return false;
-
-            for (int t = 0; t < m_signedPubKey.Length; t++)
-            {
-                if (!this.m_signedPubKey[t].Equals(that.m_signedPubKey[t]))
-                    return false;
-            }
 
-            return Equals(this.m_signature, that.m_signature);
+            return other is HssSignature that
+                && this.m_lMinus1 == that.m_lMinus1
+                && Arrays.AreEqual(this.m_signedPubKeys, that.m_signedPubKeys)
+                && Objects.Equals(this.m_signature, that.m_signature);
         }
 
         public override int GetHashCode()
         {
             int result = m_lMinus1;
-            result = 31 * result + m_signedPubKey.GetHashCode();
-            result = 31 * result + (m_signature != null ? m_signature.GetHashCode() : 0);
+            result = 31 * result + Arrays.GetHashCode(m_signedPubKeys);
+            result = 31 * result + Objects.GetHashCode(m_signature);
             return result;
         }
 
@@ -122,9 +114,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         {
             Composer composer = Composer.Compose();
             composer.U32Str(m_lMinus1);
-            if (m_signedPubKey != null)
+            if (m_signedPubKeys != null)
             {
-                foreach (LmsSignedPubKey sigPub in m_signedPubKey)
+                foreach (LmsSignedPubKey sigPub in m_signedPubKeys)
                 {
                     composer.Bytes(sigPub);
                 }
diff --git a/crypto/src/pqc/crypto/lms/HSSSigner.cs b/crypto/src/pqc/crypto/lms/HSSSigner.cs
index 9ef7b57ae..295caa00e 100644
--- a/crypto/src/pqc/crypto/lms/HSSSigner.cs
+++ b/crypto/src/pqc/crypto/lms/HSSSigner.cs
@@ -39,7 +39,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         {
             try
             {
-                return Hss.VerifySignature(pubKey, HssSignature.GetInstance(signature, pubKey.L), message);
+                return Hss.VerifySignature(pubKey, HssSignature.GetInstance(signature, pubKey.Level), message);
             }
             catch (IOException e)
             {
diff --git a/crypto/src/pqc/crypto/lms/LMOtsParameters.cs b/crypto/src/pqc/crypto/lms/LMOtsParameters.cs
index 60bf28d50..ce472c18d 100644
--- a/crypto/src/pqc/crypto/lms/LMOtsParameters.cs
+++ b/crypto/src/pqc/crypto/lms/LMOtsParameters.cs
@@ -9,22 +9,52 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
     public sealed class LMOtsParameters
     {
-        // TODO add all parameter sets
-
-        //public static int reserved = 0;
         public static LMOtsParameters sha256_n32_w1 = new LMOtsParameters(1, 32, 1, 265, 7, 8516, NistObjectIdentifiers.IdSha256);
         public static LMOtsParameters sha256_n32_w2 = new LMOtsParameters(2, 32, 2, 133, 6, 4292, NistObjectIdentifiers.IdSha256);
         public static LMOtsParameters sha256_n32_w4 = new LMOtsParameters(3, 32, 4, 67, 4, 2180, NistObjectIdentifiers.IdSha256);
         public static LMOtsParameters sha256_n32_w8 = new LMOtsParameters(4, 32, 8, 34, 0, 1124, NistObjectIdentifiers.IdSha256);
 
+        public static LMOtsParameters sha256_n24_w1 = new LMOtsParameters(5, 24, 1, 200, 8, 5436, NistObjectIdentifiers.IdSha256);
+        public static LMOtsParameters sha256_n24_w2 = new LMOtsParameters(6, 24, 2, 101, 6, 2940, NistObjectIdentifiers.IdSha256);
+        public static LMOtsParameters sha256_n24_w4 = new LMOtsParameters(7, 24, 4, 51, 4, 1500, NistObjectIdentifiers.IdSha256);
+        public static LMOtsParameters sha256_n24_w8 = new LMOtsParameters(8, 24, 8, 26, 0, 1020, NistObjectIdentifiers.IdSha256);
+
+        public static LMOtsParameters shake256_n32_w1 = new LMOtsParameters(9, 32, 1, 265, 7, 8516, NistObjectIdentifiers.IdShake256Len);
+        public static LMOtsParameters shake256_n32_w2 = new LMOtsParameters(10, 32, 2, 133, 6, 4292, NistObjectIdentifiers.IdShake256Len);
+        public static LMOtsParameters shake256_n32_w4 = new LMOtsParameters(11, 32, 4, 67, 4, 2180, NistObjectIdentifiers.IdShake256Len);
+        public static LMOtsParameters shake256_n32_w8 = new LMOtsParameters(12, 32, 8, 34, 0, 1124, NistObjectIdentifiers.IdShake256Len);
+
+        public static LMOtsParameters shake256_n24_w1 = new LMOtsParameters(13, 24, 1, 200, 8, 5436, NistObjectIdentifiers.IdShake256Len);
+        public static LMOtsParameters shake256_n24_w2 = new LMOtsParameters(14, 24, 2, 101, 6, 2940, NistObjectIdentifiers.IdShake256Len);
+        public static LMOtsParameters shake256_n24_w4 = new LMOtsParameters(15, 24, 4, 51, 4, 1500, NistObjectIdentifiers.IdShake256Len);
+        public static LMOtsParameters shake256_n24_w8 = new LMOtsParameters(16, 24, 8, 26, 0, 1020, NistObjectIdentifiers.IdShake256Len);
+
         private static Dictionary<object, LMOtsParameters> Suppliers = new Dictionary<object, LMOtsParameters>
         {
             { sha256_n32_w1.ID, sha256_n32_w1 },
             { sha256_n32_w2.ID, sha256_n32_w2 },
             { sha256_n32_w4.ID, sha256_n32_w4 },
-            { sha256_n32_w8.ID, sha256_n32_w8 }
+            { sha256_n32_w8.ID, sha256_n32_w8 },
+
+            { sha256_n24_w1.ID, sha256_n24_w1 },
+            { sha256_n24_w2.ID, sha256_n24_w2 },
+            { sha256_n24_w4.ID, sha256_n24_w4 },
+            { sha256_n24_w8.ID, sha256_n24_w8 },
+
+            { shake256_n32_w1.ID, shake256_n32_w1 },
+            { shake256_n32_w2.ID, shake256_n32_w2 },
+            { shake256_n32_w4.ID, shake256_n32_w4 },
+            { shake256_n32_w8.ID, shake256_n32_w8 },
+
+            { shake256_n24_w1.ID, shake256_n24_w1 },
+            { shake256_n24_w2.ID, shake256_n24_w2 },
+            { shake256_n24_w4.ID, shake256_n24_w4 },
+            { shake256_n24_w8.ID, shake256_n24_w8 },
         };
 
+        public static LMOtsParameters GetParametersByID(int id) =>
+            CollectionUtilities.GetValueOrNull(Suppliers, id);
+
         private readonly int m_id;
         private readonly int m_n;
         private readonly int m_w;
@@ -57,10 +87,5 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         public int SigLen => Convert.ToInt32(m_sigLen);
 
         public DerObjectIdentifier DigestOid => m_digestOid;
-
-        public static LMOtsParameters GetParametersByID(int id)
-        {
-            return CollectionUtilities.GetValueOrNull(Suppliers, id);
-        }
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs b/crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs
index 20b717af6..2a6a18155 100644
--- a/crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs
+++ b/crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs
@@ -1,8 +1,11 @@
+using System;
+
 using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public sealed class LMOtsPrivateKey
     {
         private readonly LMOtsParameters m_parameters;
@@ -20,13 +23,13 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public LmsContext GetSignatureContext(LMSigParameters sigParams, byte[][] path)
         {
-            byte[] C = new byte[LMOts.SEED_LEN];
+            byte[] C = new byte[m_parameters.N];
 
             SeedDerive derive = GetDerivationFunction();
             derive.J = LMOts.SEED_RANDOMISER_INDEX; // This value from reference impl.
             derive.DeriveSeed(false, C, 0);
 
-            IDigest ctx = DigestUtilities.GetDigest(m_parameters.DigestOid);
+            IDigest ctx = LmsUtilities.GetDigest(m_parameters);
 
             LmsUtilities.ByteArray(m_I, ctx);
             LmsUtilities.U32Str(m_q, ctx);
@@ -36,23 +39,27 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return new LmsContext(this, sigParams, ctx, C, path);
         }
 
+        public byte[] GetI() => Arrays.Clone(m_I);
+
+        public byte[] GetMasterSecret() => Arrays.Clone(m_masterSecret);
+
+        public LMOtsParameters Parameters => m_parameters;
+
+        public int Q => m_q;
+
         internal SeedDerive GetDerivationFunction()
         {
-            return new SeedDerive(m_I, m_masterSecret, DigestUtilities.GetDigest(m_parameters.DigestOid))
+            return new SeedDerive(m_I, m_masterSecret, LmsUtilities.GetDigest(m_parameters))
             {
                 Q = m_q,
                 J = 0,
             };
         }
 
-        public LMOtsParameters Parameters => m_parameters;
-
-        // FIXME
+        [Obsolete("Use 'GetI' instead")]
         public byte[] I => m_I;
 
-        public int Q => m_q;
-
-        // FIXME
+        [Obsolete("Use 'GetMasterSecret' instead")]
         public byte[] MasterSecret => m_masterSecret;
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs b/crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs
index 5be482e25..0f5d956e8 100644
--- a/crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs
+++ b/crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs
@@ -2,12 +2,12 @@ using System;
 using System.IO;
 
 using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public sealed class LMOtsPublicKey
     {
         private readonly LMOtsParameters m_parameters;
@@ -54,22 +54,21 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return new LMOtsPublicKey(parameter, I, q, K);
         }
 
-        public LMOtsParameters Parameters => m_parameters;
+        public byte[] GetI() => Arrays.Clone(m_I);
 
-        public byte[] I => m_I;
+        public byte[] GetK() => Arrays.Clone(m_K);
 
-        public int Q => m_q;
+        public LMOtsParameters Parameters => m_parameters;
 
-        public byte[] K => m_K;
+        public int Q => m_q;
 
         public override bool Equals(object obj)
         {
             if (this == obj)
                 return true;
-            if (!(obj is LMOtsPublicKey that))
-                return false;
 
-            return m_q == that.m_q
+            return obj is LMOtsPublicKey that
+                && m_q == that.m_q
                 && Objects.Equals(m_parameters, that.m_parameters)
                 && Arrays.AreEqual(m_I, that.m_I)
                 && Arrays.AreEqual(m_K, that.m_K);
@@ -77,9 +76,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public override int GetHashCode()
         {
-            int result = Objects.GetHashCode(m_parameters);
+            int result = m_q;
+            result = 31 * result + Objects.GetHashCode(m_parameters);
             result = 31 * result + Arrays.GetHashCode(m_I);
-            result = 31 * result + m_q;
             result = 31 * result + Arrays.GetHashCode(m_K);
             return result;
         }
@@ -96,26 +95,36 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         internal LmsContext CreateOtsContext(LMOtsSignature signature)
         {
-            IDigest ctx = DigestUtilities.GetDigest(m_parameters.DigestOid);
+            IDigest ctx = LmsUtilities.GetDigest(m_parameters);
 
             LmsUtilities.ByteArray(m_I, ctx);
             LmsUtilities.U32Str(m_q, ctx);
             LmsUtilities.U16Str((short)LMOts.D_MESG, ctx);
+#pragma warning disable CS0618 // Type or member is obsolete
             LmsUtilities.ByteArray(signature.C, ctx);
+#pragma warning restore CS0618 // Type or member is obsolete
 
             return new LmsContext(this, signature, ctx);
         }
 
         internal LmsContext CreateOtsContext(LmsSignature signature)
         {
-            IDigest ctx = DigestUtilities.GetDigest(m_parameters.DigestOid);
+            IDigest ctx = LmsUtilities.GetDigest(m_parameters);
 
             LmsUtilities.ByteArray(m_I, ctx);
             LmsUtilities.U32Str(m_q, ctx);
             LmsUtilities.U16Str((short)LMOts.D_MESG, ctx);
+#pragma warning disable CS0618 // Type or member is obsolete
             LmsUtilities.ByteArray(signature.OtsSignature.C, ctx);
+#pragma warning restore CS0618 // Type or member is obsolete
 
             return new LmsContext(this, signature, ctx);
         }
+
+        [Obsolete("Use 'GetI' instead")]
+        public byte[] I => m_I;
+
+        [Obsolete("Use 'GetK' instead")]
+        public byte[] K => m_K;
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/LMOtsSignature.cs b/crypto/src/pqc/crypto/lms/LMOtsSignature.cs
index afc309be6..f337461ac 100644
--- a/crypto/src/pqc/crypto/lms/LMOtsSignature.cs
+++ b/crypto/src/pqc/crypto/lms/LMOtsSignature.cs
@@ -6,6 +6,7 @@ using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public sealed class LMOtsSignature
         : IEncodable
     {
@@ -49,13 +50,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return new LMOtsSignature(parameter, C, sig);
         }
 
-        public LMOtsParameters ParamType => m_paramType;
+        public byte[] GetC() => Arrays.Clone(m_C);
 
-        // FIXME
-        public byte[] C => m_C;
+        public byte[] GetY() => Arrays.Clone(m_y);
 
-        // FIXME
-        public byte[] Y => m_y;
+        public LMOtsParameters ParamType => m_paramType;
 
         public override bool Equals(object obj)
         {
@@ -85,5 +84,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 .Bytes(m_y)
                 .Build();
         }
+
+        [Obsolete("Use 'GetC' instead")]
+        public byte[] C => m_C;
+
+        [Obsolete("Use 'GetY' instead")]
+        public byte[] Y => m_y;
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/lms/LMS.cs b/crypto/src/pqc/crypto/lms/LMS.cs
index 6174d3889..1dbf105aa 100644
--- a/crypto/src/pqc/crypto/lms/LMS.cs
+++ b/crypto/src/pqc/crypto/lms/LMS.cs
@@ -5,6 +5,7 @@ using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public static class Lms
     {
         internal static ushort D_LEAF = 0x8282;
@@ -95,7 +96,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             // tmp = H(I || u32str(node_num) || u16str(D_LEAF) || Kc)
             byte[] I = publicKey.GetI();
-            IDigest H = DigestUtilities.GetDigest(lmsParameter.DigestOid);
+            IDigest H = LmsUtilities.GetDigest(lmsParameter);
             byte[] tmp = new byte[H.GetDigestSize()];
 
             H.BlockUpdate(I, 0, I.Length);
@@ -130,6 +131,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
                 node_num = node_num / 2;
                 i++;
+                // these two can get out of sync with an invalid signature, we'll
+                // try and fail gracefully
+                if (i == path.Length && node_num > 1)
+                    return false;
             }
 
             byte[] Tc = tmp;
diff --git a/crypto/src/pqc/crypto/lms/LMSContext.cs b/crypto/src/pqc/crypto/lms/LMSContext.cs
index 6fcbd9413..83f06b4c2 100644
--- a/crypto/src/pqc/crypto/lms/LMSContext.cs
+++ b/crypto/src/pqc/crypto/lms/LMSContext.cs
@@ -17,6 +17,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         private LmsSignedPubKey[] m_signedPubKeys;
         private volatile IDigest m_digest;
 
+        // TODO[api] Make internal
         public LmsContext(LMOtsPrivateKey privateKey, LMSigParameters sigParams, IDigest digest, byte[] C,
             byte[][] path)
         {
@@ -29,6 +30,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             m_signature = null;
         }
 
+        // TODO[api] Make internal
         public LmsContext(LMOtsPublicKey publicKey, object signature, IDigest digest)
         {
             m_publicKey = publicKey;
@@ -54,6 +56,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         internal LMOtsPrivateKey PrivateKey => m_privateKey;
 
+        // TODO[api] Make internal
         public LMOtsPublicKey PublicKey => m_publicKey;
 
         internal LMSigParameters SigParams => m_sigParams;
diff --git a/crypto/src/pqc/crypto/lms/LMSException.cs b/crypto/src/pqc/crypto/lms/LMSException.cs
index 694a370ed..c581610d5 100644
--- a/crypto/src/pqc/crypto/lms/LMSException.cs
+++ b/crypto/src/pqc/crypto/lms/LMSException.cs
@@ -3,6 +3,7 @@ using System.Runtime.Serialization;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     [Serializable]
     public class LmsException
         : Exception
diff --git a/crypto/src/pqc/crypto/lms/LMSKeyPairGenerator.cs b/crypto/src/pqc/crypto/lms/LMSKeyPairGenerator.cs
index e1afb00d9..4c1f129fd 100644
--- a/crypto/src/pqc/crypto/lms/LMSKeyPairGenerator.cs
+++ b/crypto/src/pqc/crypto/lms/LMSKeyPairGenerator.cs
@@ -15,16 +15,15 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public AsymmetricCipherKeyPair GenerateKeyPair()
         {
-            SecureRandom source = m_parameters.Random;
+            var random = m_parameters.Random;
+            byte[] I = SecureRandom.GetNextBytes(random, 16);
 
-            byte[] I = new byte[16];
-            source.NextBytes(I);
+            var lmsParameters = m_parameters.LmsParameters;
+            var sigParameters = lmsParameters.LMSigParameters;
+            var otsParameters = lmsParameters.LMOtsParameters;
+            byte[] rootSecret = SecureRandom.GetNextBytes(random, sigParameters.M);
 
-            byte[] rootSecret = new byte[32];
-            source.NextBytes(rootSecret);
-
-            LmsPrivateKeyParameters privKey = Lms.GenerateKeys(m_parameters.LmsParameters.LMSigParameters,
-                m_parameters.LmsParameters.LMOtsParameters, 0, I, rootSecret);
+            LmsPrivateKeyParameters privKey = Lms.GenerateKeys(sigParameters, otsParameters, 0, I, rootSecret);
 
             return new AsymmetricCipherKeyPair(privKey.GetPublicKey(), privKey);
         }
diff --git a/crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs b/crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs
index c5a188748..96f1765c2 100644
--- a/crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs
+++ b/crypto/src/pqc/crypto/lms/LMSPrivateKeyParameters.cs
@@ -12,8 +12,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
     public sealed class LmsPrivateKeyParameters
         : LmsKeyParameters, ILmsContextBasedSigner
     {
-        private static CacheKey T1 = new CacheKey(1);
-        private static CacheKey[] internedKeys = new CacheKey[129];
+        private static readonly CacheKey T1 = new CacheKey(1);
+        private static readonly CacheKey[] internedKeys = new CacheKey[129];
+
+        private static LmsPublicKeyParameters DerivePublicKey(LmsPrivateKeyParameters privateKey)
+        {
+            return new LmsPublicKeyParameters(privateKey.parameters, privateKey.otsParameters, privateKey.FindT(T1),
+                privateKey.I);
+        }
 
         static LmsPrivateKeyParameters()
         {
@@ -40,7 +46,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         // These are not final because they can be generated.
         // They also do not need to be persisted.
         //
-        private LmsPublicKeyParameters publicKey;
+        private LmsPublicKeyParameters m_publicKey;
 
         public LmsPrivateKeyParameters(LMSigParameters lmsParameter, LMOtsParameters otsParameters, int q, byte[] I,
             int maxQ, byte[] masterSecret)
@@ -60,7 +66,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             this.masterSecret = Arrays.Clone(masterSecret);
             this.maxCacheR = 1 << (parameters.H + 1);
             this.tCache = new Dictionary<CacheKey, byte[]>();
-            this.tDigest = DigestUtilities.GetDigest(lmsParameter.DigestOid);
+            this.tDigest = LmsUtilities.GetDigest(lmsParameter);
             this.m_isPlaceholder = isPlaceholder;
         }
 
@@ -75,16 +81,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             this.masterSecret = parent.masterSecret;
             this.maxCacheR = 1 << parameters.H;
             this.tCache = parent.tCache;
-            this.tDigest = DigestUtilities.GetDigest(parameters.DigestOid);
-            this.publicKey = parent.publicKey;
+            this.tDigest = LmsUtilities.GetDigest(parameters);
+            this.m_publicKey = parent.m_publicKey;
         }
 
         public static LmsPrivateKeyParameters GetInstance(byte[] privEnc, byte[] pubEnc)
         {
             LmsPrivateKeyParameters pKey = GetInstance(privEnc);
-        
-            pKey.publicKey = LmsPublicKeyParameters.GetInstance(pubEnc);
-
+            pKey.m_publicKey = LmsPublicKeyParameters.GetInstance(pubEnc);
             return pKey;
         }
 
@@ -137,6 +141,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             lock (this)
             {
                 if (q >= maxQ)
+                    // TODO ExhaustedPrivateKeyException
                     throw new Exception("ots private keys expired");
 
                 return new LMOtsPrivateKey(otsParameters, I, q, masterSecret);
@@ -266,14 +271,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             if (m_isPlaceholder)
                 throw new Exception("placeholder only");
 
-            lock (this)
-            {
-                if (publicKey == null)
-                {
-                    publicKey = new LmsPublicKeyParameters(parameters, otsParameters, this.FindT(T1), I);
-                }
-                return publicKey;
-            }
+            return Objects.EnsureSingletonInitialized(ref m_publicKey, this, DerivePublicKey);
         }
 
         internal byte[] FindT(int r)
@@ -293,7 +291,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 if (tCache.TryGetValue(key, out byte[] t))
                     return t;
 
-                return tCache[key] = CalcT(key.index);
+                return tCache[key] = CalcT(key.m_index);
             }
         }
 
@@ -339,65 +337,29 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return T;
         }
 
-        public override bool Equals(Object o)
+        // TODO[api] Fix parameter name
+        public override bool Equals(object o)
         {
             if (this == o)
-            {
                 return true;
-            }
-            if (o == null || GetType() != o.GetType())
-            {
-                return false;
-            }
-
-            LmsPrivateKeyParameters that = (LmsPrivateKeyParameters)o;
 
-            if (q != that.q)
-            {
-                return false;
-            }
-            if (maxQ != that.maxQ)
-            {
-                return false;
-            }
-            if (!Arrays.AreEqual(I, that.I))
-            {
-                return false;
-            }
-            if (parameters != null ? !parameters.Equals(that.parameters) : that.parameters != null)
-            {
-                return false;
-            }
-            if (otsParameters != null ? !otsParameters.Equals(that.otsParameters) : that.otsParameters != null)
-            {
-                return false;
-            }
-            if (!Arrays.AreEqual(masterSecret, that.masterSecret))
-            {
-                return false;
-            }
-
-            //
-            // Only compare public keys if they both exist.
-            // Otherwise we would trigger the creation of one or both of them
-            //
-            if (publicKey != null && that.publicKey != null)
-            {
-                return publicKey.Equals(that.publicKey);
-            }
-
-            return true;
+            return o is LmsPrivateKeyParameters that
+                && this.q == that.q
+                && this.maxQ == that.maxQ
+                && Arrays.AreEqual(this.I, that.I)
+                && Objects.Equals(this.parameters, that.parameters)
+                && Objects.Equals(this.otsParameters, that.otsParameters)
+                && Arrays.AreEqual(this.masterSecret, that.masterSecret);
         }
 
         public override int GetHashCode()
         {
             int result = q;
-            result = 31 * result + Arrays.GetHashCode(I);
-            result = 31 * result + (parameters != null ? parameters.GetHashCode() : 0);
-            result = 31 * result + (otsParameters != null ? otsParameters.GetHashCode() : 0);
             result = 31 * result + maxQ;
+            result = 31 * result + Arrays.GetHashCode(I);
+            result = 31 * result + Objects.GetHashCode(parameters);
+            result = 31 * result + Objects.GetHashCode(otsParameters);
             result = 31 * result + Arrays.GetHashCode(masterSecret);
-            result = 31 * result + (publicKey != null ? publicKey.GetHashCode() : 0);
             return result;
         }
 
@@ -430,29 +392,22 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 .Build();
         }
 
-        class CacheKey
+        internal struct CacheKey
         {
-            internal int index;
+            internal readonly int m_index;
 
             public CacheKey(int index)
             {
-                this.index = index;
+                m_index = index;
             }
 
-            public override int GetHashCode()
+            public override bool Equals(object obj)
             {
-                return index;
+                return obj is CacheKey that
+                    && this.m_index == that.m_index;
             }
 
-            public override bool Equals(Object o)
-            {
-                if (o is CacheKey)
-                {
-                    return ((CacheKey)o).index == this.index;
-                }
-
-                return false;
-            }
+            public override int GetHashCode() => m_index;
         }
     }
 }
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs b/crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs
index 37fa76e2d..506b78096 100644
--- a/crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs
+++ b/crypto/src/pqc/crypto/lms/LMSPublicKeyParameters.cs
@@ -55,72 +55,33 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return new LmsPublicKeyParameters(lmsParameter, ostTypeCode, T1, I);
         }
 
-        public override byte[] GetEncoded()
-        {
-            return this.ToByteArray();
-        }
+        public override byte[] GetEncoded() => ToByteArray();
 
-        public LMSigParameters GetSigParameters()
-        {
-            return parameterSet;
-        }
+        public LMSigParameters GetSigParameters() => parameterSet;
 
-        public LMOtsParameters GetOtsParameters()
-        {
-            return lmOtsType;
-        }
+        public LMOtsParameters GetOtsParameters() => lmOtsType;
 
-        public LmsParameters GetLmsParameters()
-        {
-            return new LmsParameters(this.GetSigParameters(), this.GetOtsParameters());
-        }
+        public LmsParameters GetLmsParameters() => new LmsParameters(GetSigParameters(), GetOtsParameters());
 
-        public byte[] GetT1()
-        {
-            return Arrays.Clone(T1);
-        }
+        public byte[] GetT1() => Arrays.Clone(T1);
 
-        internal bool MatchesT1(byte[] sig)
-        {
-            return Arrays.FixedTimeEquals(T1, sig);
-        }
+        internal bool MatchesT1(byte[] sig) => Arrays.FixedTimeEquals(T1, sig);
 
-        public byte[] GetI()
-        {
-            return Arrays.Clone(I);
-        }
+        public byte[] GetI() => Arrays.Clone(I);
 
-        byte[] RefI()
-        {
-            return I;
-        }
+        internal byte[] RefI() => I;
 
-        public override bool Equals(Object o)
+        // TODO[api] Fix parameter name
+        public override bool Equals(object o)
         {
             if (this == o)
-            {
                 return true;
-            }
-            if (o == null || GetType() != o.GetType())
-            {
-                return false;
-            }
-
-            LmsPublicKeyParameters publicKey = (LmsPublicKeyParameters)o;
 
-            if (!parameterSet.Equals(publicKey.parameterSet))
-            {
-                return false;
-            }
-            if (!lmOtsType.Equals(publicKey.lmOtsType))
-            {
-                return false;
-            }
-            if (!Arrays.AreEqual(I, publicKey.I))
-            {
-                return false;
-            }
-            return Arrays.AreEqual(T1, publicKey.T1);
+            return o is LmsPublicKeyParameters that
+                && this.parameterSet.Equals(that.parameterSet)
+                && this.lmOtsType.Equals(that.lmOtsType)
+                && Arrays.AreEqual(this.I, that.I)
+                && Arrays.AreEqual(this.T1, that.T1);
         }
 
         public override int GetHashCode()
@@ -167,9 +128,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 .CreateOtsContext(S);
         }
 
-        public bool Verify(LmsContext context)
-        {
-            return Lms.VerifySignature(this, context);
-        }
+        public bool Verify(LmsContext context) => Lms.VerifySignature(this, context);
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/LMSSignature.cs b/crypto/src/pqc/crypto/lms/LMSSignature.cs
index d25a498ea..75b0bdfbb 100644
--- a/crypto/src/pqc/crypto/lms/LMSSignature.cs
+++ b/crypto/src/pqc/crypto/lms/LMSSignature.cs
@@ -6,20 +6,21 @@ using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public class LmsSignature
         : IEncodable
     {
-        private int q;
-        private LMOtsSignature otsSignature;
-        private LMSigParameters parameter;
-        private byte[][] y;
+        private readonly int m_q;
+        private readonly LMOtsSignature m_otsSignature;
+        private readonly LMSigParameters m_parameters;
+        private readonly byte[][] m_y;
 
         public LmsSignature(int q, LMOtsSignature otsSignature, LMSigParameters parameter, byte[][] y)
         {
-            this.q = q;
-            this.otsSignature = otsSignature;
-            this.parameter = parameter;
-            this.y = y;
+            m_q = q;
+            m_otsSignature = otsSignature;
+            m_parameters = parameter;
+            m_y = y;
         }
 
         public static LmsSignature GetInstance(object src)
@@ -58,74 +59,80 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return new LmsSignature(q, otsSignature, type, path);
         }
 
-        public override bool Equals(Object o)
+        // TODO[api] Fix parameter name
+        public override bool Equals(object o)
         {
             if (this == o)
-            {
                 return true;
-            }
-            if (o == null || GetType() != o.GetType())
-            {
-                return false;
-            }
-
-            LmsSignature that = (LmsSignature)o;
 
-            if (q != that.q)
-            {
-                return false;
-            }
-            if (otsSignature != null ? !otsSignature.Equals(that.otsSignature) : that.otsSignature != null)
-            {
-                return false;
-            }
-            if (parameter != null ? !parameter.Equals(that.parameter) : that.parameter != null)
-            {
-                return false;
-            }
-
-            return Compare2DArrays(y, that.y);
-        }
-
-        private bool Compare2DArrays(byte[][] a, byte[][] b)
-        {
-            for (int i = 0; i < a.Length; i++)
-            {
-                for (int j = 0; j < a[0].Length; j++)
-                {
-                    if (!a[i][j].Equals(b[i][j]))
-                        return false;
-                }
-            }
-            return true;
+            return o is LmsSignature that
+                && this.m_q == that.m_q
+                && Objects.Equals(this.m_otsSignature, that.m_otsSignature)
+                && Objects.Equals(this.m_parameters, that.m_parameters)
+                && DeepEquals(this.m_y, that.m_y);
         }
 
         public override int GetHashCode()
         {
-            int result = q;
-            result = (31 * result + (otsSignature != null ? otsSignature.GetHashCode() : 0));
-            result = (31 * result + (parameter != null ? parameter.GetHashCode() : 0));
-            // result = 31 * result + Arrays.GetHashCode(y); //Todo arrays support for 2d arrays?
+            int result = m_q;
+            result = 31 * result + Objects.GetHashCode(m_otsSignature);
+            result = 31 * result + Objects.GetHashCode(m_parameters);
+            result = 31 * result + DeepGetHashCode(m_y);
             return result;
         }
 
         public byte[] GetEncoded()
         {
             return Composer.Compose()
-                .U32Str(q)
-                .Bytes(otsSignature.GetEncoded())
-                .U32Str(parameter.ID)
-                .Bytes2(y)
+                .U32Str(m_q)
+                .Bytes(m_otsSignature.GetEncoded())
+                .U32Str(m_parameters.ID)
+                .Bytes2(m_y)
                 .Build();
         }
 
-        public int Q => q;
+        public LMOtsSignature OtsSignature => m_otsSignature;
+
+        public int Q => m_q;
+
+        public LMSigParameters SigParameters => m_parameters;
+
+        // TODO[api]
+        public byte[][] Y => m_y;
+
+        private static bool DeepEquals(byte[][] a, byte[][] b)
+        {
+            if (a == b)
+                return true;
+
+            int length = a.Length;
+            if (length != b.Length)
+                return false;
+
+            for (int i = 0; i < length; ++i)
+            {
+                if (!Arrays.AreEqual(a[i], b[i]))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static int DeepGetHashCode(byte[][] a)
+        {
+            if (a == null)
+                return 0;
 
-        public LMOtsSignature OtsSignature => otsSignature;
+            int length = a.Length;
+            int hc = length + 1;
 
-        public LMSigParameters SigParameters => parameter;
+            for (int i = 0; i < length; ++i)
+            {
+                hc *= 257;
+                hc ^= Arrays.GetHashCode(a[i]);
+            }
 
-        // FIXME
-        public byte[][] Y => y;
+            return hc;
+        }
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/LMSSignedPubKey.cs b/crypto/src/pqc/crypto/lms/LMSSignedPubKey.cs
index 5e07c9c93..666bddc7b 100644
--- a/crypto/src/pqc/crypto/lms/LMSSignedPubKey.cs
+++ b/crypto/src/pqc/crypto/lms/LMSSignedPubKey.cs
@@ -4,62 +4,52 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public class LmsSignedPubKey
         : IEncodable
     {
-        private LmsSignature signature;
-        private LmsPublicKeyParameters publicKey;
+        private readonly LmsSignature m_signature;
+        private readonly LmsPublicKeyParameters m_publicKey;
 
         public LmsSignedPubKey(LmsSignature signature, LmsPublicKeyParameters publicKey)
         {
-            this.signature = signature;
-            this.publicKey = publicKey;
+            m_signature = signature;
+            m_publicKey = publicKey;
         }
 
+        [Obsolete("Use 'PublicKey' instead")]
+        public LmsPublicKeyParameters GetPublicKey() => m_publicKey;
 
-        public LmsSignature GetSignature()
-        {
-            return signature;
-        }
+        [Obsolete("Use 'Signature' instead")]
+        public LmsSignature GetSignature() => m_signature;
 
-        public LmsPublicKeyParameters GetPublicKey()
-        {
-            return publicKey;
-        }
+        public LmsPublicKeyParameters PublicKey => m_publicKey;
 
-        public override  bool Equals(Object o)
+        public LmsSignature Signature => m_signature;
+
+        public override bool Equals(object obj)
         {
-            if (this == o)
-            {
+            if (this == obj)
                 return true;
-            }
-            if (o == null || GetType() != o.GetType())
-            {
-                return false;
-            }
-
-            LmsSignedPubKey that = (LmsSignedPubKey)o;
 
-            if (signature != null ? !signature.Equals(that.signature) : that.signature != null)
-            {
-                return false;
-            }
-            return publicKey != null ? publicKey.Equals(that.publicKey) : that.publicKey == null;
+            return obj is LmsSignedPubKey that
+                && Objects.Equals(this.m_signature, that.m_signature)
+                && Objects.Equals(this.m_publicKey, that.m_publicKey);
         }
-        
+
         public override int GetHashCode()
         {
-            int result = signature != null ? signature.GetHashCode() : 0;
-            result = 31 * result + (publicKey != null ? publicKey.GetHashCode() : 0);
+            int result = Objects.GetHashCode(m_signature);
+            result = 31 * result + Objects.GetHashCode(m_publicKey);
             return result;
         }
 
         public byte[] GetEncoded()
         {
             return Composer.Compose()
-                .Bytes(signature.GetEncoded())
-                .Bytes(publicKey.GetEncoded())
+                .Bytes(m_signature.GetEncoded())
+                .Bytes(m_publicKey.GetEncoded())
                 .Build();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/lms/LMSigParameters.cs b/crypto/src/pqc/crypto/lms/LMSigParameters.cs
index 1f2a90b2b..721560cb8 100644
--- a/crypto/src/pqc/crypto/lms/LMSigParameters.cs
+++ b/crypto/src/pqc/crypto/lms/LMSigParameters.cs
@@ -2,7 +2,6 @@ using System.Collections.Generic;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Nist;
-using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
@@ -15,15 +14,54 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         public static LMSigParameters lms_sha256_n32_h20 = new LMSigParameters(8, 32, 20, NistObjectIdentifiers.IdSha256);
         public static LMSigParameters lms_sha256_n32_h25 = new LMSigParameters(9, 32, 25, NistObjectIdentifiers.IdSha256);
 
+        public static LMSigParameters lms_sha256_n24_h5 = new LMSigParameters(10, 24, 5, NistObjectIdentifiers.IdSha256);
+        public static LMSigParameters lms_sha256_n24_h10 = new LMSigParameters(11, 24, 10, NistObjectIdentifiers.IdSha256);
+        public static LMSigParameters lms_sha256_n24_h15 = new LMSigParameters(12, 24, 15, NistObjectIdentifiers.IdSha256);
+        public static LMSigParameters lms_sha256_n24_h20 = new LMSigParameters(13, 24, 20, NistObjectIdentifiers.IdSha256);
+        public static LMSigParameters lms_sha256_n24_h25 = new LMSigParameters(14, 24, 25, NistObjectIdentifiers.IdSha256);
+
+        public static LMSigParameters lms_shake256_n32_h5 = new LMSigParameters(15, 32, 5, NistObjectIdentifiers.IdShake256Len);
+        public static LMSigParameters lms_shake256_n32_h10 = new LMSigParameters(16, 32, 10, NistObjectIdentifiers.IdShake256Len);
+        public static LMSigParameters lms_shake256_n32_h15 = new LMSigParameters(17, 32, 15, NistObjectIdentifiers.IdShake256Len);
+        public static LMSigParameters lms_shake256_n32_h20 = new LMSigParameters(18, 32, 20, NistObjectIdentifiers.IdShake256Len);
+        public static LMSigParameters lms_shake256_n32_h25 = new LMSigParameters(19, 32, 25, NistObjectIdentifiers.IdShake256Len);
+
+        public static LMSigParameters lms_shake256_n24_h5 = new LMSigParameters(20, 24, 5, NistObjectIdentifiers.IdShake256Len);
+        public static LMSigParameters lms_shake256_n24_h10 = new LMSigParameters(21, 24, 10, NistObjectIdentifiers.IdShake256Len);
+        public static LMSigParameters lms_shake256_n24_h15 = new LMSigParameters(22, 24, 15, NistObjectIdentifiers.IdShake256Len);
+        public static LMSigParameters lms_shake256_n24_h20 = new LMSigParameters(23, 24, 20, NistObjectIdentifiers.IdShake256Len);
+        public static LMSigParameters lms_shake256_n24_h25 = new LMSigParameters(24, 24, 25, NistObjectIdentifiers.IdShake256Len);
+
         private static Dictionary<int, LMSigParameters> ParametersByID = new Dictionary<int, LMSigParameters>
         {
             { lms_sha256_n32_h5.ID, lms_sha256_n32_h5 },
             { lms_sha256_n32_h10.ID, lms_sha256_n32_h10 },
             { lms_sha256_n32_h15.ID, lms_sha256_n32_h15 },
             { lms_sha256_n32_h20.ID, lms_sha256_n32_h20 },
-            { lms_sha256_n32_h25.ID, lms_sha256_n32_h25 }
+            { lms_sha256_n32_h25.ID, lms_sha256_n32_h25 },
+
+            { lms_sha256_n24_h5.ID, lms_sha256_n24_h5 },
+            { lms_sha256_n24_h10.ID, lms_sha256_n24_h10 },
+            { lms_sha256_n24_h15.ID, lms_sha256_n24_h15 },
+            { lms_sha256_n24_h20.ID, lms_sha256_n24_h20 },
+            { lms_sha256_n24_h25.ID, lms_sha256_n24_h25 },
+
+            { lms_shake256_n32_h5.ID, lms_shake256_n32_h5 },
+            { lms_shake256_n32_h10.ID, lms_shake256_n32_h10 },
+            { lms_shake256_n32_h15.ID, lms_shake256_n32_h15 },
+            { lms_shake256_n32_h20.ID, lms_shake256_n32_h20 },
+            { lms_shake256_n32_h25.ID, lms_shake256_n32_h25 },
+
+            { lms_shake256_n24_h5.ID, lms_shake256_n24_h5 },
+            { lms_shake256_n24_h10.ID, lms_shake256_n24_h10 },
+            { lms_shake256_n24_h15.ID, lms_shake256_n24_h15 },
+            { lms_shake256_n24_h20.ID, lms_shake256_n24_h20 },
+            { lms_shake256_n24_h25.ID, lms_shake256_n24_h25 },
         };
 
+        public static LMSigParameters GetParametersByID(int id) =>
+            CollectionUtilities.GetValueOrNull(ParametersByID, id);
+
         private readonly int m_id;
         private readonly int m_m;
         private readonly int m_h;
@@ -37,17 +75,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             m_digestOid = digestOid;
         }
 
-        public int ID => m_id;
+        public DerObjectIdentifier DigestOid => m_digestOid;
 
         public int H => m_h;
 
-        public int M => m_m;
-
-        public DerObjectIdentifier DigestOid => m_digestOid;
+        public int ID => m_id;
 
-        public static LMSigParameters GetParametersByID(int id)
-        {
-            return CollectionUtilities.GetValueOrNull(ParametersByID, id);
-        }
+        public int M => m_m;
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/LM_OTS.cs b/crypto/src/pqc/crypto/lms/LM_OTS.cs
index 0aa5c580e..ace40524d 100644
--- a/crypto/src/pqc/crypto/lms/LM_OTS.cs
+++ b/crypto/src/pqc/crypto/lms/LM_OTS.cs
@@ -2,11 +2,11 @@ using System;
 
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Utilities;
-using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public static class LMOts
     {
         private static ushort D_PBLC = 0x8080;
@@ -15,7 +15,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         private static int ITER_J = 22;
         
         internal static int SEED_RANDOMISER_INDEX = ~2;
-        internal static int SEED_LEN = 32;
         internal static int MAX_HASH = 32;
         internal static ushort D_MESG = 0x8181;
 
@@ -47,17 +46,19 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public static LMOtsPublicKey LmsOtsGeneratePublicKey(LMOtsPrivateKey privateKey)
         {
+#pragma warning disable CS0618 // Type or member is obsolete
             byte[] K = LmsOtsGeneratePublicKey(privateKey.Parameters, privateKey.I, privateKey.Q,
                 privateKey.MasterSecret);
             return new LMOtsPublicKey(privateKey.Parameters, privateKey.I, privateKey.Q, K);
+#pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        internal static byte[] LmsOtsGeneratePublicKey(LMOtsParameters parameter, byte[] I, int q, byte[] masterSecret)
+        internal static byte[] LmsOtsGeneratePublicKey(LMOtsParameters parameters, byte[] I, int q, byte[] masterSecret)
         {
             //
             // Start hash that computes the final value.
             //
-            IDigest publicContext = DigestUtilities.GetDigest(parameter.DigestOid);
+            IDigest publicContext = LmsUtilities.GetDigest(parameters);
             byte[] prehashPrefix = Composer.Compose()
                 .Bytes(I)
                 .U32Str(q)
@@ -66,7 +67,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 .Build();
             publicContext.BlockUpdate(prehashPrefix, 0, prehashPrefix.Length);
 
-            IDigest ctx = DigestUtilities.GetDigest(parameter.DigestOid);
+            IDigest ctx = LmsUtilities.GetDigest(parameters);
 
             byte[] buf = Composer.Compose()
                 .Bytes(I)
@@ -74,15 +75,15 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 .PadUntil(0, 23 + ctx.GetDigestSize())
                 .Build();
 
-            SeedDerive derive = new SeedDerive(I, masterSecret, DigestUtilities.GetDigest(parameter.DigestOid))
+            SeedDerive derive = new SeedDerive(I, masterSecret, LmsUtilities.GetDigest(parameters))
             {
                 Q = q,
                 J = 0,
             };
 
-            int p = parameter.P;
-            int n = parameter.N;
-            int twoToWminus1 = (1 << parameter.W) - 1;
+            int p = parameters.P;
+            int n = parameters.N;
+            int twoToWminus1 = (1 << parameters.W) - 1;
 
             for (ushort i = 0; i < p; i++)
             {
@@ -102,6 +103,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             return K;
         }
 
+        // TODO[api] Rename
         public static LMOtsSignature lm_ots_generate_signature(LMSigParameters sigParams, LMOtsPrivateKey privateKey,
             byte[][] path, byte[] message, bool preHashed)
         {
@@ -122,8 +124,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             }
             else
             {
-                C = new byte[SEED_LEN];
-                Array.Copy(message, 0, Q, 0, privateKey.Parameters.N);
+                int n = privateKey.Parameters.N;
+
+                C = new byte[n];
+                Array.Copy(message, 0, Q, 0, n);
             }
 
             return LMOtsGenerateSignature(privateKey, Q, C);
@@ -131,27 +135,29 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
         public static LMOtsSignature LMOtsGenerateSignature(LMOtsPrivateKey privateKey, byte[] Q, byte[] C)
         {
-            LMOtsParameters parameter = privateKey.Parameters;
+            LMOtsParameters parameters = privateKey.Parameters;
 
-            int n = parameter.N;
-            int p = parameter.P;
-            int w = parameter.W;
+            int n = parameters.N;
+            int p = parameters.P;
+            int w = parameters.W;
 
             byte[] sigComposer = new byte[p * n];
 
-            IDigest ctx = DigestUtilities.GetDigest(parameter.DigestOid);
+            IDigest ctx = LmsUtilities.GetDigest(parameters);
 
             SeedDerive derive = privateKey.GetDerivationFunction();
 
-            int cs = Cksm(Q, n, parameter);
+            int cs = Cksm(Q, n, parameters);
             Q[n] = (byte)((cs >> 8) & 0xFF);
             Q[n + 1] = (byte)cs;
 
+#pragma warning disable CS0618 // Type or member is obsolete
             byte[] tmp = Composer.Compose()
                 .Bytes(privateKey.I)
                 .U32Str(privateKey.Q)
                 .PadUntil(0, ITER_PREV + n)
                 .Build();
+#pragma warning restore CS0618 // Type or member is obsolete
 
             derive.J = 0;
             for (ushort i = 0; i < p; i++)
@@ -168,7 +174,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
                 Array.Copy(tmp, ITER_PREV, sigComposer, n * i, n);
             }
 
-            return new LMOtsSignature(parameter, C, sigComposer);
+            return new LMOtsSignature(parameters, C, sigComposer);
         }
 
         public static bool LMOtsValidateSignature(LMOtsPublicKey publicKey, LMOtsSignature signature, byte[] message,
@@ -177,7 +183,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             if (!signature.ParamType.Equals(publicKey.Parameters)) // todo check
                 throw new LmsException("public key and signature ots types do not match");
 
+#pragma warning disable CS0618 // Type or member is obsolete
             return Arrays.AreEqual(LMOtsValidateSignatureCalculate(publicKey, signature, message), publicKey.K);
+#pragma warning restore CS0618 // Type or member is obsolete
         }
 
         public static byte[] LMOtsValidateSignatureCalculate(LMOtsPublicKey publicKey, LMOtsSignature signature, 
@@ -193,31 +201,33 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         public static byte[] LMOtsValidateSignatureCalculate(LmsContext context)
         {
             LMOtsPublicKey publicKey = context.PublicKey;
-            LMOtsParameters parameter = publicKey.Parameters;
+            LMOtsParameters parameters = publicKey.Parameters;
             object sig = context.Signature;
             LMOtsSignature signature;
-            if (sig is LmsSignature)
+            if (sig is LmsSignature lmsSignature)
             {
-                signature = ((LmsSignature)sig).OtsSignature;
+                signature = lmsSignature.OtsSignature;
             }
             else
             {
                 signature = (LMOtsSignature)sig;
             }
 
-            int n = parameter.N;
-            int w = parameter.W;
-            int p = parameter.P;
+            int n = parameters.N;
+            int w = parameters.W;
+            int p = parameters.P;
             byte[] Q = context.GetQ();
 
-            int cs = Cksm(Q, n, parameter);
+            int cs = Cksm(Q, n, parameters);
             Q[n] = (byte)((cs >> 8) & 0xFF);
             Q[n + 1] = (byte)cs;
 
+#pragma warning disable CS0618 // Type or member is obsolete
             byte[] I = publicKey.I;
+#pragma warning restore CS0618 // Type or member is obsolete
             int q = publicKey.Q;
 
-            IDigest finalContext = DigestUtilities.GetDigest(parameter.DigestOid);
+            IDigest finalContext = LmsUtilities.GetDigest(parameters);
             LmsUtilities.ByteArray(I, finalContext);
             LmsUtilities.U32Str(q, finalContext);
             LmsUtilities.U16Str((short)D_PBLC, finalContext);
@@ -230,9 +240,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             int max_digit = (1 << w) - 1;
 
+#pragma warning disable CS0618 // Type or member is obsolete
             byte[] y = signature.Y;
+#pragma warning restore CS0618 // Type or member is obsolete
 
-            IDigest ctx = DigestUtilities.GetDigest(parameter.DigestOid);
+            IDigest ctx = LmsUtilities.GetDigest(parameters);
             for (ushort i = 0; i < p; i++)
             {
                 Pack.UInt16_To_BE(i, tmp, ITER_K);
diff --git a/crypto/src/pqc/crypto/lms/LmsUtils.cs b/crypto/src/pqc/crypto/lms/LmsUtils.cs
index e99dfe585..33cad48f9 100644
--- a/crypto/src/pqc/crypto/lms/LmsUtils.cs
+++ b/crypto/src/pqc/crypto/lms/LmsUtils.cs
@@ -1,9 +1,13 @@
 using System;
 
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public static class LmsUtilities
     {
         public static void U32Str(int n, IDigest d)
@@ -38,5 +42,90 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             LMSigParameters sigParameters = lmsParameters.LMSigParameters;
             return sigParameters.M << sigParameters.H;
         }
+
+        internal static IDigest GetDigest(LMOtsParameters otsParameters) =>
+            CreateDigest(otsParameters.DigestOid, otsParameters.N);
+
+        internal static IDigest GetDigest(LMSigParameters sigParameters) =>
+            CreateDigest(sigParameters.DigestOid, sigParameters.M);
+
+        private static IDigest CreateDigest(DerObjectIdentifier oid, int length)
+        {
+            // TODO Perhaps support length-specified digests directly in DigestUtilities?
+
+            IDigest digest = CreateDigest(oid);
+
+            if (NistObjectIdentifiers.IdShake256Len.Equals(oid) ||
+                digest.GetDigestSize() != length)
+            {
+                return new WrapperDigest(digest, length);
+            }
+
+            return digest;
+        }
+
+        private static IDigest CreateDigest(DerObjectIdentifier oid)
+        {
+            if (NistObjectIdentifiers.IdSha256.Equals(oid))
+                return DigestUtilities.GetDigest(NistObjectIdentifiers.IdSha256);
+
+            if (NistObjectIdentifiers.IdShake256Len.Equals(oid))
+                return DigestUtilities.GetDigest(NistObjectIdentifiers.IdShake256);
+
+            throw new LmsException("unrecognized digest OID: " + oid);
+        }
+
+        internal class WrapperDigest
+            : IDigest
+        {
+            private readonly IDigest m_digest;
+            private readonly int m_length;
+
+            internal WrapperDigest(IDigest digest, int length)
+            {
+                m_digest = digest;
+                m_length = length;
+            }
+
+            public string AlgorithmName => m_digest.AlgorithmName;
+
+            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 int DoFinal(byte[] output, int outOff)
+            {
+                byte[] buf = new byte[m_digest.GetDigestSize()];
+                m_digest.DoFinal(buf, 0);
+
+                Array.Copy(buf, 0, output, outOff, m_length);
+                return m_length;
+            }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public int DoFinal(Span<byte> output) 
+            {
+                int digestSize = m_digest.GetDigestSize();
+                Span<byte> buf = digestSize <= 128
+                    ? stackalloc byte[digestSize]
+                    : new byte[digestSize];
+
+                m_digest.DoFinal(buf);
+
+                buf[..m_length].CopyTo(output);
+                return m_length;
+            }
+#endif
+
+            public int GetByteLength() => m_digest.GetByteLength();
+
+            public int GetDigestSize() => m_length;
+
+            public void Reset() => m_digest.Reset();
+
+            public void Update(byte input) => m_digest.Update(input);
+        }
     }
 }
diff --git a/crypto/src/pqc/crypto/lms/SeedDerive.cs b/crypto/src/pqc/crypto/lms/SeedDerive.cs
index 38b72fd3f..98076760e 100644
--- a/crypto/src/pqc/crypto/lms/SeedDerive.cs
+++ b/crypto/src/pqc/crypto/lms/SeedDerive.cs
@@ -2,9 +2,11 @@ using System;
 
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.Lms
 {
+    // TODO[api] Make internal
     public sealed class SeedDerive
     {
         private readonly byte[] m_I;
@@ -18,15 +20,13 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             m_digest = digest;
         }
 
-        public int Q { get; set; }
+        public byte[] GetI() => Arrays.Clone(m_I);
 
-        public int J { get; set; }
+        public byte[] GetMasterSeed() => Arrays.Clone(m_masterSeed);
 
-        // FIXME
-        public byte[] I => m_I;
+        public int J { get; set; }
 
-        // FIXME
-        public byte[] MasterSeed => m_masterSeed;
+        public int Q { get; set; }
 
         public byte[] DeriveSeed(bool incJ, byte[] target, int offset)
         {
@@ -35,7 +35,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             int q = Q, j = J;
 
+#pragma warning disable CS0618 // Type or member is obsolete
             m_digest.BlockUpdate(I, 0, I.Length);
+#pragma warning restore CS0618 // Type or member is obsolete
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
             Span<byte> qj = stackalloc byte[7];
@@ -54,7 +56,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
             m_digest.Update(0xFF);
 #endif
 
+#pragma warning disable CS0618 // Type or member is obsolete
             m_digest.BlockUpdate(m_masterSeed, 0, m_masterSeed.Length);
+#pragma warning restore CS0618 // Type or member is obsolete
 
             m_digest.DoFinal(target, offset); // Digest resets here.
 
@@ -65,5 +69,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
 
             return target;
         }
+
+        [Obsolete("Use 'GetI' instead")]
+        public byte[] I => m_I;
+
+        [Obsolete("Use 'GetMasterSeed' instead")]
+        public byte[] MasterSeed => m_masterSeed;
     }
 }
diff --git a/crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs
index 4be386ed4..235478258 100644
--- a/crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs
+++ b/crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs
@@ -49,7 +49,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
             }
             if (privateKey is HssPrivateKeyParameters hssPrivateKeyParameters)
             {
-                int L = hssPrivateKeyParameters.L;
+                int L = hssPrivateKeyParameters.Level;
                 byte[] encoding = Composer.Compose().U32Str(L).Bytes(hssPrivateKeyParameters).Build();
                 byte[] pubEncoding = Composer.Compose().U32Str(L).Bytes(hssPrivateKeyParameters.GetPublicKey().LmsPublicKey).Build();
 
diff --git a/crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs
index d99b5966b..3c55705c1 100644
--- a/crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs
+++ b/crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs
@@ -48,7 +48,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities
             }
             if (publicKey is HssPublicKeyParameters hssPublicKeyParameters)
             {
-                int L = hssPublicKeyParameters.L;
+                int L = hssPublicKeyParameters.Level;
                 byte[] encoding = Composer.Compose().U32Str(L).Bytes(hssPublicKeyParameters.LmsPublicKey).Build();
 
                 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdAlgHssLmsHashsig);