From d5116b38e5b8a0425d31e5fe711866515bc91af6 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 1 May 2023 17:50:05 +0700 Subject: Update Pqc.Crypto.Crystals from bc-java - add PqcOtherInfoGenerator and supporting code --- crypto/src/asn1/bc/BCObjectIdentifiers.cs | 12 +- crypto/src/crypto/util/DerOtherInfo.cs | 90 +++++++++ crypto/src/crypto/util/DerUtilities.cs | 21 +++ crypto/src/pqc/asn1/CmcePrivateKey.cs | 15 +- crypto/src/pqc/asn1/KyberPrivateKey.cs | 115 ++++++++++++ crypto/src/pqc/crypto/IKemParameters.cs | 9 + .../dilithium/DilithiumPrivateKeyParameters.cs | 64 +++---- .../dilithium/DilithiumPublicKeyParameters.cs | 26 ++- .../crypto/crystals/dilithium/DilithiumSigner.cs | 7 +- .../pqc/crypto/crystals/kyber/KyberParameters.cs | 2 +- .../crystals/kyber/KyberPrivateKeyParameters.cs | 28 +-- .../crystals/kyber/KyberPublicKeyParameters.cs | 20 +- crypto/src/pqc/crypto/ntru/NtruParameters.cs | 2 +- .../src/pqc/crypto/utils/PqcOtherInfoGenerator.cs | 203 +++++++++++++++++++++ .../src/pqc/crypto/utils/PqcPrivateKeyFactory.cs | 62 +++---- .../pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs | 18 +- .../crypto/utils/PqcSubjectPublicKeyInfoFactory.cs | 4 +- .../src/pqc/crypto/test/CrystalsDilithiumTest.cs | 29 ++- .../test/src/pqc/crypto/test/CrystalsKyberTest.cs | 128 ++++++++++++- crypto/test/src/pqc/crypto/test/FalconTest.cs | 19 +- 20 files changed, 707 insertions(+), 167 deletions(-) create mode 100644 crypto/src/crypto/util/DerOtherInfo.cs create mode 100644 crypto/src/crypto/util/DerUtilities.cs create mode 100644 crypto/src/pqc/asn1/KyberPrivateKey.cs create mode 100644 crypto/src/pqc/crypto/IKemParameters.cs create mode 100644 crypto/src/pqc/crypto/utils/PqcOtherInfoGenerator.cs diff --git a/crypto/src/asn1/bc/BCObjectIdentifiers.cs b/crypto/src/asn1/bc/BCObjectIdentifiers.cs index face6f8d3..675350ccf 100644 --- a/crypto/src/asn1/bc/BCObjectIdentifiers.cs +++ b/crypto/src/asn1/bc/BCObjectIdentifiers.cs @@ -232,12 +232,12 @@ namespace Org.BouncyCastle.Asn1.BC */ public static readonly DerObjectIdentifier pqc_kem_kyber = bc_kem.Branch("6"); - public static readonly DerObjectIdentifier kyber512 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.8.2.2"); // pqc_kem_kyber.Branch("1"); - public static readonly DerObjectIdentifier kyber768 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.8.3.3"); // pqc_kem_kyber.Branch("2"); - public static readonly DerObjectIdentifier kyber1024 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.8.4.4"); // pqc_kem_kyber.Branch("3"); - public static readonly DerObjectIdentifier kyber512_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.10.2.2"); // pqc_kem_kyber.Branch("4"); - public static readonly DerObjectIdentifier kyber768_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.10.3.3"); // pqc_kem_kyber.Branch("5"); - public static readonly DerObjectIdentifier kyber1024_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.10.4.4"); // pqc_kem_kyber.Branch("6"); + public static readonly DerObjectIdentifier kyber512 = pqc_kem_kyber.Branch("1"); + public static readonly DerObjectIdentifier kyber768 = pqc_kem_kyber.Branch("2"); + public static readonly DerObjectIdentifier kyber1024 = pqc_kem_kyber.Branch("3"); + public static readonly DerObjectIdentifier kyber512_aes = pqc_kem_kyber.Branch("4"); + public static readonly DerObjectIdentifier kyber768_aes = pqc_kem_kyber.Branch("5"); + public static readonly DerObjectIdentifier kyber1024_aes = pqc_kem_kyber.Branch("6"); /** * BIKE diff --git a/crypto/src/crypto/util/DerOtherInfo.cs b/crypto/src/crypto/util/DerOtherInfo.cs new file mode 100644 index 000000000..158e5aca0 --- /dev/null +++ b/crypto/src/crypto/util/DerOtherInfo.cs @@ -0,0 +1,90 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Crypto.Utilities +{ + /** + * Builder and holder class for preparing SP 800-56A compliant OtherInfo. The data is ultimately encoded as a DER SEQUENCE. + * Empty octet strings are used to represent nulls in compulsory fields. + */ + public sealed class DerOtherInfo + { + /** + * Builder to create OtherInfo + */ + public sealed class Builder + { + private readonly AlgorithmIdentifier m_algorithmID; + private readonly Asn1OctetString m_partyUInfo; + private readonly Asn1OctetString m_partyVInfo; + + private Asn1TaggedObject m_suppPubInfo; + private Asn1TaggedObject m_suppPrivInfo; + + /** + * Create a basic builder with just the compulsory fields. + * + * @param algorithmID the algorithm associated with this invocation of the KDF. + * @param partyUInfo sender party info. + * @param partyVInfo receiver party info. + */ + public Builder(AlgorithmIdentifier algorithmID, byte[] partyUInfo, byte[] partyVInfo) + { + m_algorithmID = algorithmID; + m_partyUInfo = DerUtilities.GetOctetString(partyUInfo); + m_partyVInfo = DerUtilities.GetOctetString(partyVInfo); + } + + /** + * Add optional supplementary public info (DER tagged, implicit, 0). + * + * @param suppPubInfo supplementary public info. + * @return the current builder instance. + */ + public Builder WithSuppPubInfo(byte[] suppPubInfo) + { + m_suppPubInfo = new DerTaggedObject(false, 0, DerUtilities.GetOctetString(suppPubInfo)); + return this; + } + + /** + * Add optional supplementary private info (DER tagged, implicit, 1). + * + * @param suppPrivInfo supplementary private info. + * @return the current builder instance. + */ + public Builder WithSuppPrivInfo(byte[] suppPrivInfo) + { + m_suppPrivInfo = new DerTaggedObject(false, 1, DerUtilities.GetOctetString(suppPrivInfo)); + return this; + } + + /** + * Build the KTSOtherInfo. + * + * @return an KTSOtherInfo containing the data. + */ + public DerOtherInfo Build() + { + Asn1EncodableVector v = new Asn1EncodableVector(5); + + v.Add(m_algorithmID); + v.Add(m_partyUInfo); + v.Add(m_partyVInfo); + v.AddOptional(m_suppPubInfo); + v.AddOptional(m_suppPrivInfo); + + return new DerOtherInfo(new DerSequence(v)); + } + } + + private readonly DerSequence m_sequence; + + private DerOtherInfo(DerSequence sequence) + { + m_sequence = sequence; + } + + public byte[] GetEncoded() => m_sequence.GetEncoded(); + } +} diff --git a/crypto/src/crypto/util/DerUtilities.cs b/crypto/src/crypto/util/DerUtilities.cs new file mode 100644 index 000000000..9334e644a --- /dev/null +++ b/crypto/src/crypto/util/DerUtilities.cs @@ -0,0 +1,21 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Crypto.Utilities +{ + internal class DerUtilities + { + internal static Asn1OctetString GetOctetString(byte[] data) + { + byte[] contents = data == null ? Array.Empty() : (byte[])data.Clone(); + + return new DerOctetString(contents); + } + + internal static byte[] ToByteArray(Asn1Object asn1Object) + { + return asn1Object.GetEncoded(); + } + } +} diff --git a/crypto/src/pqc/asn1/CmcePrivateKey.cs b/crypto/src/pqc/asn1/CmcePrivateKey.cs index 042325d1d..44b52ca56 100644 --- a/crypto/src/pqc/asn1/CmcePrivateKey.cs +++ b/crypto/src/pqc/asn1/CmcePrivateKey.cs @@ -42,11 +42,10 @@ namespace Org.BouncyCastle.Pqc.Asn1 public CmcePrivateKey(int version, byte[] delta, byte[] c, byte[] g, byte[] alpha, byte[] s, CmcePublicKey pubKey = null) { - this.version = version; if (version != 0) - { - throw new Exception("unrecognized version"); - } + throw new Exception("unrecognized version"); + + this.version = version; this.delta = Arrays.Clone(delta); this.c = Arrays.Clone(c); this.g = Arrays.Clone(g); @@ -57,11 +56,10 @@ namespace Org.BouncyCastle.Pqc.Asn1 private CmcePrivateKey(Asn1Sequence seq) { - version = DerInteger.GetInstance(seq[0]).Value.IntValue; + version = DerInteger.GetInstance(seq[0]).IntValueExact; if (version != 0) - { throw new Exception("unrecognized version"); - } + delta = Arrays.Clone(Asn1OctetString.GetInstance(seq[1]).GetOctets()); c = Arrays.Clone(Asn1OctetString.GetInstance(seq[2]).GetOctets()); @@ -72,8 +70,7 @@ namespace Org.BouncyCastle.Pqc.Asn1 s = Arrays.Clone(Asn1OctetString.GetInstance(seq[5]).GetOctets()); - // todo optional publickey - if(seq.Count == 7) + if (seq.Count == 7) { publicKey = CmcePublicKey.GetInstance(seq[6]); } diff --git a/crypto/src/pqc/asn1/KyberPrivateKey.cs b/crypto/src/pqc/asn1/KyberPrivateKey.cs new file mode 100644 index 000000000..ee71e4f97 --- /dev/null +++ b/crypto/src/pqc/asn1/KyberPrivateKey.cs @@ -0,0 +1,115 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Pqc.Asn1 +{ + /** + * Crystal Kyber Private Key Format. + * See https://www.ietf.org/archive/id/draft-uni-qsckeys-kyber-00.html for details. + *
+     *        KyberPrivateKey ::= SEQUENCE {
+     *        version     INTEGER {v0(0)}   -- version (round 3)
+     *        s           OCTET STRING,     -- EMPTY
+     *        hpk         OCTET STRING      -- EMPTY
+     *        nonce       OCTET STRING,     -- d
+     *        publicKey   [0] IMPLICIT KyberPublicKey OPTIONAL,
+     *                                      -- see next section
+     *        }
+     *    
+ */ + public sealed class KyberPrivateKey + : Asn1Encodable + { + public static KyberPrivateKey GetInstance(object obj) + { + if (obj == null) + return null; + if (obj is KyberPrivateKey kyberPublicKey) + return kyberPublicKey; + return new KyberPrivateKey(Asn1Sequence.GetInstance(obj)); + } + + public static KyberPrivateKey GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(taggedObject, declaredExplicit)); + } + + private int version; + private byte[] s; +#pragma warning disable CS0618 // Type or member is obsolete + private KyberPublicKey publicKey; +#pragma warning restore CS0618 // Type or member is obsolete + private byte[] hpk; + private byte[] nonce; + +#pragma warning disable CS0618 // Type or member is obsolete + public KyberPrivateKey(int version, byte[] s, byte[] hpk, byte[] nonce, KyberPublicKey publicKey) + { + this.version = version; + this.s = s; + this.publicKey = publicKey; + this.hpk = hpk; + this.nonce = nonce; + } +#pragma warning restore CS0618 // Type or member is obsolete + + public KyberPrivateKey(int version, byte[] s, byte[] hpk, byte[] nonce) + : this(version, s, hpk, nonce, null) + { + } + + private KyberPrivateKey(Asn1Sequence seq) + { + version = DerInteger.GetInstance(seq[0]).IntValueExact; + if (version != 0) + throw new ArgumentException("unrecognized version"); + + s = Arrays.Clone(Asn1OctetString.GetInstance(seq[1]).GetOctets()); + + int skipPubKey = 1; + if (seq.Count == 5) + { + skipPubKey = 0; +#pragma warning disable CS0618 // Type or member is obsolete + publicKey = KyberPublicKey.GetInstance(seq[2]); +#pragma warning restore CS0618 // Type or member is obsolete + } + + hpk = Arrays.Clone(Asn1OctetString.GetInstance(seq[3 - skipPubKey]).GetOctets()); + + nonce = Arrays.Clone(Asn1OctetString.GetInstance(seq[4 - skipPubKey]).GetOctets()); + } + + public int Version => version; + + public byte[] GetS() => Arrays.Clone(s); + +#pragma warning disable CS0618 // Type or member is obsolete + public KyberPublicKey PublicKey => publicKey; +#pragma warning restore CS0618 // Type or member is obsolete + + public byte[] GetHpk() => Arrays.Clone(hpk); + + public byte[] GetNonce() => Arrays.Clone(nonce); + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(5); + + v.Add(new DerInteger(version)); + v.Add(new DerOctetString(s)); + if (publicKey != null) + { +#pragma warning disable CS0618 // Type or member is obsolete + v.Add(new KyberPublicKey(publicKey.T, publicKey.Rho)); +#pragma warning restore CS0618 // Type or member is obsolete + } + v.Add(new DerOctetString(hpk)); + v.Add(new DerOctetString(nonce)); + + return new DerSequence(v); + } + } +} diff --git a/crypto/src/pqc/crypto/IKemParameters.cs b/crypto/src/pqc/crypto/IKemParameters.cs new file mode 100644 index 000000000..9852946ba --- /dev/null +++ b/crypto/src/pqc/crypto/IKemParameters.cs @@ -0,0 +1,9 @@ +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Pqc.Crypto +{ + public interface IKemParameters + : ICipherParameters + { + } +} diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs index b5f3c2a32..66a518c93 100644 --- a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs +++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs @@ -5,45 +5,47 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium public sealed class DilithiumPrivateKeyParameters : DilithiumKeyParameters { - internal byte[] rho; - internal byte[] k; - internal byte[] tr; - internal byte[] s1; - internal byte[] s2; - internal byte[] t0; - - private byte[] t1; - - public DilithiumPrivateKeyParameters(DilithiumParameters parameters, byte[] rho, byte[] K, byte[] tr, byte[] s1, byte[] s2, byte[] t0, byte[] t1) + internal byte[] m_rho; + internal byte[] m_k; + internal byte[] m_tr; + internal byte[] m_s1; + internal byte[] m_s2; + internal byte[] m_t0; + + private byte[] m_t1; + + public DilithiumPrivateKeyParameters(DilithiumParameters parameters, byte[] rho, byte[] K, byte[] tr, + byte[] s1, byte[] s2, byte[] t0, byte[] t1) : base(true, parameters) { - this.rho = Arrays.Clone(rho); - this.k = Arrays.Clone(K); - this.tr = Arrays.Clone(tr); - this.s1 = Arrays.Clone(s1); - this.s2 = Arrays.Clone(s2); - this.t0 = Arrays.Clone(t0); - this.t1 = Arrays.Clone(t1); + m_rho = Arrays.Clone(rho); + m_k = Arrays.Clone(K); + m_tr = Arrays.Clone(tr); + m_s1 = Arrays.Clone(s1); + m_s2 = Arrays.Clone(s2); + m_t0 = Arrays.Clone(t0); + m_t1 = Arrays.Clone(t1); } - - public byte[] Rho => Arrays.Clone(rho); - public byte[] K => Arrays.Clone(k); + public byte[] GetEncoded() => Arrays.ConcatenateAll(m_rho, m_k, m_tr, m_s1, m_s2, m_t0); - public byte[] Tr => Arrays.Clone(tr); + public byte[] K => Arrays.Clone(m_k); - public byte[] S1 => Arrays.Clone(s1); + public byte[] GetPublicKey() => DilithiumPublicKeyParameters.GetEncoded(m_rho, m_t1); - public byte[] S2 => Arrays.Clone(s2); - + public DilithiumPublicKeyParameters GetPublicKeyParameters() => + new DilithiumPublicKeyParameters(Parameters, m_rho, m_t1); - public byte[] T0 => Arrays.Clone(t0); + public byte[] Rho => Arrays.Clone(m_rho); - public byte[] T1 => t1; + public byte[] S1 => Arrays.Clone(m_s1); - public byte[] GetEncoded() - { - return Arrays.ConcatenateAll(rho, k, tr, s1, s2, t0); - } + public byte[] S2 => Arrays.Clone(m_s2); + + public byte[] T0 => Arrays.Clone(m_t0); + + public byte[] T1 => Arrays.Clone(m_t1); + + public byte[] Tr => Arrays.Clone(m_tr); } -} \ No newline at end of file +} diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs index 681435439..d47c4804e 100644 --- a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs +++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs @@ -5,31 +5,29 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium public sealed class DilithiumPublicKeyParameters : DilithiumKeyParameters { - internal byte[] rho; - internal byte[] t1; + internal static byte[] GetEncoded(byte[] rho, byte[] t1) => Arrays.Concatenate(rho, t1); - public DilithiumPublicKeyParameters(DilithiumParameters parameters, byte[] pkEncoded) - : base(false, parameters) - { - this.rho = Arrays.CopyOfRange(pkEncoded, 0, DilithiumEngine.SeedBytes); - this.t1 = Arrays.CopyOfRange(pkEncoded, DilithiumEngine.SeedBytes, pkEncoded.Length); - } + internal readonly byte[] m_rho; + internal readonly byte[] m_t1; public DilithiumPublicKeyParameters(DilithiumParameters parameters, byte[] rho, byte[] t1) : base(false, parameters) { - this.rho = Arrays.Clone(rho); - this.t1 = Arrays.Clone(t1); + m_rho = Arrays.Clone(rho); + m_t1 = Arrays.Clone(t1); } - public byte[] GetEncoded() + public DilithiumPublicKeyParameters(DilithiumParameters parameters, byte[] pkEncoded) + : base(false, parameters) { - return Arrays.Concatenate(rho, t1); + m_rho = Arrays.CopyOfRange(pkEncoded, 0, DilithiumEngine.SeedBytes); + m_t1 = Arrays.CopyOfRange(pkEncoded, DilithiumEngine.SeedBytes, pkEncoded.Length); } - internal byte[] Rho => rho; + public byte[] GetEncoded() => GetEncoded(m_rho, m_t1); - internal byte[] T1 => t1; + public byte[] GetRho() => Arrays.Clone(m_rho); + public byte[] GetT1() => Arrays.Clone(m_t1); } } diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumSigner.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumSigner.cs index d60c24222..7611f3032 100644 --- a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumSigner.cs +++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumSigner.cs @@ -42,14 +42,15 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium { DilithiumEngine engine = privKey.Parameters.GetEngine(random); byte[] sig = new byte[engine.CryptoBytes]; - engine.Sign(sig, sig.Length, message, message.Length, privKey.rho, privKey.k, privKey.tr, privKey.t0, privKey.s1, privKey.s2); + engine.Sign(sig, sig.Length, message, message.Length, privKey.m_rho, privKey.m_k, privKey.m_tr, + privKey.m_t0, privKey.m_s1, privKey.m_s2); return sig; } public bool VerifySignature(byte[] message, byte[] signature) { DilithiumEngine engine = pubKey.Parameters.GetEngine(random); - return engine.SignOpen(message,signature, signature.Length, pubKey.rho, pubKey.t1 ); + return engine.SignOpen(message,signature, signature.Length, pubKey.m_rho, pubKey.m_t1); } } -} \ No newline at end of file +} diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberParameters.cs index 5b3f71000..195831433 100644 --- a/crypto/src/pqc/crypto/crystals/kyber/KyberParameters.cs +++ b/crypto/src/pqc/crypto/crystals/kyber/KyberParameters.cs @@ -3,7 +3,7 @@ using Org.BouncyCastle.Crypto; namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber { public sealed class KyberParameters - : ICipherParameters + : IKemParameters { public static KyberParameters kyber512 = new KyberParameters("kyber512", 2, 128, false); public static KyberParameters kyber768 = new KyberParameters("kyber768", 3, 192, false); diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs index 0fac30e47..08b4fbe86 100644 --- a/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs +++ b/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs @@ -11,7 +11,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber private readonly byte[] m_t; private readonly byte[] m_rho; - public KyberPrivateKeyParameters(KyberParameters parameters, byte[] s, byte[] hpk, byte[] nonce, byte[] t, byte[] rho) + public KyberPrivateKeyParameters(KyberParameters parameters, byte[] s, byte[] hpk, byte[] nonce, byte[] t, + byte[] rho) : base(true, parameters) { m_s = Arrays.Clone(s); @@ -21,16 +22,21 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber m_rho = Arrays.Clone(rho); } - public byte[] GetEncoded() - { - return Arrays.ConcatenateAll(m_s, m_t, m_rho, m_hpk, m_nonce); - } + public byte[] GetEncoded() => Arrays.ConcatenateAll(m_s, m_t, m_rho, m_hpk, m_nonce); + + public byte[] GetHpk() => Arrays.Clone(m_hpk); + + public byte[] GetNonce() => Arrays.Clone(m_nonce); + + public byte[] GetPublicKey() => KyberPublicKeyParameters.GetEncoded(m_t, m_rho); + + public KyberPublicKeyParameters GetPublicKeyParameters() => + new KyberPublicKeyParameters(Parameters, m_t, m_rho); + + public byte[] GetRho() => Arrays.Clone(m_rho); - internal byte[] S => m_s; - internal byte[] Hpk => m_hpk; - internal byte[] Nonce => m_nonce; - internal byte[] T => m_t; - internal byte[] Rho => m_rho; + public byte[] GetS() => Arrays.Clone(m_s); + public byte[] GetT() => Arrays.Clone(m_t); } -} \ No newline at end of file +} diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs index efc582abc..dadfe8498 100644 --- a/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs +++ b/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs @@ -5,12 +5,16 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber public sealed class KyberPublicKeyParameters : KyberKeyParameters { + internal static byte[] GetEncoded(byte[] t, byte[] rho) => Arrays.Concatenate(t, rho); + private readonly byte[] m_t; private readonly byte[] m_rho; - public byte[] GetEncoded() + public KyberPublicKeyParameters(KyberParameters parameters, byte[] t, byte[] rho) + : base(false, parameters) { - return Arrays.Concatenate(m_t, m_rho); + m_t = Arrays.Clone(t); + m_rho = Arrays.Clone(rho); } public KyberPublicKeyParameters(KyberParameters parameters, byte[] encoding) @@ -20,15 +24,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber m_rho = Arrays.CopyOfRange(encoding, encoding.Length - KyberEngine.SymBytes, encoding.Length); } - public KyberPublicKeyParameters(KyberParameters parameters, byte[] t, byte[] rho) - : base(false, parameters) - { - m_t = Arrays.Clone(t); - m_rho = Arrays.Clone(rho); - } + public byte[] GetEncoded() => GetEncoded(m_t, m_rho); + + public byte[] GetRho() => Arrays.Clone(m_rho); - internal byte[] T => m_t; - internal byte[] Rho => m_rho; + public byte[] GetT() => Arrays.Clone(m_t); } } \ No newline at end of file diff --git a/crypto/src/pqc/crypto/ntru/NtruParameters.cs b/crypto/src/pqc/crypto/ntru/NtruParameters.cs index 934cc21cb..0c5158f5f 100644 --- a/crypto/src/pqc/crypto/ntru/NtruParameters.cs +++ b/crypto/src/pqc/crypto/ntru/NtruParameters.cs @@ -4,7 +4,7 @@ using Org.BouncyCastle.Pqc.Crypto.Ntru.ParameterSets; namespace Org.BouncyCastle.Pqc.Crypto.Ntru { public sealed class NtruParameters - : ICipherParameters + : IKemParameters { public static readonly NtruParameters NtruHps2048509 = new NtruParameters("ntruhps2048509", new NtruHps2048509()); diff --git a/crypto/src/pqc/crypto/utils/PqcOtherInfoGenerator.cs b/crypto/src/pqc/crypto/utils/PqcOtherInfoGenerator.cs new file mode 100644 index 000000000..0d3af4a51 --- /dev/null +++ b/crypto/src/pqc/crypto/utils/PqcOtherInfoGenerator.cs @@ -0,0 +1,203 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; +using Org.BouncyCastle.Pqc.Crypto.Ntru; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Pqc.Crypto.Utilities +{ + /** + * OtherInfo Generator for which can be used for populating the SuppPrivInfo field used to provide shared + * secret data used with NIST SP 800-56A agreement algorithms. + */ + public abstract class PqcOtherInfoGenerator + { + protected readonly DerOtherInfo.Builder m_otherInfoBuilder; + protected readonly SecureRandom m_random; + + protected bool m_used = false; + + /** + * Create a basic builder with just the compulsory fields. + * + * @param algorithmID the algorithm associated with this invocation of the KDF. + * @param partyUInfo sender party info. + * @param partyVInfo receiver party info. + * @param random a source of randomness. + */ + internal PqcOtherInfoGenerator(AlgorithmIdentifier algorithmID, byte[] partyUInfo, byte[] partyVInfo, + SecureRandom random) + { + m_otherInfoBuilder = new DerOtherInfo.Builder(algorithmID, partyUInfo, partyVInfo); + m_random = random; + } + + /** + * Party U (initiator) generation. + */ + public sealed class PartyU + : PqcOtherInfoGenerator + { + private AsymmetricCipherKeyPair m_aKp; + private IEncapsulatedSecretExtractor m_encSE; + + /** + * Create a basic builder with just the compulsory fields for the initiator. + * + * @param kemParams the key type parameters for populating the private info field. + * @param algorithmID the algorithm associated with this invocation of the KDF. + * @param partyUInfo sender party info. + * @param partyVInfo receiver party info. + * @param random a source of randomness. + */ + public PartyU(IKemParameters kemParams, AlgorithmIdentifier algorithmID, byte[] partyUInfo, + byte[] partyVInfo, SecureRandom random) + : base(algorithmID, partyUInfo, partyVInfo, random) + { + if (kemParams is KyberParameters kyberParameters) + { + KyberKeyPairGenerator kpg = new KyberKeyPairGenerator(); + kpg.Init(new KyberKeyGenerationParameters(random, kyberParameters)); + + m_aKp = kpg.GenerateKeyPair(); + + m_encSE = new KyberKemExtractor((KyberPrivateKeyParameters)m_aKp.Private); + } + else if (kemParams is NtruParameters ntruParameters) + { + NtruKeyPairGenerator kpg = new NtruKeyPairGenerator(); + kpg.Init(new NtruKeyGenerationParameters(random, ntruParameters)); + + m_aKp = kpg.GenerateKeyPair(); + + m_encSE = new NtruKemExtractor((NtruPrivateKeyParameters)m_aKp.Private); + } + else + { + throw new ArgumentException("unknown IKemParameters"); + } + } + + /** + * Add optional supplementary public info (DER tagged, implicit, 0). + * + * @param suppPubInfo supplementary public info. + * @return the current builder instance. + */ + public PqcOtherInfoGenerator WithSuppPubInfo(byte[] suppPubInfo) + { + m_otherInfoBuilder.WithSuppPubInfo(suppPubInfo); + return this; + } + + public byte[] GetSuppPrivInfoPartA() + { + return GetEncoded(m_aKp.Public); + } + + public DerOtherInfo Generate(byte[] suppPrivInfoPartB) + { + m_otherInfoBuilder.WithSuppPrivInfo(m_encSE.ExtractSecret(suppPrivInfoPartB)); + + return m_otherInfoBuilder.Build(); + } + } + + /** + * Party V (responder) generation. + */ + public sealed class PartyV + : PqcOtherInfoGenerator + { + private IEncapsulatedSecretGenerator m_encSG; + + /** + * Create a basic builder with just the compulsory fields for the responder. + * + * @param kemParams the key type parameters for populating the private info field. + * @param algorithmID the algorithm associated with this invocation of the KDF. + * @param partyUInfo sender party info. + * @param partyVInfo receiver party info. + * @param random a source of randomness. + */ + public PartyV(IKemParameters kemParams, AlgorithmIdentifier algorithmID, byte[] partyUInfo, + byte[] partyVInfo, SecureRandom random) + : base(algorithmID, partyUInfo, partyVInfo, random) + { + if (kemParams is KyberParameters) + { + m_encSG = new KyberKemGenerator(random); + } + else if (kemParams is NtruParameters) + { + m_encSG = new NtruKemGenerator(random); + } + else + { + throw new ArgumentException("unknown IKemParameters"); + } + } + + /** + * Add optional supplementary public info (DER tagged, implicit, 0). + * + * @param suppPubInfo supplementary public info. + * @return the current builder instance. + */ + public PqcOtherInfoGenerator WithSuppPubInfo(byte[] suppPubInfo) + { + m_otherInfoBuilder.WithSuppPubInfo(suppPubInfo); + return this; + } + + public byte[] GetSuppPrivInfoPartB(byte[] suppPrivInfoPartA) + { + m_used = false; + + try + { + ISecretWithEncapsulation bEp = m_encSG.GenerateEncapsulated(GetPublicKey(suppPrivInfoPartA)); + + m_otherInfoBuilder.WithSuppPrivInfo(bEp.GetSecret()); + + return bEp.GetEncapsulation(); + } + catch (IOException e) + { + throw new ArgumentException("cannot decode public key", e); + } + } + + public DerOtherInfo Generate() + { + if (m_used) + throw new InvalidOperationException("builder already used"); + + m_used = true; + + return m_otherInfoBuilder.Build(); + } + } + + private static byte[] GetEncoded(AsymmetricKeyParameter pubKey) + { + try + { + return PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey).GetEncoded(); + } + catch (IOException) + { + return null; + } + } + + private static AsymmetricKeyParameter GetPublicKey(byte[] enc) + { + return PqcPublicKeyFactory.CreateKey(enc); + } + } +} diff --git a/crypto/src/pqc/crypto/utils/PqcPrivateKeyFactory.cs b/crypto/src/pqc/crypto/utils/PqcPrivateKeyFactory.cs index 01566c8b0..1deb2c8d6 100644 --- a/crypto/src/pqc/crypto/utils/PqcPrivateKeyFactory.cs +++ b/crypto/src/pqc/crypto/utils/PqcPrivateKeyFactory.cs @@ -134,55 +134,35 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities return new HqcPrivateKeyParameters(hqcParams, keyEnc); } - if (algOID.Equals(BCObjectIdentifiers.kyber512) - || algOID.Equals(BCObjectIdentifiers.kyber512_aes) - || algOID.Equals(BCObjectIdentifiers.kyber768) - || algOID.Equals(BCObjectIdentifiers.kyber768_aes) - || algOID.Equals(BCObjectIdentifiers.kyber1024) - || algOID.Equals(BCObjectIdentifiers.kyber1024_aes)) + if (algOID.On(BCObjectIdentifiers.pqc_kem_kyber)) { - Asn1Sequence keyEnc = Asn1Sequence.GetInstance(keyInfo.ParsePrivateKey()); - - KyberParameters spParams = PqcUtilities.KyberParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); + KyberPrivateKey kyberKey = KyberPrivateKey.GetInstance(keyInfo.ParsePrivateKey()); + KyberParameters kyberParams = PqcUtilities.KyberParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); - int version = DerInteger.GetInstance(keyEnc[0]).Value.IntValue; - if (version != 0) - { - throw new IOException("unknown private key version: " + version); - } - - if (keyInfo.PublicKeyData != null) - { - Asn1Sequence pubKey = Asn1Sequence.GetInstance(keyInfo.PublicKeyData.GetOctets()); - return new KyberPrivateKeyParameters(spParams, - Asn1OctetString.GetInstance(keyEnc[1]).GetDerEncoded(), - Asn1OctetString.GetInstance(keyEnc[2]).GetOctets(), - Asn1OctetString.GetInstance(keyEnc[3]).GetOctets(), - Asn1OctetString.GetInstance(pubKey[0]).GetOctets(), // t - Asn1OctetString.GetInstance(pubKey[1]).GetOctets()); // rho - } - else +#pragma warning disable CS0618 // Type or member is obsolete + KyberPublicKey pubKey = kyberKey.PublicKey; +#pragma warning restore CS0618 // Type or member is obsolete + if (pubKey != null) { - return new KyberPrivateKeyParameters(spParams, - Asn1OctetString.GetInstance(keyEnc[1]).GetOctets(), - Asn1OctetString.GetInstance(keyEnc[2]).GetOctets(), - Asn1OctetString.GetInstance(keyEnc[3]).GetOctets(), - null, - null); + return new KyberPrivateKeyParameters(kyberParams, kyberKey.GetS(), kyberKey.GetHpk(), + kyberKey.GetNonce(), pubKey.T, pubKey.Rho); } + return new KyberPrivateKeyParameters(kyberParams, kyberKey.GetS(), kyberKey.GetHpk(), + kyberKey.GetNonce(), null, null); } - if (algOID.Equals(BCObjectIdentifiers.dilithium2) - || algOID.Equals(BCObjectIdentifiers.dilithium3) - || algOID.Equals(BCObjectIdentifiers.dilithium5) - || algOID.Equals(BCObjectIdentifiers.dilithium2_aes) - || algOID.Equals(BCObjectIdentifiers.dilithium3_aes) - || algOID.Equals(BCObjectIdentifiers.dilithium5_aes)) + if (algOID.Equals(BCObjectIdentifiers.dilithium2) || + algOID.Equals(BCObjectIdentifiers.dilithium3) || + algOID.Equals(BCObjectIdentifiers.dilithium5) || + algOID.Equals(BCObjectIdentifiers.dilithium2_aes) || + algOID.Equals(BCObjectIdentifiers.dilithium3_aes) || + algOID.Equals(BCObjectIdentifiers.dilithium5_aes)) { Asn1Sequence keyEnc = Asn1Sequence.GetInstance(keyInfo.ParsePrivateKey()); - DilithiumParameters spParams = PqcUtilities.DilithiumParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); + DilithiumParameters spParams = PqcUtilities.DilithiumParamsLookup( + keyInfo.PrivateKeyAlgorithm.Algorithm); - int version = DerInteger.GetInstance(keyEnc[0]).Value.IntValue; + int version = DerInteger.GetInstance(keyEnc[0]).IntValueExact; if (version != 0) throw new IOException("unknown private key version: " + version); @@ -216,7 +196,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities FalconParameters spParams = PqcUtilities.FalconParamsLookup(keyInfo.PrivateKeyAlgorithm.Algorithm); DerBitString publicKeyData = keyInfo.PublicKeyData; - int version = DerInteger.GetInstance(keyEnc[0]).Value.IntValue; + int version = DerInteger.GetInstance(keyEnc[0]).IntValueExact; if (version != 1) throw new IOException("unknown private key version: " + version); diff --git a/crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs index 6f7bed2ca..3e2832713 100644 --- a/crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs +++ b/crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs @@ -129,21 +129,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities } if (privateKey is KyberPrivateKeyParameters kyberPrivateKeyParameters) { - Asn1EncodableVector v = new Asn1EncodableVector(4); - v.Add(new DerInteger(0)); - v.Add(new DerOctetString(kyberPrivateKeyParameters.S)); - v.Add(new DerOctetString(kyberPrivateKeyParameters.Hpk)); - v.Add(new DerOctetString(kyberPrivateKeyParameters.Nonce)); - AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier( PqcUtilities.KyberOidLookup(kyberPrivateKeyParameters.Parameters)); - Asn1EncodableVector vPub = new Asn1EncodableVector(2); - vPub.Add(new DerOctetString(kyberPrivateKeyParameters.T)); - vPub.Add(new DerOctetString(kyberPrivateKeyParameters.Rho)); +#pragma warning disable CS0618 // Type or member is obsolete + KyberPublicKey kyberPub = new KyberPublicKey(kyberPrivateKeyParameters.GetT(), + kyberPrivateKeyParameters.GetRho()); +#pragma warning restore CS0618 // Type or member is obsolete + KyberPrivateKey kyberPriv = new KyberPrivateKey(0, kyberPrivateKeyParameters.GetS(), + kyberPrivateKeyParameters.GetHpk(), kyberPrivateKeyParameters.GetNonce(), kyberPub); - return new PrivateKeyInfo(algorithmIdentifier, new DerSequence(v), attributes, - new DerSequence(vPub).GetEncoded()); + return new PrivateKeyInfo(algorithmIdentifier, kyberPriv, attributes); } if (privateKey is DilithiumPrivateKeyParameters dilithiumPrivateKeyParameters) { diff --git a/crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs b/crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs index 1bdf031f6..946aca963 100644 --- a/crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs +++ b/crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs @@ -132,8 +132,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Utilities { AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier( PqcUtilities.DilithiumOidLookup(dilithiumPublicKeyParameters.Parameters)); - - return new SubjectPublicKeyInfo(algorithmIdentifier, Arrays.Concatenate(dilithiumPublicKeyParameters.Rho, dilithiumPublicKeyParameters.T1)); + + return new SubjectPublicKeyInfo(algorithmIdentifier, dilithiumPublicKeyParameters.GetEncoded()); } if (publicKey is BikePublicKeyParameters bikePublicKeyParameters) { diff --git a/crypto/test/src/pqc/crypto/test/CrystalsDilithiumTest.cs b/crypto/test/src/pqc/crypto/test/CrystalsDilithiumTest.cs index 10ac3db5c..bb6da3ffb 100644 --- a/crypto/test/src/pqc/crypto/test/CrystalsDilithiumTest.cs +++ b/crypto/test/src/pqc/crypto/test/CrystalsDilithiumTest.cs @@ -67,13 +67,15 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests DilithiumKeyGenerationParameters kparam = new DilithiumKeyGenerationParameters(random, DilithiumParameters.Dilithium2); DilithiumKeyPairGenerator kpg = new DilithiumKeyPairGenerator(); kpg.Init(kparam); - AsymmetricCipherKeyPair ackp = kpg.GenerateKeyPair(); + AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair(); - AsymmetricKeyParameter pub = ackp.Public; - AsymmetricKeyParameter priv = ackp.Private; + AsymmetricKeyParameter pub = kp.Public; + AsymmetricKeyParameter priv = kp.Private; - AsymmetricKeyParameter pubDec = PqcPublicKeyFactory.CreateKey(PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub)); - AsymmetricKeyParameter privDec = PqcPrivateKeyFactory.CreateKey(PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(priv)); + AsymmetricKeyParameter pubDec = PqcPublicKeyFactory.CreateKey( + PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub)); + AsymmetricKeyParameter privDec = PqcPrivateKeyFactory.CreateKey( + PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(priv)); Assert.AreEqual(((DilithiumPublicKeyParameters)pub).GetEncoded(), ((DilithiumPublicKeyParameters)pubDec).GetEncoded()); Assert.AreEqual(((DilithiumPrivateKeyParameters)priv).GetEncoded(), ((DilithiumPrivateKeyParameters)privDec).GetEncoded()); @@ -126,17 +128,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests // Generate keys and test. // kpGen.Init(genParams); - AsymmetricCipherKeyPair ackp = kpGen.GenerateKeyPair(); - - - DilithiumPublicKeyParameters pubParams = (DilithiumPublicKeyParameters)ackp.Public; - DilithiumPrivateKeyParameters privParams = (DilithiumPrivateKeyParameters)ackp.Private; - - //Console.WriteLine(string.Format("{0} Expected pk = {1}", pk.Length, Convert.ToHexString(pk))); - //Console.WriteLine(String.Format("{0} Actual Public key = {1}", pubParams.GetEncoded().Length, Convert.ToHexString(pubParams.GetEncoded()))); + AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair(); - pubParams = (DilithiumPublicKeyParameters)PqcPublicKeyFactory.CreateKey(PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(ackp.Public)); - privParams = (DilithiumPrivateKeyParameters)PqcPrivateKeyFactory.CreateKey(PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(ackp.Private)); + DilithiumPublicKeyParameters pubParams = (DilithiumPublicKeyParameters)PqcPublicKeyFactory.CreateKey( + PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo((DilithiumPublicKeyParameters)kp.Public)); + DilithiumPrivateKeyParameters privParams = (DilithiumPrivateKeyParameters)PqcPrivateKeyFactory.CreateKey( + PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo((DilithiumPrivateKeyParameters)kp.Private)); Assert.True(Arrays.AreEqual(pk, pubParams.GetEncoded()), name + " " + count + ": public key"); Assert.True(Arrays.AreEqual(sk, privParams.GetEncoded()), name + " " + count + ": secret key"); @@ -145,7 +142,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests // Signature test // DilithiumSigner signer = new DilithiumSigner(); - DilithiumPrivateKeyParameters skparam = (DilithiumPrivateKeyParameters)ackp.Private; + DilithiumPrivateKeyParameters skparam = (DilithiumPrivateKeyParameters)kp.Private; signer.Init(true, skparam); byte[] sigGenerated = signer.GenerateSignature(msg); diff --git a/crypto/test/src/pqc/crypto/test/CrystalsKyberTest.cs b/crypto/test/src/pqc/crypto/test/CrystalsKyberTest.cs index 44ad1cbe9..0c4a78022 100644 --- a/crypto/test/src/pqc/crypto/test/CrystalsKyberTest.cs +++ b/crypto/test/src/pqc/crypto/test/CrystalsKyberTest.cs @@ -3,8 +3,13 @@ using System.IO; using NUnit.Framework; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber; +using Org.BouncyCastle.Pqc.Crypto.Utilities; +using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.Utilities.Test; @@ -38,6 +43,120 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests "kyber1024aes.rsp", }; + [Test] + public void TestKyber() + { + /* + count = 0 + seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 + pk = D22302CBD3399FACC630991FC8F28BDB4354762541527678BCF61F65C241146C426D23B9BFAA6B7DF18C97F20C1B6125BF874B1D89475852C448215DB0EB7737F91480E8CEBD9A0871574F5AB62D9020175EC6927CA0B54C09818E42CF92A383172422C7DC1831D63B0C295DE75159DB8034E9E07F7B0B910C3C1E5FB66B3DC523F1FA6EB4910CB89A6C17562C83AB4C18D0CD7E0796592A372AA409B1C557347CCACDC4644A119064D06DD474929D1C6FB4D686E5491CE4BC89A30BB4B8C41BCE5157DFC1360823B1AB618C14B10F98C25067398EA7018C278A4B3DF31334D603B2044EF187CD9BC6CE42725BD962C264983E9E18155A8B9C47143D70460A26A56FE7658C1F150348C6087EF758AD167887860A007A5FC37358D43B5EBEE820ACEA474F0AC07B76802866199C61231D5C747C93774D2C1E0C1C67E6C81B82752173E125BAF39B4FD19A4F453DC57976B1D97FE6996992BBB65B7CB25D077BBAA6A13322899AF659CF1B3558C1B5001154B625809ED89AEEBB89E6EA7D67F723D045AB05715C42355DA6A5C8DD39C8ABE3037751A01ED1C7374919F3121B5A52C53D1487316769F80721DEEAAAD3C90F76E7AE9E12BA92B32B5FD457E3C752C2650DFB885771CB77AC3C785A8C562E6A1C63C2A55EA47CF8B90EB8225C123C346452566235B2F31823A33521E087937A345D8D663EEAA05658917BBAA008C2E335F8850A90A326D0E66432F44CEB8289E4ECB2D12958E984072ECACB88E1348FF0B55654ACBA5B54971CBAEBA88EC4B91A94C37192FA982BECB9F3DA421603B61A51BC8E36CBD053851C77B1B926B17A272AA9023246B02B3ED47F66A00BD5684823634E7CE58CF8F306E35B1E5322824D904801F0A2FA7C2BC9C252B0A56B7BA2AB0F636021745A70A9A43E2B0A8D615970B65309624B5184BCC30B911679AEDD76025FE3908FD67897B0CF4BE5A6F5413D7DD98564B23E42A93E4AA8821CD45054C643EDC1158DB6B3DEB13FB5A51EBD1A8A78B87225A7338E101104C4A220D9BDEDD48C85A1C2DAE781A80C40E13B87EAC73A764201C9B760CCFB1AE392699C7039D27C39362B27B8FC6F07A8A3D4410F1547C48A9997F62C61074452EF1515F8A649EBCA9437205A4E8A61606B41DAF6834D671F4D852C0C9C4096611648C6A3170678B1537CC1828D93580C9E5849A9653175ACB753F2BE7437BE45F6C603E485F2EC301BB42B6C37C225D7495A584AE231890AB5C8C35C268CF4BBB0213C096019319561A8A6947637AA40D006B415BB2CFA2237E0890B6A3BC134ABF8F6585E108D15940F91F4BF5B0C818055B21DEA6E63B553988C47F4B94E7CF800A493B4734705EDC56A4B6021C629500675876804CF0B951F038A5C7FE58E89774EF2992FD7C63099D352A7D21560B788B405709861817E59A96B3A3A83CBA803B16934331071905BBEC6532900155D8AC88CB32E4E21A3BD3A03FDEC325A51CD2773964E6784FCF1853737AA64EB67564727272661ABF84313A57A44B123C65509CFB7A6F6641CDCC3B57FE628C7B8192DB44FFBF5796A8613B1FA126F6076883C783DC24E2A4464C40B3A41CA70AE87620866CF4FCB2BD204BF5C283812BA056AC0C345E379C4BA24D750901279BB2F3A16F612BFADB35703332C7C136F68EAB6755C66B6A4AD1AABA7B768A58ACAACC10A459A1CC8EF29377BC200E4D315A30A6BCC3256F9734D06E9779CAA5442A9A16069081377C76E75154368072DC446ED6C8B8E622A21E383CF9BA1FB434E2ECC81E7B78CEE986B8FF798AB18CF9634543546284EDA2A26B47F05B735BCDB1202220076DC8B4E4B9F853533C8F6C7FF38817BA49712835785F17F14CA01D0C1C1E98810FE0B36E5B427157B9418449CEDD641A4293C85C32700102ACEC22EBAD98ED160A5F027BD4CDA57F1F3720A12C134654DD5E73F829676495390D0E7929D6034E9C55F7D55BA658BC587988E8AF94960F6CFB8D5AF7A0021535A6E25E437D49A780698BE22AC9953949F571B85A685725F8207A2B0AE849B601AB91B159B3DF4A154C2041E776070AFC42969322380917C97510799F3149131477E16663D3174C7C1CAEA788535C6C005A64F2868631B31B66E205FD38C1D84542D0F1B578F58C9BF5A0FAEAB6AB6494893053165EAFD465FC64A0C5F8F3F9003489415899D59A543D8208C54A3166529B53922 + sk = 07638FB69868F3D320E5862BD96933FEB311B362093C9B5D50170BCED43F1B536D9A204BB1F22695950BA1F2A9E8EB828B284488760B3FC84FABA04275D5628E39C5B2471374283C503299C0AB49B66B8BBB56A4186624F919A2BA59BB08D8551880C2BEFC4F87F25F59AB587A79C327D792D54C974A69262FF8A78938289E9A87B688B083E0595FE218B6BB1505941CE2E81A5A64C5AAC60417256985349EE47A52420A5F97477B7236AC76BC70E8288729287EE3E34A3DBC3683C0B7B10029FC203418537E7466BA6385A8FF301EE12708F82AAA1E380FC7A88F8F205AB7E88D7E95952A55BA20D09B79A47141D62BF6EB7DD307B08ECA13A5BC5F6B68581C6865B27BBCDDAB142F4B2CBFF488C8A22705FAA98A2B9EEA3530C76662335CC7EA3A00777725EBCCCD2A4636B2D9122FF3AB77123CE0883C1911115E50C9E8A94194E48DD0D09CFFB3ADCD2C1E92430903D07ADBF00532031575AA7F9E7B5A1F3362DEC936D4043C05F2476C07578BC9CBAF2AB4E382727AD41686A96B2548820BB03B32F11B2811AD62F489E951632ABA0D1DF89680CC8A8B53B481D92A68D70B4EA1C3A6A561C0692882B5CA8CC942A8D495AFCB06DE89498FB935B775908FE7A03E324D54CC19D4E1AABD3593B38B19EE1388FE492B43127E5A504253786A0D69AD32601C28E2C88504A5BA599706023A61363E17C6B9BB59BDC697452CD059451983D738CA3FD034E3F5988854CA05031DB09611498988197C6B30D258DFE26265541C89A4B31D6864E9389B03CB74F7EC4323FB9421A4B9790A26D17B0398A26767350909F84D57B6694DF830664CA8B3C3C03ED2AE67B89006868A68527CCD666459AB7F056671000C6164D3A7F266A14D97CBD7004D6C92CACA770B844A4FA9B182E7B18CA885082AC5646FCB4A14E1685FEB0C9CE3372AB95365C04FD83084F80A23FF10A05BF15F7FA5ACC6C0CB462C33CA524FA6B8BB359043BA68609EAA2536E81D08463B19653B5435BA946C9ADDEB202B04B031CC960DCC12E4518D428B32B257A4FC7313D3A7980D80082E934F9D95C32B0A0191A23604384DD9E079BBBAA266D14C3F756B9F2133107433A4E83FA7187282A809203A4FAF841851833D121AC383843A5E55BC2381425E16C7DB4CC9AB5C1B0D91A47E2B8DE0E582C86B6B0D907BB360B97F40AB5D038F6B75C814B27D9B968D419832BC8C2BEE605EF6E5059D33100D90485D378450014221736C07407CAC260408AA64926619788B8601C2A752D1A6CBF820D7C7A04716203225B3895B9342D147A8185CFC1BB65BA06B4142339903C0AC4651385B45D98A8B19D28CD6BAB088787F7EE1B12461766B43CBCCB96434427D93C065550688F6948ED1B5475A425F1B85209D061C08B56C1CC069F6C0A7C6F29358CAB911087732A649D27C9B98F9A48879387D9B00C25959A71654D6F6A946164513E47A75D005986C2363C09F6B537ECA78B9303A5FA457608A586A653A347DB04DFCC19175B3A301172536062A658A95277570C8852CA8973F4AE123A334047DD711C8927A634A03388A527B034BF7A8170FA702C1F7C23EC32D18A2374890BE9C787A9409C82D192C4BB705A2F996CE405D85A4C1A1AB9B6AEB49CCE1C2F8A97C3516C72A00A46263BAA696BF25727719C3216423618FF33380934A6C10545C4C5C5155B12486181FC7A2319873978B6A2A67490F8256BD2196FE1792A4C00077B812EAE8BED3572499684AB3371876761E450C9F9D2768A36806D7AB2046C91F17599E9AC592990808DCD7B4D0919072F14EC361773B7252444C323C308326F4A30F8680D2F748F56A132B82674ED0184620B82AD2CB182C97B481626647491290A011CC73828685A8C367A5B9CF8D621B0D5C1EFF03172758BD004978C251CD51342228989CAE6332AC486437CB5C57D4307462865253BE217B3515C73DF405B7F28217AD0B8CF60C2FFFAA0A0048B1FB4ACDCDC38B5250CFEC356A6DE26CFA7A588FDC86F98C854AC64C7BFAA96F5A32CC0610934BAA6A586B9A2054F13BA274174AA0D2B3A81B96A940666F789B5A6BCDC0A6A0178A0C9A02578A493F6EEA0D2E6C13951C9F249A5E8DD71DD49A742D451F1ABBA19AF8C547855E0AFC728E90ABB499C9BEEB766F4729CDA22263E324D22302CBD3399FACC630991FC8F28BDB4354762541527678BCF61F65C241146C426D23B9BFAA6B7DF18C97F20C1B6125BF874B1D89475852C448215DB0EB7737F91480E8CEBD9A0871574F5AB62D9020175EC6927CA0B54C09818E42CF92A383172422C7DC1831D63B0C295DE75159DB8034E9E07F7B0B910C3C1E5FB66B3DC523F1FA6EB4910CB89A6C17562C83AB4C18D0CD7E0796592A372AA409B1C557347CCACDC4644A119064D06DD474929D1C6FB4D686E5491CE4BC89A30BB4B8C41BCE5157DFC1360823B1AB618C14B10F98C25067398EA7018C278A4B3DF31334D603B2044EF187CD9BC6CE42725BD962C264983E9E18155A8B9C47143D70460A26A56FE7658C1F150348C6087EF758AD167887860A007A5FC37358D43B5EBEE820ACEA474F0AC07B76802866199C61231D5C747C93774D2C1E0C1C67E6C81B82752173E125BAF39B4FD19A4F453DC57976B1D97FE6996992BBB65B7CB25D077BBAA6A13322899AF659CF1B3558C1B5001154B625809ED89AEEBB89E6EA7D67F723D045AB05715C42355DA6A5C8DD39C8ABE3037751A01ED1C7374919F3121B5A52C53D1487316769F80721DEEAAAD3C90F76E7AE9E12BA92B32B5FD457E3C752C2650DFB885771CB77AC3C785A8C562E6A1C63C2A55EA47CF8B90EB8225C123C346452566235B2F31823A33521E087937A345D8D663EEAA05658917BBAA008C2E335F8850A90A326D0E66432F44CEB8289E4ECB2D12958E984072ECACB88E1348FF0B55654ACBA5B54971CBAEBA88EC4B91A94C37192FA982BECB9F3DA421603B61A51BC8E36CBD053851C77B1B926B17A272AA9023246B02B3ED47F66A00BD5684823634E7CE58CF8F306E35B1E5322824D904801F0A2FA7C2BC9C252B0A56B7BA2AB0F636021745A70A9A43E2B0A8D615970B65309624B5184BCC30B911679AEDD76025FE3908FD67897B0CF4BE5A6F5413D7DD98564B23E42A93E4AA8821CD45054C643EDC1158DB6B3DEB13FB5A51EBD1A8A78B87225A7338E101104C4A220D9BDEDD48C85A1C2DAE781A80C40E13B87EAC73A764201C9B760CCFB1AE392699C7039D27C39362B27B8FC6F07A8A3D4410F1547C48A9997F62C61074452EF1515F8A649EBCA9437205A4E8A61606B41DAF6834D671F4D852C0C9C4096611648C6A3170678B1537CC1828D93580C9E5849A9653175ACB753F2BE7437BE45F6C603E485F2EC301BB42B6C37C225D7495A584AE231890AB5C8C35C268CF4BBB0213C096019319561A8A6947637AA40D006B415BB2CFA2237E0890B6A3BC134ABF8F6585E108D15940F91F4BF5B0C818055B21DEA6E63B553988C47F4B94E7CF800A493B4734705EDC56A4B6021C629500675876804CF0B951F038A5C7FE58E89774EF2992FD7C63099D352A7D21560B788B405709861817E59A96B3A3A83CBA803B16934331071905BBEC6532900155D8AC88CB32E4E21A3BD3A03FDEC325A51CD2773964E6784FCF1853737AA64EB67564727272661ABF84313A57A44B123C65509CFB7A6F6641CDCC3B57FE628C7B8192DB44FFBF5796A8613B1FA126F6076883C783DC24E2A4464C40B3A41CA70AE87620866CF4FCB2BD204BF5C283812BA056AC0C345E379C4BA24D750901279BB2F3A16F612BFADB35703332C7C136F68EAB6755C66B6A4AD1AABA7B768A58ACAACC10A459A1CC8EF29377BC200E4D315A30A6BCC3256F9734D06E9779CAA5442A9A16069081377C76E75154368072DC446ED6C8B8E622A21E383CF9BA1FB434E2ECC81E7B78CEE986B8FF798AB18CF9634543546284EDA2A26B47F05B735BCDB1202220076DC8B4E4B9F853533C8F6C7FF38817BA49712835785F17F14CA01D0C1C1E98810FE0B36E5B427157B9418449CEDD641A4293C85C32700102ACEC22EBAD98ED160A5F027BD4CDA57F1F3720A12C134654DD5E73F829676495390D0E7929D6034E9C55F7D55BA658BC587988E8AF94960F6CFB8D5AF7A0021535A6E25E437D49A780698BE22AC9953949F571B85A685725F8207A2B0AE849B601AB91B159B3DF4A154C2041E776070AFC42969322380917C97510799F3149131477E16663D3174C7C1CAEA788535C6C005A64F2868631B31B66E205FD38C1D84542D0F1B578F58C9BF5A0FAEAB6AB6494893053165EAFD465FC64A0C5F8F3F9003489415899D59A543D8208C54A3166529B539228A39E87D531F3527C207EDCC1DB7FADDCF9628391879B335C707839A0DB051A88626ED79D451140800E03B59B956F8210E556067407D13DC90FA9E8B872BFB8F + ct = A6AF29D5F5B80BD130F518BADDD6C8F17545413D860FB3DE451979EBFA5E4E3112C7C0ADF99824BB526F2C3550748ED0E134F0457A7C61F9F526F002BAADC03FC13E38131219513C3EDE061661E74F603C4FCF7951C8E52C9C213B0D22D9293663D669A6B58ED8FCEFCF8249D7BB5298F55761445B2B83CE7F005CB04248AEC8BDA22FD2D42AA766322014EA038CC32C55C8E4B9E28EC9119F527341E4F66A035121073B85DE6706DA19E0838A9F33B719A68F039B664DC002659EABFC398679AA7009CE0CD01CDAFB6CD2A26FE4101672C98FF58F7C47D5BDA2906653B3A6F9651F7A121EA77EA74723FAE5B873F9BB7B664F0C8A93831EF9D51C7CC1EF44AC0E55A55CA76D137FE9B75F40509CEF156E5AD18F9FB999680008E547D55EECD5B4D1CB1D9F076CEC21501C7402509ECB77AFB2CB9A61340A8BD1514C6E71B4AA45E47EC37512271B911F8FB46C9082C9DF07204ABB5A50E6E3647A8AD4D8D5D7BFF19C8A509308BCFB895536D045CA2B97CB16A29BB7181CAD0509DDB91735028EBA8C31D74BD275EAA65B5340B3A43FBFE0B3061D6BAE7E75B7098CDABE91D4B31E36C9AA7A8298862AD63C8FD282E03B460B3AB464CE0F27B1C3D11155ACAA011EB9E2AE3E6DDA07D6F491737CBCE9B05F9BC56BE20E8D326BA132C57FB235161144519CDF40560FBE279BDE411E112531F826D6AB10D4547350ADD2A9DE8D62C2AC82CABE6815646F4DC9742BB0C2A3F77EC7B46C6B537605FA31798CD89281221A33DFB9796E644305630332C2CB931408AB481A16D953F6BEAE3891D6D9AC1FAB38222D9271872D9D0CADB91ABE9B4E265F75C6E5E829E146C3D8CE1E9D12E0D129801957F46B0D2DBE1F749B1D08E2345F6239A731342EB75B0CF1BF411749BC2CAF2810B788C6B7238B4D3DA2D6315CE9542E24404F145755A30AB851E4445841BD33F716A586884888ECC6BC6498AA32919AE81D20C26973C2BD54582A0F6AD98ABFD2627E15690A727E69F581DD2A7127982A90E33E2D4A03FE339142C7E44C326AC46ED395A225D3033389917328B45316B1585A01B2C304B2944E903ABBB3EC5619441CFC8965A446DF75DEFA80C6E15ADBD506B7AB2DE12DDA9BC81441CFC89052E2E5808F7126C6FD3AC6AC8081258A84A09AE50F6CD7CC0F4AF336FD1D643E99079996268C2D32D909F22E3504F07FBB563196D4312FDDB9335D5C1D36E8C5EEA2278DBA23B94D193C947CC41CA993DC7DB1396340AD9C4FE687DD7B8D0C7A5120AE0204F2C665BD5F473D644C7FF26BFFBA7A36980830702128A7E661D677A092A36E7428A4139FB29B0095CC11086F447D2A9EF6C9B161F189C6299E084CB7AA00FAF787797BFB069FBC087FDE26252A1664F19C5A8A22EC5EE1AEB076357B7DC37E6B0F1520F958F7851BACB92C89FD114A72FEAC54652D45B09E1AE7651ABD164BCD537D58FA39D3EC8ACDCDF98425005862FA59692DE162B77E6297C66233348408A8AB695CE2F2728DB9FBE27E958967EC5974767C5A66023074B4A71AFD264AD2890E970A1F31D6E3311B736F9F9488793DDC88F23458064254C82A1D9E59EAD2FCEC40B430687C4B7E28960926AFCACC9BD756A71088C78450E20A2E980AEDE9EBEDFE7FABD6ABFE96F934C4B02C01CA194D01B73C25D5997039D3FCD0F099521F70CAEE69110AC1FC5A99917AD752FC96ADFAD7186D0A7C9CFE5601C07514EA6448D661C57AA20242103C4276A070A489A4CB6BCA0F9ECC4379FB220215FD91F81019D5B0AE619358B52468F272C178E3A74CF6775AA924FE329C3175D9E4C3E21AB9EC836EDC3ACAB2E3891EE8DEDA515D39AF9B8DDD0EE7B0164F805C3835F6D2BABDB30EAB4756E7EC7F829ECE01E8EADFBBED12FC283B3D4C69F575E7F80417689FDFCFC7BE27EE3B8CDF57AAEBEC4A95B7E5BB585B85227F7C32BE30DB3E65E42E30DCF5A5FA073DBA399D942F2222ADB9B9898102AFE5432EDC7F04AE34A8FEC2D81CB49A9A9B43814CE71D97F726E2B1E8F64B50E65DFB4816E12E82A3197484A4E9BBA4D2D69E3F19D0B75C21E2BFFE9FC0C98CF48A3AAF08D467F72687DF0178174B7897F734349B181ECA86A598A0C5E8C25946F24DC5572BD324A40458A788E5137F3C7A7C97FC9F12A3C463A8FE9449101CCE966D7C009323932998D56EF430C73BC24F5D95F737858DDC4F32C013 + ss = B10F7394926AD3B49C5D62D5AEB531D5757538BCC0DA9E550D438F1B61BD7419 + */ + string temp = "061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"; + string expectedPubKey = "D22302CBD3399FACC630991FC8F28BDB4354762541527678BCF61F65C241146C426D23B9BFAA6B7DF18C97F20C1B6125BF874B1D89475852C448215DB0EB7737F91480E8CEBD9A0871574F5AB62D9020175EC6927CA0B54C09818E42CF92A383172422C7DC1831D63B0C295DE75159DB8034E9E07F7B0B910C3C1E5FB66B3DC523F1FA6EB4910CB89A6C17562C83AB4C18D0CD7E0796592A372AA409B1C557347CCACDC4644A119064D06DD474929D1C6FB4D686E5491CE4BC89A30BB4B8C41BCE5157DFC1360823B1AB618C14B10F98C25067398EA7018C278A4B3DF31334D603B2044EF187CD9BC6CE42725BD962C264983E9E18155A8B9C47143D70460A26A56FE7658C1F150348C6087EF758AD167887860A007A5FC37358D43B5EBEE820ACEA474F0AC07B76802866199C61231D5C747C93774D2C1E0C1C67E6C81B82752173E125BAF39B4FD19A4F453DC57976B1D97FE6996992BBB65B7CB25D077BBAA6A13322899AF659CF1B3558C1B5001154B625809ED89AEEBB89E6EA7D67F723D045AB05715C42355DA6A5C8DD39C8ABE3037751A01ED1C7374919F3121B5A52C53D1487316769F80721DEEAAAD3C90F76E7AE9E12BA92B32B5FD457E3C752C2650DFB885771CB77AC3C785A8C562E6A1C63C2A55EA47CF8B90EB8225C123C346452566235B2F31823A33521E087937A345D8D663EEAA05658917BBAA008C2E335F8850A90A326D0E66432F44CEB8289E4ECB2D12958E984072ECACB88E1348FF0B55654ACBA5B54971CBAEBA88EC4B91A94C37192FA982BECB9F3DA421603B61A51BC8E36CBD053851C77B1B926B17A272AA9023246B02B3ED47F66A00BD5684823634E7CE58CF8F306E35B1E5322824D904801F0A2FA7C2BC9C252B0A56B7BA2AB0F636021745A70A9A43E2B0A8D615970B65309624B5184BCC30B911679AEDD76025FE3908FD67897B0CF4BE5A6F5413D7DD98564B23E42A93E4AA8821CD45054C643EDC1158DB6B3DEB13FB5A51EBD1A8A78B87225A7338E101104C4A220D9BDEDD48C85A1C2DAE781A80C40E13B87EAC73A764201C9B760CCFB1AE392699C7039D27C39362B27B8FC6F07A8A3D4410F1547C48A9997F62C61074452EF1515F8A649EBCA9437205A4E8A61606B41DAF6834D671F4D852C0C9C4096611648C6A3170678B1537CC1828D93580C9E5849A9653175ACB753F2BE7437BE45F6C603E485F2EC301BB42B6C37C225D7495A584AE231890AB5C8C35C268CF4BBB0213C096019319561A8A6947637AA40D006B415BB2CFA2237E0890B6A3BC134ABF8F6585E108D15940F91F4BF5B0C818055B21DEA6E63B553988C47F4B94E7CF800A493B4734705EDC56A4B6021C629500675876804CF0B951F038A5C7FE58E89774EF2992FD7C63099D352A7D21560B788B405709861817E59A96B3A3A83CBA803B16934331071905BBEC6532900155D8AC88CB32E4E21A3BD3A03FDEC325A51CD2773964E6784FCF1853737AA64EB67564727272661ABF84313A57A44B123C65509CFB7A6F6641CDCC3B57FE628C7B8192DB44FFBF5796A8613B1FA126F6076883C783DC24E2A4464C40B3A41CA70AE87620866CF4FCB2BD204BF5C283812BA056AC0C345E379C4BA24D750901279BB2F3A16F612BFADB35703332C7C136F68EAB6755C66B6A4AD1AABA7B768A58ACAACC10A459A1CC8EF29377BC200E4D315A30A6BCC3256F9734D06E9779CAA5442A9A16069081377C76E75154368072DC446ED6C8B8E622A21E383CF9BA1FB434E2ECC81E7B78CEE986B8FF798AB18CF9634543546284EDA2A26B47F05B735BCDB1202220076DC8B4E4B9F853533C8F6C7FF38817BA49712835785F17F14CA01D0C1C1E98810FE0B36E5B427157B9418449CEDD641A4293C85C32700102ACEC22EBAD98ED160A5F027BD4CDA57F1F3720A12C134654DD5E73F829676495390D0E7929D6034E9C55F7D55BA658BC587988E8AF94960F6CFB8D5AF7A0021535A6E25E437D49A780698BE22AC9953949F571B85A685725F8207A2B0AE849B601AB91B159B3DF4A154C2041E776070AFC42969322380917C97510799F3149131477E16663D3174C7C1CAEA788535C6C005A64F2868631B31B66E205FD38C1D84542D0F1B578F58C9BF5A0FAEAB6AB6494893053165EAFD465FC64A0C5F8F3F9003489415899D59A543D8208C54A3166529B53922"; + string expectedPrivKey = "07638FB69868F3D320E5862BD96933FEB311B362093C9B5D50170BCED43F1B536D9A204BB1F22695950BA1F2A9E8EB828B284488760B3FC84FABA04275D5628E39C5B2471374283C503299C0AB49B66B8BBB56A4186624F919A2BA59BB08D8551880C2BEFC4F87F25F59AB587A79C327D792D54C974A69262FF8A78938289E9A87B688B083E0595FE218B6BB1505941CE2E81A5A64C5AAC60417256985349EE47A52420A5F97477B7236AC76BC70E8288729287EE3E34A3DBC3683C0B7B10029FC203418537E7466BA6385A8FF301EE12708F82AAA1E380FC7A88F8F205AB7E88D7E95952A55BA20D09B79A47141D62BF6EB7DD307B08ECA13A5BC5F6B68581C6865B27BBCDDAB142F4B2CBFF488C8A22705FAA98A2B9EEA3530C76662335CC7EA3A00777725EBCCCD2A4636B2D9122FF3AB77123CE0883C1911115E50C9E8A94194E48DD0D09CFFB3ADCD2C1E92430903D07ADBF00532031575AA7F9E7B5A1F3362DEC936D4043C05F2476C07578BC9CBAF2AB4E382727AD41686A96B2548820BB03B32F11B2811AD62F489E951632ABA0D1DF89680CC8A8B53B481D92A68D70B4EA1C3A6A561C0692882B5CA8CC942A8D495AFCB06DE89498FB935B775908FE7A03E324D54CC19D4E1AABD3593B38B19EE1388FE492B43127E5A504253786A0D69AD32601C28E2C88504A5BA599706023A61363E17C6B9BB59BDC697452CD059451983D738CA3FD034E3F5988854CA05031DB09611498988197C6B30D258DFE26265541C89A4B31D6864E9389B03CB74F7EC4323FB9421A4B9790A26D17B0398A26767350909F84D57B6694DF830664CA8B3C3C03ED2AE67B89006868A68527CCD666459AB7F056671000C6164D3A7F266A14D97CBD7004D6C92CACA770B844A4FA9B182E7B18CA885082AC5646FCB4A14E1685FEB0C9CE3372AB95365C04FD83084F80A23FF10A05BF15F7FA5ACC6C0CB462C33CA524FA6B8BB359043BA68609EAA2536E81D08463B19653B5435BA946C9ADDEB202B04B031CC960DCC12E4518D428B32B257A4FC7313D3A7980D80082E934F9D95C32B0A0191A23604384DD9E079BBBAA266D14C3F756B9F2133107433A4E83FA7187282A809203A4FAF841851833D121AC383843A5E55BC2381425E16C7DB4CC9AB5C1B0D91A47E2B8DE0E582C86B6B0D907BB360B97F40AB5D038F6B75C814B27D9B968D419832BC8C2BEE605EF6E5059D33100D90485D378450014221736C07407CAC260408AA64926619788B8601C2A752D1A6CBF820D7C7A04716203225B3895B9342D147A8185CFC1BB65BA06B4142339903C0AC4651385B45D98A8B19D28CD6BAB088787F7EE1B12461766B43CBCCB96434427D93C065550688F6948ED1B5475A425F1B85209D061C08B56C1CC069F6C0A7C6F29358CAB911087732A649D27C9B98F9A48879387D9B00C25959A71654D6F6A946164513E47A75D005986C2363C09F6B537ECA78B9303A5FA457608A586A653A347DB04DFCC19175B3A301172536062A658A95277570C8852CA8973F4AE123A334047DD711C8927A634A03388A527B034BF7A8170FA702C1F7C23EC32D18A2374890BE9C787A9409C82D192C4BB705A2F996CE405D85A4C1A1AB9B6AEB49CCE1C2F8A97C3516C72A00A46263BAA696BF25727719C3216423618FF33380934A6C10545C4C5C5155B12486181FC7A2319873978B6A2A67490F8256BD2196FE1792A4C00077B812EAE8BED3572499684AB3371876761E450C9F9D2768A36806D7AB2046C91F17599E9AC592990808DCD7B4D0919072F14EC361773B7252444C323C308326F4A30F8680D2F748F56A132B82674ED0184620B82AD2CB182C97B481626647491290A011CC73828685A8C367A5B9CF8D621B0D5C1EFF03172758BD004978C251CD51342228989CAE6332AC486437CB5C57D4307462865253BE217B3515C73DF405B7F28217AD0B8CF60C2FFFAA0A0048B1FB4ACDCDC38B5250CFEC356A6DE26CFA7A588FDC86F98C854AC64C7BFAA96F5A32CC0610934BAA6A586B9A2054F13BA274174AA0D2B3A81B96A940666F789B5A6BCDC0A6A0178A0C9A02578A493F6EEA0D2E6C13951C9F249A5E8DD71DD49A742D451F1ABBA19AF8C547855E0AFC728E90ABB499C9BEEB766F4729CDA22263E324D22302CBD3399FACC630991FC8F28BDB4354762541527678BCF61F65C241146C426D23B9BFAA6B7DF18C97F20C1B6125BF874B1D89475852C448215DB0EB7737F91480E8CEBD9A0871574F5AB62D9020175EC6927CA0B54C09818E42CF92A383172422C7DC1831D63B0C295DE75159DB8034E9E07F7B0B910C3C1E5FB66B3DC523F1FA6EB4910CB89A6C17562C83AB4C18D0CD7E0796592A372AA409B1C557347CCACDC4644A119064D06DD474929D1C6FB4D686E5491CE4BC89A30BB4B8C41BCE5157DFC1360823B1AB618C14B10F98C25067398EA7018C278A4B3DF31334D603B2044EF187CD9BC6CE42725BD962C264983E9E18155A8B9C47143D70460A26A56FE7658C1F150348C6087EF758AD167887860A007A5FC37358D43B5EBEE820ACEA474F0AC07B76802866199C61231D5C747C93774D2C1E0C1C67E6C81B82752173E125BAF39B4FD19A4F453DC57976B1D97FE6996992BBB65B7CB25D077BBAA6A13322899AF659CF1B3558C1B5001154B625809ED89AEEBB89E6EA7D67F723D045AB05715C42355DA6A5C8DD39C8ABE3037751A01ED1C7374919F3121B5A52C53D1487316769F80721DEEAAAD3C90F76E7AE9E12BA92B32B5FD457E3C752C2650DFB885771CB77AC3C785A8C562E6A1C63C2A55EA47CF8B90EB8225C123C346452566235B2F31823A33521E087937A345D8D663EEAA05658917BBAA008C2E335F8850A90A326D0E66432F44CEB8289E4ECB2D12958E984072ECACB88E1348FF0B55654ACBA5B54971CBAEBA88EC4B91A94C37192FA982BECB9F3DA421603B61A51BC8E36CBD053851C77B1B926B17A272AA9023246B02B3ED47F66A00BD5684823634E7CE58CF8F306E35B1E5322824D904801F0A2FA7C2BC9C252B0A56B7BA2AB0F636021745A70A9A43E2B0A8D615970B65309624B5184BCC30B911679AEDD76025FE3908FD67897B0CF4BE5A6F5413D7DD98564B23E42A93E4AA8821CD45054C643EDC1158DB6B3DEB13FB5A51EBD1A8A78B87225A7338E101104C4A220D9BDEDD48C85A1C2DAE781A80C40E13B87EAC73A764201C9B760CCFB1AE392699C7039D27C39362B27B8FC6F07A8A3D4410F1547C48A9997F62C61074452EF1515F8A649EBCA9437205A4E8A61606B41DAF6834D671F4D852C0C9C4096611648C6A3170678B1537CC1828D93580C9E5849A9653175ACB753F2BE7437BE45F6C603E485F2EC301BB42B6C37C225D7495A584AE231890AB5C8C35C268CF4BBB0213C096019319561A8A6947637AA40D006B415BB2CFA2237E0890B6A3BC134ABF8F6585E108D15940F91F4BF5B0C818055B21DEA6E63B553988C47F4B94E7CF800A493B4734705EDC56A4B6021C629500675876804CF0B951F038A5C7FE58E89774EF2992FD7C63099D352A7D21560B788B405709861817E59A96B3A3A83CBA803B16934331071905BBEC6532900155D8AC88CB32E4E21A3BD3A03FDEC325A51CD2773964E6784FCF1853737AA64EB67564727272661ABF84313A57A44B123C65509CFB7A6F6641CDCC3B57FE628C7B8192DB44FFBF5796A8613B1FA126F6076883C783DC24E2A4464C40B3A41CA70AE87620866CF4FCB2BD204BF5C283812BA056AC0C345E379C4BA24D750901279BB2F3A16F612BFADB35703332C7C136F68EAB6755C66B6A4AD1AABA7B768A58ACAACC10A459A1CC8EF29377BC200E4D315A30A6BCC3256F9734D06E9779CAA5442A9A16069081377C76E75154368072DC446ED6C8B8E622A21E383CF9BA1FB434E2ECC81E7B78CEE986B8FF798AB18CF9634543546284EDA2A26B47F05B735BCDB1202220076DC8B4E4B9F853533C8F6C7FF38817BA49712835785F17F14CA01D0C1C1E98810FE0B36E5B427157B9418449CEDD641A4293C85C32700102ACEC22EBAD98ED160A5F027BD4CDA57F1F3720A12C134654DD5E73F829676495390D0E7929D6034E9C55F7D55BA658BC587988E8AF94960F6CFB8D5AF7A0021535A6E25E437D49A780698BE22AC9953949F571B85A685725F8207A2B0AE849B601AB91B159B3DF4A154C2041E776070AFC42969322380917C97510799F3149131477E16663D3174C7C1CAEA788535C6C005A64F2868631B31B66E205FD38C1D84542D0F1B578F58C9BF5A0FAEAB6AB6494893053165EAFD465FC64A0C5F8F3F9003489415899D59A543D8208C54A3166529B539228A39E87D531F3527C207EDCC1DB7FADDCF9628391879B335C707839A0DB051A88626ED79D451140800E03B59B956F8210E556067407D13DC90FA9E8B872BFB8F"; + + byte[] seed = Hex.Decode(temp); + + NistSecureRandom random = new NistSecureRandom(seed, null); + + KyberKeyPairGenerator keyGen = new KyberKeyPairGenerator(); + keyGen.Init(new KyberKeyGenerationParameters(random, KyberParameters.kyber1024)); + + AsymmetricCipherKeyPair keyPair = keyGen.GenerateKeyPair(); + Assert.IsTrue(Arrays.AreEqual(Hex.Decode(expectedPubKey), ((KyberPublicKeyParameters)keyPair.Public).GetEncoded())); + + Assert.IsTrue(Arrays.AreEqual(Hex.Decode(expectedPrivKey), ((KyberPrivateKeyParameters)keyPair.Private).GetEncoded())); + + KyberKemGenerator kemGen = new KyberKemGenerator(random); + + ISecretWithEncapsulation secretEncap = kemGen.GenerateEncapsulated(keyPair.Public); + + string expectedSharedSecret = "B10F7394926AD3B49C5D62D5AEB531D5757538BCC0DA9E550D438F1B61BD7419"; + + Assert.IsTrue(Arrays.AreEqual(Hex.Decode(expectedSharedSecret), secretEncap.GetSecret())); + + string expectedCipherText = "A6AF29D5F5B80BD130F518BADDD6C8F17545413D860FB3DE451979EBFA5E4E3112C7C0ADF99824BB526F2C3550748ED0E134F0457A7C61F9F526F002BAADC03FC13E38131219513C3EDE061661E74F603C4FCF7951C8E52C9C213B0D22D9293663D669A6B58ED8FCEFCF8249D7BB5298F55761445B2B83CE7F005CB04248AEC8BDA22FD2D42AA766322014EA038CC32C55C8E4B9E28EC9119F527341E4F66A035121073B85DE6706DA19E0838A9F33B719A68F039B664DC002659EABFC398679AA7009CE0CD01CDAFB6CD2A26FE4101672C98FF58F7C47D5BDA2906653B3A6F9651F7A121EA77EA74723FAE5B873F9BB7B664F0C8A93831EF9D51C7CC1EF44AC0E55A55CA76D137FE9B75F40509CEF156E5AD18F9FB999680008E547D55EECD5B4D1CB1D9F076CEC21501C7402509ECB77AFB2CB9A61340A8BD1514C6E71B4AA45E47EC37512271B911F8FB46C9082C9DF07204ABB5A50E6E3647A8AD4D8D5D7BFF19C8A509308BCFB895536D045CA2B97CB16A29BB7181CAD0509DDB91735028EBA8C31D74BD275EAA65B5340B3A43FBFE0B3061D6BAE7E75B7098CDABE91D4B31E36C9AA7A8298862AD63C8FD282E03B460B3AB464CE0F27B1C3D11155ACAA011EB9E2AE3E6DDA07D6F491737CBCE9B05F9BC56BE20E8D326BA132C57FB235161144519CDF40560FBE279BDE411E112531F826D6AB10D4547350ADD2A9DE8D62C2AC82CABE6815646F4DC9742BB0C2A3F77EC7B46C6B537605FA31798CD89281221A33DFB9796E644305630332C2CB931408AB481A16D953F6BEAE3891D6D9AC1FAB38222D9271872D9D0CADB91ABE9B4E265F75C6E5E829E146C3D8CE1E9D12E0D129801957F46B0D2DBE1F749B1D08E2345F6239A731342EB75B0CF1BF411749BC2CAF2810B788C6B7238B4D3DA2D6315CE9542E24404F145755A30AB851E4445841BD33F716A586884888ECC6BC6498AA32919AE81D20C26973C2BD54582A0F6AD98ABFD2627E15690A727E69F581DD2A7127982A90E33E2D4A03FE339142C7E44C326AC46ED395A225D3033389917328B45316B1585A01B2C304B2944E903ABBB3EC5619441CFC8965A446DF75DEFA80C6E15ADBD506B7AB2DE12DDA9BC81441CFC89052E2E5808F7126C6FD3AC6AC8081258A84A09AE50F6CD7CC0F4AF336FD1D643E99079996268C2D32D909F22E3504F07FBB563196D4312FDDB9335D5C1D36E8C5EEA2278DBA23B94D193C947CC41CA993DC7DB1396340AD9C4FE687DD7B8D0C7A5120AE0204F2C665BD5F473D644C7FF26BFFBA7A36980830702128A7E661D677A092A36E7428A4139FB29B0095CC11086F447D2A9EF6C9B161F189C6299E084CB7AA00FAF787797BFB069FBC087FDE26252A1664F19C5A8A22EC5EE1AEB076357B7DC37E6B0F1520F958F7851BACB92C89FD114A72FEAC54652D45B09E1AE7651ABD164BCD537D58FA39D3EC8ACDCDF98425005862FA59692DE162B77E6297C66233348408A8AB695CE2F2728DB9FBE27E958967EC5974767C5A66023074B4A71AFD264AD2890E970A1F31D6E3311B736F9F9488793DDC88F23458064254C82A1D9E59EAD2FCEC40B430687C4B7E28960926AFCACC9BD756A71088C78450E20A2E980AEDE9EBEDFE7FABD6ABFE96F934C4B02C01CA194D01B73C25D5997039D3FCD0F099521F70CAEE69110AC1FC5A99917AD752FC96ADFAD7186D0A7C9CFE5601C07514EA6448D661C57AA20242103C4276A070A489A4CB6BCA0F9ECC4379FB220215FD91F81019D5B0AE619358B52468F272C178E3A74CF6775AA924FE329C3175D9E4C3E21AB9EC836EDC3ACAB2E3891EE8DEDA515D39AF9B8DDD0EE7B0164F805C3835F6D2BABDB30EAB4756E7EC7F829ECE01E8EADFBBED12FC283B3D4C69F575E7F80417689FDFCFC7BE27EE3B8CDF57AAEBEC4A95B7E5BB585B85227F7C32BE30DB3E65E42E30DCF5A5FA073DBA399D942F2222ADB9B9898102AFE5432EDC7F04AE34A8FEC2D81CB49A9A9B43814CE71D97F726E2B1E8F64B50E65DFB4816E12E82A3197484A4E9BBA4D2D69E3F19D0B75C21E2BFFE9FC0C98CF48A3AAF08D467F72687DF0178174B7897F734349B181ECA86A598A0C5E8C25946F24DC5572BD324A40458A788E5137F3C7A7C97FC9F12A3C463A8FE9449101CCE966D7C009323932998D56EF430C73BC24F5D95F737858DDC4F32C013"; + + Assert.IsTrue(Arrays.AreEqual(Hex.Decode(expectedCipherText), secretEncap.GetEncapsulation())); + + KyberKemExtractor kemExtract = new KyberKemExtractor((KyberPrivateKeyParameters)keyPair.Private); + + byte[] decryptedSharedSecret = kemExtract.ExtractSecret(secretEncap.GetEncapsulation()); + + Assert.IsTrue(Arrays.AreEqual(Hex.Decode(expectedSharedSecret), decryptedSharedSecret)); + } + + [Test] + public void TestKyberRandom() + { + SecureRandom random = new SecureRandom(); + KyberKeyPairGenerator keyGen = new KyberKeyPairGenerator(); + + keyGen.Init(new KyberKeyGenerationParameters(random, KyberParameters.kyber1024)); + + for (int i = 0; i != 1000; i++) + { + AsymmetricCipherKeyPair keyPair = keyGen.GenerateKeyPair(); + + KyberKemGenerator kemGen = new KyberKemGenerator(random); + + ISecretWithEncapsulation secretEncap = kemGen.GenerateEncapsulated(keyPair.Public); + + KyberKemExtractor kemExtract = new KyberKemExtractor((KyberPrivateKeyParameters)keyPair.Private); + + byte[] decryptedSharedSecret = kemExtract.ExtractSecret(secretEncap.GetEncapsulation()); + + Assert.IsTrue(Arrays.AreEqual(secretEncap.GetSecret(), decryptedSharedSecret)); + } + } + + [Test] + public void TestParameters() + { + Assert.AreEqual(128, KyberParameters.kyber512.SessionKeySize); + Assert.AreEqual(192, KyberParameters.kyber768.SessionKeySize); + Assert.AreEqual(256, KyberParameters.kyber1024.SessionKeySize); + } + + [Test] + public void TestPrivInfoGeneration() + { + SecureRandom random = new SecureRandom(); + PqcOtherInfoGenerator.PartyU partyU = new PqcOtherInfoGenerator.PartyU(KyberParameters.kyber512, + new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), Hex.Decode("beef"), Hex.Decode("cafe"), random); + + byte[] partA = partyU.GetSuppPrivInfoPartA(); + + PqcOtherInfoGenerator.PartyV partyV = new PqcOtherInfoGenerator.PartyV(KyberParameters.kyber512, + new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), Hex.Decode("beef"), Hex.Decode("cafe"), random); + + byte[] partB = partyV.GetSuppPrivInfoPartB(partA); + + DerOtherInfo otherInfoU = partyU.Generate(partB); + + DerOtherInfo otherInfoV = partyV.Generate(); + + Assert.IsTrue(Arrays.AreEqual(otherInfoU.GetEncoded(), otherInfoV.GetEncoded())); + } + + [Test] + public void TestRng() + { + string temp = "061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"; + byte[] seed = Hex.Decode(temp); + NistSecureRandom r = new NistSecureRandom(seed, null); + byte[] testBytes = new byte[48]; + r.NextBytes(testBytes); + + string randBytesString = "7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E47"; + byte[] randBytes = Hex.Decode(randBytesString); + + Assert.IsTrue(Arrays.AreEqual(randBytes, testBytes)); + } + [TestCaseSource(nameof(TestVectorFiles))] [Parallelizable(ParallelScope.All)] public void TV(string testVectorFile) @@ -71,11 +190,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests // Generate keys and test. // kpGen.Init(genParam); - AsymmetricCipherKeyPair ackp = kpGen.GenerateKeyPair(); - + AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair(); - KyberPublicKeyParameters pubParams = (KyberPublicKeyParameters)ackp.Public; - KyberPrivateKeyParameters privParams = (KyberPrivateKeyParameters)ackp.Private; + KyberPublicKeyParameters pubParams = (KyberPublicKeyParameters)PqcPublicKeyFactory.CreateKey( + PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo((KyberPublicKeyParameters)kp.Public)); + KyberPrivateKeyParameters privParams = (KyberPrivateKeyParameters)PqcPrivateKeyFactory.CreateKey( + PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo((KyberPrivateKeyParameters)kp.Private)); Assert.True(Arrays.AreEqual(pk, pubParams.GetEncoded()), name + " " + count + ": public key"); Assert.True(Arrays.AreEqual(sk, privParams.GetEncoded()), name + " " + count + ": secret key"); diff --git a/crypto/test/src/pqc/crypto/test/FalconTest.cs b/crypto/test/src/pqc/crypto/test/FalconTest.cs index be2d0d352..c8424b37a 100644 --- a/crypto/test/src/pqc/crypto/test/FalconTest.cs +++ b/crypto/test/src/pqc/crypto/test/FalconTest.cs @@ -108,17 +108,22 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests FalconKeyGenerationParameters kparam = new FalconKeyGenerationParameters(random, falconParameters); FalconKeyPairGenerator kpg = new FalconKeyPairGenerator(); kpg.Init(kparam); - AsymmetricCipherKeyPair ackp = kpg.GenerateKeyPair(); - byte[] respk = ((FalconPublicKeyParameters)ackp.Public).GetEncoded(); - byte[] ressk = ((FalconPrivateKeyParameters)ackp.Private).GetEncoded(); - - //keygen + AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair(); + + FalconPublicKeyParameters pubParams = (FalconPublicKeyParameters)PqcPublicKeyFactory.CreateKey( + PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo((FalconPublicKeyParameters)kp.Public)); + FalconPrivateKeyParameters privParams = (FalconPrivateKeyParameters)PqcPrivateKeyFactory.CreateKey( + PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo((FalconPrivateKeyParameters)kp.Private)); + + byte[] respk = pubParams.GetEncoded(); + byte[] ressk = privParams.GetEncoded(); + Assert.True(Arrays.AreEqual(respk, 0, respk.Length, pk, 1, pk.Length), name + " " + count + " public key"); Assert.True(Arrays.AreEqual(ressk, 0, ressk.Length, sk, 1, sk.Length), name + " " + count + " private key"); // sign FalconSigner signer = new FalconSigner(); - ParametersWithRandom skwrand = new ParametersWithRandom(ackp.Private, random); + ParametersWithRandom skwrand = new ParametersWithRandom(kp.Private, random); signer.Init(true, skwrand); byte[] sig = signer.GenerateSignature(msg); byte[] ressm = new byte[2 + msg.Length + sig.Length]; @@ -131,7 +136,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests // verify FalconSigner verifier = new FalconSigner(); - FalconPublicKeyParameters pkparam = (FalconPublicKeyParameters)ackp.Public; + FalconPublicKeyParameters pkparam = (FalconPublicKeyParameters)kp.Public; verifier.Init(false, pkparam); bool vrfyrespass = verifier.VerifySignature(msg, sig); sig[42]++; // changing the signature by 1 byte should cause it to fail -- cgit 1.4.1