diff --git a/ant/jdk13.xml b/ant/jdk13.xml
index 650561b6ad..f410102f0b 100644
--- a/ant/jdk13.xml
+++ b/ant/jdk13.xml
@@ -56,6 +56,7 @@
+
@@ -90,6 +91,7 @@
+
@@ -249,6 +251,7 @@
+
@@ -329,19 +332,29 @@
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ant/jdk15+.xml b/ant/jdk15+.xml
index d283fa807b..08cd4ddbc9 100644
--- a/ant/jdk15+.xml
+++ b/ant/jdk15+.xml
@@ -10,6 +10,7 @@
+
diff --git a/bc-build.properties b/bc-build.properties
index f4195c8416..1c72785619 100644
--- a/bc-build.properties
+++ b/bc-build.properties
@@ -6,7 +6,7 @@
release.suffix: 1.79
release.name: 1.79
release.version: 1.79
-release.debug: false
+release.debug: true
mail.jar.home: ./libs/javax.mail-1.4.7.jar
activation.jar.home: ./libs/activation-1.1.1.jar
diff --git a/core/src/main/jdk1.3/org/bouncycastle/util/io/pem/PemReader.java b/core/src/main/jdk1.3/org/bouncycastle/util/io/pem/PemReader.java
new file mode 100644
index 0000000000..3f81a29de9
--- /dev/null
+++ b/core/src/main/jdk1.3/org/bouncycastle/util/io/pem/PemReader.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.util.io.pem;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+//import java.util.logging.Level;
+//import java.util.logging.Logger;
+
+import org.bouncycastle.util.encoders.Base64;
+
+/**
+ * A generic PEM reader, based on the format outlined in RFC 1421
+ */
+public class PemReader
+ extends BufferedReader
+{
+ public static final String LAX_PEM_PARSING_SYSTEM_PROPERTY_NAME = "org.bouncycastle.pemreader.lax";
+
+ private static final String BEGIN = "-----BEGIN ";
+ private static final String END = "-----END ";
+ //private static final Logger LOG = Logger.getLogger(PemReader.class.getName());
+
+ public PemReader(Reader reader)
+ {
+ super(reader);
+ }
+
+ /**
+ * Read the next PEM object as a blob of raw data with header information.
+ *
+ * @return the next object in the stream, null if no objects left.
+ * @throws IOException in case of a parse error.
+ */
+ public PemObject readPemObject()
+ throws IOException
+ {
+ String line = readLine();
+
+ while (line != null && !line.startsWith(BEGIN))
+ {
+ line = readLine();
+ }
+
+ if (line != null)
+ {
+ line = line.substring(BEGIN.length()).trim();
+ int index = line.indexOf('-');
+
+ if (index > 0 && line.endsWith("-----") && (line.length() - index) == 5)
+ {
+ String type = line.substring(0, index);
+
+ return loadObject(type);
+ }
+ }
+
+ return null;
+ }
+
+ private PemObject loadObject(String type)
+ throws IOException
+ {
+ String line;
+ String endMarker = END + type + "-----";
+ StringBuffer buf = new StringBuffer();
+ List headers = new ArrayList();
+
+ while ((line = readLine()) != null)
+ {
+ int index = line.indexOf(':');
+ if (index >= 0)
+ {
+ String hdr = line.substring(0, index);
+ String value = line.substring(index + 1).trim();
+
+ headers.add(new PemHeader(hdr, value));
+
+ continue;
+ }
+
+ if (System.getProperty(LAX_PEM_PARSING_SYSTEM_PROPERTY_NAME, "false").equalsIgnoreCase("true"))
+ {
+ String trimmedLine = line.trim();
+ //if (!trimmedLine.equals(line) && LOG.isLoggable(Level.WARNING))
+ //{
+ //LOG.log(Level.WARNING, "PEM object contains whitespaces on -----END line", new Exception("trace"));
+ //}
+ line = trimmedLine;
+ }
+
+ if (line.indexOf(endMarker) == 0)
+ {
+ break;
+ }
+
+ buf.append(line.trim());
+ }
+
+ if (line == null)
+ {
+ throw new IOException(endMarker + " not found");
+ }
+
+ return new PemObject(type, headers, Base64.decode(buf.toString()));
+ }
+
+}
diff --git a/core/src/test/jdk1.3/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/jdk1.3/org/bouncycastle/crypto/test/CipherTest.java
new file mode 100644
index 0000000000..377ea7ce8c
--- /dev/null
+++ b/core/src/test/jdk1.3/org/bouncycastle/crypto/test/CipherTest.java
@@ -0,0 +1,188 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.modes.AEADCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.test.SimpleTest;
+
+public abstract class CipherTest
+ extends SimpleTest
+{
+ private SimpleTest[] _tests;
+ private BlockCipher _engine;
+ private KeyParameter _validKey;
+
+// protected CipherTest(
+// SimpleTest[] tests)
+// {
+// _tests = tests;
+// }
+
+ protected CipherTest(
+ SimpleTest[] tests,
+ BlockCipher engine,
+ KeyParameter validKey)
+ {
+ _tests = tests;
+ _engine = engine;
+ _validKey = validKey;
+ }
+
+ public abstract String getName();
+
+ public void performTest()
+ throws Exception
+ {
+ for (int i = 0; i != _tests.length; i++)
+ {
+ _tests[i].performTest();
+ }
+
+ if (_engine != null)
+ {
+ //
+ // state tests
+ //
+ byte[] buf = new byte[128];
+
+ try
+ {
+ _engine.processBlock(buf, 0, buf, 0);
+
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+
+ bufferSizeCheck((_engine));
+ }
+ }
+
+ private void bufferSizeCheck(
+ BlockCipher engine)
+ {
+ byte[] correctBuf = new byte[engine.getBlockSize()];
+ byte[] shortBuf = new byte[correctBuf.length / 2];
+
+ engine.init(true, _validKey);
+
+ try
+ {
+ engine.processBlock(shortBuf, 0, correctBuf, 0);
+
+ fail("failed short input check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ engine.processBlock(correctBuf, 0, shortBuf, 0);
+
+ fail("failed short output check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ engine.init(false, _validKey);
+
+ try
+ {
+ engine.processBlock(shortBuf, 0, correctBuf, 0);
+
+ fail("failed short input check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ engine.processBlock(correctBuf, 0, shortBuf, 0);
+
+ fail("failed short output check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+
+ interface Instace
+ {
+ AEADCipher CreateInstace();
+ }
+
+ static void checkCipher(int aeadLen, int ivLen, int msgLen, Instace instace)
+ {
+ AEADCipher pCipher = instace.CreateInstace();
+
+ try
+ {
+ /* Obtain some random data */
+ final byte[] myData = new byte[msgLen];
+ final SecureRandom myRandom = new SecureRandom();
+ myRandom.nextBytes(myData);
+
+ /* Obtain some random AEAD */
+ final byte[] myAEAD = new byte[aeadLen];
+ myRandom.nextBytes(myAEAD);
+
+ /* Create the Key parameters */
+ final CipherKeyGenerator myGenerator = new CipherKeyGenerator();
+ final KeyGenerationParameters myGenParams = new KeyGenerationParameters(myRandom, 128);
+ myGenerator.init(myGenParams);
+ final byte[] myKey = myGenerator.generateKey();
+ final KeyParameter myKeyParams = new KeyParameter(myKey);
+
+ /* Create the nonce */
+ final byte[] myNonce = new byte[ivLen];
+ myRandom.nextBytes(myNonce);
+ final ParametersWithIV myParams = new ParametersWithIV(myKeyParams, myNonce);
+
+ /* Initialise the cipher for encryption */
+ pCipher.init(true, myParams);
+ final int myMaxOutLen = pCipher.getOutputSize(msgLen);
+ final byte[] myEncrypted = new byte[myMaxOutLen];
+ pCipher.processAADBytes(myAEAD, 0, aeadLen);
+ int myOutLen = pCipher.processBytes(myData, 0, msgLen, myEncrypted, 0);
+ myOutLen += pCipher.doFinal(myEncrypted, myOutLen);
+
+ /* Note that myOutLen is too large by DATALEN */
+ pCipher = instace.CreateInstace();
+ /* Initialise the cipher for decryption */
+ pCipher.init(false, myParams);
+ final int myMaxClearLen = pCipher.getOutputSize(myOutLen);
+ final byte[] myDecrypted = new byte[myMaxClearLen];
+ pCipher.processAADBytes(myAEAD, 0, aeadLen);
+ int myClearLen = pCipher.processBytes(myEncrypted, 0, myEncrypted.length, myDecrypted, 0);
+ myClearLen += pCipher.doFinal(myDecrypted, myClearLen);
+ final byte[] myResult = Arrays.copyOf(myDecrypted, msgLen);
+
+ /* Check that we have the same result */
+ if (!Arrays.areEqual(myData, myResult))
+ {
+ System.out.println("Cipher " + pCipher.getAlgorithmName() + " failed");
+ }
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new RuntimeException(e.toString());
+ }
+ }
+}
diff --git a/docs/releasenotes.html b/docs/releasenotes.html
index 45738ba5f6..a2a9f6f87b 100644
--- a/docs/releasenotes.html
+++ b/docs/releasenotes.html
@@ -30,7 +30,7 @@ 2.1.2 Defects Fixed
ERSInputStreamData would fail to generate the correct hash if called a second time with a different hash algorithm. This has been fixed.
A downcast in the CrlCache which would cause FTP based CRLs to fail to load has been removed.
ECUtil.getNamedCurveOid() now trims curve names of excess space before look up.
-The PhotonBeetle and Xoodyak did not reset properly after a doFinal() call. This has been fixed.
+The PhotonBeetle and Xoodyak digests did not reset properly after a doFinal() call. This has been fixed.
Malformed AlgorithmIdentifiers in CertIDs could cause caching issues in the OCSP cache. This has been fixed.
With Java 21 a provider service class will now be returned with a null class name where previously a null would have been returned for a service. This can cause a NullPointerException to be thrown by the BC provider if a non-existant service is requested. This issue has now been worked around.
CMS: OtherKeyAttribute.keyAttr now treated as optional.
@@ -49,7 +49,7 @@ 2.1.3 Additional Features and Functionality
Delta Certificates now support the latest revision of the delta certificate extension draft.
A general KeyIdentifier class, encapsulating both PGP KeyID and the PGP key fingerprint has been added to the PGP API.
Support for the LibrePGP PreferredEncryptionModes signature subpacket has been added to the PGP API.
-Support Version 6 signatures, including salts, has been added to the PGP API.
+Support for Version 6 signatures, including salts, has been added to the PGP API.
Support for the PreferredKeyServer signature supacket has been added to the PGP API.
Support for RFC 9269, "Using KEMs in Cryptographic Message Syntax (CMS)", has been added to the CMS API.
Support for the Argon2 S2K has been added to the PGP API.
diff --git a/pg/src/main/jdk1.3/org/bouncycastle/bcpg/StreamUtil.java b/pg/src/main/jdk1.3/org/bouncycastle/bcpg/StreamUtil.java
index 147669f33f..8cb743f043 100644
--- a/pg/src/main/jdk1.3/org/bouncycastle/bcpg/StreamUtil.java
+++ b/pg/src/main/jdk1.3/org/bouncycastle/bcpg/StreamUtil.java
@@ -1,90 +1,199 @@
package org.bouncycastle.bcpg;
import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import org.bouncycastle.util.Arrays;
+
class StreamUtil
{
+ private static final long MAX_MEMORY = Integer.MAX_VALUE;
+
/**
- * Find out possible longest length...
+ * Find out possible longest length, capped by available memory.
*
* @param in input stream of interest
* @return length calculation or MAX_VALUE.
*/
static int findLimit(InputStream in)
{
+ // TODO: this can obviously be improved.
if (in instanceof ByteArrayInputStream)
{
return ((ByteArrayInputStream)in).available();
}
- return Integer.MAX_VALUE;
+ if (MAX_MEMORY > Integer.MAX_VALUE)
+ {
+ return Integer.MAX_VALUE;
+ }
+
+ return (int)MAX_MEMORY;
+ }
+
+ static void writeNewPacketLength(OutputStream out, long bodyLen)
+ throws IOException
+ {
+ if (bodyLen < 192)
+ {
+ out.write((byte)bodyLen);
+ }
+ else if (bodyLen <= 8383)
+ {
+ bodyLen -= 192;
+
+ out.write((byte)(((bodyLen >> 8) & 0xff) + 192));
+ out.write((byte)bodyLen);
+ }
+ else
+ {
+ out.write(0xff);
+ writeBodyLen(out, bodyLen);
+ }
+ }
+
+ static void writeBodyLen(OutputStream out, long bodyLen)
+ throws IOException
+ {
+ out.write((byte)(bodyLen >> 24));
+ out.write((byte)(bodyLen >> 16));
+ out.write((byte)(bodyLen >> 8));
+ out.write((byte)bodyLen);
+ }
+
+ static void writeKeyID(BCPGOutputStream pOut, long keyID)
+ throws IOException
+ {
+ pOut.write((byte)(keyID >> 56));
+ pOut.write((byte)(keyID >> 48));
+ pOut.write((byte)(keyID >> 40));
+ pOut.write((byte)(keyID >> 32));
+ pOut.write((byte)(keyID >> 24));
+ pOut.write((byte)(keyID >> 16));
+ pOut.write((byte)(keyID >> 8));
+ pOut.write((byte)(keyID));
+ }
+
+ static long readKeyID(BCPGInputStream in)
+ throws IOException
+ {
+ long keyID = (long)in.read() << 56;
+ keyID |= (long)in.read() << 48;
+ keyID |= (long)in.read() << 40;
+ keyID |= (long)in.read() << 32;
+ keyID |= (long)in.read() << 24;
+ keyID |= (long)in.read() << 16;
+ keyID |= (long)in.read() << 8;
+ return keyID | in.read();
+ }
+
+ static void writeTime(BCPGOutputStream pOut, long time)
+ throws IOException
+ {
+ StreamUtil.write4OctetLength(pOut, (int)time);
+ }
+
+ static long readTime(BCPGInputStream in)
+ throws IOException
+ {
+ return (long)read4OctetLength(in) * 1000L;
+ }
+
+ static void write2OctetLength(OutputStream pOut, int len)
+ throws IOException
+ {
+ pOut.write(len >> 8);
+ pOut.write(len);
+ }
+
+ static int read2OctetLength(InputStream in)
+ throws IOException
+ {
+ return (in.read() << 8) | in.read();
+ }
+
+ static void write4OctetLength(OutputStream pOut, int len)
+ throws IOException
+ {
+ pOut.write(len >> 24);
+ pOut.write(len >> 16);
+ pOut.write(len >> 8);
+ pOut.write(len);
+ }
+
+ static int read4OctetLength(InputStream in)
+ throws IOException
+ {
+ return (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
+ }
+
+ static int flag_eof = 0;
+ static int flag_isLongLength = 1;
+ static int flag_partial = 2;
+ /**
+ * Note: flags is an array of three boolean values:
+ * flags[0] indicates l is negative, flag for eof
+ * flags[1] indicates (is)longLength = true
+ * flags[2] indicate partial = true
+ */
+ static int readBodyLen(InputStream in, boolean[] flags)
+ throws IOException
+ {
+ Arrays.fill(flags, false);
+ int l = in.read();
+ int bodyLen = -1;
+ if (l < 0)
+ {
+ flags[flag_eof] = true;
+ }
+ if (l < 192)
+ {
+ bodyLen = l;
+ }
+ else if (l <= 223)
+ {
+ bodyLen = ((l - 192) << 8) + (in.read()) + 192;
+ }
+ else if (l == 255)
+ {
+ flags[flag_isLongLength] = true;
+ bodyLen = StreamUtil.read4OctetLength(in);
+ }
+ else
+ {
+ flags[flag_partial] = true;
+ bodyLen = 1 << (l & 0x1f);
+ }
+ return bodyLen;
+ }
+
+ static void write8OctetLength(OutputStream pOut, long len)
+ throws IOException
+ {
+ pOut.write((int) (len >> 56));
+ pOut.write((int) (len >> 48));
+ pOut.write((int) (len >> 40));
+ pOut.write((int) (len >> 32));
+ pOut.write((int) (len >> 24));
+ pOut.write((int) (len >> 16));
+ pOut.write((int) (len >> 8));
+ pOut.write((int) len);
+ }
+
+ static long read8OctetLength(InputStream in)
+ throws IOException
+ {
+ return ((long) in.read() << 56) |
+ ((long) in.read() << 48) |
+ ((long) in.read() << 40) |
+ ((long) in.read() << 32) |
+ ((long) in.read() << 24) |
+ ((long) in.read() << 16) |
+ ((long) in.read() << 8) |
+ ((long) in.read());
}
- static void writeNewPacketLength(OutputStream out, long bodyLen)
- throws IOException
- {
- if (bodyLen < 192)
- {
- out.write((byte)bodyLen);
- }
- else if (bodyLen <= 8383)
- {
- bodyLen -= 192;
-
- out.write((byte)(((bodyLen >> 8) & 0xff) + 192));
- out.write((byte)bodyLen);
- }
- else
- {
- out.write(0xff);
- writeBodyLen(out, bodyLen);
- }
- }
-
- static void writeBodyLen(OutputStream out, long bodyLen)
- throws IOException
- {
- out.write((byte)(bodyLen >> 24));
- out.write((byte)(bodyLen >> 16));
- out.write((byte)(bodyLen >> 8));
- out.write((byte)bodyLen);
- }
-
- static void writeKeyID(BCPGOutputStream pOut, long keyID)
- throws IOException
- {
- pOut.write((byte)(keyID >> 56));
- pOut.write((byte)(keyID >> 48));
- pOut.write((byte)(keyID >> 40));
- pOut.write((byte)(keyID >> 32));
- pOut.write((byte)(keyID >> 24));
- pOut.write((byte)(keyID >> 16));
- pOut.write((byte)(keyID >> 8));
- pOut.write((byte)(keyID));
- }
-
- static long readKeyID(BCPGInputStream in)
- throws IOException
- {
- long keyID = (long)in.read() << 56;
- keyID |= (long)in.read() << 48;
- keyID |= (long)in.read() << 40;
- keyID |= (long)in.read() << 32;
- keyID |= (long)in.read() << 24;
- keyID |= (long)in.read() << 16;
- keyID |= (long)in.read() << 8;
- return keyID | in.read();
- }
-
- static void writeTime(BCPGOutputStream pOut, long time)
- throws IOException
- {
- pOut.write((byte)(time >> 24));
- pOut.write((byte)(time >> 16));
- pOut.write((byte)(time >> 8));
- pOut.write((byte)time);
- }
}
diff --git a/pg/src/main/jdk1.3/org/bouncycastle/openpgp/operator/bc/BcAEADSecretKeyEncryptorBuilder.java b/pg/src/main/jdk1.3/org/bouncycastle/openpgp/operator/bc/BcAEADSecretKeyEncryptorBuilder.java
new file mode 100644
index 0000000000..bb11c87979
--- /dev/null
+++ b/pg/src/main/jdk1.3/org/bouncycastle/openpgp/operator/bc/BcAEADSecretKeyEncryptorBuilder.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.openpgp.operator.bc;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.bcpg.AEADUtils;
+import org.bouncycastle.bcpg.PacketTags;
+import org.bouncycastle.bcpg.PublicKeyPacket;
+import org.bouncycastle.bcpg.S2K;
+import org.bouncycastle.bcpg.SymmetricKeyUtils;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.HKDFParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
+import org.bouncycastle.util.Arrays;
+
+public class BcAEADSecretKeyEncryptorBuilder
+{
+
+ private int aeadAlgorithm;
+ private int symmetricAlgorithm;
+ private S2K.Argon2Params argon2Params;
+
+ public BcAEADSecretKeyEncryptorBuilder(int aeadAlgorithm, int symmetricAlgorithm, S2K.Argon2Params argon2Params)
+ {
+ this.aeadAlgorithm = aeadAlgorithm;
+ this.symmetricAlgorithm = symmetricAlgorithm;
+ this.argon2Params = argon2Params;
+ }
+
+ public PBESecretKeyEncryptor build(char[] passphrase, final PublicKeyPacket pubKey)
+ {
+ return new PBESecretKeyEncryptor(symmetricAlgorithm, aeadAlgorithm, argon2Params, new SecureRandom(), passphrase)
+ {
+ private byte[] iv;
+
+ {
+ iv = new byte[AEADUtils.getIVLength(this.aeadAlgorithm)];
+ random.nextBytes(iv);
+ }
+
+ @Override
+ public byte[] encryptKeyData(byte[] key, byte[] keyData, int keyOff, int keyLen)
+ throws PGPException
+ {
+ int packetTag = pubKey.getPacketTag() == PacketTags.PUBLIC_KEY ? PacketTags.SECRET_KEY : PacketTags.SECRET_SUBKEY;
+ byte[] hkdfInfo = new byte[] {
+ (byte) (0xC0 | packetTag),
+ (byte) pubKey.getVersion(),
+ (byte) symmetricAlgorithm,
+ (byte) this.aeadAlgorithm
+ };
+
+ HKDFParameters hkdfParameters = new HKDFParameters(
+ getKey(),
+ null,
+ hkdfInfo);
+
+ HKDFBytesGenerator hkdfGen = new HKDFBytesGenerator(new SHA256Digest());
+ hkdfGen.init(hkdfParameters);
+ key = new byte[SymmetricKeyUtils.getKeyLengthInOctets(encAlgorithm)];
+ hkdfGen.generateBytes(key, 0, key.length);
+
+ try
+ {
+ byte[] aad = Arrays.prepend(pubKey.getEncodedContents(), (byte) (0xC0 | packetTag));
+ AEADBlockCipher cipher = BcAEADUtil.createAEADCipher(encAlgorithm, this.aeadAlgorithm);
+ cipher.init(true, new AEADParameters(
+ new KeyParameter(key),
+ 128,
+ getCipherIV(),
+ aad
+ ));
+ int dataLen = cipher.getOutputSize(keyData.length);
+ byte[] encKey = new byte[dataLen];
+ dataLen = cipher.processBytes(keyData, 0, keyData.length, encKey, 0);
+
+ cipher.doFinal(encKey, dataLen);
+ return encKey;
+ }
+ catch (IOException e)
+ {
+ throw new PGPException("Exception AEAD protecting private key material", e);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new PGPException("Exception AEAD protecting private key material", e);
+ }
+ }
+
+ @Override
+ public byte[] getCipherIV()
+ {
+ return iv;
+ }
+ };
+ }
+}
diff --git a/pg/src/main/jdk1.3/org/bouncycastle/openpgp/operator/jcajce/JcaAEADSecretKeyEncryptorBuilder.java b/pg/src/main/jdk1.3/org/bouncycastle/openpgp/operator/jcajce/JcaAEADSecretKeyEncryptorBuilder.java
new file mode 100644
index 0000000000..a7e2a22f3a
--- /dev/null
+++ b/pg/src/main/jdk1.3/org/bouncycastle/openpgp/operator/jcajce/JcaAEADSecretKeyEncryptorBuilder.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.openpgp.operator.jcajce;
+
+import java.security.Provider;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.bcpg.AEADUtils;
+import org.bouncycastle.bcpg.PacketTags;
+import org.bouncycastle.bcpg.PublicKeyPacket;
+import org.bouncycastle.bcpg.S2K;
+import org.bouncycastle.bcpg.SymmetricKeyUtils;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
+import org.bouncycastle.crypto.params.HKDFParameters;
+import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
+import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
+import org.bouncycastle.util.Arrays;
+
+public class JcaAEADSecretKeyEncryptorBuilder
+{
+ private int aeadAlgorithm;
+ private int symmetricAlgorithm;
+ private S2K.Argon2Params argon2Params;
+
+ private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
+ private JceAEADUtil aeadUtil = new JceAEADUtil(helper);
+
+ public JcaAEADSecretKeyEncryptorBuilder(int aeadAlgorithm, int symmetricAlgorithm, S2K.Argon2Params argon2Params)
+ {
+ this.aeadAlgorithm = aeadAlgorithm;
+ this.symmetricAlgorithm = symmetricAlgorithm;
+ this.argon2Params = argon2Params;
+ }
+
+ public JcaAEADSecretKeyEncryptorBuilder setProvider(Provider provider)
+ {
+ this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+ this.aeadUtil = new JceAEADUtil(helper);
+
+ return this;
+ }
+
+ public JcaAEADSecretKeyEncryptorBuilder setProvider(String providerName)
+ {
+ this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+ this.aeadUtil = new JceAEADUtil(helper);
+
+ return this;
+ }
+
+ public PBESecretKeyEncryptor build(char[] passphrase, final PublicKeyPacket pubKey)
+ {
+ return new PBESecretKeyEncryptor(symmetricAlgorithm, aeadAlgorithm, argon2Params, new SecureRandom(), passphrase)
+ {
+ private byte[] iv;
+
+ {
+ iv = new byte[AEADUtils.getIVLength(this.aeadAlgorithm)];
+ random.nextBytes(iv);
+ }
+
+ @Override
+ public byte[] encryptKeyData(byte[] key, byte[] keyData, int keyOff, int keyLen)
+ throws PGPException
+ {
+ int packetTag = pubKey.getPacketTag() == PacketTags.PUBLIC_KEY ? PacketTags.SECRET_KEY : PacketTags.SECRET_SUBKEY;
+ byte[] hkdfInfo = new byte[] {
+ (byte) (0xC0 | packetTag),
+ (byte) pubKey.getVersion(),
+ (byte) symmetricAlgorithm,
+ (byte) this.aeadAlgorithm
+ };
+
+ HKDFParameters hkdfParameters = new HKDFParameters(
+ getKey(),
+ null,
+ hkdfInfo);
+
+ HKDFBytesGenerator hkdfGen = new HKDFBytesGenerator(new SHA256Digest());
+ hkdfGen.init(hkdfParameters);
+ key = new byte[SymmetricKeyUtils.getKeyLengthInOctets(encAlgorithm)];
+ hkdfGen.generateBytes(key, 0, key.length);
+
+ try
+ {
+ byte[] aad = Arrays.prepend(pubKey.getEncodedContents(), (byte) (0xC0 | packetTag));
+ SecretKey secretKey = new SecretKeySpec(key, PGPUtil.getSymmetricCipherName(encAlgorithm));
+ final Cipher c = aeadUtil.createAEADCipher(encAlgorithm, this.aeadAlgorithm);
+
+ JceAEADCipherUtil.setUpAeadCipher(c, secretKey, Cipher.ENCRYPT_MODE, iv, 128, aad);
+ byte[] data = c.doFinal(keyData, keyOff, keyLen);
+ return data;
+ }
+ catch (Exception e)
+ {
+ throw new PGPException("Exception AEAD protecting private key material", e);
+ }
+ }
+
+ @Override
+ public byte[] getCipherIV()
+ {
+ return iv;
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/jdk1.3/org/bouncycastle/cms/jcajce/JceKEMRecipientId.java b/pkix/src/main/jdk1.3/org/bouncycastle/cms/jcajce/JceKEMRecipientId.java
new file mode 100644
index 0000000000..f3f73ba28e
--- /dev/null
+++ b/pkix/src/main/jdk1.3/org/bouncycastle/cms/jcajce/JceKEMRecipientId.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.math.BigInteger;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cms.KEMRecipientId;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.jce.PrincipalUtil;
+
+public class JceKEMRecipientId
+ extends KEMRecipientId
+{
+ private static X509Principal extractIssuer(X509Certificate certificate)
+ {
+ try
+ {
+ return PrincipalUtil.getIssuerX509Principal(certificate);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException(e.toString());
+ }
+ }
+
+
+ /**
+ * Construct a recipient id based on the issuer, serial number and subject key identifier (if present) of the passed in
+ * certificate.
+ *
+ * @param certificate certificate providing the issue and serial number and subject key identifier.
+ */
+ public JceKEMRecipientId(X509Certificate certificate)
+ {
+ super(convertPrincipal(extractIssuer(certificate)), certificate.getSerialNumber(), CMSUtils.getSubjectKeyId(certificate));
+ }
+
+ /**
+ * Construct a recipient id based on the provided issuer and serial number..
+ *
+ * @param issuer the issuer to use.
+ * @param serialNumber the serial number to use.
+ */
+ public JceKEMRecipientId(X509Principal issuer, BigInteger serialNumber)
+ {
+ super(convertPrincipal(issuer), serialNumber);
+ }
+
+ /**
+ * Construct a recipient id based on the provided issuer, serial number, and subjectKeyId..
+ *
+ * @param issuer the issuer to use.
+ * @param serialNumber the serial number to use.
+ * @param subjectKeyId the subject key ID to use.
+ */
+ public JceKEMRecipientId(X509Principal issuer, BigInteger serialNumber, byte[] subjectKeyId)
+ {
+ super(convertPrincipal(issuer), serialNumber, subjectKeyId);
+ }
+
+ private static X500Name convertPrincipal(X509Principal issuer)
+ {
+ if (issuer == null)
+ {
+ return null;
+ }
+
+ return X500Name.getInstance(issuer.getEncoded());
+ }
+}
diff --git a/pkix/src/test/jdk1.3/org/bouncycastle/pkcs/test/PKCS10Test.java b/pkix/src/test/jdk1.3/org/bouncycastle/pkcs/test/PKCS10Test.java
index 9a296fa37a..8a18411856 100644
--- a/pkix/src/test/jdk1.3/org/bouncycastle/pkcs/test/PKCS10Test.java
+++ b/pkix/src/test/jdk1.3/org/bouncycastle/pkcs/test/PKCS10Test.java
@@ -22,6 +22,7 @@
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.spec.MLDSAParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
import org.bouncycastle.operator.ContentSigner;
@@ -34,7 +35,6 @@
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
-import org.bouncycastle.pqc.jcajce.spec.DilithiumParameterSpec;
import org.bouncycastle.test.PrintTestResult;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;
@@ -195,15 +195,15 @@ public void testAltRequestAttributes()
p256Kpg.initialize(new ECNamedCurveGenParameterSpec("P-256"));
KeyPair p256Kp = p256Kpg.generateKeyPair();
- KeyPairGenerator dilKpg = KeyPairGenerator.getInstance("Dilithium", "BC");
- dilKpg.initialize(DilithiumParameterSpec.dilithium2);
+ KeyPairGenerator dilKpg = KeyPairGenerator.getInstance("ML-DSA", "BC");
+ dilKpg.initialize(MLDSAParameterSpec.ml_dsa_44);
KeyPair dilKp = dilKpg.generateKeyPair();
JcaPKCS10CertificationRequestBuilder jcaPkcs10Builder = new JcaPKCS10CertificationRequestBuilder(new X500Name("CN=Test"), p256Kp.getPublic());
- ContentSigner altSigner = new JcaContentSignerBuilder("Dilithium2").setProvider("BC").build(dilKp.getPrivate());
+ ContentSigner altSigner = new JcaContentSignerBuilder("ML-DSA-44").setProvider("BC").build(dilKp.getPrivate());
- PKCS10CertificationRequest request = jcaPkcs10Builder.build(((JcaContentSignerBuilder)new JcaContentSignerBuilder("SHA256withECDSA").setProvider("BC")).build(p256Kp.getPrivate()), dilKp.getPublic(), altSigner);
+ PKCS10CertificationRequest request = jcaPkcs10Builder.build(new JcaContentSignerBuilder("SHA256withECDSA").setProvider("BC").build(p256Kp.getPrivate()), dilKp.getPublic(), altSigner);
assertTrue(request.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(p256Kp.getPublic())));
@@ -219,13 +219,13 @@ public void testDeltaRequestAttribute()
p256Kpg.initialize(new ECNamedCurveGenParameterSpec("P-256"));
KeyPair p256Kp = p256Kpg.generateKeyPair();
- KeyPairGenerator dilKpg = KeyPairGenerator.getInstance("Dilithium", "BC");
- dilKpg.initialize(DilithiumParameterSpec.dilithium2);
+ KeyPairGenerator dilKpg = KeyPairGenerator.getInstance("ML-DSA", "BC");
+ dilKpg.initialize(MLDSAParameterSpec.ml_dsa_44);
KeyPair dilKp = dilKpg.generateKeyPair();
PKCS10CertificationRequestBuilder pkcs10Builder = new JcaPKCS10CertificationRequestBuilder(new X500Name("CN=Test"), p256Kp.getPublic());
- ContentSigner deltaSigner = new JcaContentSignerBuilder("Dilithium2").setProvider("BC").build(dilKp.getPrivate());
+ ContentSigner deltaSigner = new JcaContentSignerBuilder("ML-DSA-44").setProvider("BC").build(dilKp.getPrivate());
DeltaCertificateRequestAttributeValueBuilder deltaAttrBldr = new DeltaCertificateRequestAttributeValueBuilder(
SubjectPublicKeyInfo.getInstance(dilKp.getPublic().getEncoded()));
diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMCipherSpi.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMCipherSpi.java
new file mode 100644
index 0000000000..8731c92a51
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMCipherSpi.java
@@ -0,0 +1,363 @@
+package org.bouncycastle.jcajce.provider.asymmetric.mlkem;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.SecretWithEncapsulation;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.jcajce.spec.KEMParameterSpec;
+import org.bouncycastle.jcajce.spec.KTSParameterSpec;
+import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMExtractor;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMGenerator;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters;
+import org.bouncycastle.pqc.jcajce.provider.util.WrapUtil;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Exceptions;
+
+class MLKEMCipherSpi
+ extends CipherSpi
+{
+ private final String algorithmName;
+ private MLKEMGenerator kemGen;
+ private KTSParameterSpec kemParameterSpec;
+ private BCMLKEMPublicKey wrapKey;
+ private BCMLKEMPrivateKey unwrapKey;
+
+ private AlgorithmParameters engineParams;
+ private MLKEMParameters mlkemParamters;
+
+ MLKEMCipherSpi(String algorithmName)
+ {
+ this.algorithmName = algorithmName;
+ this.mlkemParamters = null;
+ }
+
+ MLKEMCipherSpi(MLKEMParameters kyberParameters)
+ {
+ this.mlkemParamters = kyberParameters;
+ this.algorithmName = kyberParameters.getName();
+ }
+
+ @Override
+ protected void engineSetMode(String mode)
+ throws NoSuchAlgorithmException
+ {
+ throw new NoSuchAlgorithmException("Cannot support mode " + mode);
+ }
+
+ @Override
+ protected void engineSetPadding(String padding)
+ throws NoSuchPaddingException
+ {
+ throw new NoSuchPaddingException("Padding " + padding + " unknown");
+ }
+
+ protected int engineGetKeySize(
+ Key key)
+ {
+ return 2048; // TODO
+ //throw new IllegalArgumentException("not an valid key!");
+ }
+
+ @Override
+ protected int engineGetBlockSize()
+ {
+ return 0;
+ }
+
+ @Override
+ protected int engineGetOutputSize(int i)
+ {
+ return -1; // can't use with update/doFinal
+ }
+
+ @Override
+ protected byte[] engineGetIV()
+ {
+ return null;
+ }
+
+ @Override
+ protected AlgorithmParameters engineGetParameters()
+ {
+ if (engineParams == null)
+ {
+ try
+ {
+ engineParams = AlgorithmParameters.getInstance(algorithmName, "BCPQC");
+
+ engineParams.init(kemParameterSpec);
+ }
+ catch (Exception e)
+ {
+ throw Exceptions.illegalStateException(e.toString(), e);
+ }
+ }
+
+ return engineParams;
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, SecureRandom random)
+ throws InvalidKeyException
+ {
+ try
+ {
+ engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
+ }
+ catch (InvalidAlgorithmParameterException e)
+ {
+ throw Exceptions.illegalArgumentException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec paramSpec, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ if (paramSpec == null)
+ {
+ // TODO: default should probably use shake.
+ kemParameterSpec = new KEMParameterSpec("AES-KWP");
+ }
+ else
+ {
+ if (!(paramSpec instanceof KTSParameterSpec))
+ {
+ throw new InvalidAlgorithmParameterException(algorithmName + " can only accept KTSParameterSpec");
+ }
+
+ kemParameterSpec = (KTSParameterSpec)paramSpec;
+ }
+
+ if (opmode == Cipher.WRAP_MODE)
+ {
+ if (key instanceof BCMLKEMPublicKey)
+ {
+ wrapKey = (BCMLKEMPublicKey)key;
+ kemGen = new MLKEMGenerator(CryptoServicesRegistrar.getSecureRandom(random));
+ }
+ else
+ {
+ throw new InvalidKeyException("Only a " + algorithmName + " public key can be used for wrapping");
+ }
+ }
+ else if (opmode == Cipher.UNWRAP_MODE)
+ {
+ if (key instanceof BCMLKEMPrivateKey)
+ {
+ unwrapKey = (BCMLKEMPrivateKey)key;
+ }
+ else
+ {
+ throw new InvalidKeyException("Only a " + algorithmName + " private key can be used for unwrapping");
+ }
+ }
+ else
+ {
+ throw new InvalidParameterException("Cipher only valid for wrapping/unwrapping");
+ }
+
+ if (mlkemParamters != null)
+ {
+ String canonicalAlgName = MLKEMParameterSpec.fromName(mlkemParamters.getName()).getName();
+ if (!canonicalAlgName.equals(key.getAlgorithm()))
+ {
+ throw new InvalidKeyException("cipher locked to " + canonicalAlgName);
+ }
+ }
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameters algorithmParameters, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ AlgorithmParameterSpec paramSpec = null;
+
+ if (algorithmParameters != null)
+ {
+ try
+ {
+ paramSpec = algorithmParameters.getParameterSpec(KEMParameterSpec.class);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidAlgorithmParameterException("can't handle parameter " + algorithmParameters.toString());
+ }
+ }
+
+ engineInit(opmode, key, paramSpec, random);
+ }
+
+ @Override
+ protected byte[] engineUpdate(byte[] bytes, int i, int i1)
+ {
+ throw new IllegalStateException("Not supported in a wrapping mode");
+ }
+
+ @Override
+ protected int engineUpdate(byte[] bytes, int i, int i1, byte[] bytes1, int i2)
+ throws ShortBufferException
+ {
+ throw new IllegalStateException("Not supported in a wrapping mode");
+ }
+
+ @Override
+ protected byte[] engineDoFinal(byte[] bytes, int i, int i1)
+ throws IllegalBlockSizeException, BadPaddingException
+ {
+ throw new IllegalStateException("Not supported in a wrapping mode");
+ }
+
+ @Override
+ protected int engineDoFinal(byte[] bytes, int i, int i1, byte[] bytes1, int i2)
+ throws ShortBufferException, IllegalBlockSizeException, BadPaddingException
+ {
+ throw new IllegalStateException("Not supported in a wrapping mode");
+ }
+
+ protected byte[] engineWrap(
+ Key key)
+ throws IllegalBlockSizeException, InvalidKeyException
+ {
+ byte[] encoded = key.getEncoded();
+ if (encoded == null)
+ {
+ throw new InvalidKeyException("Cannot wrap key, null encoding.");
+ }
+
+ SecretWithEncapsulation secEnc = null;
+ try
+ {
+ secEnc = kemGen.generateEncapsulated(wrapKey.getKeyParams());
+
+ Wrapper kWrap = WrapUtil.getKeyWrapper(kemParameterSpec, secEnc.getSecret());
+
+ byte[] encapsulation = secEnc.getEncapsulation();
+
+ byte[] keyToWrap = key.getEncoded();
+
+ byte[] rv = Arrays.concatenate(encapsulation, kWrap.wrap(keyToWrap, 0, keyToWrap.length));
+
+ Arrays.clear(keyToWrap);
+
+ return rv;
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new IllegalBlockSizeException("unable to generate KTS secret: " + e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ if (secEnc != null)
+ {
+ secEnc.destroy();
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IllegalBlockSizeException("unable to destroy interim values: " + e.getMessage());
+ }
+ }
+ }
+
+ protected Key engineUnwrap(
+ byte[] wrappedKey,
+ String wrappedKeyAlgorithm,
+ int wrappedKeyType)
+ throws InvalidKeyException, NoSuchAlgorithmException
+ {
+ // TODO: add support for other types.
+ if (wrappedKeyType != Cipher.SECRET_KEY)
+ {
+ throw new InvalidKeyException("only SECRET_KEY supported");
+ }
+ byte[] secret = null;
+ try
+ {
+ MLKEMExtractor kemExt = new MLKEMExtractor(unwrapKey.getKeyParams());
+
+ secret = kemExt.extractSecret(Arrays.copyOfRange(wrappedKey, 0, kemExt.getEncapsulationLength()));
+
+ Wrapper kWrap = WrapUtil.getKeyUnwrapper(kemParameterSpec, secret);
+
+ byte[] keyEncBytes = Arrays.copyOfRange(wrappedKey, kemExt.getEncapsulationLength(), wrappedKey.length);
+
+ SecretKey rv = new SecretKeySpec(kWrap.unwrap(keyEncBytes, 0, keyEncBytes.length), wrappedKeyAlgorithm);
+
+ return rv;
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new NoSuchAlgorithmException("unable to extract KTS secret: " + e.getMessage());
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new InvalidKeyException("unable to extract KTS secret: " + e.getMessage());
+ }
+ finally
+ {
+ if (secret != null)
+ {
+ Arrays.clear(secret);
+ }
+ }
+ }
+
+ public static class Base
+ extends MLKEMCipherSpi
+ {
+ public Base()
+ throws NoSuchAlgorithmException
+ {
+ super("MLKEM");
+ }
+ }
+
+ public static class MLKEM512
+ extends MLKEMCipherSpi
+ {
+ public MLKEM512()
+ {
+ super(MLKEMParameters.ml_kem_512);
+ }
+ }
+
+ public static class MLKEM768
+ extends MLKEMCipherSpi
+ {
+ public MLKEM768()
+ {
+ super(MLKEMParameters.ml_kem_768);
+ }
+ }
+
+ public static class MLKEM1024
+ extends MLKEMCipherSpi
+ {
+ public MLKEM1024()
+ {
+ super(MLKEMParameters.ml_kem_1024);
+ }
+ }
+}
diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMKeyGeneratorSpi.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMKeyGeneratorSpi.java
new file mode 100644
index 0000000000..6b09d0f4f0
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMKeyGeneratorSpi.java
@@ -0,0 +1,159 @@
+package org.bouncycastle.jcajce.provider.asymmetric.mlkem;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.KeyGeneratorSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.crypto.SecretWithEncapsulation;
+import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
+import org.bouncycastle.jcajce.spec.KEMExtractSpec;
+import org.bouncycastle.jcajce.spec.KEMGenerateSpec;
+import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMExtractor;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMGenerator;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters;
+import org.bouncycastle.pqc.jcajce.provider.util.KdfUtil;
+import org.bouncycastle.util.Arrays;
+
+public class MLKEMKeyGeneratorSpi
+ extends KeyGeneratorSpi
+{
+ private KEMGenerateSpec genSpec;
+ private SecureRandom random;
+ private KEMExtractSpec extSpec;
+ private MLKEMParameters kyberParameters;
+
+ public MLKEMKeyGeneratorSpi()
+ {
+ this(null);
+ }
+
+ protected MLKEMKeyGeneratorSpi(MLKEMParameters kyberParameters)
+ {
+ this.kyberParameters = kyberParameters;
+ }
+
+ protected void engineInit(SecureRandom secureRandom)
+ {
+ throw new UnsupportedOperationException("Operation not supported");
+ }
+
+ protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec, SecureRandom secureRandom)
+ throws InvalidAlgorithmParameterException
+ {
+ this.random = secureRandom;
+ if (algorithmParameterSpec instanceof KEMGenerateSpec)
+ {
+ this.genSpec = (KEMGenerateSpec)algorithmParameterSpec;
+ this.extSpec = null;
+ if (kyberParameters != null)
+ {
+ String canonicalAlgName = MLKEMParameterSpec.fromName(kyberParameters.getName()).getName();
+ if (!canonicalAlgName.equals(genSpec.getPublicKey().getAlgorithm()))
+ {
+ throw new InvalidAlgorithmParameterException("key generator locked to " + canonicalAlgName);
+ }
+ }
+ }
+ else if (algorithmParameterSpec instanceof KEMExtractSpec)
+ {
+ this.genSpec = null;
+ this.extSpec = (KEMExtractSpec)algorithmParameterSpec;
+ if (kyberParameters != null)
+ {
+ String canonicalAlgName = MLKEMParameterSpec.fromName(kyberParameters.getName()).getName();
+ if (!canonicalAlgName.equals(extSpec.getPrivateKey().getAlgorithm()))
+ {
+ throw new InvalidAlgorithmParameterException("key generator locked to " + canonicalAlgName);
+ }
+ }
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("unknown spec");
+ }
+ }
+
+ protected void engineInit(int i, SecureRandom secureRandom)
+ {
+ throw new UnsupportedOperationException("Operation not supported");
+ }
+
+ protected SecretKey engineGenerateKey()
+ {
+ if (genSpec != null)
+ {
+ BCMLKEMPublicKey pubKey = (BCMLKEMPublicKey)genSpec.getPublicKey();
+ MLKEMGenerator kemGen = new MLKEMGenerator(random);
+
+ SecretWithEncapsulation secEnc = kemGen.generateEncapsulated(pubKey.getKeyParams());
+
+ byte[] sharedSecret = secEnc.getSecret();
+
+ byte[] secret = KdfUtil.makeKeyBytes(genSpec, sharedSecret);
+
+ Arrays.clear(sharedSecret);
+
+ SecretKey rv = new SecretKeyWithEncapsulation(new SecretKeySpec(secret, genSpec.getKeyAlgorithmName()), secEnc.getEncapsulation());
+
+ try
+ {
+ secEnc.destroy();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException("key cleanup failed");
+ }
+
+ return rv;
+ }
+ else
+ {
+ BCMLKEMPrivateKey privKey = (BCMLKEMPrivateKey)extSpec.getPrivateKey();
+ MLKEMExtractor kemExt = new MLKEMExtractor(privKey.getKeyParams());
+
+ byte[] encapsulation = extSpec.getEncapsulation();
+ byte[] sharedSecret = kemExt.extractSecret(encapsulation);
+ byte[] secret = KdfUtil.makeKeyBytes(extSpec, sharedSecret);
+
+ Arrays.clear(sharedSecret);
+
+ SecretKey rv = new SecretKeyWithEncapsulation(new SecretKeySpec(secret, extSpec.getKeyAlgorithmName()), encapsulation);
+
+ Arrays.clear(secret);
+
+ return rv;
+ }
+ }
+
+ public static class MLKEM512
+ extends MLKEMKeyGeneratorSpi
+ {
+ public MLKEM512()
+ {
+ super(MLKEMParameters.ml_kem_512);
+ }
+ }
+
+ public static class MLKEM768
+ extends MLKEMKeyGeneratorSpi
+ {
+ public MLKEM768()
+ {
+ super(MLKEMParameters.ml_kem_768);
+ }
+ }
+
+ public static class MLKEM1024
+ extends MLKEMKeyGeneratorSpi
+ {
+ public MLKEM1024()
+ {
+ super(MLKEMParameters.ml_kem_1024);
+ }
+ }
+}
diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jce/cert/X509CertSelector.java b/prov/src/main/jdk1.3/org/bouncycastle/jce/cert/X509CertSelector.java
index f971924e8d..5816c4bd1e 100644
--- a/prov/src/main/jdk1.3/org/bouncycastle/jce/cert/X509CertSelector.java
+++ b/prov/src/main/jdk1.3/org/bouncycastle/jce/cert/X509CertSelector.java
@@ -7,7 +7,6 @@
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -36,6 +35,7 @@
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.Arrays;
/**
* A CertSelector
that selects
@@ -1685,7 +1685,7 @@ public Collection getSubjectAlternativeNames()
data = obj.get(1);
if (data instanceof byte[])
{
- data = data.clone();
+ data = Arrays.clone((byte[])data);
}
returnList.add(data);
returnAltNames.add(returnList);
@@ -1809,7 +1809,7 @@ public Collection getPathToNames()
data = obj.get(1);
if (data instanceof byte[])
{
- data = data.clone();
+ data = Arrays.clone((byte[])data);
}
returnList.add(data);
returnPathToNames.add(returnList);
@@ -2023,12 +2023,12 @@ public boolean match(Certificate cert)
try
{
byte[] testData = ASN1OctetString.getInstance(data).getOctets();
- if (!Arrays.equals(subjectKeyID, testData))
+ if (!Arrays.areEqual(subjectKeyID, testData))
{
return false;
}
}
- catch (IOException ex)
+ catch (Exception ex)
{
return false;
}
@@ -2043,12 +2043,12 @@ public boolean match(Certificate cert)
try
{
byte[] testData = ASN1OctetString.getInstance(data).getOctets();
- if (!Arrays.equals(authorityKeyID, testData))
+ if (!Arrays.areEqual(authorityKeyID, testData))
{
return false;
}
}
- catch (IOException ex)
+ catch (Exception ex)
{
return false;
}
@@ -2113,7 +2113,7 @@ public boolean match(Certificate cert)
}
if (subjectPublicKeyByte != null)
{
- if (!Arrays.equals(subjectPublicKeyByte, certX509.getPublicKey().getEncoded()))
+ if (!Arrays.areEqual(subjectPublicKeyByte, certX509.getPublicKey().getEncoded()))
{
return false;
}
@@ -2302,7 +2302,7 @@ public Object clone()
}
if (subjectAltNames != null)
{
- copy.subjectAltNames = getSubjectAlternativeNames();
+ copy.subjectAltNames = (Set)getSubjectAlternativeNames();
Iterator iter = subjectAltNamesByte.iterator();
List obj;
List cloneObj;
@@ -2317,7 +2317,7 @@ public Object clone()
}
if (pathToNames != null)
{
- copy.pathToNames = getPathToNames();
+ copy.pathToNames = (Set)getPathToNames();
Iterator iter = pathToNamesByte.iterator();
List obj;
List cloneObj;
diff --git a/prov/src/main/jdk1.3/org/bouncycastle/pqc/jcajce/provider/kyber/KyberCipherSpi.java b/prov/src/main/jdk1.3/org/bouncycastle/pqc/jcajce/provider/kyber/KyberCipherSpi.java
index b5903870fc..a1090e3bcb 100644
--- a/prov/src/main/jdk1.3/org/bouncycastle/pqc/jcajce/provider/kyber/KyberCipherSpi.java
+++ b/prov/src/main/jdk1.3/org/bouncycastle/pqc/jcajce/provider/kyber/KyberCipherSpi.java
@@ -22,30 +22,38 @@
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.SecretWithEncapsulation;
import org.bouncycastle.crypto.Wrapper;
-import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jcajce.spec.KEMParameterSpec;
-import org.bouncycastle.pqc.crypto.mlkem.KyberKEMExtractor;
-import org.bouncycastle.pqc.crypto.mlkem.KyberKEMGenerator;
+import org.bouncycastle.jcajce.spec.KTSParameterSpec;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMExtractor;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMGenerator;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters;
import org.bouncycastle.pqc.jcajce.provider.util.WrapUtil;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Exceptions;
+import org.bouncycastle.util.Strings;
class KyberCipherSpi
extends CipherSpi
{
private final String algorithmName;
- private KyberKEMGenerator kemGen;
- private KEMParameterSpec kemParameterSpec;
+ private MLKEMGenerator kemGen;
+ private KTSParameterSpec kemParameterSpec;
private BCKyberPublicKey wrapKey;
private BCKyberPrivateKey unwrapKey;
- private SecureRandom random;
private AlgorithmParameters engineParams;
+ private MLKEMParameters kyberParameters;
KyberCipherSpi(String algorithmName)
- throws NoSuchAlgorithmException
{
this.algorithmName = algorithmName;
+ this.kyberParameters = null;
+ }
+
+ KyberCipherSpi(MLKEMParameters kyberParameters)
+ {
+ this.kyberParameters = kyberParameters;
+ this.algorithmName = Strings.toUpperCase(kyberParameters.getName());
}
@Override
@@ -125,11 +133,6 @@ protected void engineInit(int opmode, Key key, SecureRandom random)
protected void engineInit(int opmode, Key key, AlgorithmParameterSpec paramSpec, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
- if (random == null)
- {
- this.random = CryptoServicesRegistrar.getSecureRandom();
- }
-
if (paramSpec == null)
{
// TODO: default should probably use shake.
@@ -137,12 +140,12 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec paramSpec,
}
else
{
- if (!(paramSpec instanceof KEMParameterSpec))
+ if (!(paramSpec instanceof KTSParameterSpec))
{
throw new InvalidAlgorithmParameterException(algorithmName + " can only accept KTSParameterSpec");
}
- kemParameterSpec = (KEMParameterSpec)paramSpec;
+ kemParameterSpec = (KTSParameterSpec)paramSpec;
}
if (opmode == Cipher.WRAP_MODE)
@@ -150,11 +153,11 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec paramSpec,
if (key instanceof BCKyberPublicKey)
{
wrapKey = (BCKyberPublicKey)key;
- kemGen = new KyberKEMGenerator(random);
+ kemGen = new MLKEMGenerator(CryptoServicesRegistrar.getSecureRandom(random));
}
else
{
- throw new InvalidKeyException("Only an RSA public key can be used for wrapping");
+ throw new InvalidKeyException("Only a " + algorithmName + " public key can be used for wrapping");
}
}
else if (opmode == Cipher.UNWRAP_MODE)
@@ -165,17 +168,26 @@ else if (opmode == Cipher.UNWRAP_MODE)
}
else
{
- throw new InvalidKeyException("Only an RSA private key can be used for unwrapping");
+ throw new InvalidKeyException("Only a " + algorithmName + " private key can be used for unwrapping");
}
}
else
{
throw new InvalidParameterException("Cipher only valid for wrapping/unwrapping");
}
+
+ if (kyberParameters != null)
+ {
+ String canonicalAlgName = Strings.toUpperCase(kyberParameters.getName());
+ if (!canonicalAlgName.equals(key.getAlgorithm()))
+ {
+ throw new InvalidKeyException("cipher locked to " + canonicalAlgName);
+ }
+ }
}
@Override
- protected void engineInit(int opmode, Key key, AlgorithmParameters algorithmParameters, SecureRandom secureRandom)
+ protected void engineInit(int opmode, Key key, AlgorithmParameters algorithmParameters, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
AlgorithmParameterSpec paramSpec = null;
@@ -223,8 +235,8 @@ protected int engineDoFinal(byte[] bytes, int i, int i1, byte[] bytes1, int i2)
}
protected byte[] engineWrap(
- Key key)
- throws IllegalBlockSizeException, InvalidKeyException
+ Key key)
+ throws IllegalBlockSizeException, InvalidKeyException
{
byte[] encoded = key.getEncoded();
if (encoded == null)
@@ -232,20 +244,15 @@ protected byte[] engineWrap(
throw new InvalidKeyException("Cannot wrap key, null encoding.");
}
+ SecretWithEncapsulation secEnc = null;
try
{
- SecretWithEncapsulation secEnc = kemGen.generateEncapsulated(wrapKey.getKeyParams());
-
- Wrapper kWrap = WrapUtil.getWrapper(kemParameterSpec.getKeyAlgorithmName());
-
- KeyParameter keyParameter = new KeyParameter(secEnc.getSecret());
+ secEnc = kemGen.generateEncapsulated(wrapKey.getKeyParams());
- kWrap.init(true, keyParameter);
+ Wrapper kWrap = WrapUtil.getKeyWrapper(kemParameterSpec, secEnc.getSecret());
byte[] encapsulation = secEnc.getEncapsulation();
- secEnc.destroy();
-
byte[] keyToWrap = key.getEncoded();
byte[] rv = Arrays.concatenate(encapsulation, kWrap.wrap(keyToWrap, 0, keyToWrap.length));
@@ -258,9 +265,19 @@ protected byte[] engineWrap(
{
throw new IllegalBlockSizeException("unable to generate KTS secret: " + e.getMessage());
}
- catch (Exception e)
+ finally
{
- throw new IllegalBlockSizeException("unable to destroy interim values: " + e.getMessage());
+ try
+ {
+ if (secEnc != null)
+ {
+ secEnc.destroy();
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IllegalBlockSizeException("unable to destroy interim values: " + e.getMessage());
+ }
}
}
@@ -275,26 +292,19 @@ protected Key engineUnwrap(
{
throw new InvalidKeyException("only SECRET_KEY supported");
}
+ byte[] secret = null;
try
{
- KyberKEMExtractor kemExt = new KyberKEMExtractor(unwrapKey.getKeyParams());
-
- byte[] secret = kemExt.extractSecret(Arrays.copyOfRange(wrappedKey, 0, kemExt.getEncapsulationLength()));
+ MLKEMExtractor kemExt = new MLKEMExtractor(unwrapKey.getKeyParams());
- Wrapper kWrap = WrapUtil.getWrapper(kemParameterSpec.getKeyAlgorithmName());
+ secret = kemExt.extractSecret(Arrays.copyOfRange(wrappedKey, 0, kemExt.getEncapsulationLength()));
- KeyParameter keyParameter = new KeyParameter(secret);
-
- Arrays.clear(secret);
-
- kWrap.init(false, keyParameter);
+ Wrapper kWrap = WrapUtil.getKeyUnwrapper(kemParameterSpec, secret);
byte[] keyEncBytes = Arrays.copyOfRange(wrappedKey, kemExt.getEncapsulationLength(), wrappedKey.length);
SecretKey rv = new SecretKeySpec(kWrap.unwrap(keyEncBytes, 0, keyEncBytes.length), wrappedKeyAlgorithm);
- Arrays.clear(keyParameter.getKey());
-
return rv;
}
catch (IllegalArgumentException e)
@@ -305,6 +315,13 @@ protected Key engineUnwrap(
{
throw new InvalidKeyException("unable to extract KTS secret: " + e.getMessage());
}
+ finally
+ {
+ if (secret != null)
+ {
+ Arrays.clear(secret);
+ }
+ }
}
public static class Base
@@ -313,7 +330,34 @@ public static class Base
public Base()
throws NoSuchAlgorithmException
{
- super("Kyber");
+ super("KYBER");
+ }
+ }
+
+ public static class Kyber512
+ extends KyberCipherSpi
+ {
+ public Kyber512()
+ {
+ super(MLKEMParameters.ml_kem_512);
+ }
+ }
+
+ public static class Kyber768
+ extends KyberCipherSpi
+ {
+ public Kyber768()
+ {
+ super(MLKEMParameters.ml_kem_768);
+ }
+ }
+
+ public static class Kyber1024
+ extends KyberCipherSpi
+ {
+ public Kyber1024()
+ {
+ super(MLKEMParameters.ml_kem_1024);
}
}
}
diff --git a/prov/src/main/jdk1.3/org/bouncycastle/pqc/jcajce/provider/kyber/KyberKeyGeneratorSpi.java b/prov/src/main/jdk1.3/org/bouncycastle/pqc/jcajce/provider/kyber/KyberKeyGeneratorSpi.java
index 2962f97d02..33355b88a5 100644
--- a/prov/src/main/jdk1.3/org/bouncycastle/pqc/jcajce/provider/kyber/KyberKeyGeneratorSpi.java
+++ b/prov/src/main/jdk1.3/org/bouncycastle/pqc/jcajce/provider/kyber/KyberKeyGeneratorSpi.java
@@ -12,9 +12,11 @@
import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
import org.bouncycastle.jcajce.spec.KEMExtractSpec;
import org.bouncycastle.jcajce.spec.KEMGenerateSpec;
-import org.bouncycastle.pqc.crypto.mlkem.KyberKEMExtractor;
-import org.bouncycastle.pqc.crypto.mlkem.KyberKEMGenerator;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMExtractor;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMGenerator;
+import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters;
import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
public class KyberKeyGeneratorSpi
extends KeyGeneratorSpi
@@ -22,6 +24,17 @@ public class KyberKeyGeneratorSpi
private KEMGenerateSpec genSpec;
private SecureRandom random;
private KEMExtractSpec extSpec;
+ private MLKEMParameters kyberParameters;
+
+ public KyberKeyGeneratorSpi()
+ {
+ this(null);
+ }
+
+ protected KyberKeyGeneratorSpi(MLKEMParameters kyberParameters)
+ {
+ this.kyberParameters = kyberParameters;
+ }
protected void engineInit(SecureRandom secureRandom)
{
@@ -36,11 +49,27 @@ protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec, SecureR
{
this.genSpec = (KEMGenerateSpec)algorithmParameterSpec;
this.extSpec = null;
+ if (kyberParameters != null)
+ {
+ String canonicalAlgName = Strings.toUpperCase(kyberParameters.getName());
+ if (!canonicalAlgName.equals(genSpec.getPublicKey().getAlgorithm()))
+ {
+ throw new InvalidAlgorithmParameterException("key generator locked to " + canonicalAlgName);
+ }
+ }
}
else if (algorithmParameterSpec instanceof KEMExtractSpec)
{
this.genSpec = null;
this.extSpec = (KEMExtractSpec)algorithmParameterSpec;
+ if (kyberParameters != null)
+ {
+ String canonicalAlgName = Strings.toUpperCase(kyberParameters.getName());
+ if (!canonicalAlgName.equals(extSpec.getPrivateKey().getAlgorithm()))
+ {
+ throw new InvalidAlgorithmParameterException("key generator locked to " + canonicalAlgName);
+ }
+ }
}
else
{
@@ -58,11 +87,16 @@ protected SecretKey engineGenerateKey()
if (genSpec != null)
{
BCKyberPublicKey pubKey = (BCKyberPublicKey)genSpec.getPublicKey();
- KyberKEMGenerator kemGen = new KyberKEMGenerator(random);
+ MLKEMGenerator kemGen = new MLKEMGenerator(random);
SecretWithEncapsulation secEnc = kemGen.generateEncapsulated(pubKey.getKeyParams());
- SecretKey rv = new SecretKeyWithEncapsulation(new SecretKeySpec(secEnc.getSecret(), genSpec.getKeyAlgorithmName()), secEnc.getEncapsulation());
+ byte[] sharedSecret = secEnc.getSecret();
+ byte[] secret = Arrays.copyOfRange(sharedSecret, 0, (genSpec.getKeySize() + 7) / 8);
+
+ Arrays.clear(sharedSecret);
+
+ SecretKey rv = new SecretKeyWithEncapsulation(new SecretKeySpec(secret, genSpec.getKeyAlgorithmName()), secEnc.getEncapsulation());
try
{
@@ -78,10 +112,13 @@ protected SecretKey engineGenerateKey()
else
{
BCKyberPrivateKey privKey = (BCKyberPrivateKey)extSpec.getPrivateKey();
- KyberKEMExtractor kemExt = new KyberKEMExtractor(privKey.getKeyParams());
+ MLKEMExtractor kemExt = new MLKEMExtractor(privKey.getKeyParams());
byte[] encapsulation = extSpec.getEncapsulation();
- byte[] secret = kemExt.extractSecret(encapsulation);
+ byte[] sharedSecret = kemExt.extractSecret(encapsulation);
+ byte[] secret = Arrays.copyOfRange(sharedSecret, 0, (extSpec.getKeySize() + 7) / 8);
+
+ Arrays.clear(sharedSecret);
SecretKey rv = new SecretKeyWithEncapsulation(new SecretKeySpec(secret, extSpec.getKeyAlgorithmName()), encapsulation);
@@ -90,4 +127,31 @@ protected SecretKey engineGenerateKey()
return rv;
}
}
+
+ public static class Kyber512
+ extends KyberKeyGeneratorSpi
+ {
+ public Kyber512()
+ {
+ super(MLKEMParameters.ml_kem_512);
+ }
+ }
+
+ public static class Kyber768
+ extends KyberKeyGeneratorSpi
+ {
+ public Kyber768()
+ {
+ super(MLKEMParameters.ml_kem_768);
+ }
+ }
+
+ public static class Kyber1024
+ extends KyberKeyGeneratorSpi
+ {
+ public Kyber1024()
+ {
+ super(MLKEMParameters.ml_kem_1024);
+ }
+ }
}