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