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>() : (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.
+ * <pre>
+ * 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
+ * }
+ * </pre>
+ */
+ 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
|