summary refs log tree commit diff
path: root/crypto/src/pqc
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-05-01 17:50:05 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-05-01 17:50:05 +0700
commitd5116b38e5b8a0425d31e5fe711866515bc91af6 (patch)
tree19d43d38824512db369b739dcab78f71304c221a /crypto/src/pqc
parentPQC test cleanup (diff)
downloadBouncyCastle.NET-ed25519-d5116b38e5b8a0425d31e5fe711866515bc91af6.tar.xz
Update Pqc.Crypto.Crystals from bc-java
- add PqcOtherInfoGenerator and supporting code
Diffstat (limited to 'crypto/src/pqc')
-rw-r--r--crypto/src/pqc/asn1/CmcePrivateKey.cs15
-rw-r--r--crypto/src/pqc/asn1/KyberPrivateKey.cs115
-rw-r--r--crypto/src/pqc/crypto/IKemParameters.cs9
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs64
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs26
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumSigner.cs7
-rw-r--r--crypto/src/pqc/crypto/crystals/kyber/KyberParameters.cs2
-rw-r--r--crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs28
-rw-r--r--crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs20
-rw-r--r--crypto/src/pqc/crypto/ntru/NtruParameters.cs2
-rw-r--r--crypto/src/pqc/crypto/utils/PqcOtherInfoGenerator.cs203
-rw-r--r--crypto/src/pqc/crypto/utils/PqcPrivateKeyFactory.cs62
-rw-r--r--crypto/src/pqc/crypto/utils/PqcPrivateKeyInfoFactory.cs18
-rw-r--r--crypto/src/pqc/crypto/utils/PqcSubjectPublicKeyInfoFactory.cs4
14 files changed, 441 insertions, 134 deletions
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)
             {