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); + } + } }