From fb78aa65513a5ccf287c0851ed2aa60074975ff3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 13 Dec 2023 01:23:50 +0700 Subject: LMS updates --- crypto/src/pqc/crypto/lms/HSS.cs | 35 ++- .../src/pqc/crypto/lms/HSSPrivateKeyParameters.cs | 251 +++++++++------------ .../src/pqc/crypto/lms/HSSPublicKeyParameters.cs | 51 +++-- crypto/src/pqc/crypto/lms/HSSSignature.cs | 44 ++-- crypto/src/pqc/crypto/lms/HSSSigner.cs | 2 +- crypto/src/pqc/crypto/lms/LMOtsParameters.cs | 43 +++- crypto/src/pqc/crypto/lms/LMOtsPrivateKey.cs | 27 ++- crypto/src/pqc/crypto/lms/LMOtsPublicKey.cs | 33 ++- crypto/src/pqc/crypto/lms/LMOtsSignature.cs | 17 +- crypto/src/pqc/crypto/lms/LMS.cs | 7 +- crypto/src/pqc/crypto/lms/LMSContext.cs | 3 + crypto/src/pqc/crypto/lms/LMSException.cs | 1 + crypto/src/pqc/crypto/lms/LMSKeyPairGenerator.cs | 15 +- .../src/pqc/crypto/lms/LMSPrivateKeyParameters.cs | 115 +++------- .../src/pqc/crypto/lms/LMSPublicKeyParameters.cs | 74 ++---- crypto/src/pqc/crypto/lms/LMSSignature.cs | 121 +++++----- crypto/src/pqc/crypto/lms/LMSSignedPubKey.cs | 56 ++--- crypto/src/pqc/crypto/lms/LMSigParameters.cs | 53 ++++- crypto/src/pqc/crypto/lms/LM_OTS.cs | 66 +++--- crypto/src/pqc/crypto/lms/LmsUtils.cs | 89 ++++++++ crypto/src/pqc/crypto/lms/SeedDerive.cs | 22 +- .../pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs | 2 +- .../crypto/utils/PqcSubjectPublicKeyInfoFactory.cs | 2 +- crypto/src/util/Arrays.cs | 67 ++++++ crypto/test/src/pqc/crypto/lms/test/HssTests.cs | 8 +- crypto/test/src/pqc/crypto/lms/test/TypeTests.cs | 2 +- crypto/test/src/pqc/crypto/test/HSSTest.cs | 23 ++ crypto/test/src/pqc/crypto/test/LMSTest.cs | 165 ++++++++++++++ 28 files changed, 858 insertions(+), 536 deletions(-) (limited to 'crypto') 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 keys; - private IList sig; - private long indexLimit; - private long index = 0; + private readonly int m_level; + private readonly bool m_isShard; + private IList m_keys; + private IList m_sig; + private readonly long m_indexLimit; + private long m_index = 0; - private HssPublicKeyParameters publicKey; + private HssPublicKeyParameters m_publicKey; public HssPrivateKeyParameters(int l, IList keys, IList sig, long index, long indexLimit) - :base(true) + : base(true) { - this.l = l; - this.keys = new List(keys); - this.sig = new List(sig); - this.index = index; - this.indexLimit = indexLimit; - this.isShard = false; + m_level = l; + m_isShard = false; + m_keys = new List(keys); + m_sig = new List(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(keys); - this.sig = new List(sig); - this.index = index; - this.indexLimit = indexLimit; - this.isShard = isShard; + m_level = l; + m_isShard = isShard; + m_keys = new List(keys); + m_sig = new List(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(newKeys); - sig = new List(newSig); + m_keys = new List(newKeys); + m_sig = new List(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(this.GetKeys()); var sig = new List(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 GetKeys() { - lock (this) return keys; + lock (this) return m_keys; } internal IList 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(keys); + var newKeys = new List(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(sig); + var newSig = new List(m_sig); newSig[d - 1] = Lms.GenerateSign(newKeys[d - 1], newKeys[d].GetPublicKey().ToByteArray()); - this.keys = new List(newKeys); - this.sig = new List(newSig); + this.m_keys = new List(newKeys); + this.m_sig = new List(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(IList arr1, IList arr2) - { - for (int i=0; i> 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(IList arr1, IList 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 Suppliers = new Dictionary { { 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(); - 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 ParametersByID = new Dictionary { { 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 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 output) + { + int digestSize = m_digest.GetDigestSize(); + Span 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 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); diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index f99065512..ab96357b1 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -115,6 +115,44 @@ namespace Org.BouncyCastle.Utilities return true; } + public static bool AreEqual(object[] a, object[] b) + { + if (a == b) + return true; + + if (a == null || b == null) + return false; + + int length = a.Length; + if (length != b.Length) + return false; + + for (int i = 0; i < length; ++i) + { + if (!Objects.Equals(a[i], b[i])) + return false; + } + + return true; + } + + public static bool AreEqual(object[] a, int aFromIndex, int aToIndex, object[] b, int bFromIndex, int bToIndex) + { + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; ++i) + { + if (!Objects.Equals(a[aFromIndex + i], b[bFromIndex + i])) + return false; + } + + return true; + } + [Obsolete("Use 'FixedTimeEquals' instead")] public static bool ConstantTimeAreEqual(byte[] a, byte[] b) { @@ -530,6 +568,35 @@ namespace Org.BouncyCastle.Utilities return hc; } + public static int GetHashCode(object[] data) + { + if (data == null) + return 0; + + int len = data.Length; + int hc = len + 1; + for (int i = 0; i < len; ++i) + { + hc *= 257; + hc ^= Objects.GetHashCode(data[i]); + } + return hc; + } + + public static int GetHashCode(object[] data, int off, int len) + { + if (data == null) + return 0; + + int hc = len + 1; + for (int i = 0; i < len; ++i) + { + hc *= 257; + hc ^= Objects.GetHashCode(data[off + i]); + } + return hc; + } + public static bool[] Clone(bool[] data) { return data == null ? null : (bool[])data.Clone(); diff --git a/crypto/test/src/pqc/crypto/lms/test/HssTests.cs b/crypto/test/src/pqc/crypto/lms/test/HssTests.cs index cbfbdbdd7..29da1f8c6 100644 --- a/crypto/test/src/pqc/crypto/lms/test/HssTests.cs +++ b/crypto/test/src/pqc/crypto/lms/test/HssTests.cs @@ -69,7 +69,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms.Tests HssPublicKeyParameters publicKey = HssPublicKeyParameters.GetInstance(blocks[0]); byte[] message = blocks[1]; - HssSignature signature = HssSignature.GetInstance(blocks[2], publicKey.L); + HssSignature signature = HssSignature.GetInstance(blocks[2], publicKey.Level); Assert.True(Hss.VerifySignature(publicKey, signature, message), "Test Case 1 "); } @@ -85,7 +85,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms.Tests HssPublicKeyParameters publicKey = HssPublicKeyParameters.GetInstance(blocks[0]); byte[] message = blocks[1]; byte[] sig = blocks[2]; - HssSignature signature = HssSignature.GetInstance(sig, publicKey.L); + HssSignature signature = HssSignature.GetInstance(sig, publicKey.Level); Assert.True(Hss.VerifySignature(publicKey, signature, message), "Test Case 2 Signature"); LmsPublicKeyParameters lmsPub = LmsPublicKeyParameters.GetInstance(blocks[3]); @@ -548,7 +548,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms.Tests Assert.True(Hss.VerifySignature(pubKeyGenerated, sigCalculated, message)); HssSignature sigFromVector = HssSignature.GetInstance(sigVectors[c], - pubKeyFromVector.L); + pubKeyFromVector.Level); Assert.True(Hss.VerifySignature(pubKeyFromVector, sigFromVector, message)); Assert.True(Hss.VerifySignature(pubKeyGenerated, sigFromVector, message)); @@ -790,7 +790,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms.Tests // byte[] rawSig = sig.GetEncoded(); rawSig[100] ^= 1; - HssSignature parsedSig = HssSignature.GetInstance(rawSig, pk.L); + HssSignature parsedSig = HssSignature.GetInstance(rawSig, pk.Level); Assert.False(Hss.VerifySignature(pk, parsedSig, message)); try diff --git a/crypto/test/src/pqc/crypto/lms/test/TypeTests.cs b/crypto/test/src/pqc/crypto/lms/test/TypeTests.cs index 10f21e9f3..f15121bb0 100644 --- a/crypto/test/src/pqc/crypto/lms/test/TypeTests.cs +++ b/crypto/test/src/pqc/crypto/lms/test/TypeTests.cs @@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms.Tests } { - object o = new HssPublicKeyParameters(0, null); + object o = new HssPublicKeyParameters(0, new LmsPublicKeyParameters(null, null, null, null)); Assert.True(o.Equals(HssPublicKeyParameters.GetInstance(o))); } diff --git a/crypto/test/src/pqc/crypto/test/HSSTest.cs b/crypto/test/src/pqc/crypto/test/HSSTest.cs index 45cabb906..445e5d6e5 100644 --- a/crypto/test/src/pqc/crypto/test/HSSTest.cs +++ b/crypto/test/src/pqc/crypto/test/HSSTest.cs @@ -67,6 +67,29 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests Assert.True(signer.VerifySignature(msg, sig)); } + [Test] + public void TestHssKeyGenAndSign() + { + byte[] msg = Strings.ToByteArray("Hello, world!"); + + IAsymmetricCipherKeyPairGenerator kpGen = new HssKeyPairGenerator(); + kpGen.Init(new HssKeyGenerationParameters( + new LmsParameters[]{ + new LmsParameters(LMSigParameters.lms_sha256_n24_h5, LMOtsParameters.sha256_n24_w4), + new LmsParameters(LMSigParameters.lms_sha256_n24_h5, LMOtsParameters.sha256_n24_w4) + }, + 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.IsTrue(signer.VerifySignature(msg, sig)); + } + [Test] public void TestKeyGenAndUsage() { diff --git a/crypto/test/src/pqc/crypto/test/LMSTest.cs b/crypto/test/src/pqc/crypto/test/LMSTest.cs index b06fc48fc..179911e54 100644 --- a/crypto/test/src/pqc/crypto/test/LMSTest.cs +++ b/crypto/test/src/pqc/crypto/test/LMSTest.cs @@ -8,6 +8,7 @@ using Org.BouncyCastle.Pqc.Crypto.Lms; using Org.BouncyCastle.Pqc.Crypto.Utilities; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Pqc.Crypto.Tests { @@ -36,6 +37,46 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests Assert.True(signer.VerifySignature(msg, sig)); } + [Test] + public void TestKeyGenAndSignSha256_192() + { + byte[] msg = Strings.ToByteArray("Hello, world!"); + + IAsymmetricCipherKeyPairGenerator kpGen = new LmsKeyPairGenerator(); + kpGen.Init(new LmsKeyGenerationParameters( + new LmsParameters(LMSigParameters.lms_sha256_n24_h5, LMOtsParameters.sha256_n24_w4), + new SecureRandom())); + + AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair(); + + LmsSigner signer = new LmsSigner(); + signer.Init(true, kp.Private); + byte[] sig = signer.GenerateSignature(msg); + + signer.Init(false, kp.Public); + Assert.IsTrue(signer.VerifySignature(msg, sig)); + } + + [Test] + public void TestKeyGenAndSignShake256_192() + { + byte[] msg = Strings.ToByteArray("Hello, world!"); + + IAsymmetricCipherKeyPairGenerator kpGen = new LmsKeyPairGenerator(); + kpGen.Init(new LmsKeyGenerationParameters( + new LmsParameters(LMSigParameters.lms_shake256_n24_h5, LMOtsParameters.shake256_n24_w4), + new SecureRandom())); + + AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair(); + + LmsSigner signer = new LmsSigner(); + signer.Init(true, kp.Private); + byte[] sig = signer.GenerateSignature(msg); + + signer.Init(false, kp.Public); + Assert.IsTrue(signer.VerifySignature(msg, sig)); + } + [Test] public void TestKeyGenAndSignTwoSigsWithShard() { @@ -107,5 +148,129 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests Assert.True(signer.VerifySignature(msg1, sig1)); } + + [Test] + public void ShouldRecogniseInvalidSignature() + { + byte[] lmsPub = Hex.Decode( + "0000001800000010D88330CA119BBFF43228CD435B63966089A997C6083CC2AF9BFE4A9789888694EC2A92883B36015A"); + byte[] msg = Hex.Decode("900E8860388B34E30A103184F8EA198CC8B397AD5D7D99C554B966B3D18E938754B77F8252642A17A6F75CEA0945D4385BC37B75ED67C20F90DAC462A3DADB67FB3BC9CC23754F43298515208C2AA0181C098DACF58EC21D84A2F7A2A8539CBB17E915AA5EF042E9707104B08546B1887D6CB54096F25010E527BD655EBFB52D"); + byte[] sig = Hex.Decode("00AFBA640000001030734BD0D2A048BAFE3FDDE5828D5D1431A6B02B9BC4F428266277D400CB1DD82E8939940D4E4835825088D6B5ED893B285E1136E39F06BD41DDA933847B6B0E2CDDDEFC7B2041BB75D10CD9904D6665A03C9BFA82989BEB903F0E8B8ECB2BC8C356CCB55225B6D0ED03EDD57B28ACC7911397FAE097F7B8BB888464FA56DC5D18483C5406A9FB51B361ACD45025304210A7031D18CC692F62797F0BB1682DC424DC309616D26C2FFF12DB9B7AC9BED9E35FC31D6113422D14EED59078AA29CE5E0C02F63D3CCBC870F1D5BA65C16DD42D2A373D51ED384307F9C412F7D6B4B7ED7A1C3EDD749BCCFDAEE66F1922249F9DF3EEB3452481B20DF8C63576680F27FE727AD7207389F9335844F8AEE3B8F31DB99D317174F04A133D4E011F352C74E1B3A5D2CA921D03A395B917FD486BC8C59E06C1BB1F49BF1556DCE070C3E94FD14877AC84A86A83C868F5E2307141972A7F7C6823C1A7ED9DEE07B10E0C9C6D65D004B3E841ADD1C3DE4CBBEBA9701B40DAD2D6016A88B68EF76F974B6942B4F13388A47131BC41F4BADAAABA37965EBE1566A921A7BDA4960F77638B5145AE6A8BCCBB96C0D9A433DB2AE60F85CD43963A12BD555786F887794C8734A3C329E85138958DCB5AF95C0E430220514525F65CCDB2DF360546CDE80525739DEEC5947F25A8F9F5FE7CB775A73112367D7C171EBDC9164568AF4F40399B7FA170A7F145A98E03955A3226E2946510E763B56AA619B46325E98B5E7CB5AC07E1ED42A010ECC807BDF4C6809908B8E7C03734B8A979A89A63FEC1F9C6F3878A73954E8C4A1C1914A2356D82A4F4804EEDE288A75BF755654F26AC4955428E9FE81289E0A55B2AF7BA235C98ACD89485EE04DCDC337228FF1B0C465C5FA7456FB1DCEB6D2DFF6C95431E6600000015EDED6089F9AA257DE3C3FD6E9AFB511E2A344A5DB043D5C6CE97D8648C4B125EAC8DD85036291DAF73F32CFAD7FEB07297DDFBECF0471EDC2908C3BE91A4DCB8CF532C9F37E5A4E704A1196405C71115FC622BD51F993D44D3116B0CA31D538E4249B54A67D13C8A02B5434C761B04CE43224D252079231EA031164C8BC3D51F5634FE881C6A0376FA5F0A9FFC5BD6F0118847CC793BCB57CC9C4D4602B0234CD03B8D3C259AF1B3A70C312070D3E8762BBECA5B3E951B3F29C51B6C1A75D7606B89653E9E0148C3FE095752FDC65D3AA2C854240ABE63440204C29BBF4C81D6D87D2FB522CCA245E03132DBA4DC49E193387C7E127C40A7F9B4995752DB1EB347DD5E1FE00203C78AC297E87F56C0F8C581ECBA774CC55BBD03EF335390F7E04CDA220220D1B4870379598583C057327053F7179D3802501E01162D5DB837B344E428443DCAE9B00FE01DF67627BAEBCD60D44A9B21EC0B6576E50DAB72CD0D55A7023391B393311B3B256A3BC42045208E118847F12633FB1890D78F0A8CD5614075A02046215D850112D111414906394FB2096E414AA90591857486945BF57D30706D2491C00796599E7DC1CB7504BAC7A7C7BA5CF0195AAE8162422DCE061C907DBD21A26BE844D6251AECA33DEC28F57C2A0C38D3D4E2894B5B7F9393AD52849D531E7EA95C6D19483FDA55DD9A9E3A4028BB6C7B90A7FD1B65B4EBCC12AFEEA0382E24CDD3261DCDC1E7A9A4227FE76C11EA73EDE79F8E332727767484123500EC3D82256843503DD771B0BE626D7BBDD2AE838A166032F8ACC635B4B9FEED9A31EFFCC87320D969FC0FE3890DBE40268922D12ABE"); + + LmsPublicKeyParameters pup = LmsPublicKeyParameters.GetInstance(lmsPub); + LmsSigner lmsSigner = new LmsSigner(); + + lmsSigner.Init(false, pup); + + Assert.IsFalse(lmsSigner.VerifySignature(msg, sig)); + } + + [Test] + public void Should_verify_sha256_n24_w1() + { + byte[] lmsPub = Hex.Decode( + "0000000A00000005BB9C1B5FCF1286C41F7DCDE9FB53A539C7E3E5EEF5FAB878BD36C04EAB54DEF7CC258C32A842C4FD"); + byte[] msg = Hex.Decode("1BCC459B62CDA60458092ABF9BA110DEDC38B6CD819FDA8AEB7ABF935919F60FD73D6F4108CD86243D317802EE46392E7FC50BF3D7A80D8A0D4E68E8F1E336D00C1905D4CF90A4E82B8D758E29E8D85462B17790E90328DD123ED1ACB5B2B2B3C0A2EAC79A9BDFF655F5F600851FC8DEFA1E9AA397F672F6E0A06FAE86F9DD22"); + byte[] sig = Hex.Decode("00000001000000052EDE1C14648A2B009E21AE1BC181BDCA54818914475BB58F40C5E602EEB4BB6E33E4256BD64008876E9C8691209C372D5A7A770A1CB819A1758CDD6BEBA1402B58DAA45DFF0DE3F7C15789925D6E8E86577872F879721FC6F07937E88646E417E9320FA1BDED37DC6C5346160170F9D4A0E8E47EA5A2C3D025A5D53E8A7BD87D1827FD421701C775323AB4274679BBD038C438A05E1AB7B91875D4B5BDE3E3E4D26B60141AE8B6349E0380E63D7E6B3A6B2B5CD1BF54A8B89DA615324222009107A131D1AA7F84D1771916C68AE6D477365D2030C2DB69F51D0B4D14FE482A03D204E33DF679040FA2246D9528DEA205841CA6F327696E370AED4A96C965A8E7677BECE391847287D44F3DC6C3334779AF3F4941C3F43550A0D4638A050BB90F587AB06CEB7431F486251F61356C8E7FF318DB2027A825EA89B8547BB346F1A7E0013173DE85357A6232871D3AED6E503B4CB8272F7F0BA004CD9E918A660B7E8114FAF3CC7D9F4FE69F5435C0E6A5BFF5BB63C0BD575775764B73C21F0F2931F3EE9AE58D0B924530601D01E6C2A2AB21BA0312722EB94D5651337E9055DE1F1B8EA9CBC993E2BD202251F9F1B76054F0B495EC6C32725B663E95C82D5003CAA34EB69A7D2A4901766945195CBFFBE72B940B2A6BA12F56BC4AA62D5961DDB97B564B7855EF2BCD686D34FCF0C8DE3B0EC6CDD3A4FB21EC53AA7016BEF53F8F3915B8990129E2F67E51FEE3909B8083915022664F196A662ABBF3DF24C63C207CF53358A2CD5C7EEB297B71208F81A641316F251445C015DE363A2F8AC5AA4B5BEC50C6B1768668FE2B0B858820D27F99742CBF5675FFC6CC9E33A4A3795D8CAFF83726F31C780EB9971E75E46C55FEFD081C3FF6784F7D0C9566E4ED92EFB23E4B5A0514DC5D1104E8845C1F58D0B0A2A2F69E04E66119BA96F480E11831751AE7FE6369330A79FDF5006ACA4717496C023055F5366092ED9835F6F57BB4601654BCF4AFD8CAA305ABCEEE0CD154346F146F188177CB2B203D220C24CF5D1E77912A0F01D8F883055A18C3222A2CF4E34FEBF3660D12B97675D9A4B6382C85674DFE2B660A99C24C2227BA0B96CDFAEEBC8EFA0718E473A4BCBD70ECBE840FAADE55B18299AFA9118FC27F482B69417EFAADD0710CFBCF3CA12F400FDBA3E8BD377FACF38B01A5A07187BA93CA52AB0B49F510A2953C16C888A1F18C129988DD6F4637DB7CA561A9A03229BA2C4B0EC13D2590241BFDC68D8F5038089A35592799AEB37D99E81A374EC78668FBB1E38546671A017AF33022F531C3185BE1092D59D17FB56F4CF1489799F0480B6FF521967F652B46AF7E94FC64602AFF2B8F6A73882BD0452C4A1AC68B4BCBE0C3702768A5D602A24ACFEF51A0B053C1E66D4D1C64CFD8EA8FF76AFB35A152485FFF64EDF6FB18CBBDE7E88CF30A4F76B0C12B71D0F3A3BBABF8A32DE6A60F59E28FEAB4A3B874D7B746099286152B2CCA4B2C3F98ED665B9FA1243649A2B0F10DCF10C26521A7A8C875B234003B57E90243D74B677313C3F1BA7CCD4D0B42ED7EA79D50178F29CD432F423C101705E291535B02DC1BC9694236B4C628EE0D45BB0FE06E4E450D63D457D71C0BFC2FC70A66DA02DC437A75652AD8833A0D2D328F5C8993EF07EA64E2A17BF2F1792078C2D7031CBE17E53509B9CC9EC44CDB6C01211CC97001ECFEC24DC71C14609158A79BE8BBDF1DE0AFFE73C30DF6F5A5392ED60F02033C7CA03DE232D4B0C7F5190A47D07EFA1AEDD5ADE1B0B086BF8F540B9B2E29E3E670D4538053830864AE03813C0FD1B667CC91416A64220E5B8B5C43454E7AEDFD22E68987598F21D4FAB24953A3216A12FD55F73168EE98E0E7391A69A5FA6418520B0C78B816FE846B15743886C5B0B582B43F264147040A3D2646B0686D5280A9F20DB9E6B4B918CCDB760060F33F6E4F9A3F90ADDC9CAC3D9D77E0D0AB479FE710BED79D150A290B037BA0071592A896E90980444C66B0ADE9852657140226F4C5E97919CC9561AA72EDEAAAAA610B8767DF3A2DC3FE844E98B37D26C8277C9103278CD655BB8E9453ED8AD61A54359F80E51F0E65B09A0E515C3D001209B1592CA87C87DE853E7905765718755124CC04B7C9F972BE8F731FFA87F541423E767C33A7D52B7219CAF53DAC8EB5852FCC91B01315A6211E789ADFC5716C8D4EBBD95B39975EC7B88DD282109AC71D4676F2EA1F9743E619D58BCCDBF837BAB74C4AC846D4BF6C8AB77151B42FBABB887AFBA87F74EEBECECC8C45ECA8FE26EC9CFA3C89DFA62F8BA39087B81F76E929B8B5112E8457EA42C9516E2BF3466427EC0F7572774B11BD626CC12ADEBC0238A932B75CE9FF9900F332D1818E5C0451A1CE7A74EF063E8F371E2183ACDF0C38B1DB098320F6BBA960B80A6925C6BDE61EAD96BEA847F776266A9EE226858E02AD785DC4F12E5287B389BEAE8B4C77B77480D1A5F357BF62AA41EF06FD859B178D17CFC6DEE117FCC8645F2CB5D666A589570643D4C1B5D44DF1552E3FF401B26D70C6C26C400914DBC63157380B8211B6EE9A713B05FAF7AB70D16E2BFBEECA75BC28BA7EE82D4B646D863DA5B77FCF3BA7121123267B0031647724E4A5B9593D922D5590991DC90A19EA4D9E5A567BB3DE3959EF1BA47FC29A9A331E34A3C3D4BDA1936F98C1AC3B006BD1528BEC0A60CA472ADE66D2FBAF7727FDF72BE2D5E93120308C16DA6E508442A2B6C22CFE7A43A58AF3FAD55FBDC8BFCC26CAEF4CB3DF62BC9A63263CAD84CCD47B2299D471173184988249837B59A2D99B7A6B05A3656EE32F1AC52B975682F1EC6FB132433F75C6BB1E7CDC7679340AC991706CBBACE6D8137C18E83C2A74BA5B80D7D5530362C0BF2BDBF62B6FC71CD01AB2C0E512A7B53624C58FCE7F7ED7D27DE9C5585DD76BE3C9A09E943D1A3A6BA6A3CF01AE47CC29ED201CD19CAAD15149F39791520E6235B8F3442F614E2880DF4BC7FA89478EA1930FDC9CAE7042E3BFDE540BB3C7E7B263B759094EA52DA3710FA7850F031F47DF4DF8B2466D608049D142CCD492E6CEDAC0BBE15ABD20082DF14E7CBAE77B80EC1C7077EE2BF31214AAE833CFD392246F947F76C35FCF6598C35F37020C0C9BB8E7FF67F2259CADEFF5F468B5CAAFC810541D332FB132DC8900D6C1415B71E5D6E9BD9A2A91E7CE41FA20CCE431399F20224DE076A88829DF8BC857A4E0D5819DEE384B15AF08A6D66CEB3DDFF9108D66D75EB321CCC3D8909951451B34DFE78E721A7151696AF3227203EDDC88B44BE7E2C3AD01BED76CF209C01D5C1E218F995F1486F0B86CBD6826CF0F11D605FE8273BDBE2259F54083F7F937273BF00983F072DFE4064F9A47124CE53BEE19F287882F27FE309D2DE0AA89EFEA412843C0853D4C437A6DB8E2F54BA6D5BE0E93467375B1D03449E0FE865D81E890D237744CAECDEB97707E00C4E255486A739E4A0E89971ACCFF973951E0EFF6CD1DC0DA0E5DC7A6BF60798A6998EA57790D01AF63312C096D5141E5E8A72CB62FD6E451E054F58FB13B8848FA6C5D020D5F35D4D1A920248003A478417C8F28AA44E7C327147BA1EBF1A600689913008F118CBD5517D4D2F1A6EB238D9C6D9F9E27ECA0552192BAC389C79BE528D03769D8232C53933099F5711E2FD7DB50614201E8F06636BC2B2B3C6AF7FA731565578E57ED4DACE821F8B06E147EA5FA050F913F6D6EA242DA02F32D229DC10D79069662376FEF772359FE3AB538D095E2FAB48AE7A048F5ACA53FEFB9367ECEBAB12738FDA5C9C70ACC17328BD1EA2CCE1185F11C6308EDE61BA5B0901B2A78D57178333A6095E3A2FBAE9CFE4B4BA8036D1E551A21A9BFD1265A6F8F4ACD40FCF32D1AE7B626D553C51242A3223AF016E41D55D7D632E5DBE3CA4FC0DD4B608F8F532D6011945471481498D0B04CD9F7099BBF73798518D6AD8E4D326D5D1306ADD455033872FDC9F4B459D7440113654ED4E4AFD1CA292ACCC776CB98FB9C2767EF51221AB148C9D7F7D6D8499EA6863BA7AED43A4F0A9EDDD6490EC60FA2F86A3BF4848479E70ABD8E2DCD0B763662A775C5B5A1AB26BEAB6E2B9E827E76D2373093CDB428BEF2998D2AFBE147392A2372BDD8926267AA87CABB51C031D53DDBC2B90ECEDCF36CC6A3167D77C13304E6307464EF527B793B89D27D6E71D5D6BC85F270FB8D9546531A314DFBED1CB6F70740BE4BC9BCD9BCE0C1D119C8259858F2532F674E6F5E1151989BE61BEAEACC57D36317ADA9A79C45B4175638D57A45CA53CA475298B561C36BB727833322182BD1C149FED8957820120117CEE99D2C2392E8AEB13B4D6EF25D87CE7CB223CCCB7E89E61A970B81BE9353098219AB439E7EA8ED239D79E261F3F7B87A9F0EA7BB0DA82BF314C4D4CD553973E7411037AB50EDB330C132CBB69D5A9883DAFF081961D7C1D49B1EFC33982992967AFD90BD8A65C816281FD62779E5B9C82796D4251EB503ECF4B292168F72267655E0694285E28FB0D6282A8DBEEB807D252950AB85DC9ADD61CD2CD49CCAC2C74D1803B59DCE91AAC667E81D924D967A47796D53CBF46A192C5A8E91489B67F37E4554FDC0537B9323DC2CDF4EF3297CA8A2F7DC9FCA65D981C7F18E00CE9D35C18EFB8B70444CC6013E0F22F391C0202183A2DE02A59D356C1CF2E55D65EED1FBE6C719F66A92B2E425B320407D2668CDEF4CEEEB7679424A5C1404C88198F22370BA33090BD25F568F6FB9AC3A933A4FF68AC9BCCBA5EE7B0831D5648B126BA9190B284F2630128AA64247D805DDF7E882C2AF1342E49824C70035550E20B755EBFE5600B8FB1472306848A8D4D1E3B79A941AE0C8E9529F3716B855677C949DE3148908D15EE1DA69BEAB409427B5471EA2BD9308A4264D24D2A60AADA5CCEBB88D7F4EADF77189E29E19E3A2348B1D074FB22BA71E682F631EAFF35CC7E56C236E708C18C7C57CC6FEBEA18C486D625D2B4AD69C51A479F22CBF9540F3FB9CB3CBEA8702BC6845E9EBC0F35C630B87FDFF16694F3019E0BD1BD9886660FD91AD0F49C25C8DCD4099B5D8E4B24BD102AF7D65C6F51EDC8D262DF3629811EADFB4EA5FF27CE5CA7B6DC0DDADE55E5452FCFD5C3E33D19D3E66C4929C6FAFFA17753AD40C74E2986CB8C17AB45ADECF8BB7AFC9F7F240063A4D5D864340A79AEE28B333D9B26E28441157C96F533068FCD4649A2E3203691268ED8A67701D5CD48F01B1543A27BE04A57015F5B4A52FF16D3E0F33C9E138966383C4CA5B52A812F9515F39E5DBA852D4F9252F497014241A18277D9838641C7D3C387187ABDA9ABC631B314B94F362397BFDEE023028AE83C389B2606D10B45A27CB0122106FA4F6978BF751104CC87B2C51CDEF8A174D77F9134E66068F6EF406D9662542F8FD776E489375EAD90DAA6ECB9A643CB769A0AE13A38C20DFE7B00B4951F5E8F8FDBDCD3ADADAD6714A787E71277DA5BF11E8E40E0BADF926A03C2176951BA0E79CE5BD6D505801FBFC663B5F78117BC6D9BFB81FB5AED66A297B21619100689BF9B2C140FC04399BB9ADD387522667780CCD8FFDCEB0E0EAF40091363420E70700471732BA76B82CA5FF609BB2867EBB18E6731FB376E0ED0B1A16E04D710A3B73D9D9A994A9A48105E72016F61C0DCDA3A4BFD7AB8E4D49452040C526082D63C47D2A7B8B6B04A607143F7A4F6970DE903BCE024B380CCB388580DA345F6089B93853309A77135A2AA98BB269DA7C47513DE8E39CA249739F0F805E6C19268E2902FBEF02104FE10B3026251D13F469D1544A7428E248133E1B08713075620DB81D982740DD784A449FD5D5598CD15F5B4B0AF73D9182190EED53273B4AAB3E07455FB577CE5DBA27F63A349D86665DAC82D3349127E27854C125C717CFDD3326D92C62C362734FB0107AB1DCDC960052E1FC796F72AD793B3023F32AD38E58B65AD46D6C1387DC425D33DCA3DAF153E71CE60A126C13594B2B79592735DBA631323D28C15E46BEFD2A1406055F0881C188F4912F2952EE51852FD5F0665567F66B0AFA1865292CDDD239161F7221F43379D4CB650D3CB3095C547A4171C32A9067239BB1D77CFEA8DD915AF62468BAC13F62E90D15D5420666215951094E54F6A1AB51944F93939943663E927997EBF2FBFD3D4EFB04D2D616D7E99942281595282EBBA23181E1C55FACA1217D06B931088D0EC446B1DEB281BFB1365457B8F8E28F4C1F2C840AA76383EF8A92053866E48F2E64E8C3FFE6AD84A5633B97A8BD775CDBCDF8D23D2CEFAF401B7E6236471618D63BC2C2FA3E0EFB6884CCB5DE0A16A31FDE0EB47BF811AFB138D2107CD49783F5A9DA214247258828759E44E8AEF92A7AA26A95081B6741DE9C15A5DCE127197396AFB5C93B0458C0F106181636789F327207A49FB23D037F0A7188A7E63DA968007119D6F918671951F77E03D121A2F31B0AE38AFE2AB2F0EE993A2D2CF49C531E66BE4CCD86B28CE4DAF9D3E71307CA87AE56F60B93B286E7828657BB6265FADB82A1BE5772E7F5E89BA2866C16400A2E0EF744830D644F7F976118CCE238EBB0B5564F6AEB37A9051EB4EECDFD7E51266FD14B56B66DEF1182C3FBEFA0DD2DAF7209FCE80A5C28B7A842CC0D376636B165079D68A53E3380EEAAF19624B334A7E95FC317A0123C670F97DAF9F38BEB80250DF03BC462F3740A864604C2D29B749458AA5389625645B06D90BCF7139267261E7294527FA96DFEA0018AEF0E053361F6E0BC6AD99E25C54E375AD32ADD2A0C45659027D4EF052A65D857B6A055F16092DB8BD226BA199512459089E0340000000A4C5E553CA23432A03396418B3C2F073F1F172FA526BF63EC88FA128DA3218BE6EB95396C602275A47D45C569A85A358F26AC695649C5053445CD90C56C05392810806D86F7BDDB4E4D2ABE9932D083F18F2A50C17294B7F865EA8ED09F17DBD884EC26100EB3C2CD06E4598F054CC09E21979B9D80CE0599"); + + LmsPublicKeyParameters pup = LmsPublicKeyParameters.GetInstance(lmsPub); + ValidateSig(pup, sig, msg); + } + + [Test] + public void Should_verify_shake_n24_w1() + { + byte[] lmsPub = Hex.Decode( + "000000180000000DE188C16F2640A361227C63532B1003E806038B1ADF081AFD9D4C2C5702BAE427DCA7FD40D8135830"); + byte[] msg = Hex.Decode("37965CE6378A0EAC5FF4180AFE4D08A23047F9781540263FBC5488F2760D2247032827FB45D6688370BBF25FA1A9D401350EE3D63714045E91A2BDA00E7E685153BAB4D34E3DB1141E318C199FB7E27AF49771DF5F7501B7FB7639F7EB86B5FDD591178324C898A6ED434B39CCFFD5CB99A926C37C3EE36A3DD89BE7E49F5769"); + byte[] sig = Hex.Decode("003A808F0000000D409AB9BD0993BAD6FDD74779682B06AF517E6236449EBD40C51496C6534AE146ED6528BEFB3028A50089A33411E7B916520E6FD07FC51317FD990F184426A38F97CF78BCFB3BA46A287FF9EA892E9308E745F01BD4494854D0E9243A6EE139BF39E42CAC8EB568D3285ABCDDF6FB18E071B6A383455B8C2DEE94E88A5ED961274BD98CC4D293B869320FF22BCA1B69ACE337B7A1A1EC9179131023F8BCAC112DEB3B146D4409A99674D3C3F7D52B9FFD59A459F778F4E0E3AB91B5BE5289EDD47A7CEEA0A7B368F3637BAD33741AB3FD60E2B4842415ACCBF3B9A516CDC870DAA840C35D71DC3D3C6F5FCEE2FFEB6BFD2EABB34B01263E3C963867BBDBB8726B7204B1C78D3A4137CBEA598DB8277AF03EAFEC7D1342A874E4311430D796AC7EDC6608C5F91ED17E6B7087FAED38B940AC1E456749AFD4A6EC17F22BBFEC18473E1ACDD3D55CD0480941009C5CEBE49CA7671DCF0544AE6FEF13DCEE911C6D525E2CF6CFD0AA6CF20754328FD75FE271A79F10999715613F7BFA3E07BE783C61B65DE252D56B98455366410545710747623A26F7AF365F00DCE90948ABE79C98C58226378CFEA7B5FE479B4978201C3C172478E90EAB0B5D1F3B08B515FD08F7D30B3E08084174ECE5DAFBE6382E5B157D171E26408276DE527D088505FCD02E24E86745AD9CB97A09D799DADA00E9DE7E92B7333AF5A84A3FE7F935307EBAF6E7281D2E67F886368972D8EEE2BC518650C0660634E01D440FDE0BAAD232F2997AAF887647A557A5E6D91583EF9B061F36002F36724EE08939148C33C5DF83F9972C3F511D522740D0282762DC08D2F6A9CC3D5CEB8EA9A2DF8F9C2D8D675986E4396FD46F692BA3DE864969C63596BE105500FCC27EC15D33365ADFA5BACAFD6CF9C88ECD55947DB233D74A7CA711E4AB93BB48CA326B0944DA3BD4EB70DA3770C3BA8A5BC3BA96263E16394877EFAC613FB9AB5644F9744080548732804028A89F762EC4613545DE4AEE7306511ECBCDB02A35B771729953DD00250F4656ADC0EF1015C5FAC92D770158A5537BA54EA8205EDED7D9D703DC66CF5146AAE9B8259640B24B75C3FF1DB2B7474770C608C8B7248E8707967C1F5020AD74AF18BCD0BBE3824258F6ED47865DF2732572E137C87320366F251A13C8310241B216567B1D85D4FA76999AF64CBA131BEEB54F5DD56CE6F2477AE76A1F9755B57FD6A376F3E3BAD965F877C656A4BD7F071FE867F5C1DCAAC69FCD2A1C362B7CB9304A315C800F7E5F012E438CE2DB8D6C9613E2D13CCF2D703E659491A08B21A9C472F45931A0F3F2643C5E7EB4B8F0983AD44BBA5B6D7FA6A03ED4D0AE492F749329AB8688260258A260946014EB983E4AC62588707617BAC78E1E87CDB9204C1E86BB6715BF28F323DE120B6F684C827EFFBBCE7ACDC97ABC6B59BC87AB4A8F23CE5ABAA1B783B53EE8C1BA700A6FE847BC890A118FFA314E9F7DC905283BB02A84172D92CD2DA566B9A095DFBBBEA10CAEC6A989861536AF1804F5C30E9BD0F1AB4E8B5166A2A1348F58C5968FF9EB23519271941FF5D62FF83079CBA0BAC2C6C4A9B141CAD2838CB6FCD04644813FB7FD12E7D9E9B719DB33DA9DCE4A9178CAA8472805D199AAB655DC9B9D7F0BB47DF07A10850D42E0C8555144E6F02EC87FCABFB896BD144770FD98E426972B5FDD37C0A44F453AAFDA66605E8F78FB79AAE1A8923DDEA6EE979DB6576A9B962B92C0C1FCC8D1B2C774CDC50CFE6AC19BB4D058C533F4BFF795E0314385B58631A60D3F12F34543439D364A5DF5A8AB153DBC60A587D85F3B805885591232F31A2B972CCF5658A9993047F64E4B259EBC0D525DB23FAE95F0DF7296F1B35ED479CB124A18808133C48387CF79FA63408B0367DFF0522B161D2588DB5CEE667F435109DBF14CA9FD4A408B4EA0995EA1CEBDFAC320680B23D4EB50DF75F3A4263A8ABD88E5AD3A62DF7ACC6E2CFEB583A4DA5F919BE8FCD85D16D36791784B50EED2F072E23CB62741AF1680B62BF880C18F1231DD91A55D7ACB61FD6A34348F4311394B39C26E19154EC04F01A622C66CC528069BEA5E5D807342FCB31E2DE474D60E11EAF0B9E24F8F06DF72DC68EEFCAE17FA9A3704FEA8196675E2E0FF287DB1E025877B08005B7A0B1E96BFDD54C5F90C5803E3E552151244C03CCEFDC43689740E24981109898F5CDE4C16E4B29D074B73ACBFC4B288F7512F5580B04AAE0385AF1C2AF1FF4BAF4D8D8410F8DC2D0506755DD5C5BC0F7CC21DEFADA8AE32A59B790AAA2826681C7DD30AE0C723C3906E52720C2C61C7AAF162A833FF45A6710BE122019391E2DB6C3AF5985B2F905808A0ACB7EAC5F4C5D462D2E67D0596B15C58CE2228187DB68C06E2C56F2228CD8B9B9DF74C7D4FDA7B99499E49495FCDD275C4D5A48C4AAAA54C4D6A91A1DA40601933F97A7C843BF59F5F773F656362709F564815516D8A97AB17BA92A7C8300A572D8F8A71463EC446FB00A8D8459A030495D2C7F83FB48CD665CC750318BC6F1F79C9A8B5F515B0F7C6F9DE926824E6DBBE96F175B6FF0369DD0FD5EFC91A256A88D5D3E825065EAFFA98ED51470BE4DBFDABFFFA7351C8D779933C362FF41E3CF387AFF42841B208B1572AF015A08A3D7AE69D476660AB6E49247DB1A5FCB9E09F0804A3D72F1178F443BA64F8764BC4AF4746F815817623D91AF3403203AF89A5A8F60F8063AA481712EEC3E9E4BD1025725E068272C4742AB874D5AB2434A49450F9BC4BA581D582FDFF785AE4E663EFA3C9DC4AA63FF22CB96875C43FC9E19B33ECAA1AA6B6AB670EDDB843D7D218F7A3A26F10618AF43E246C3649034733F5EE86DB8BB2DA5BDF0B61B6BED4A8F774A07F62057758899537BCD46656231C2F470A9D382362CCB8ED747C25F1473C74A661A346202449F5B55EB65A68FCBE250F187BFA2B599AE7677DD8F27DF7369257F90435099D3D0E663626CBB9848CEF2115844A930DBDA36876A36C8469139EB25C660251E7E4B370EFC52CD046B01B8901C359A492F91D3B14388200F3470934E851887D49A9021A3129E9B7A0D83DF5E24B98906487E32004BB57FA4DE6A3FBCB2AC67D2428711ED5DB076396DECA297137FF2D33A61E8596248A0268F5776AF906E7F0744B684707F2C0E54B5B3FE59404FC999BE10CF5802533352584CACE95AA5B97AD04BD9685B43B2AB01663E181618010DDDA4C0EF5B10F4D50E96559EBDB6694BEEF2F299BA092C55470FF744F7AFE524E04447918B2600BFC68657CA79D3E453E85FA68E61F72B72A8123814ED0BA121205F13586DE25B66C3B824C6561DAD2F1145FE4D735EE76EA177D1F44242AE1D62D9B4845F45B44532F3CAFBEDD8F404BFA04736E8CB8F6A4002A7985BEC7360CBD2416416321C258CCBC70CF63ECE7766C9F6A1054588DD9DC74C5AEFE2821E97D72F4E232E35BB8A9DCBCF693976C3405D8BF95DE16AB807B0AB74A9CDB765F550FBFBFE1FCACC9DDB01A673BDED3A45491B4E525D6FF036E1FB8B3281F06FD0ED9932AF00B7353580B80CFC704DC242A9BB936302ED3DE804577E204E8FEE73932C291193BE50292B52916B7B68294B1CCB36C04AE7BEB29433AC458F526D9BAF304B136682EB5B6890F76D269A19C4D6D4B0073DE9055F48F3C4C60C97C4283DEBE41EEBF77CFD60AF50FA47D342EC8F529256DC66B38D3D03975C0B0A721F8D013A2721256D17EA524E17709B188B9453A8EAFBA9E3BDD6DE828BBA69F9BA4DC773C1715EDF9AB917CB22FEC219F35816BD9F2506882E4FF68FA6088FFA97AC402FFC07547283C4397C176CEDF72745064713CA1F3E71936D1E03A7BFC1F5E2F29F105337EACAB2E137BA86482517D04E3585C961AD0F2349E44BEB99CF8281FD6BB550A13CD691C47A11B0FAF99E187835BC603B67FC8CC1D87CD4DB11A4248A3730AD1DD3FF801BCB211F484DE41B1DD080169CC022FE7B793AF4C73952860B0866DA41A1A76B1BD6C7EC0E8FCE01129026194F74CAAEE3621578E45CE3A318F08CD2AB3A51269707875A0D23DF959860144E63578568D94F2821981EECBD4D40E3605F968B491D522A6A5E52969D7E020C8D7103FE7B001BBA75658A32DB147AF4B7C4BFAE5134640539BEA2CBD6B5F0C0FB3393CEDC3BAF5A3249810E47702CA6FEA5A124E933A4F6689FA8FA4C5E0AFCB87BBAC3AA09E55656CB922924A62A976C339EF163BF59D628118312A7B8F166EF92C6390134A5763A9EF62233BFAADF35E161C93163C0B65F9B09F56F6F9C3A650EBDAB88DE5B5DDE673605D71A5CCD0DC70FC77E575E0B9234834351FEAD0DC5D39B3E02689E0DBA7A37594CABD585587985A7B3077E61ABB026570D77DD1512E44A68113E86A569B5BC4CF68997B8E2ED2FACE7AA787CE10CD16CBDD5DD8DC85014B3583DBB3F0DC0A927579A9BE5F0937A1D92CCE62858DAF027F7CC88AE3430C9C0B3ACE11FC0CBB0E3B58FD6C69A6E03A5D79A3EE6ED649EF28A4FFF7EBF0A04C26E30E911DE0F42935D00181DB8AFB7C066E0B739A20AEF725816728F144FA4AE04179905CBD6B69443DE46D0D2BFB556685C11BC1D14D782592705CE89D26725D29D33643813F7A8FFC3237D2633D0C5BA210C5099EBD4B0DEA523FFFBE43A2F76E82130B8E0972B9F3419D6B4CFBE627AA547D4C811178638E104C7A4F17577A6BB0ED7B44CE7191BDBDAB14FA86DBCF82238DBA89A3DFC309F4A6FF6088FDF55BA4F24D7F78B5CAA625C2EE0B820148FDEE9D224A6A6483952281B1A27B7A7B0D7CF1DACF9EA607C35F4F4E5DF12C3531BB4FCF03D69EF7F26415C5AF7D4D8A3B5D5A58E68621C97789BB91E522ECA4499FFA6FFCAE2CE89A339CB290957386BBAA96479CD1FF4C1F9B81267B0DA71E8320CA31E56648DC369093C259E1B8A09A0F9A154CC3FC69CCD4B6C4BDF661F00A10BF6F49216A69AEA2ABCCA9891DDA801C3C93AF35000C51375A417BBD071D5C2C1C999D428126EC0D3590B7613AE2E683BAB3E68C176488424964027DBE633AB33F175827E7AEBF6A85C8A24DE63E6DA473829FE05C32C3BAC64FFB32EF5B5DFF18FC2B69AA8E78D6241A62E63DF19DE90523E01BDBD64D85E349E8C002B3C115067F70AE6ECF56A8B74CAC450B7476B236FF50904A3B3FFB0F021722F6FF13603B153D4236476A921784D88FFB3E26436E10BE441BF9FA6443203C67356903D01281051547776484FA38C68DD30A30466427B4C74A30E8C315D9454B7A9DAD7FCEFAD4B266A8A0F400D78B6A247CD80A4E9745A9B03E258AF019BD1ACC46BC322C5CE74F3861BF49A2A44F74EF7A676109378ACBFBC0AB0128F358EC1CADB9E22408DA5A05EA61B048BCFDDF6DAE2D20BD67B6F0350B6F40F3DF8F2A7842102FA096EB6C04B773CDDE83F2B21255E2A7A6D8D75B69541AAB744CC2D5E76D947DBE83151826455FCC185E5658A98C66CFDDB446934B53681BA51C0B4C1DB19D4A19602DB53E591D589187076E674F915E680580EAE077D3B2CF5F9343654A02EA1967201664C0E0C0298A3624196C8151E5706926EEFC79DF82FAF4A47E67449F0C9ECB91866F2CFF0BA8423787EAA415A69F2A2BDACA67BF05666D08A2058AA0A6012035ABB9C46CCF1B8AE95476B25FAC406270AD1C2DC48F762676D37B9543236C7B382C761FF12B7E5B738F8B266444AE1C718C09CF598482AB0DBAB8107ABA6FC465938C9CB1F305A0FC4EE7E3991D674C0F5E0C99E335CE6F1F2AD45FD617FED27E6F7090CDF228168DDD94CA396B47F10DB14A32FCF45A7AE7EE3B480E9FED7555DF608F5A2F9E89252C411D186BB5A6C30E47C6896138A6F1197795BA793C161029FBC6926030910E9D8D64C8482C282ABFFE81592CD13CD65C569A1E23E68C983C462A9950E15362BFA4AAAF78E86CC27171CE12DD7E018019A565E70611FE2288243AFF78FD27A08555F698AA1D809BE529A9537EE737C7903286A1AD5898A25E26E259095BA572B007066E1356921B023F8A17E288AB16D902F9F550599C6060A02FF09D05A70DDF74A5FB44ADD74780C233C978127DE646E00702F2F6C70816C1D281ABACB19F88FE5066A7B58A9B675FBE224A4AA6BEB692CCD62065A7368169E3C022F1383DAB6075690633B15DCF6E4BE6912F63E3EE58A860C2E139199101175ECE852F3AD5B91F56A417CC04B3F20E1DF1FAFF4CACD88153A9A9A84EA07415B3824FC8C5DAD91D2A9D373DAC5B1EA968297DE3889AD3E45EDF401201C04B3FFFE64C866C0B995A92C0247EAF49DF7C358730801E1158132EA5876D049B3185326BDFDD796ABA930F64716D21308AAC87AF5FEEAED35D1BC5EDAB2F079600ACD31F6786729CB36B545AA45A5F91EA6E14CDA7697129D0F6F6AE1A31D5D72E934273C965D0FCF5A27F68B3D29A2A46047B42708D999905D0CDC8DB052B362CA39F91D23257C1DC7B65C7D341689A8C8E1C5DD49C21776FFF0BDFDE54B8B87EEECE9062739E216D28EB86112F2F26F892BF64599E9F8B708FF9759C8B516EFA52D306C993D907E230CAA5365200A6504BF5A804636AAB43D74488E627ECB4AFFDC6B9270970C0FD556F1DE2B5E4154C539AF1064206D030ADE2B6E95D7C46AF08C6FAC6545FC10425AAA37B18540C05072FC7A1797466D5B38F7291DD21FD3811F92C550EA3F53A94ACF23A8B7543244C3065D216732A0E6A160784DD96A3D74DCD4B762FF06BC5E2D2892366345593B8FCD8DDE7F4AFC731AF8A3B77CE5EE98BBCB8001DFCA8B5CC282B0ECAF9E2682397CCE86E9865DA379236643C128E000000187D14B68025915B5F25949A569969578748E2058F65678698DE19D5D2707A3FBB54FA854935F833555CB69396C4A11E9880B7F991498D3077099632D19AD407F2FF1ADDFF5885D4F0FD3EFA9F5869BD0793B96DFA730C6F0EA1BD63E05FAFB084BEB80F549740EA4A9032E534DCCF469BFCDAFE5D58C1B41832102BBC0C5BE6945AB3662C239CA66AB4FFD8A679AE2E9C67749CE6E578D969CFB3405F7E71569797D759FCA5C1D3F8C8F17787D70DBF05F72734822A393DD3C4B123DF4BF9E984B420E35EACF645DE1D401CC5E5D6E32C9D15DAD80CBD493E74E7B75094983D077DD4046E530BC181D958CDE3BD77E2F71C1868DF461A048F603FD6ADF4C802250DD1AF6EE962A4B6FA8CD347FE9B662CB70AAC271E77356A0465CE6A2FDE7658FECCD1C582E4E733F98F12FF0DBF560070032E73B4BB14B106AB6D4E4F2C02D074368194468BB788975F77C806004ED8D6D5AF2A9A27CA8EB774037A9B505E25EF66AB692A27E2918427466646888A606DD71B6786FF109B0C3FEF6667B67A7C6034FEC2371F59EFE4231406A5CA61851DCF92FE76004BC838124273D253CCCAE3C077C3204490B20200CC283B5425C8C138313B4331638CC48E330C052C2902147A3F82CEA7C7344455234EF48791ACCF50965B6ECFE24AFE070004F1A9FE58FBE691FC8DD81AFB8ABD89272E033E590DD14C1B59BB38BFD3E6D9EADD250A944F8F2D68AC48EF0E59E6FA56C44F88F5E00DE8ACB300EB15DE2B3EDEBCDD731C934D64A4B321F3225BEB8010B5027DB3FF655E63ACB6B51C3B242B58AFEA6E379199A356BD83A28EFFAA8C9BBE3AB9519D409923525E34F0"); + + LmsPublicKeyParameters pup = LmsPublicKeyParameters.GetInstance(lmsPub); + ValidateSig(pup, sig, msg); + } + + [Test] + public void Should_verify_shake_n24_w2() + { + byte[] lmsPub = Hex.Decode( + "000000170000000E5AB18087CB8CBE2C7884853AC5FB8E6E59EAA74E7DCD418FC0B4545BF12FC894B163C438C7B993D2"); + byte[] msg = Hex.Decode("E95E2F0D429AC1A228DFE1F4D11675562A9DE1AA22AEC7E769F8609E2467083BC9382017FBAD3AFF52B1B91F7DEC31B6099F6F3F5A85060A762BBA7C991353E1FF5FE003CC7FF699DC3C3028B83E122475FAB27849089BAA3BED97F147163B82D9725E8F10A7B0BC17B947F52F41F70D1D0193D5952D535DEFF0AB7D3B0E5224"); + byte[] sig = Hex.Decode("000459150000000E562823BD69497DC096C8DCF8DF74BAB93F3B4F7EDBAC174EA411A2279FA65295D82F397A0F84D830A9F8FAB22C3CBF52294452432A4769355B9188B38EB2C85FD315FC1208A05F06B5B8FFD7C10E6EBE103C5A1F18FEF97FAADF7400D71C2A2375ABAE2755A8895A159B9DD0CE15764D9F4A4EFE2E7BA471AC99793EE5F66C79743783015B51A72CEF3AD4F3EF34293C80863AC4526B97F22E21B2E11C5D24CDC04A8CA61BAFF3AC14CD12D3B040C8995A49B16A1C64CBB1AF22937688E776F0E0FF5B936E912615AD36F55ED573889C7C05634A923167FA5218738B6359F6016D3FE80A1F8A32426CD9D6C4871C6BFAA7650F1C7D139BF1AA2B5BD2B9211723D1592845D760A8E2448892B800273A2438854624707C60EB36AF2235B1E1C0911B05BDA5BB8C16E846910EAC51BAE0478A594DCDC335D939C1F1788EBFC99BBF9E903CDE01ABAB1E9E28047E380851012B1FF596A544A98574A72494DB6D7771017AC5127724AB596DAFC30B4643D590184FCCA3C118CC96D606C962C9807A16EF208D5849FC9287C4A7801803810D3DF028F17BA7BC6C09630E841FDB9B51DDDF909AF09BBF8B4F0120F962F6AB35F7B462E6C9E0C560E6FF530C417BC4E0E0B70BF1FB2F16E6903D9B15F940655E964078AA5F3406E70A20FFF375D8D0B0EB8218D265C9FBF185CB0C815BD50DD99A32992CF1D69660C690A4CB7EF4E27639B5E84061E7A37658570E06594CF45914B1D6FB36C1F6074326BE75E3110DB8787FD227902B93E56ED313FD08E43D6E5E8DC0236CABE0A8EED16CFF6228213DBB5875F6C129AF747A0843860690CF2C5F3FC090F0BACD70B918B40D90EF30D83828B5325EB930B0B755CFDEB227EB455B9AE2302C76B02334B419DB5812328D354A5DFD529CE6F062AA95179A226B06B041516BF9E3639BF8A1856C2E5174DEC88F5363BE1427645435C5D508EEA10B239F5988A0CB9672CF5B30961C626A498C170549D3BBB409DE88F9C5D8018DE2CDF938812F4DF5FAF91EC438CB770B76F8373A230E731A6A331F9376331EB3B6F105A1550FC1B8F69E4DC1C8BB11B740B5FA9070DD2640F5AF80852A8FBEC12825A0EEAB93E0E57EAE511B141FEEE4B11ECF0E9CAC764E08372291D73C99B7EAAC4327D9DF85D5336E6DC1BE662F7C7E07D37A479D963028E10F2D7F98516CBB0A017D3BFB95CE9558C65AE2623AEBBCAD409590289E53990C8029BB78FA1F03A480B74AF4C2F782A538999CB21DCF8DAD41BADECC2451D40BCE0B2C7BC32C38FDC119E055853C00E58BC50E0155AAC80FE6AD4391E94497417CE29FAFAC3B9C7EFF053CCF7D95641863D6F689F16B306C44E2492EA9DD8907E7ECB6FA0ECDE555B3AD1D5DF94526BAD2D57D9C639B2A482054A0598A4385F2D586EAB8D89E6CACB2214D1B5CA6806F7B3E0428E9129319A3F92BD4481AAEE820C41F79090775EDDBF862DE5C170F9C582D2CBA5FBA02373853A157094E1897FD476096E7522BA9D806D4A5C6B27729ECF38AAFD5EC6424E12DA2950F294BB9174880A843635F37773209F54E6BE92DF682D4F78A2D05BEBC6DC27E305BD31D94E75138856DAEB5B62F32FD70EAF3A2B2760EA2A43A8182651AFDE85BED1440ED8A9D26197319C3CA76798CF0497A76AAED4E98062AF8439D3B4EB298C4660DE1821530AEB73BBCF4D44F4550BFE87A5A17DA48DF7977EAE61D8480EA368C4A971CD05A260A2F596E9651782531531BD98DAD3C85F71EC58AB321C4CA1E6A26DC64466A080CEFBC108E0AF434FFFD65D51209ADB356976485523E59907245E79D21DFE74084C73DD5290F18527B5905B202963992758ACE41ADBF8F0988C4D185ABADFC98B25A5B1DB614B5A88E2A27B75CEF4416962FC366DA1557A7D6C5E2654D5300020FEB409D2CA6E53C3D5AB6F09015C60FD7B8C6E58692FDD3A6B05616FC485FF12410C49BF031F3BCC517F0D2F21DF4A86C000C0313FA3180D502F0A61B780C782D59D3C6B6458F6D86D7C18320D3543EF4533EDCEE82628EB411481D82166CDDEB9E65EEF053C327AA73C13763F889E80299D9F925CD7D4964FAFAAA796E811C5794277E33199191EE50D0E6E1C5AC27E746D6C32E7EDBE3ED772009D2F317424A799325984B44D019BA87139F06C91B678D9B58B3EA8B0235CAD7197D304B44A733D93CDE76D64595729C84D730B48368148999003873E40338752C460B4E7DCE2E1D04B7C8BFDC1BDDAC8D03C540A1192BC9011A08AD5BDD52E856D8AE2492F7644A5F212BB35607AC7B5E625074FF28D72E9DA179551A04A01506DABBADB14C2496CC6C0CEBFDDBCC39F3B5FC56C3778594F2DB804CFDA033F07E1DD9CB906ADE6D9FA215324AE9023CCF41C4BB06A4B9D09F3EECD45E9CC20FEB77DECB91B8D21E5E8E3001DB88187B3D451F915115A38F31317C1839730E4441ED309E5D51580A8EBB72672BC283B2D908A80A50E7CC2E4CBBE78687A0E83DFCF835EA441253F0C843EB01C953AB22E4AA8988C27D4207DBB1FEDCAD719C6FF839CFE15BD9D05CA61C3C0674F7089ADA5B85FD28184F24F09AC02F8A5B7B919F341B27B8F8143C4CA0E62BDA8678AD4731DB7A44B686E9C3CDFE64743FB88F0EFD21BB8E184B8F322ECDAA30D83471D785FEBF8B321BD47470A1010C175400B208E31ADB354DE2461CF23320E6F8B4AEEB9C35827085F2CE4D983B13D57C5268E29DA6D1155B7FFEC621CC0320ED1E41B81DC712B94C6AC0CB21E51A0B356DC2C9647052573D114203FEB57E48DB1508A3D530EE8E9D3D393E71CA72BBEE3934E5539E63DA7B51E31CD2A8469E06ED13C77FC05EC212A3B37093C9A8C2D66C952A347573870FBAD8865D9660A47A3B482D4930AE245EDB5F1D373DC293E1BD9EED01C79F9B090EB0131CA236A93C3989DAC5B30DBA1F0AB26DA805839C789BB74392485FC9B46DAE7DE98FA082E0A436FCC9CB2CD481FC625DFB6A2709455C693D5292E4E5C46CA9835E9D16F1E5D167968792BD89FFB42036E2611CF183380CC2015E85864FCD1340692C431ED54C6A54AC6DDAAAD79BBAB1DB0274EB852F646F3628C2AA880BF6082111607C38B6E35AF633A802BE7221FE7BF5F9F2A3447D229994613E548D80ECE807F504BB491851E866E7D910F32873DEC85D6F5099359657967E32AC70C57ABA8F9A7E45B9D80E12E7895764EE2ABBE081A62214A637680AEF17593E2D7AB057217A1941267E73F8FDA9B3D5DD9D2D6F874041DE41460F44F84CFA264EEBB939E1FB87ECB1C4E0F8671448956A76626AAA9DDF76E5CFC2CC83DFECD874AEE2F9D42E178C112E93C9468F5FAB27D7E1E0A364A215E25BCB230B9EEB844B80C62C2CDF9145379AA0F76D07155D1A2214E2D43368B0256325A266A98632ADE9F897B52294B43B5AAFD087C31772CE0E55FA32C7FD467A8110281803E81A4E0000001713C9D996AC9EC1518D4ACB08C00395221795B6024553C53AE08F21CA02C91E5D00D7FC1EC7FE6B441DB6F3AF4E1D54D9ED280EECD3C8FF4E3216DC1E59EC850502AB1C3F48B86B1599C47001AFE0E400745D07986C8B9B367EC9E08B1C36557C97A89BE099554DF248A5C7B1020F8F6AF009E86513974CA7B5E2BB57B172956C0542C09D75ADFB1F4F9A3E90B6FD4F840211C41175A28B166D3C8565B8761B42B38E8E81FE763D50AE2400AE6FCDF489340DF451EFF36AE265D335EB56240DA6FC7962DA822D6458C20D813BA657F806D81FA5A871C79A888B1C11F8866C27E96104130FA68C24C56F650C20174454115CC3E896429A47032FE77042B02580258A3DD837CC5CDFF7A82C5F5B4DC751BF5BF455C8A8D3403A96B680F0E73FE3A36F5612E6A26407E73F873E890E01451C3BFAB3B5CE147083CE53CF659A8D522B432B4EE4009ACCF60132D3EEC6B03A598581EEF5D9086E601DCA67E9237BE39DEA8BCA8B4424508592F9397ED0A78955E242DC7BCB5DAF4DE3CCC920A34CDD3060CEC6701C412AC92683D9661DD7AB9D9A4CF5EE088B50C3A72311FAEBD33475CD17340F3899EF8D863FF9E566A462BB0EB415D0C8B2003CD6EBC50626403012FF997CA69D4AC7BEEFE591E19F00EEE46A2A026101939B82F59A07DEEFDB032D"); + + LmsPublicKeyParameters pup = LmsPublicKeyParameters.GetInstance(lmsPub); + ValidateSig(pup, sig, msg); + } + + [Test] + public void Should_verify_shake_n24_w4() + { + byte[] lmsPub = Hex.Decode( + "000000150000000F054479157C09EB7E34A7916BF4C034E24F02017B1465BE851833CB98DB4D7F8BDAADAE44FF2071BB"); + byte[] msg = Hex.Decode("34A6368F588463E30473D6AEA5F2A00DAF793569ED56264209B3FDEAFDD9A8A4DCB0B82EAC503F2A939ABDCE273F3D246DF3E62791E4DCECD4E3E098AFF67DC141FDEEE3160E0096A1C74DCA43FA25E94BADE07A9BEAA2CA93CF3CADE7E0F1E16500C84D2989D14E6E821234AF70BC1090C779A1D8E2C20A4A176CE85C6E18B3"); + byte[] sig = Hex.Decode("000001140000000F610AF02360FC98234DEA82F7ABFBE4D7172D1472B5AA50EE293C8137FD43508400233BD5F3CE2656C12FDFC2235D7BF081AB8D6444DD41B9FA393362C0A7DD8737880AECB9503C95AD5A02FC1E10BCEC808CA381972A282245BEC9FD146947E0E6E55F417A2851D48A1EA235D2268E84E87BB254477528C70F16AF8F9060D483777A586080E072B6333463D033F3636DCF738A8EBC25D39A0922A53346E9C16A66F4F3086BAA8EEFDE0236BAE1C2AD34175FF7902A0623BC7E70C7399D28134CA7845BCF2503B729ED7DA7DDEE5C67183883BB3BD819777CBB6CECEA92412C1660310ED4860F242F380B86202764C0658293BC3A8582F18F3D1AD17328EE3EDF91E2483A6F4C4B2F80B19C949DAFC1EABBF989185E5F84D9B0C20F00BB4299CB600A5E7A113A0B7C51C413ADCC91237625FE2B101C563AFD92CFAA2CB46635871FD1F53EECB75F1B34C016F1C5B3681A3E78BB99006AA207B694FEA2EF248AFC76A8DD493BF75A5F2B04D791014429DA4B45AD29943FD1A63F4DB14DA8C66651EA066177B425D01F8A7085E1CCC7A8F03690292C1217A94EB10329222BFFE29DECA42C57353CB2EF5870F2280E30C4FA336D5EFEBDDE938A81EC79A35A55814E4FCDCDC281FDB38319ADCB579F3983050556AC8B18CE1F23F7563D7393CF5A19D993955AE9D481D24855CEB03B7083C087DEBE441CB3D779702BDB6F123DAC4D3520CF787FACC9C1FB9EA9F4A16173822FF65DF93F657B3D16113CBB6A9AF37E4FB6FD3161FB8F20BA6B57FBE787AE8F8DE22B1A14FD5BA3DCADD8F3B73479086EE09814503D60C95709886B066C6A93DBA5C42F507CDD4DCA930A1806000D8F3F5B13C70F807EB4CA6C47FAB4014FE69D7CD6EFC1176E075B4B5F849EA48EF124E0AFBEB97FE6A00B0ECBD91D118ACF34F963E716362B6DBED5175BBEDF5BB8ACCBA80C2B1D3B2906D49CF27380406062947D5869669A524FD1AFBA970541921D6A7E7B849905699ED414CFEED47414BE208895E352AF7A8D6ECC3C0903F5F9748B0B2A6A7E333789F063DD522E7473C518CD0E4AF6F7286332A9E4EC3A33E01B3809E7B3C64525F8FB65E22FDE422FD9BF3D491CA2FCC43A8558C1AD0DE7092AE4CF5F87F5B7E871C32C2F0E70E07BCBDC3062F50630F1EDEDE66337404D3A5968139276A6034A2326F27BB22310A109A3E3DD40E20F34FD2E6716304D946441BE5227867D316D687955B975D4E19476C70103865AB935E8CB347EDCE783E9DDE38C84E277E9FCAF0B0F6B37F6E2CE17D36EB2DEBD43521A5AAA2475FF851C02617B4F9EE62687497BD62CFDC553A89DFAF553C70D20B4EBA42600D343649ECD45D1F6EF2E2F42592A9D7B90ABBCFD64A1E742F1C3C000D13ED0ED5B66AF879342CC24A44E2B84E400D0298E9923306F6D4832DF847E37F79E58457B3BA5FF4E46B41419E8F7D884311EDDA47B6575EF3571513FA24D9FAC2596707234D67127C60145F18E669B2F1BF06BD27572F56B539EA61A6CA98DB4347F3E89D482C17A963B6E8148870F18B8C9DA51216925C86A0C9FBDE75F13663C236A041463B40FD187FDCA53E6823EA55A4BBE9062AA45535F5EABAD2BC23777882E0F0C71E4E0881598D86B0F588DB9C90550B587D5B33A8D9B33980B03247E722843EB54C94E5E18A22B3326FBA8FE917C3048EBADD2CC415B49EC754504A689B96C86E6AB15A7A315A6041EA71960992DEE40E36A23D28AC8682D3C7E977D3EE295D8E92900000015675208ED60F2C8D6FC359396B44719BE9615452BCB68F35A7FBB1FF9335A421AADE038D188A1941D16FCF525958E678BCE6D17682CBAD986AB6AE78CD1ED56D1530DAB56EF8AE0FCEE2CB7882F55D1233046A38E21F7EB8C4F8E9C60F2DC692E562C113E2A5058AF5F319CFB5C0F9F344CB3219313A70CEE7C2D82AF77B0418AC61411A87A20B01EB39931457CB2946C551EF17E0021669A3658F71B37E1D7B7647EC6E08444E9B7AEE8789D79276CE53AE47734002F1E1E2D426C95E454E1B3F4FE2B2E3F12EB1CE541D4021E294F3C31C79A2A2D8E8652A371D3302D8EC4C987557836C9809210D167CFE47EDA0AAD"); + + LmsPublicKeyParameters pup = LmsPublicKeyParameters.GetInstance(lmsPub); + ValidateSig(pup, sig, msg); + } + + [Test] + public void Should_verify_shake_n24_w8() + { + byte[] lmsPub = Hex.Decode( + "00000016000000104A90A81A143279659797B572866CAD245B3E55268DDF686AAE7BB9A5E77304A0C8220ECB42BD0088"); + byte[] msg = Hex.Decode("0B2A3604599F2675E41098DF4EF0A570270FD86C3531E33F6C7AF3820F8BE2C1D0F4138657244ECA0AB02CF32CCEA7BD0EEC561EC9DFAA1FB08D29A86930261103C4AEF5ADB35D7D2DA24CDF5093CC95225DB3369CE6867EEAC139B4A27C6C56907D86F964CA1BC123F2044F5E94E0EA42CB24D687B68E8ABD13C0C974807032"); + byte[] sig = Hex.Decode("00002F9C0000001063557BCA0E9BEF9B863138BF1701A277520C6E7E020AC0ECC0877DA741B5F21B93A98EBA55DDA341B24CE7AA336204AE0144BA4716E3304B66BEA54FB3D4177078727A5420BDA5F3E2D0F9C643CDA3B38FCF8DD3E02D169C0A8B3797BD013B6C28F26A8C2993F7712B0DA09929FF80A2CA21A6E4949558ECE114826DF6F3D8C620A1323C2CC8DCF79F917A2FF891B8B375465A77D67ED9486B4FA4F27F022E66CE076F8C908FB2BDD822B1F890E68AE57DD28CBC8943F67B74D1A3D074CB41F32658C563AF59AF583B488DBB34F52622E2359AB0805E1F4E2769C2AC4DDC3595018EE4C27799BBF3C30987263F5CAF5566E7581CCE73B1652D6BE4F8E1F6A55E00FC4F263132A025507F342B914152F9A5DC05E0EB980637C19A250B8CDBC7B68266695FC803DE027121C635438C1408CEBCD9FAD46586EEF23FEA11A724B5F515600BAF951EC8617DDB3E5FAF5D13314266F4C0F3429327E46A580213134954C4971E53B45B322CC7FF003D1C7716DBB5CE34BA8778DB8E09F399A7A99FAF402EE97F6C007FB1647DB87B0D5AE05165BA192D32AA33E3246C85D02ED388333F02BB96217C2DCC45F01791907A78FF52DF69337F3AB4D0F2515752CF75960BBBBC09020651738495AE0C5A159040E07293FDB98B8665E82166D15F5960D3A58988E9466DE59ACF4D724D254BE72820FE9A8E634B351D4B8C07E911D23FC8B0E87684BFE54C50444665FBC0FA6430D61A8BB24E2AC08BB8A7BB39866C61D5180D6F0D1DB0411A923F5050FE3E2CF2CD0692A9593EBB2D0C71221AFF04AA489218A0A5451273B2D1EB136B1758B77CC113186A2319313C06489ABE3CE22B932D2010716A06F1DC76F0BF519C16248D21544447E1FB7A002EFB39F8CC38968E1907E8DEBE076656762A0000001648AB38A35FF650BBEE245BE96EB43AC44A4C8B03D1F33CA59025C728C5703088A157BF9CDFBDDDA49E46F8A2606217C8C3AD12E68BAF369E40E24C05297383583A88DDE17F34D7347C46C6CB400239A0DE9DA2197A178C6551D563FB6A12BBE4F6524CB80B3DCFA75052DD0969C453132F7BB1636CE1B221434309D860ADBAFA86620A9778E03DE506E0C56F1D4E024B66643F2E4AE2065619575483F31ECA4DD7FA2D81180DBB9DCFE60CE660051751A4958F1683EFC5622DCD25613463F27CBD31E67FA1A84DCAEBE12787509EF6F9FEBFA83CE35A2DF445E59F60A097BEE4BE0D58068F6DA39800045458DA799ED07EE05309DF9D62D1869662A975101D1C4004B301CBE12B98DEECF2B07F3C9D0FDDE64181D4C568DD34655EE9C514576F422992E80AC9F4BE79A068F44042487B454948B58F474037C06F6973142109D1028127765236E4EB93DB6CF02D09281C8A0C690339B25BE4ED52A0D47367BCB5A77957A932FDA1A0"); + + LmsPublicKeyParameters pup = LmsPublicKeyParameters.GetInstance(lmsPub); + ValidateSig(pup, sig, msg); + } + + [Test] + public void Should_verify_shake_n24_w2_2() + { + byte[] lmsPub = Hex.Decode( + "000000140000000EF45E0A263A79A7304D94B38738FA7E8AF4D7503DCC65163326FBF7AC0C8C938B2D1324456FB956D7"); + byte[] msg = Hex.Decode("2EB454D4116CECF6A6CF72BEFDDCB5B6204E5689BF8901B5926692BA04BC7EBC8EC21E2B4645C99F85C9E4D9A3A855594D019264DC1C034DE09B1ADA0B71C08BA81567533503B8B2B232A5C3D6E150AC409D2CC54727D2F616532FFEF87CD12B8CBBBBF45596F779C6D98BAF121AA010991256DC9C4D46640ABCEA2C7D7E8C3E"); + byte[] sig = Hex.Decode("000000010000000E31D755A2CAB086F5FA0FEADE2EB40D95FE51D049A4EF92BFC8A1C8C35AB5F53BA4FBA7159FBD49FB92D31E9E9C46C4ABBCEC9B5FC30AE94BA57F1069B189BA8014BCC57F34C7313D40C709600C45626A92E59976156A60778625891835AD009AC535AD6330376467113C40FA2C205C39BDDF64295C6ABFDCF492BDC17894EF07E697A420EB68CF91CEA8D7E35A05E5678F4F5A6A6CB9A51D11BBCE52493B9B6522CCC360ECB1B5994E695B98145FCD952341ED0FDB2847E77B82ED72921178BE12BEA40EECB0639C483AC78958F940A674E7A9A998D44B4FC854A303C2BD22738579D1D6F3F05A1018DB195906C2A56EF46A59D793C5F3FF41BF7C6A94B33FB905C21542D19EBC3AADB87ACF1D591323FDAD0A46C6E3C89765BE90709197F9B3421E6B5CEAAA4F9B28708CAF2CFC474277EB6E7C92270CE06D7948AF4EAAE482FEF2C14ABB25CC97B9CC3232C83C86315EE61949450FBE1CCF9B73A6AAEBED983E765726563A16E119FFC746DD02FFB33E434656488F5E89E322726E10F498F3BA5E40A3B2ADB97523AFF313FBCA7C953A1BF91266E6F6C86689DD95F7130EB037FB32ECC525C9A925567B3EB0C2A8CF1C008B03C60B2F8FA3762791D424F65B576E133E833B9B551546298825470B72D50089F8C9881EA7528653F2FE38734212BD03A4B2032E79365016671459515B7A0495944479EF9D8FA34E00F20CBC6CE25707E84A4926590AD87B5DF82F5930A4F9212CD0469AD55495EBEA029E12847B67FCA1857E26CF51C2F803A6B35A06DDEAC58F8D774ACE41B9765E7F950DA906709D0EAD10660458C8409C779B4A6F17E8D2262D56C727DBC57100F21FF105A83D3867D2B18147291D79E5536BD8FC2F130E6B16E7326B96D79011C38B16A983459E0B448B4AD2A004ADC38088D005F9ADEC41796E3BF1ED724A28FFA49CE287F20C894A46DA7CEBDB8AF829D32094405ECAE9A7150CCCB4D4E11E73A99CD5894880E1B6C4553A329B5788F80020E1517E64F8C1FC48CD11A3FF1CDF2E7A409B7EAA9F9DCF17855648C325C4E5AFFD9873CC49EC6C18FE592A4A1587D9EABE7DEDA216706EC699E39C7BA838DE01408EA74F8A00C9331BBC3997F7C2229E9B3C92CFD53A20D170A20D1F7EF5178315DF64265181C109689B78BF5EAC05795EB9C45C7D5ABA5B6CCFFA042A3C7D8C4B9131DEB8F665F4C01C898AC8431BB5E3B5A0872FC0929E21F66A1A92147CBC4D1975C19EB527CEE7AD046C111BFEA353AD3D8F8F5185C1E10126AE8B0FA77E10C94070EB4AF81990BCBBBFAB3C1B5036C2B5302E87B1C92A734FBBC5E79DF9AF356A0405789B166F256A5D0D0058FC32F3367EF48A29F1717FBF808D0CE1EF93E5000A4FFE711342834C03508F86DC31F55328D684B918684860E292FD15C718747A4791F11270D81EB71D0F69FE3B18A3222151125D3705FA6438510712DB2C40F776B2D81DEEF8104DBFE15C2D4B5293D6C99C734BA99B0EBFB2759B815D84F986C898051939B78E66A1E731A7F123E181F7A272FB1B8851C9B3720C3D863894A1C0AD23A0F26F72C842DCA2502C1A9987C73C9C8A1DA8B5499F0DD24E5251581718673088B65BABAAA9A235D5A49BA67A575FD632B48F48AD7E723440C5BE15BDC94897C0D1A4C1100BB6F2DBB579BA38D8FBADCAEF73A52ACEEA062A4961EF60BB73235CCD5FDD0FE99BB3E74F8473B4061FDE91CDB7E590FBEA7CDF50AA6D22986D7DE635CDC0CFFCC2F13A8222B4A6FAECED2602984CE9BB0274B059A4E4D6CBA5F4D77053FBB498772E871DC25CCF4A94AFB87F7D04C3BDF2C61D0E4216357096813E610401B440527779915CB63186DAB8F906C21252AA0BCDC263424AEC2EE33870D6D927CDA2D7D078B4B57230882F6DB6ECB2DEB273277159B3D49CAE0C7E464CA2987AC6C9FED3B57686161899E87024E079A6AEA7EE532FC1E45E84C9CA2133EEDD86D385A8FAF6A580EF62C43A551196DD18806CFF5D561C73DD29BDC2D48426E16DAB827567641B33A62EA82826E65866DF9B645DB4263C9B521AA9D403D1EEB5398E9820998516ED119A9146669BE98DC3759C094A953AE4FC1A9E645384639E88B58113971FB2B209C0F9DD5DD4F0725F0BBCC52FB1D2D98B91B980F9FD5C57860174EAF90E08A1D2F25FB12FF82071A8890DF59569EE3CEFA6BE0F43132D811FFF44E6C49D70F58E91DE03929557151155B8DD1C6A2C831E01F83C1F26052F3BC4BBE985C71A47FB41CF1A52D961E6092E9801E0116DA5E699A13A86B982ADD22427868114A062E9A55767A205B1054A7C75E9CAFA96C0C8A6AD02EE826BD3DE79DAADBD8C0FA37133768A2075394EA10491B208231F4645C991F6E15C1C73CA7AAE2C5C8C1412DA69B575F0F53AC4647D887AECA56B375570ECB2F97F100955F56FC4F40E5E0B84124EC857B1720057ABA1CE592315D0FD659B0BB403B5D5E662C44069DCB6277189C734402F8B7169EB457B8D6F6BCEFB77943D4D23F7155A0F0D33D2395CC900D7E3AC8D9AD2A341AA108EF9E16C31273714447587C98179B8C5D0449C6041983EE5F44DA3A53C56EE4F29D8EF491BF4B1D217EFD61FAAEB3714A5567C4A1607186B97F36264AF6E97BDFD32B36821B780C314D230708EB6384CF9ADAFCDEF44716E221510CF956A238E80F44F131CB523AA97FB335694753693909B55A9B39FF7A8DCA4581D35BAF87A62F42184BBBD6854CB9723F5ADBFE7A6229BE516F1727A8D95ABAC64796B857A39391AE648DC45F7F3F4C39449F7D70209489A1AAC18AAD3BF618DC2A0B6981619D7B36B2787E260602CE5F326C82C0CF6F9902A0315D501503D79A525348C18DB0BF94345E2C25CF2B5DCE32B9951D5A5D700637A9DF892E8E80B9B80FDD6E137A56AE3511CDF1D0BB3B1880BB58E40F015856D01937347E5CAAA3DADD6C579FE9CDC44222350C00BB946F6E5FDFF82C16C1BB23B2657263A00D4AE864D349A671D571A39AA80F1CF0A6979B944A1667E2AE217615BDBED3035BBA0607E096EF52ABCD3783B5C4DD11FF8E6AE5ACD465870253A5A847A37005837E0090E7E7497D7DB470FFD5728988E615610826B35D0800110ED76EB7AD10E0CC4E810929F3EC6BED6C2B5553202B4A34E0B2D7396FD5949A0A1EDA72DC7F22D5169615A8B467339ED521BEF34FF129E4E2A7DA421A0470E8787278F75579E8AFFC5AC4A089EDBA4C8DD72D4C4B1C1AFE86B7637663B81E63C1D57BE2760645A0CF40E796ED24CABE60BBAB2B66B25B62E24F0386B0A9FFBA481480D9762A4954CD89FCE01DEDE0FA3C70EF45798995446CE41044D59BFBCFF753A298161A5B912E2399C3AA237F1231247D2E17388406A0669D8FF31A719A18F6314750C95E9A66874DDC9FE9075815FE3EC4DDED0C636C090E024173A4DA4BF4301716434FB829AA971EB36DEC0A55500000014E4CDB5EC4182BD3C77EA1AF9A071D2FDBEAB9146FC4FA25CDDF308297C97F2094CCA4C4A2721BAF19B32906412BD062EB870C846C35BE01623704CC3EE555C1F7A68188D5059B1A44448103DC28661F0EAC5EF5E396C036DCDDBFB1A626ED6D6AB294B738CCE71D2C8547081D319854734D871B0EAD97E81"); + + LmsPublicKeyParameters pup = LmsPublicKeyParameters.GetInstance(lmsPub); + + ValidateSig(pup, sig, msg); + } + + [Test] + public void Should_verify_shake_n32_w8() + { + byte[] lmsPub = Hex.Decode( + "000000130000000C972FE777D40FE1EFD97603A62733CD1CD2D6F13C54A085F23017BBD46965C0853ABC363FABCED9119A26337A306A7B88"); + byte[] msg = Hex.Decode("901F4B2AD9C744E3818519BD93C0578AC9AA6FE2B3BC3FE35EA569847C1FE797D240D846C2BFAD1FD72A784F46F393B1C1035170AEA0A569650DE986E71B0EF9D560B02772198D1B1A1E825926A5F1482C509F8A18BDF782674A5F4FDBC83D59839EF4373BBF01618D6A2DA5220DB4C55CC54F25759297C997ECE7737A1B8648"); + byte[] sig = Hex.Decode("01BEA7690000000C85018B7FBF611F91DBFACC1B753B62F70652C4D50E2304209B56B2796B1F640845C5DC51829CB63C945637C786C63C5C6FF2246FA8C4F3ACABAF9CFC988486C99ABE9603E434D84EDE0A792A910870C32E0AA3C8A75E354F253F15A2D614DF441082663A96B7BDE3A3F1DFE06A98AC4AB4F104929A7E2E4EE8092B9209B0D9D0EF8A3CE9108692737B4116176C53264D4C69FBFD6669033B596065FE540BF0CF02E7F814EF821FAA5D06CF3C5C258A95A74939CEC696AB12ACBE7D2C1828249EB1AB2866B95BA1BB54BADA4E0BDC4C2F85B922B6E78793BE654F12F6C47DCB2A6505601BEE6EAD8A54CA4EAE6F778433C19FF61A6D9277DE3AE206F8C56D86839360AB4E07C1FA39D04303306967E41480C817CF1A0A9E1E731EB725138A7C6DD2D19D03AEF13032846AD0B55BCD0949B2CF48B8144F0DB17848BB957DDFB1FACD6234435D48012720F78BA6AC44531D5F98694D7C590FC69BCD332246DA9D522B423E5D2956C8A742F9E00327806B4D443EE4DF953BFDF5213FC791240326AB1D842FABCEA8896669B602AAFCCE63CC9D1AA12DE6E5E40F268F2B3AA53E10D892E3440AB8FC55D5ED743CA6E08B8615832AD3E8E30A68445DAF88F7262A6D308069627293376A30911BBB3674D099DE2DA8B700E66112EE98FA00A3C883B1FF614A685B108FE310EBF5D4FCDB457717E0287D6A3DBD71CD2B3D8B24865831291D7A4139FC36549A32EB9495A11CE6C39363528922923C5F11FCE3DC62AE05F8BA557896B28E7A280B84C7F5FBB0F6C9EB101EF95016662B8BA1856C40BA0FB074BB9136D497D90C280DFC3D4D3DF702EE22C1DED8656FE1068CB312DBA589F8529B192C3AE46BD63CCA9DBA00A9001198298E33064A187EDDBB3844469424868BC36CC54B67A427AE716616FC5EA856B6FCC08D3DCD293EC12927CC4C8E6B5A3043A03CA7DFAAADEEABDA6E5CF3D70B3F82E0132A84561FE182AAC77D180D6F787BD99BA1E1C4C4FD8CE7513A4E2446D48994BD7D58C79923B282BE71E4591812AB6D1C55A09BBF995909FAB9932668EFB3C30F123D3AA463B473037A36770325A25F401B32E6A1195D559B73EE53EA4C59BB402C21EC3CBD91B225CD059A1A9D32BBC720FD7549F0E2369F4093121A0B079D3BE5E4248EA97677FF16AFDC6193F898ADD87CD603965FDFF5887DBE29DC8E4F7CC6A7D90C9CB090AFDFCDE260CBFBE773FAAD9B59A5E8338FB918498BB333DA1877A7D1343B5903353F7AC8B79980FB062BE0D8F4DE892C647A47B1FCC058C3FA1539928854A86B7B48CA66A61B8AB79F3F4D8925D9E7472A10D3500509904A9D014ACE9B987AF34E4A893F7A87E1E52E90A0A20CD5D9D07522A4E1E6FA5D2A5A7E333D8084068A26E3758C132C111DFBA3061AA3CDBB4D0D563A3073815A1FF99FFCE701CA90747D8060C91937A3E2A0AE7781385179723EE95302C0C77C5BA2433C5B13D90B27100BAEC47C3C22C4942FF4C94543D86FB50CD6B3C551944AACB833AA76F27F6A120CB7531D6D04568CB50CB92F5EC35E8B9080E171367FD49F07B728AB210A2D1B845B92E7000000136B31C160A7BEFC143EE9A4DB1234C3BF42E11D0BE58BE77A9DBA08E9863C4737A363A15DF0D3D699365A6BAFEFDFA12DF37EC7FFE1AD91F0F0E2849F2E2207E804E244A81211B1FF8242DA525F45AAC685C0CEA936984D848376834065250025AEA126EA4C1138FD4C7AFC2C7698108A767A953EF7CDE4998FFF65B4FD4D21251F2E91411D121B6E025B6BDF6A4819AF9E094C511CD558CF65CB818A2B8EAD0936BEFDF0775C1F0A3BB67C81170C0B35F0818C747E01BF97C88FD70A93AEDE476B07BD76D4211A6650FA7485B0A985010C14A9A05776DCA4F9E8442AD1D99C57A4760342B925A0510D4132B504AB7AEB9A8FC3F43BD3CBD0EC08AAB1543C2DEAEA41FBEBAA3B18E59D5300822C2AD4ACB651FD16ADC158B9D07B3A63A6045AC0C3A9BDF37B3DF5A6186DC7EA543FFE995B6FC1C0BDEC121326A10AB866B15E9E59F8EEC40E13AEFA8B455C6093C2D0FB919BE4E3CC70A484A26DE71F11264D3E1BFD153C744EEAFFF8AFE6B08C9FF11363CCFDDA59A690A9CAB0743505A1AA99397AE4410EEA5718F003C846332F1976067224574C716A07B896668902E9701D431BF91B3E3A8CDE1E1E6680538E2BB6845D2405E7319D1731D48A40BA818BD42B1D00BA0AC6914D4E80DE31766C32C03DA53DBD20F8E773F84A79829919CA1B7FCB7CDDAD1F1710B23D19897F30E1752BF9A805A657D0CF762FE3174D450C8090A4442CAE229919C80E39550E32FF9741FD02CFF3C012E77722125D3252E144F5F6AE985324254A1582FF06F5EE565815CD0C9B820593B481C8CE3BB1CA01E51E35E8664FAF0A13ABA483B8E478DE4DD04F3CFB8A180CDC04BD2367037038CB027E146485EBE2E425AD9DC60AF5DFC1746E60265546C0B1B0513B5D047C3320A69619548D1F5143592034260B0A1777DA81C080637F4FE2DDFB94CDBFD1C09344FF72A661474721B0EEC6B52E812A8F81613EC4088F28B95F3B0D94F419AE0627121F05337872642EECE6529AE7DB49AB9A38A62AC80DD2241FA61F1A80536D75F519CA0F0145E77B9C682C47EA9D88B5CA609C1E915C37B667099FC61A15E7109DFBF24BF067271B2025C6473AB268E385CF103B0149F41F2141F312F531E4"); + + LmsPublicKeyParameters pup = LmsPublicKeyParameters.GetInstance(lmsPub); + + ValidateSig(pup, sig, msg); + } + + [Test] + public void Should_verify_shake_n32_w1() + { + byte[] lmsPub = Hex.Decode( + "0000001100000009747BBE12A6157879FF4FD22FEBCDB557661035CE843BBFD05D1D83E97F9635762D6E274278F02129E3E6E2E012859F1E"); + byte[] msg = Hex.Decode("DDABE5B8C14869E4B912CFABE87998D626B47E608D6250946CD6D5815DB5F38DC4D7F77BFF6C12D02C86CC9A9B6F33EAF76E025F90BA3CD527E425D318966A86AC6A1A1C648F4428130F1AC937777992AAEE17C73BA0F8DEF45C0B449525DF474F04121501DDD80F353EFF899830313229BAF9F8DDBA8ABF801CFA3C9E7A6713"); + byte[] sig = Hex.Decode("0000311A0000000952DD96FC53E40A9F4BC7EA5BF637C097A57384F0663BE885CDEFF9CA8AF258370F09B7C85CDF4773452AF60343D02F51CAF05FA06F83D77B4031D32CC6D05E2CDA6AADA444B1C58ABFD9AF25B60124DD2FF7B051AEE9212DB3BA3B65E9960FA49E5B01BCCAEB576D28CDFCEFBA6EF152546485E2E242BCF3C12624B22AB28185E7091B6C0DFDBFD2C3D356A785975414312300D1E6291A603C5DC29C1B25EEF149B6DA75C3D20633441DD010BEE918F039A2BD56EB4A7C1A8E002D8FA9C6C4FD1090040E3399477ACDA67ADAB1A8A4D32C6D19A1C2BBE75D093994CDAEA3252F6079CC1FCC3A6DC94178B58A31235EE52E4C825B07C82C8EEB1FCE218988B889E77017C4BD6F292A6566F5CBD8F00A1156666AA55A93852C478436E7B6C8F1DCCE8414F584F2051DAAF2841D0FAE0AB1285F1163E0C1BD143373321081231B22D4AC1F94247F490A2D1C72336F267DCDB4314D3DFAAFC4C19C83C19F6976475BA2B1D6A415B63D6E02383F8E207DB024DB10729FC9C73E512BDFD7B62D07D05DE4B153535E8B66C4AC114C03819EA39EB6C93E682044B1C60ED732A1ED8661438A6568A9D8B3D7CC0E0FDCA2D960EC223FEB2FB20479C75A5CF282BA7A722FFBC297582267AE65BC1FF14929F8CA77FB068B97DC258A798D3FC69464FB808E258D39E9F027E503D89A7A9DA7346E8DF695177A8567603E37458A77344C56D6897E4900BD17769E3869DB2C68819527C43B6813B10AC024F72811E154F9A8688F00AB549597BCC15AE5B050409C72168197974FB18A77AD8BAFD9983FBD3428EED40F0641914800431548DDBABBDC0FAB12D826EF8025410A0849A1591CC823B44AEFA11DF73C8670274A31DB11EB975229CED2AA6CB18F0B5B0FF6DF2AA3D064186FD75E93871C3385899FB93B989C59D4161BFBACD249AB5ABB1311FDD9337C0A278162B1A042538970DED88B56659079AD332BAD360AD4FDAF895F399EDED428374918B6A028A03E6FA91ED86A3D1282CB85A9CF8C038D9EB094854C919468F374EE3C3156A2469643F5E1A5A53D1F168FC225116944AAADDA902DDF74EAC2583B1C7A3C584B9BA95FF37244BDF429D1EED00F6E6474704E6AF36E4FB52A43A9B8EC4D2633BE686F1B2C1E3697EA4C4316380513DDFA2F6426D0E485F70AEE39208F3C685F9DDB7DCD059944FCC83D2A55FE59933412F65B816CC1618C584EEC8024254093E20AE97820894D64CB5F78BC2A29B8F1FCDECC8EC1F612029AD465C36BE0872EA94E07A654FB11226F79BD86297195ADD70E93EF3D44B4E132BE5A4A0C6E14BA5518408A549420E8FFC6970E72E22D7953241755D556D7C61C03065E58E89183367CBA98C9080A872BA948A7386FB2647A2272D09B84ED3E84873A4A4452A824D044DDDA74BAD4A14E19BA4F95EC606B291729AF057470E4A1C1F23DFDD8F436BE595B52052A649879C557CCA7A912FCDF6CC19A75DCA05463F98D581F726C60FFD9334FEBAFD512C846C171211069F865E7107E2F144FEC970F2AC7D729A347DAE279BF2B0AB2EB1D614509F3DC409DF59F4F070F24329358B3E9DA8D55E88A9C993507FF7E6547271F6D745846C3683176B066D6B5601774793C1998B5364A7AED6DEE1D10A6B640E09C93470B8F224859D66A922D6B43E044C02E547F511025E0379D2E7CEE8DA593F31C5DA0581B4F81271474DA124E8B890A68068BF4D166C29C04E756C98E0795B41736531B298786918B28DC972D6200FF53E2675EBBCE85C6B2196BAE7B12E18E724DA75262149C86329A3AB9F2E9811D5A521B010A0038EBCF67D146C36A8C19B3EA75FAC90FF0253E767954460BD533A2A9FC11FF253B8AC90D1CDA3E423B8A0779FFD3C99F8E48387E2D4DF4F58F0A39B64D176D1A728D08B8ACDE7FB95D8E5DFAF3C75BA4592C9397CF4F3DB4BC0C89B762CDE36E8F2E3B2BDE9C61625847949C93748CDC74D9D0EECBF0CE74820025ED6E5BEA0F1AD859A1AEB8A46772368B73DABE5D85280D7451788CEF8A8B2245AF36BFC07866BDDF43300443BDCCB81BB295082764F928B4B9F616BDE74EF47122C21285BE6B0FB7B118E66A8367677F981284B493506B258E230150EA6A4252AEF37BE771F9A3678EB56AA7DE787A2CBC7FFA3B221ED1AA3165CABCF39ADBD91BC199A226F02819DB44ECA17D2EC6F74BBD08DE84E27BB0DA0CE5FB838B955B16B9EF4FB9224F558FED474368DE479D1209AA029A8D5296571C863B6C6D1D0ED91F4C4F96A7C6FBD8E4BF00AAC214D7266655FE0092BACDFB8B755D44C74DA32E04B4ED3DA8466A4F28679BF0AC6F520F45836C4D4CD9922F170E98C999049816C6172F98BBB8505F663D6708D221783F02257ADC2EBFBDC4EB8F6E9333991BF6145AB4F4AAD1F3A276C975F183FB4C0A718F01C00197E4EDBD2EEBB9846DAAFCA14234917775CAE6401B8429E51004CEBF0F82E160DBA8C71065DE77AE34ADA1FABACC2882A9B6B07AFBB233E27620508F89C39304878F9386754801364E34BAEE0C84F61E403145898B46D8CCB46A83739381CE192C2BE23F56671AFAFEEA57B41AC213FCCC17768A5B2E2E670A16645F2E75B49140E49A1FA453BF7366A88F7A54ACBC6E8B700787327F631097DB7897B2D01E30D1764043B5ECC1B9782DFCCCF27AD9ABC6A1A7C34A6F101C2FAD29751F55676D9B0779BF96490CB89ADAF6C1DFD09E25C545B8867449C0D77B13DE71C83EAA64C3B62D03100F88344C6DE26269911052FF9AC458E2BCB14CC0D5791CA7136EF7FC5F49A321CBD71D414AD05E2F74F8C6A31B84148F793522265BBBBC818E2866929111B57918750CB09241B57438857472E3ECDD8FB00AEA0BCDE629B08AA2468D01D7A41ADF4257CED61E0D883D5E647B2261A9842BEE976EC14D8C9A5C94549287264A87C5BBE62B4AB1DEDB2056C662515FA59F95F1F73899A4D2BB25D6E05D85A299AB2859E04B4843F2BDE850025578429D4D3CCE37239AFD0346DC808FC14C0E82D14693F83D7CE6C1637B79652820A26A0E06B681166778C8FDE79F752F6C49897C8A5F35C3204E5F413843111D1FC56494B78C95E60A8B18E3353DD6BB0BBE3FEEB4A8A2365F40D24045C02B7579343F8AD58F96FAE17C6E4F0258923A94177E439B6AE1A1AD6A4FA7B3845CF5379345312A9421FC54EB14E94DE4E03F1CEEAFEEB91D276C04C35B09F267899D063D4A92C52A8E7F4A50CD3013937316DB538AA9D1FFBDCF10734563B44E0FEE8A763E1A8BDE960B2DE2FF2A872B87523C416058B6986E905710493DE4DD4AA382E36CCC3C7649E9498F410975A89F92FE543D8C7B4DEB6F91C76EA825D003DDCB9AD7CC5E4FA798271574A0AD8D0B8AC94819AC62D0DF98A80EE926270CB84305DAC94E8BA106BFAF9CF7C38E8C876842D9F8993C31A87B664DE7096608B9B31989C57743FFFEAC484F74BE49C7813D4D6175CA01B416D099A70EC05D1FE4EFDE10FC5ED3DE79ED22885C248A06C7B67DC6EC01AB7BF550000F2CD52AEE34D824BFE9A96B12379363F9B557E28495EA979012A8B93B08DBA296BEAF8FC69523B52D2CE640B867D1E9C01B8E3E99D3325EA6AFB5F255436ABCA27DFC0AE7B25FA2ACD5977523682EDBDB98A80536D4C222CBE2EE1CE48A2B49E7BDFB0E7E9C2FBFD2EE789A3C0CD8EDB5A27CB8C1E6E569AC70C062A4F4718A93FFC9E1F7A60CFA209C48D304F5B21B74B36CB8E4011E4EA813305A0908DE1F5B6E5970AE81C055DC75E43564A0EB0A9C0791311CD67DF4DA1C14F7F322E45CA1FC5A3BC6F92260349EA1F2D9DC35BCC1F3444DBFC4BBE54EC7ACA6E7C82661FF60BC47131CC368B06B96C87A132CD9F2C56ADDDA673D41238C114EFF765B2AAB915EE26F4240A76004C14248119C0097282245AB410ED3180A6BE3EC851B77150497AD8E478FE8BAF8C3218DCEFBEA141784F93B361A7039CB1AFA305537E41D7232181D5D3D83A357C0C9987E6134E19C3C155791A35969881C77218BF721B39CB98601ADD773DA256A32BAF29CF6E30CC11129FF7848F19EA43ABDCBCAEBD0441460E7EA0BBF6D1DEF00935A0B305749137DA8E7774CA30A54997832693589E6ACCF461F1CEDD5C3648738F87F12B70D79A90FD2BD8125ADD14D58B56BBB06498B4F77FCEA60CCD53A113873010920ED8459CC91F19304E50693EE63F114B23E6057B67AA87F11C326CD2DEF9B77213B69A7B5A32F5A3D5325DA18E66250EA70A5D0B345561FF8C13A3989D56AC676DCDD950EE9E6A2050C9E78B95DCE80D3D4EF383F3DFD3CF01A876C6EAE02012B43990E21BBC03ADDDC64D9D665AC8C9B3407D00C6BF6A0AC558C261D18F715B53EE02719A2C458CEF92D7A8F81E090365A9D593427BFFE4259E6BED46E7A3C45840865C300A23D3BBC787975852A79D248299888587AEBFAE09D8FC873D9708C7C37A577C5B45FE46D2A37D3E2AB6155BF23E7079F39D4B94A8644C8157505BFF98A9C6045ED689FB0A36205E8F1C49DAE5CC7E403299EC0D8921D12EAEA703A7FBCC4A990775C08B674394B0073DDD0C1347FFBC8E2FA9C0F9E321E6E78CE5BF4BF513A4972AC87D691389D08621DB73FE7BE858674840B0CEF7E541C611729EB9F91C012803B9CFFBE96E9F198D7090F5CBE3796AE3414F31D899F3A18800E8DBDCBDB1C4F06713CE80F142515B9DA59B42180015DF7DBB17AE1FAD0E97784F604A46D49459A3050AC4659C6142FD226CCFC0B96733FD31958EF70DAE54815E5D7E694BE995483BE57F37093834A963434142978D6A87A50C1384F4C5B39D7FF1A6B7B13A41CC88F47FD1A12E66163FA7411C6A18F3BF64D6D567CF0AA4608CCFAFDFC6252D899B78908BBFDC6F6F37B0FBBAB8D0FC8A0D04BD23BA3AFAB03789963743958A5CBEA0A82DD7148ED9F99B08640ADE7C00E243B7B367A818A6252CF9FE5A7C4D7CAC2BE34712F72EEE09069CBE283599A98FC314C7C66803280AB8DF1D06D20A6181285BF705FE5E64F10B1A747A810BEB16D1143A0BAD75509DF0FC84C6463B8E04B23436DDC747096047D1430C5276FEF15489D4ED30BB46E220A73142B3F479A403A356D87966022C30D2D274AB6FA9D3613663EDD00B0297F3EEBE2BF9AA05D37F53107BF9780F3A52BEA488D87E3621CBD8A2184E4F158AF83EC9354F502D46424093A1F043E6464320DB1A2801EF3EBC77D8B209D2E6C003FB4184B6D07F6986A201447024F844DBB1A1B0088F0944FCD9F917C6C3898B0585ABCA5318B4FAC2D567EE22E6EF576D05F837AF6FCAD2ED5D9EE8828501A1EF28B66F1C5EBB93A74827E25D1DE81CE764D269FBA0C4D4142960090671BB08E2A4CF6FA7902F54DEEA604555049B820B40DDDE914D38DA6C02C788A0C32C83D0060A84B77E5772B2F34418B5D45C55624E76AAC4A72A22EBCCB1D70BFBC180F6E69BE4E4B235EB476194012A0ACCB368D8933D43AE072D7D3B6C5C69722FDEF93D1579B63B5E97BB1EE8E532B6B12265B76AC97B095B26F197281199B75EE4A42D13B2E4326D6A133D0E2D31A213C2A227AFF7EF1A125F7B109766E08AEDDCD95CE76C4F9581B908D7AD61C13EB231F1E4373823FF8804A87472EE3255C513E48BFDCFD84643CA35427A01C50BCCE3DBF92D1B386641FC53AA3088ADB972D9A8284769B3AF6DF45157C4FAC869C3C2F9FB41539CE08A23B3FDD5C638329A9FF3CCFBF97CF31F3752F1DA4F8AE6F68777338ABA4DD74F6D0E2B41CC3F7196D7B42AB2B576D400363EC73F92F491D7F5341BB0ADAE4F28003C1FC889D2CDCA1E5B737DC701FFEFFDCF9D758411D610DBC994BD29F271140BB2F10A70CE373EE2E2C507F3F6A3B7AF9FE7B9A9788E3FF00D3B61BED24C5CCBC05FC7E168D418076F535162ADAFB071D4A03FA390108C3195163853620BBE389ABCF478DC9D3BF016F8DA6FDC8A42A41C68EBA40909FBE4B299F2CA3BB92E5A3BF8F6A333E90754F6B1B986053E82CB84635D02A49BC7F906D607CCBAAB8C00B7D00DDC5AB798CC5B2AC548789C55F5E8A11E36D82BBEA46D89703582263B0E51D7BE93C395C74680C7F91937050FB36F91EB0ED6BC69D1766A6FF6B4F63D3ABE81757F4662657B0C6FD5D6055656E9BC5860A1BCE1EA8516B92A1C91A30F17A0098399912113913DB3E0D8DB210B4A849383BAC10908BB34A8C4CFC389EC9B554373CACCC0BDBF6EADBB98F8E234B56DBB915CB05F7BFE12CAA308037B14421FC78146BB7D0A431F1C48D6464D693969CAB900E556DB22600058A3C7D892488389E95F6EAF47A2149271ADB0B2F899BFDF7D8AD90D255BC16ABA7A89981784C832ADF17846E80BE03D91AD04E435C22AE837DB91B138A17D3A2848F43DB72329E2426C6AEF8464B70D076CF33E1F2491BCAC5A73130A92D470A27EADFC3D27B9BA7BE3C41BA19A22C221FC5406639CC17CF4774B72EC3966981645C0465B8BB642213748D9D8577312570B1B6E6330F0008EF7B9EF14181B021A7E4D6F2F7C3C851B575202E95A1EB629BB445C862BD3D8B685AAC120B68702B185C40EF4C6D476808457BDFB2D9240FC68EE394A5C7BDDF2D6F94A712CD23B1244D264C951B1F255DBCDB73824A090848DF23F6BF81C09D08B22C39D2F5E5D0DDF25DE1AEF8BF582AF8B63C336A94951222F9BCBA5F6378A5DCF3A0EDF099182F139B78D9B4FF36FADA90A6B81ECCB1433145C8B2EE134C40D8A62EE5D93581ED4357EB438911984222710C73E00E0EDFCC4496C3C5AD9B8108C739919B47B0DA2190729061BD231968755F9FA20E9592328A1D38D6E163F90674EE5DDDE399047DFD8D9DEDE9C8B4C400F679715F94E280A82E656120A7C07397BD45699B592B47BB46E5900DBF960E9CD2FDBF7E42EC03132ADBC4ABE84B5AB5B99C25A970680DFCE0EB5D80E9CFD322B3ACEB482E9E4E41E94D115E32FA59DB80DE5DE5F7A0961EBE503E910D04C8348CA0643B0CBCDF3EDE408521ECC0E3C51D2F7A585B7D0C932219CE10DB7D379B7ADA72E140FE2FB874A2B8A9F3AE9DCDA72997EA250D89AE01A1D068C3CF45999C41A64B9BCA2580E2E908861AFE20B8E2EAEEB681B999198947AA1A8C40868B768ACB27667309C2D8EB23FEA15BB913F578E7E68DD46902BBED322D6522C636A34BAEF8D1A90F276F2E9731B8FE52EC138A77B1419AD55E76EB7582772C0F52E92D626FF35C2C1DCFDF2D06097A1D11AFB229D6243FEBDDFDD8661A8C0099E57FED15872B03C64DADE274BFF9BB0FFFBCFFE39DCBCF4907765424904FDB45AE1685C661DDE3105D9B654601945D1B203593A244C96104E23FA67A348C55A47D0D29BD3834212692AAB1D202C806D27F2212A5A8B0A70A9C69FB3F484BA018BE45029ED18F6147A0DB6DD6222C227A6E65055805075EFD205DE6E6C9381F4C8B27AC3ECD2E6824CB173964294AEADC29A50B82C72ABF8E20A8EAA722FD60A68DA2BCB97DD3AA64942A1D36057BD66768FFE73DC004A09D8410C94CBE4DEFC198F8D7727B5486A87FB4B8C349AED75184EA870B878A1977612CCA52BFF2C3D1D4BB2F362118DA2F0E2069434EC1809145B878A5F8FC908A3D5C26BD9B7721FC94CF20BD9003F4226CB3A1E082C4B6D313AE3EF5864FEE44C37AF446C12BE60003EA632A581FCDD92ED6E4B71A3E0660F35C2708FFE81A1D87EA58A66CCFED6D1EEAEE92233404C55B94BF53E1B14AF54C9685B2746569BC6916E526589710881FA533DBF303055224909F547CCBB2B0CB084F07DB6BDB5CE21B3E4AB8F898C1EF41349C3EFFBBA1E06DBF90CA1F6A8C52403DC8752FDA0E394E9CA917FBF6C93B81174DD8724105D44C3DE8A6ECEDD821CD21A126311B94D20AEFF994DD606D37DC9FF109C130037CAA06EDC7E9575D22E0DC773566196FE6D939868BAF108827CDD2751EC6C79ABDAF9B13DBE8457ECFFD1DBFB8417E6051E1284244EFD8FA8674EFEBC872A6A5EE2F953CB2B4CC79871A0CC35A8128BF9297C8DE3DE2EB340001239660E4B4283B9A731F00339ADC3487DC9D6663D9FCD14A3F51874A20F06EDD43A2F51DEB1EC3FEBA2A2EAD966394179E5DFD052F368C30D6A624E127BC8D669E04A2755E6FA23FE63F89EC6CF429978FA798AEA92F11D26438A744D9295AB5C5893EEA9F4581C5F6D2F714E849D73F2CD28B97CF0E65D30FA3BE012819DFFE0ED03FD0E7585E04E6FC26D351A32D78CE6C73A7F6B8DD3749D466C64A9A6DECDB6F2CC8E08A036814369D8EB62093038CA2071542664F2FD404D3708E4986148D83A220757A561101BAC4291A7709F2FFFF3F58BDD204F52CF99BE94BF785DA56D14A2DAFF3034C8B100804AB3F770404C61EE3B94D5E1A0E777FEE2335838A16FFDA4864EFE6E7279C8843A741C72C14D22B6DBCC25E98BDFEADCCC7644FD6A0013DEDCCF2A41FA662D35AFF87B2A9A6E520A273F789EA078972833901B7AB992B5B15E5CEE61B8A3DE3FC6889212EA3D3D036F2E4E20C6B4BA48985286268A8A2ED1A3999A9DB45F204064609B01399E5B3ADC938D3D4A382A387BBF6EEAA9FFDC74E766A17E43E47EE3926C8EAD5890B90E8F999F53F5769F2FF70F3C8A925ACEEDD1628505BD5BBE4C5AB16B8495B1D1C94919300CBB37787630CB18F0014FD4B3C6EB861EEFFE30D49C63AE6E7126A3B4627AED08AE12989B60816A65E8BC2EFAD36C5ACCFC56FD00ED46DF05452A8B2904D215E82B54CEE9B3174628D48B89B78DB4DE765E45D43B01967D3088B80D73091BEEA082DB9B335B2E736B3F7D8EF13F7B2B6C478737B86F82150170588CD5CC1833191F0DD59634B8F931B9CD5196D8C3A2FF3F5D8B47EB0D811197757A2DD3E01C5414FD3557F9B65C683130C0E5D8ED861F78C3C46B7A531535BC7B0D63990F1B1E1B7545B278F355BD84AB17538BB09D6AD1AF260F87266178B01C55DD99A02D7FC090C81AD3C71FF350F21C7E8D47C24CBD270D256C3F5C8892C4F6E0B164DCBF9F7A8638F100BDAA74809FE8CB9739896DDD7A4A2319000575010BE77A56A6E38E56563CE5B7F84C227D5C8AF8DB7E52AA65B3D6ED1B8C0456B8A0B27CBF918703977F0B14C41A855A776067A99D07EC16BCE384F24FB0AC8C114A57557958C56C522804D350CA628B7216598261EE17826398440B0BABDB8A5D33686796CBB5D92C5F2A61C52EA4118E9C3114AE909937C80BAEE2A1615625ED6510A21F38A21C6937BF4E2A8146A834D00F0A0B92D9548E36E426673DB303EBB3014C8517E5FFC5E7C129DC85DB8295469A909A5E30A70FC005953C1EE798288457D1A44613803E7877BB1F9EE4BD655B3BACC7DBB262099439BD651864833A08F5029CD9BE1CFBEDE6D3E41E592E76BA026FA4CD452B35E26EE5F22E639414AAC8A5EB39D9F6328CE0A005D6B219A33A87035654C82DCFAE8ACCD7E8DF2DBE6F90BE0C3D92D7330D2B13A8F670FEAC799D4F363FAB14DB819A4F2DA5C1A625FA727ADBE95F584659294D0F5263DF841F844C801D88F8F3C8651F3BAB8C57BDA7A975A25ABD890148DE6FF5BA0A5FE2629BC075DC256A13FEBBA0C9141F1816C2A723328C647A27BC9C3C40FC6C3AD6DE0C0A128EB3FB4572D373A4D1FD6418B7941F4491C9E80E075D79CCF5C410CD258FB98046DC4D44B292EA522314254C2FF3B3E03646352B238D96B779E06625C9E0CE3025C43854B2DC5A9CBCD164D1891D48297AFFF53CC8A415745157C77B86FD7D18BF72D1B3782FE05B993695BFD185680915B1548F607430FA198523F679AC6054D2A1F5CD7C4288088A1E06FFA0DABCC625701BA121DF5E7B325275062016575BC57D41BB201731EF316D77BB005704E1206D61FBD3940AC6B88B856306DFAF482B7BFF58ACF2F48BA949401291E925813AE305578237657FD1272029C8EDB7B746DD97B063A748DD27B3D127F11DE4CA095BF65198783F38FF0178BD9DE2613D09BD82F40E6261B1C6E1D783C90918D8346B73653F8394DDAA4225C8194F727A3E5B5889363290499402C3D5A25782B9CD6F445C782767D07B9581BEE9BEF27772DA3A9D3F4EA979736FF8FBA7CFD316C9DB70E009275B94F6618A149F6CEAFF0043811002D8EBC02AC8CD27E666234BBEC4532CC1E56FE2CD60A3A5899CC2F705B7CEB8D1CF926AA6B22E8D6E6DC9591D335F59C452FF0E9DE1D3CE3E2059232C2B20CA082F882A7D580038F75E83717151F1B664F047E8C3D67782200B65B3C168D8EF63936670DABC4163E4C2EC4CABCBBA17BA551854A22469B1438665473819F5FD513AE1609234CB32738E13F2505B12B6445BE0A2702BCB0C83D3EB6B6132AB99F1E5D85883141BC7A728CADF4DCA75EE10DA77B7E76098DF23A94A14263253548C9B52636A5B450C6B768A4323A11B1BA1D9B5AFCAE2C2F16F4CD12F93A3E699216A0DA59C5A786A060B13A8D6979E1AD0E3C677B4FC7B9A95C064ABD49B774259DB60327324CA794626EB78028834173B9DDC44CC8436B96872F3360DF52694E144510F6E6DE8802A8388BE71959824F9D1E4910DD8262DA9F2DB2164603544C7360A9D34CEE8CEC1DC0CCCBA0DCD04B5AA34A025BE887657C0FAF54D5B816CE727EE6C580569B5575DAAF097BC001A0AA8576BD79B501E86F8D76478DFBE049833F13B9BC582B190929D7235F1C6F4006F98F95DCC6ADE891A975B3DCE565906949CCCA81C7C349F1A836726775E2180E61288D072C4B241677224490EA6C4EB198F47FCB8A5033945B917DB7A2DDD78030BF20A61454B7D34762E7F1CC605A1F110B7E3B71AB51368979383E63BA15019F588BCC9A6CA4CF31560B183630FB3C72307792349FF9854C50BC5496D04C83968739150CFEDE73BA1D14270B435D3279EC44DEA35A54EBBE8E510A96600E6E7C716C520E36A6BE39F4AE6E2CB3E1E308B3850BE07FCEC9D6194F736C4E76DB331FF635780F3F01CE22483ED9AA4306EC02DC5D93173E25FCAC18F22A9F7D561EDB2F991157D5B3955AC1B8BF9C9711D16EE270BD1C1063C13A97225F7183F9E79DF127D95B57FF7AAA1806BEF90A69A2098E4380FF41B9F49BEBEF163843499FD7A7E1C11448E2AF7FCFC75EE12FE7B0E52FF564B3D933A30A4CF2B6F61D429E26486A486A2216867EC27C1E09B0E7B91601D8CF8D9AEEF9C21611704A03EE133E889900B5B844B14684FAF3C1B0CA90BD29BDA01AE2ADA810ECBFFBA4E049E57FAD602F52E44CDA8DB83BB2EDE93013CA0B93F8A96F2F10E86EA2B97544A9FBD4BC552905239F68862CF0BB1DB58F2DB72494843AA6E02CCB511C8033E3E0C81CC5AAD37B6ABF50A6FFF01479F62CE75EFE8B10CFF11198C796466D5E88F82DD34AF6B7B053E57CF434D343EF797A8ADA19FD54CBEA5269C4CC98A4BA08A84F0F3FD99C9EFECC52C809DDA5C8D7EAF07E3F04F522C1E391F66095B0BECE865B687457608287271354EB93AA7696735A953874E6ACA9FDA6A0FB6CB09F2047862076FF26F585CF7A153E4B7D0B8C44F5090D6B306DC353E527EF3D640415D0D26F66597576DF563930D2D56292A79D407E180D6CAE2D09BAF5399827161BA7709F94838B2F2A54E3F609070CFBD07F125ED3FCCAFF31055E7E5D490D71F1A7933BD1DE69F7A4FB90F55D3BB5D24D65D93F139E460707FCBEFF7BC576AD569425EDA6DBB9C5447BBDA584D6E917429EA97753D77FC5617002AA5AD59199EAC87D6BCD014BABA2AA4C540F04CFF6688E55D73CFE243866EA513B708640704667F5D57F07197DFB543A846B57BE678A981231CC31307D9757BC74424A7384965C0206A6EA6D38C51708BDFE14AADF9098F5A70453687D7B34C63BA049D6C76F7D8BF27A4BC1ADE1E69FE8A3A4C0940546164C779E5CC8A87C1846172E995F63CD13000E911659553887D1A1D30970FB8BC4CB945E62F15640C4FCD17C6816E3EFBFAA6597583B6E5438AE90AF1FDEFF8006F233F15BBAB11C4A7798826B92EBF413921BDD5A6AA6A4955515488D52640942321700000011C0FA3E14FEAE4083E11A1F1CF9D2838E320F200E7BB540EBEEA4112178CF61D30C30AC84ABBCF974D68351778A9237C5046B257A6DD13912889EE7A319348592AB8BF53033D9861F701275E75F6D4D131DCC07B103544E20803A7668E9A73D8462645460DD7591F7C176EA2854A90BC6D00BE11D4929703C1A3358EAEB7A8963198DE33243AF991589AAC9697677EA08466CC44186CBAC009254051E00F117DDC6045C8D47D351D50B8CA60B75B9E1B06F30102F1941C22FA02C93E818A7BA4299A663B730FB9A66F331B394CDABFDF38294CA7B6E9A18E8BC146BEE4811C3E621C544BF9DC211663D23B883D93AD0A35FA6EB13985111F7DDE724934BD8DAFD16EC1B94136592E8ED70F0D5853059D89657630EBBC6C4A31E7BCB995B9B70D558FCFEF9E0DD455E0641180470B7C568410FD5D2377252E975F738B9E0DCE7059B6718741793FABD2601E0F4711F4FBAA3E0C24B214FD0A9F828851D427901BA5959C1E1454766995EA5D57A474A0DEA12D0D8B4837B435A503DEE193E0E295E0442DAB2AA09A4F97DB027E64777167B7BEABEB9F57A9791581935131BBEB3B877B4B8CF761CDBF46A7FA5315E86D5C6C6AADE19645229E639C8CEFDE810A8FD1B9D12C90379E42E0F406E64784D9146C175C3B630847664AB7D311348F9E1BA"); + + LmsPublicKeyParameters pup = LmsPublicKeyParameters.GetInstance(lmsPub); + + ValidateSig(pup, sig, msg); + } + + private void ValidateSig(LmsPublicKeyParameters lmsPub, byte[] sig, byte[] msg) + { + LmsSigner lmsSigner = new LmsSigner(); + + lmsSigner.Init(false, lmsPub); + + Assert.IsTrue(lmsSigner.VerifySignature(msg, sig)); + } } } \ No newline at end of file -- cgit 1.4.1