BouncyCastle 1.80 source files, unmodified, as a base for future merges

will not compile
This commit is contained in:
zzz
2025-06-13 08:48:34 -04:00
parent 4d1ba03823
commit 1705b6e25e
47 changed files with 4916 additions and 0 deletions

View File

@ -0,0 +1,61 @@
package org.bouncycastle.crypto;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
/**
* a holding class for public/private parameter pairs.
*/
public class AsymmetricCipherKeyPair
{
private AsymmetricKeyParameter publicParam;
private AsymmetricKeyParameter privateParam;
/**
* basic constructor.
*
* @param publicParam a public key parameters object.
* @param privateParam the corresponding private key parameters.
*/
public AsymmetricCipherKeyPair(
AsymmetricKeyParameter publicParam,
AsymmetricKeyParameter privateParam)
{
this.publicParam = publicParam;
this.privateParam = privateParam;
}
/**
* basic constructor.
*
* @param publicParam a public key parameters object.
* @param privateParam the corresponding private key parameters.
* @deprecated use AsymmetricKeyParameter
*/
public AsymmetricCipherKeyPair(
CipherParameters publicParam,
CipherParameters privateParam)
{
this.publicParam = (AsymmetricKeyParameter)publicParam;
this.privateParam = (AsymmetricKeyParameter)privateParam;
}
/**
* return the public key parameters.
*
* @return the public key parameters.
*/
public AsymmetricKeyParameter getPublic()
{
return publicParam;
}
/**
* return the private key parameters.
*
* @return the private key parameters.
*/
public AsymmetricKeyParameter getPrivate()
{
return privateParam;
}
}

View File

@ -0,0 +1,22 @@
package org.bouncycastle.crypto;
/**
* interface that a public/private key pair generator should conform to.
*/
public interface AsymmetricCipherKeyPairGenerator
{
/**
* intialise the key pair generator.
*
* @param param the parameters the key pair is to be initialised with.
*/
public void init(KeyGenerationParameters param);
/**
* return an AsymmetricCipherKeyPair containing the generated keys.
*
* @return an AsymmetricCipherKeyPair containing the generated keys.
*/
public AsymmetricCipherKeyPair generateKeyPair();
}

View File

@ -0,0 +1,8 @@
package org.bouncycastle.crypto;
/**
* all parameter classes implement this.
*/
public interface CipherParameters
{
}

View File

@ -0,0 +1,48 @@
package org.bouncycastle.crypto;
/**
* the foundation class for the hard exceptions thrown by the crypto packages.
*/
public class CryptoException
extends Exception
{
private Throwable cause;
/**
* base constructor.
*/
public CryptoException()
{
}
/**
* create a CryptoException with the given message.
*
* @param message the message to be carried with the exception.
*/
public CryptoException(
String message)
{
super(message);
}
/**
* Create a CryptoException with the given message and underlying cause.
*
* @param message message describing exception.
* @param cause the throwable that was the underlying cause.
*/
public CryptoException(
String message,
Throwable cause)
{
super(message);
this.cause = cause;
}
public Throwable getCause()
{
return cause;
}
}

View File

@ -0,0 +1,12 @@
package org.bouncycastle.crypto;
public interface CryptoServiceProperties
{
int bitsOfSecurity();
String getServiceName();
CryptoServicePurpose getPurpose();
Object getParams();
}

View File

@ -0,0 +1,15 @@
package org.bouncycastle.crypto;
public enum CryptoServicePurpose
{
AGREEMENT,
ENCRYPTION,
DECRYPTION,
KEYGEN,
SIGNING, // for signatures (and digests)
VERIFYING,
AUTHENTICATION, // for MACs (and digests)
VERIFICATION,
PRF,
ANY
}

View File

@ -0,0 +1,541 @@
package org.bouncycastle.crypto;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHValidationParameters;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAValidationParameters;
import org.bouncycastle.util.Properties;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;
/**
* Basic registrar class for providing defaults for cryptography services in this module.
*/
public final class CryptoServicesRegistrar
{
private static final Logger LOG = Logger.getLogger(CryptoServicesRegistrar.class.getName());
private static final Permission CanSetDefaultProperty = new CryptoServicesPermission(CryptoServicesPermission.GLOBAL_CONFIG);
private static final Permission CanSetThreadProperty = new CryptoServicesPermission(CryptoServicesPermission.THREAD_LOCAL_CONFIG);
private static final Permission CanSetDefaultRandom = new CryptoServicesPermission(CryptoServicesPermission.DEFAULT_RANDOM);
private static final Permission CanSetConstraints = new CryptoServicesPermission(CryptoServicesPermission.CONSTRAINTS);
private static final ThreadLocal<Map<String, Object[]>> threadProperties = new ThreadLocal<Map<String, Object[]>>();
private static final Map<String, Object[]> globalProperties = Collections.synchronizedMap(new HashMap<String, Object[]>());
private static final SecureRandomProvider defaultRandomProviderImpl = new ThreadLocalSecureRandomProvider();
private static final CryptoServicesConstraints noConstraintsImpl = new CryptoServicesConstraints()
{
public void check(CryptoServiceProperties service)
{
// anything goes.
}
};
private static final AtomicReference<SecureRandomProvider> defaultSecureRandomProvider = new AtomicReference<SecureRandomProvider>();
private static final boolean preconfiguredConstraints;
private static final AtomicReference<CryptoServicesConstraints> servicesConstraints = new AtomicReference<CryptoServicesConstraints>();
static
{
// default domain parameters for DSA and Diffie-Hellman
DSAParameters def512Params = new DSAParameters(
new BigInteger("fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e17", 16),
new BigInteger("962eddcc369cba8ebb260ee6b6a126d9346e38c5", 16),
new BigInteger("678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4", 16),
new DSAValidationParameters(Hex.decodeStrict("b869c82b35d70e1b1ff91b28e37a62ecdc34409b"), 123));
DSAParameters def768Params = new DSAParameters(
new BigInteger("e9e642599d355f37c97ffd3567120b8e25c9cd43e927b3a9670fbec5" +
"d890141922d2c3b3ad2480093799869d1e846aab49fab0ad26d2ce6a" +
"22219d470bce7d777d4a21fbe9c270b57f607002f3cef8393694cf45" +
"ee3688c11a8c56ab127a3daf", 16),
new BigInteger("9cdbd84c9f1ac2f38d0f80f42ab952e7338bf511", 16),
new BigInteger("30470ad5a005fb14ce2d9dcd87e38bc7d1b1c5facbaecbe95f190aa7" +
"a31d23c4dbbcbe06174544401a5b2c020965d8c2bd2171d366844577" +
"1f74ba084d2029d83c1c158547f3a9f1a2715be23d51ae4d3e5a1f6a" +
"7064f316933a346d3f529252", 16),
new DSAValidationParameters(Hex.decodeStrict("77d0f8c4dad15eb8c4f2f8d6726cefd96d5bb399"), 263));
DSAParameters def1024Params = new DSAParameters(
new BigInteger("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80" +
"b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b" +
"801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" +
"1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675" +
"f3ae2b61d72aeff22203199dd14801c7", 16),
new BigInteger("9760508f15230bccb292b982a2eb840bf0581cf5", 16),
new BigInteger("f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b" +
"3d0782675159578ebad4594fe67107108180b449167123e84c281613" +
"b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" +
"0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06" +
"928b665e807b552564014c3bfecf492a", 16),
new DSAValidationParameters(Hex.decodeStrict("8d5155894229d5e689ee01e6018a237e2cae64cd"), 92));
DSAParameters def2048Params = new DSAParameters(
new BigInteger("95475cf5d93e596c3fcd1d902add02f427f5f3c7210313bb45fb4d5b" +
"b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
"8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
"c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
"65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
"e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
"506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
"147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
"50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
"a406cb0b", 16),
new BigInteger("f8183668ba5fc5bb06b5981e6d8b795d30b8978d43ca0ec572e37e09939a9773", 16),
new BigInteger("42debb9da5b3d88cc956e08787ec3f3a09bba5f48b889a74aaf53174" +
"aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
"c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
"4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
"f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
"beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
"b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
"ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
"e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
"78693c7a", 16),
new DSAValidationParameters(Hex.decodeStrict("b0b4417601b59cbc9d8ac8f935cadaec4f5fbb2f23785609ae466748d9b5a536"), 497));
localSetGlobalProperty(Property.DSA_DEFAULT_PARAMS, def512Params, def768Params, def1024Params, def2048Params);
localSetGlobalProperty(Property.DH_DEFAULT_PARAMS, toDH(def512Params), toDH(def768Params), toDH(def1024Params), toDH(def2048Params));
servicesConstraints.set(getDefaultConstraints());
preconfiguredConstraints = (servicesConstraints.get() != noConstraintsImpl);
}
private CryptoServicesRegistrar()
{
}
/**
* Return the default source of randomness.
*
* @return the default SecureRandom
*/
public static SecureRandom getSecureRandom()
{
defaultSecureRandomProvider.compareAndSet(null, defaultRandomProviderImpl);
return defaultSecureRandomProvider.get().get();
}
/**
* Return either the passed-in SecureRandom, or if it is null, then the default source of randomness.
*
* @param secureRandom the SecureRandom to use if it is not null.
* @return the SecureRandom parameter if it is not null, or else the default SecureRandom
*/
public static SecureRandom getSecureRandom(SecureRandom secureRandom)
{
return null == secureRandom ? getSecureRandom() : secureRandom;
}
/**
* Set a default secure random to be used where none is otherwise provided.
*
* @param secureRandom the SecureRandom to use as the default.
*/
public static void setSecureRandom(final SecureRandom secureRandom)
{
checkPermission(CanSetDefaultRandom);
if (secureRandom == null)
{
defaultSecureRandomProvider.set(defaultRandomProviderImpl);
}
else
{
defaultSecureRandomProvider.set(new SecureRandomProvider()
{
public SecureRandom get()
{
return secureRandom;
}
});
}
}
/**
* Set a default secure random provider to be used where none is otherwise provided.
*
* @param secureRandomProvider a provider SecureRandom to use when a default SecureRandom is requested.
*/
public static void setSecureRandomProvider(SecureRandomProvider secureRandomProvider)
{
checkPermission(CanSetDefaultRandom);
defaultSecureRandomProvider.set(secureRandomProvider);
}
/**
* Return the current algorithm/services constraints.
*
* @return the algorithm/services constraints.
*/
public static CryptoServicesConstraints getServicesConstraints()
{
return servicesConstraints.get();
}
/**
* Check a service to make sure it meets the current constraints.
*
* @param cryptoService the service to be checked.
* @throws CryptoServiceConstraintsException if the service violates the current constraints.
*/
public static void checkConstraints(CryptoServiceProperties cryptoService)
{
servicesConstraints.get().check(cryptoService);
}
/**
* Set the current algorithm constraints.
*/
public static void setServicesConstraints(CryptoServicesConstraints constraints)
{
checkPermission(CanSetConstraints);
CryptoServicesConstraints newConstraints = (constraints == null) ? noConstraintsImpl : constraints;
if (preconfiguredConstraints)
{
if (Properties.isOverrideSet("org.bouncycastle.constraints.allow_override"))
{
servicesConstraints.set(newConstraints);
}
else
{
LOG.warning("attempt to override pre-configured constraints ignored");
}
}
else
{
// TODO: should this only be allowed once?
servicesConstraints.set(newConstraints);
}
}
/**
* Return the default value for a particular property if one exists. The look up is done on the thread's local
* configuration first and then on the global configuration in no local configuration exists.
*
* @param property the property to look up.
* @param <T> the type to be returned
* @return null if the property is not set, the default value otherwise,
*/
public static <T> T getProperty(Property property)
{
Object[] values = lookupProperty(property);
if (values != null)
{
return (T)values[0];
}
return null;
}
private static Object[] lookupProperty(Property property)
{
Map<String, Object[]> properties = threadProperties.get();
Object[] values;
if (properties == null || !properties.containsKey(property.name))
{
values = globalProperties.get(property.name);
}
else
{
values = properties.get(property.name);
}
return values;
}
/**
* Return an array representing the current values for a sized property such as DH_DEFAULT_PARAMS or
* DSA_DEFAULT_PARAMS.
*
* @param property the name of the property to look up.
* @param <T> the base type of the array to be returned.
* @return null if the property is not set, an array of the current values otherwise.
*/
public static <T> T[] getSizedProperty(Property property)
{
Object[] values = lookupProperty(property);
if (values == null)
{
return null;
}
return (T[])values.clone();
}
/**
* Return the value for a specific size for a sized property such as DH_DEFAULT_PARAMS or
* DSA_DEFAULT_PARAMS.
*
* @param property the name of the property to look up.
* @param size the size (in bits) of the defining value in the property type.
* @param <T> the type of the value to be returned.
* @return the current value for the size, null if there is no value set,
*/
public static <T> T getSizedProperty(Property property, int size)
{
Object[] values = lookupProperty(property);
if (values == null)
{
return null;
}
if (property.type.isAssignableFrom(DHParameters.class))
{
for (int i = 0; i != values.length; i++)
{
DHParameters params = (DHParameters)values[i];
if (params.getP().bitLength() == size)
{
return (T)params;
}
}
}
else if (property.type.isAssignableFrom(DSAParameters.class))
{
for (int i = 0; i != values.length; i++)
{
DSAParameters params = (DSAParameters)values[i];
if (params.getP().bitLength() == size)
{
return (T)params;
}
}
}
return null;
}
/**
* Set the value of the the passed in property on the current thread only. More than
* one value can be passed in for a sized property. If more than one value is provided the
* first value in the argument list becomes the default value.
*
* @param property the name of the property to set.
* @param propertyValue the values to assign to the property.
* @param <T> the base type of the property value.
*/
public static <T> void setThreadProperty(Property property, T... propertyValue)
{
checkPermission(CanSetThreadProperty);
if (!property.type.isAssignableFrom(propertyValue[0].getClass()))
{
throw new IllegalArgumentException("Bad property value passed");
}
localSetThread(property, propertyValue.clone());
}
/**
* Set the value of the the passed in property globally in the JVM. More than
* one value can be passed in for a sized property. If more than one value is provided the
* first value in the argument list becomes the default value.
*
* @param property the name of the property to set.
* @param propertyValue the values to assign to the property.
* @param <T> the base type of the property value.
*/
public static <T> void setGlobalProperty(Property property, T... propertyValue)
{
checkPermission(CanSetDefaultProperty);
localSetGlobalProperty(property, propertyValue.clone());
}
private static <T> void localSetThread(Property property, T[] propertyValue)
{
Map<String, Object[]> properties = threadProperties.get();
if (properties == null)
{
properties = new HashMap<String, Object[]>();
threadProperties.set(properties);
}
properties.put(property.name, propertyValue);
}
private static <T> void localSetGlobalProperty(Property property, T... propertyValue)
{
if (!property.type.isAssignableFrom(propertyValue[0].getClass()))
{
throw new IllegalArgumentException("Bad property value passed");
}
// set the property for the current thread as well to avoid mass confusion
localSetThread(property, propertyValue);
globalProperties.put(property.name, propertyValue);
}
/**
* Clear the global value for the passed in property.
*
* @param property the property to be cleared.
* @param <T> the base type of the property value
* @return an array of T if a value was previously set, null otherwise.
*/
public static <T> T[] clearGlobalProperty(Property property)
{
checkPermission(CanSetDefaultProperty);
// clear the property for the current thread as well to avoid confusion
localClearThreadProperty(property);
return (T[])globalProperties.remove(property.name);
}
/**
* Clear the thread local value for the passed in property.
*
* @param property the property to be cleared.
* @param <T> the base type of the property value
* @return an array of T if a value was previously set, null otherwise.
*/
public static <T> T[] clearThreadProperty(Property property)
{
checkPermission(CanSetThreadProperty);
return (T[])localClearThreadProperty(property);
}
private static Object[] localClearThreadProperty(Property property)
{
Map<String, Object[]> properties = threadProperties.get();
if (properties == null)
{
properties = new HashMap<String, Object[]>();
threadProperties.set(properties);
}
return properties.remove(property.name);
}
private static void checkPermission(final Permission permission)
{
final SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null)
{
AccessController.doPrivileged(new PrivilegedAction<Object>()
{
public Object run()
{
securityManager.checkPermission(permission);
return null;
}
});
}
}
private static DHParameters toDH(DSAParameters dsaParams)
{
int pSize = dsaParams.getP().bitLength();
int m = chooseLowerBound(pSize);
return new DHParameters(dsaParams.getP(), dsaParams.getG(), dsaParams.getQ(), m, 0, null,
new DHValidationParameters(dsaParams.getValidationParameters().getSeed(), dsaParams.getValidationParameters().getCounter()));
}
// based on lower limit of at least 2^{2 * bits_of_security}
private static int chooseLowerBound(int pSize)
{
int m = 160;
if (pSize > 1024)
{
if (pSize <= 2048)
{
m = 224;
}
else if (pSize <= 3072)
{
m = 256;
}
else if (pSize <= 7680)
{
m = 384;
}
else
{
m = 512;
}
}
return m;
}
private static CryptoServicesConstraints getDefaultConstraints()
{
// TODO: return one based on system/security properties if set.
return noConstraintsImpl;
}
/**
* Available properties that can be set.
*/
public static final class Property
{
/**
* The parameters to be used for processing implicitlyCA X9.62 parameters
*/
public static final Property EC_IMPLICITLY_CA = new Property("ecImplicitlyCA", X9ECParameters.class);
/**
* The default parameters for a particular size of Diffie-Hellman key.This is a sized property.
*/
public static final Property DH_DEFAULT_PARAMS = new Property("dhDefaultParams", DHParameters.class);
/**
* The default parameters for a particular size of DSA key. This is a sized property.
*/
public static final Property DSA_DEFAULT_PARAMS = new Property("dsaDefaultParams", DSAParameters.class);
private final String name;
private final Class type;
private Property(String name, Class type)
{
this.name = name;
this.type = type;
}
}
private static class ThreadLocalSecureRandomProvider
implements SecureRandomProvider
{
final ThreadLocal<SecureRandom> defaultRandoms = new ThreadLocal<SecureRandom>();
public SecureRandom get()
{
if (defaultRandoms.get() == null)
{
defaultRandoms.set(new SecureRandom());
}
return defaultRandoms.get();
}
}
}

View File

@ -0,0 +1,29 @@
package org.bouncycastle.crypto;
/**
* this exception is thrown if a buffer that is meant to have output
* copied into it turns out to be too short, or if we've been given
* insufficient input. In general this exception will get thrown rather
* than an ArrayOutOfBounds exception.
*/
public class DataLengthException
extends RuntimeCryptoException
{
/**
* base constructor.
*/
public DataLengthException()
{
}
/**
* create a DataLengthException with the given message.
*
* @param message the message to be carried with the exception.
*/
public DataLengthException(
String message)
{
super(message);
}
}

View File

@ -0,0 +1,51 @@
package org.bouncycastle.crypto;
/**
* interface that a message digest conforms to.
*/
public interface Digest
{
/**
* return the algorithm name
*
* @return the algorithm name
*/
public String getAlgorithmName();
/**
* return the size, in bytes, of the digest produced by this message digest.
*
* @return the size, in bytes, of the digest produced by this message digest.
*/
public int getDigestSize();
/**
* update the message digest with a single byte.
*
* @param in the input byte to be entered.
*/
public void update(byte in);
/**
* update the message digest with a block of bytes.
*
* @param in the byte array containing the data.
* @param inOff the offset into the byte array where the data starts.
* @param len the length of the data.
*/
public void update(byte[] in, int inOff, int len);
/**
* close the digest, producing the final digest value. The doFinal
* call leaves the digest reset.
*
* @param out the array the digest is to be copied into.
* @param outOff the offset into the out array the digest is to start at.
*/
public int doFinal(byte[] out, int outOff);
/**
* reset the digest back to it's initial state.
*/
public void reset();
}

View File

@ -0,0 +1,13 @@
package org.bouncycastle.crypto;
public interface ExtendedDigest
extends Digest
{
/**
* Return the size in bytes of the internal buffer the digest applies it's compression
* function to.
*
* @return byte length of the digests internal buffer.
*/
public int getByteLength();
}

View File

@ -0,0 +1,48 @@
package org.bouncycastle.crypto;
import java.security.SecureRandom;
/**
* The base class for parameters to key generators.
*/
public class KeyGenerationParameters
{
private SecureRandom random;
private int strength;
/**
* initialise the generator with a source of randomness
* and a strength (in bits).
*
* @param random the random byte source.
* @param strength the size, in bits, of the keys we want to produce.
*/
public KeyGenerationParameters(
SecureRandom random,
int strength)
{
this.random = CryptoServicesRegistrar.getSecureRandom(random);
this.strength = strength;
}
/**
* return the random source associated with this
* generator.
*
* @return the generators random source.
*/
public SecureRandom getRandom()
{
return random;
}
/**
* return the bit strength for keys produced by this generator,
*
* @return the strength of the keys this generator produces (in bits).
*/
public int getStrength()
{
return strength;
}
}

View File

@ -0,0 +1,26 @@
package org.bouncycastle.crypto;
/**
* the foundation class for the exceptions thrown by the crypto packages.
*/
public class RuntimeCryptoException
extends RuntimeException
{
/**
* base constructor.
*/
public RuntimeCryptoException()
{
}
/**
* create a RuntimeCryptoException with the given message.
*
* @param message the message to be carried with the exception.
*/
public RuntimeCryptoException(
String message)
{
super(message);
}
}

View File

@ -0,0 +1,24 @@
package org.bouncycastle.crypto;
import javax.security.auth.Destroyable;
/**
* Interface describing secret with encapsulation details.
*/
public interface SecretWithEncapsulation
extends Destroyable
{
/**
* Return the secret associated with the encapsulation.
*
* @return the secret the encapsulation is for.
*/
byte[] getSecret();
/**
* Return the data that carries the secret in its encapsulated form.
*
* @return the encapsulation of the secret.
*/
byte[] getEncapsulation();
}

View File

@ -0,0 +1,30 @@
package org.bouncycastle.crypto;
/**
* With FIPS PUB 202 a new kind of message digest was announced which supported extendable output, or variable digest sizes.
* This interface provides the extra method required to support variable output on an extended digest implementation.
*/
public interface Xof
extends ExtendedDigest
{
/**
* Output the results of the final calculation for this digest to outLen number of bytes.
*
* @param out output array to write the output bytes to.
* @param outOff offset to start writing the bytes at.
* @param outLen the number of output bytes requested.
* @return the number of bytes written
*/
int doFinal(byte[] out, int outOff, int outLen);
/**
* Start outputting the results of the final calculation for this digest. Unlike doFinal, this method
* will continue producing output until the Xof is explicitly reset, or signals otherwise.
*
* @param out output array to write the output bytes to.
* @param outOff offset to start writing the bytes at.
* @param outLen the number of output bytes requested.
* @return the number of bytes written
*/
int doOutput(byte[] out, int outOff, int outLen);
}

View File

@ -0,0 +1,17 @@
package org.bouncycastle.crypto.digests;
/**
* Encodable digests allow you to download an encoded copy of their internal state. This is useful for the situation where
* you need to generate a signature on an external device and it allows for "sign with last round", so a copy of the
* internal state of the digest, plus the last few blocks of the message are all that needs to be sent, rather than the
* entire message.
*/
public interface EncodableDigest
{
/**
* Return an encoded byte array for the digest's internal state
*
* @return an encoding of the digests internal state.
*/
byte[] getEncodedState();
}

View File

@ -0,0 +1,443 @@
package org.bouncycastle.crypto.digests;
import org.bouncycastle.crypto.CryptoServiceProperties;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.ExtendedDigest;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;
/**
* implementation of Keccak based on following KeccakNISTInterface.c from https://keccak.noekeon.org/
* <p>
* Following the naming conventions used in the C source code to enable easy review of the implementation.
*/
public class KeccakDigest
implements ExtendedDigest
{
private static long[] KeccakRoundConstants = new long[]{ 0x0000000000000001L, 0x0000000000008082L,
0x800000000000808aL, 0x8000000080008000L, 0x000000000000808bL, 0x0000000080000001L, 0x8000000080008081L,
0x8000000000008009L, 0x000000000000008aL, 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000aL,
0x000000008000808bL, 0x800000000000008bL, 0x8000000000008089L, 0x8000000000008003L, 0x8000000000008002L,
0x8000000000000080L, 0x000000000000800aL, 0x800000008000000aL, 0x8000000080008081L, 0x8000000000008080L,
0x0000000080000001L, 0x8000000080008008L };
protected final CryptoServicePurpose purpose;
protected long[] state = new long[25];
protected byte[] dataQueue = new byte[192];
protected int rate;
protected int bitsInQueue;
protected int fixedOutputLength;
protected boolean squeezing;
public KeccakDigest()
{
this(288, CryptoServicePurpose.ANY);
}
public KeccakDigest(CryptoServicePurpose purpose)
{
this(288, purpose);
}
public KeccakDigest(int bitLength)
{
this(bitLength, CryptoServicePurpose.ANY);
}
public KeccakDigest(int bitLength, CryptoServicePurpose purpose)
{
this.purpose = purpose;
init(bitLength);
CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
}
public KeccakDigest(KeccakDigest source)
{
this.purpose = source.purpose;
System.arraycopy(source.state, 0, this.state, 0, source.state.length);
System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length);
this.rate = source.rate;
this.bitsInQueue = source.bitsInQueue;
this.fixedOutputLength = source.fixedOutputLength;
this.squeezing = source.squeezing;
CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
}
public String getAlgorithmName()
{
return "Keccak-" + fixedOutputLength;
}
public int getDigestSize()
{
return fixedOutputLength / 8;
}
public void update(byte in)
{
absorb(in);
}
public void update(byte[] in, int inOff, int len)
{
absorb(in, inOff, len);
}
public int doFinal(byte[] out, int outOff)
{
squeeze(out, outOff, fixedOutputLength);
reset();
return getDigestSize();
}
/*
* TODO Possible API change to support partial-byte suffixes.
*/
protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits)
{
if (partialBits > 0)
{
absorbBits(partialByte, partialBits);
}
squeeze(out, outOff, fixedOutputLength);
reset();
return getDigestSize();
}
public void reset()
{
init(fixedOutputLength);
}
/**
* Return the size of block that the compression function is applied to in bytes.
*
* @return internal byte length of a block.
*/
public int getByteLength()
{
return rate / 8;
}
private void init(int bitLength)
{
switch (bitLength)
{
case 128:
case 224:
case 256:
case 288:
case 384:
case 512:
initSponge(1600 - (bitLength << 1));
break;
default:
throw new IllegalArgumentException("bitLength must be one of 128, 224, 256, 288, 384, or 512.");
}
}
private void initSponge(int rate)
{
if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0))
{
throw new IllegalStateException("invalid rate value");
}
this.rate = rate;
for (int i = 0; i < state.length; ++i)
{
state[i] = 0L;
}
Arrays.fill(this.dataQueue, (byte)0);
this.bitsInQueue = 0;
this.squeezing = false;
this.fixedOutputLength = (1600 - rate) / 2;
}
protected void absorb(byte data)
{
if ((bitsInQueue % 8) != 0)
{
throw new IllegalStateException("attempt to absorb with odd length queue");
}
if (squeezing)
{
throw new IllegalStateException("attempt to absorb while squeezing");
}
dataQueue[bitsInQueue >>> 3] = data;
if ((bitsInQueue += 8) == rate)
{
KeccakAbsorb(dataQueue, 0);
bitsInQueue = 0;
}
}
protected void absorb(byte[] data, int off, int len)
{
if ((bitsInQueue % 8) != 0)
{
throw new IllegalStateException("attempt to absorb with odd length queue");
}
if (squeezing)
{
throw new IllegalStateException("attempt to absorb while squeezing");
}
int bytesInQueue = bitsInQueue >>> 3;
int rateBytes = rate >>> 3;
int available = rateBytes - bytesInQueue;
if (len < available)
{
System.arraycopy(data, off, dataQueue, bytesInQueue, len);
this.bitsInQueue += len << 3;
return;
}
int count = 0;
if (bytesInQueue > 0)
{
System.arraycopy(data, off, dataQueue, bytesInQueue, available);
count += available;
KeccakAbsorb(dataQueue, 0);
}
int remaining;
while ((remaining = (len - count)) >= rateBytes)
{
KeccakAbsorb(data, off + count);
count += rateBytes;
}
System.arraycopy(data, off + count, dataQueue, 0, remaining);
this.bitsInQueue = remaining << 3;
}
protected void absorbBits(int data, int bits)
{
if (bits < 1 || bits > 7)
{
throw new IllegalArgumentException("'bits' must be in the range 1 to 7");
}
if ((bitsInQueue % 8) != 0)
{
throw new IllegalStateException("attempt to absorb with odd length queue");
}
if (squeezing)
{
throw new IllegalStateException("attempt to absorb while squeezing");
}
int mask = (1 << bits) - 1;
dataQueue[bitsInQueue >>> 3] = (byte)(data & mask);
// NOTE: After this, bitsInQueue is no longer a multiple of 8, so no more absorbs will work
bitsInQueue += bits;
}
private void padAndSwitchToSqueezingPhase()
{
dataQueue[bitsInQueue >>> 3] |= (byte)(1 << (bitsInQueue & 7));
if (++bitsInQueue == rate)
{
KeccakAbsorb(dataQueue, 0);
}
else
{
int full = bitsInQueue >>> 6, partial = bitsInQueue & 63;
int off = 0;
for (int i = 0; i < full; ++i)
{
state[i] ^= Pack.littleEndianToLong(dataQueue, off);
off += 8;
}
if (partial > 0)
{
long mask = (1L << partial) - 1L;
state[full] ^= Pack.littleEndianToLong(dataQueue, off) & mask;
}
}
state[(rate - 1) >>> 6] ^= (1L << 63);
bitsInQueue = 0;
squeezing = true;
}
protected void squeeze(byte[] output, int offset, long outputLength)
{
if (!squeezing)
{
padAndSwitchToSqueezingPhase();
}
if ((outputLength % 8) != 0)
{
throw new IllegalStateException("outputLength not a multiple of 8");
}
long i = 0;
while (i < outputLength)
{
if (bitsInQueue == 0)
{
KeccakExtract();
}
int partialBlock = (int)Math.min((long)bitsInQueue, outputLength - i);
System.arraycopy(dataQueue, (rate - bitsInQueue) / 8, output, offset + (int)(i / 8), partialBlock / 8);
bitsInQueue -= partialBlock;
i += partialBlock;
}
}
private void KeccakAbsorb(byte[] data, int off)
{
// assert 0 == bitsInQueue || (dataQueue == data && 0 == off);
int count = rate >>> 6;
for (int i = 0; i < count; ++i)
{
state[i] ^= Pack.littleEndianToLong(data, off);
off += 8;
}
KeccakPermutation();
}
private void KeccakExtract()
{
// assert 0 == bitsInQueue;
KeccakPermutation();
Pack.longToLittleEndian(state, 0, rate >>> 6, dataQueue, 0);
this.bitsInQueue = rate;
}
private void KeccakPermutation()
{
long[] A = state;
long a00 = A[ 0], a01 = A[ 1], a02 = A[ 2], a03 = A[ 3], a04 = A[ 4];
long a05 = A[ 5], a06 = A[ 6], a07 = A[ 7], a08 = A[ 8], a09 = A[ 9];
long a10 = A[10], a11 = A[11], a12 = A[12], a13 = A[13], a14 = A[14];
long a15 = A[15], a16 = A[16], a17 = A[17], a18 = A[18], a19 = A[19];
long a20 = A[20], a21 = A[21], a22 = A[22], a23 = A[23], a24 = A[24];
for (int i = 0; i < 24; i++)
{
// theta
long c0 = a00 ^ a05 ^ a10 ^ a15 ^ a20;
long c1 = a01 ^ a06 ^ a11 ^ a16 ^ a21;
long c2 = a02 ^ a07 ^ a12 ^ a17 ^ a22;
long c3 = a03 ^ a08 ^ a13 ^ a18 ^ a23;
long c4 = a04 ^ a09 ^ a14 ^ a19 ^ a24;
long d1 = (c1 << 1 | c1 >>> -1) ^ c4;
long d2 = (c2 << 1 | c2 >>> -1) ^ c0;
long d3 = (c3 << 1 | c3 >>> -1) ^ c1;
long d4 = (c4 << 1 | c4 >>> -1) ^ c2;
long d0 = (c0 << 1 | c0 >>> -1) ^ c3;
a00 ^= d1; a05 ^= d1; a10 ^= d1; a15 ^= d1; a20 ^= d1;
a01 ^= d2; a06 ^= d2; a11 ^= d2; a16 ^= d2; a21 ^= d2;
a02 ^= d3; a07 ^= d3; a12 ^= d3; a17 ^= d3; a22 ^= d3;
a03 ^= d4; a08 ^= d4; a13 ^= d4; a18 ^= d4; a23 ^= d4;
a04 ^= d0; a09 ^= d0; a14 ^= d0; a19 ^= d0; a24 ^= d0;
// rho/pi
c1 = a01 << 1 | a01 >>> 63;
a01 = a06 << 44 | a06 >>> 20;
a06 = a09 << 20 | a09 >>> 44;
a09 = a22 << 61 | a22 >>> 3;
a22 = a14 << 39 | a14 >>> 25;
a14 = a20 << 18 | a20 >>> 46;
a20 = a02 << 62 | a02 >>> 2;
a02 = a12 << 43 | a12 >>> 21;
a12 = a13 << 25 | a13 >>> 39;
a13 = a19 << 8 | a19 >>> 56;
a19 = a23 << 56 | a23 >>> 8;
a23 = a15 << 41 | a15 >>> 23;
a15 = a04 << 27 | a04 >>> 37;
a04 = a24 << 14 | a24 >>> 50;
a24 = a21 << 2 | a21 >>> 62;
a21 = a08 << 55 | a08 >>> 9;
a08 = a16 << 45 | a16 >>> 19;
a16 = a05 << 36 | a05 >>> 28;
a05 = a03 << 28 | a03 >>> 36;
a03 = a18 << 21 | a18 >>> 43;
a18 = a17 << 15 | a17 >>> 49;
a17 = a11 << 10 | a11 >>> 54;
a11 = a07 << 6 | a07 >>> 58;
a07 = a10 << 3 | a10 >>> 61;
a10 = c1;
// chi
c0 = a00 ^ (~a01 & a02);
c1 = a01 ^ (~a02 & a03);
a02 ^= ~a03 & a04;
a03 ^= ~a04 & a00;
a04 ^= ~a00 & a01;
a00 = c0;
a01 = c1;
c0 = a05 ^ (~a06 & a07);
c1 = a06 ^ (~a07 & a08);
a07 ^= ~a08 & a09;
a08 ^= ~a09 & a05;
a09 ^= ~a05 & a06;
a05 = c0;
a06 = c1;
c0 = a10 ^ (~a11 & a12);
c1 = a11 ^ (~a12 & a13);
a12 ^= ~a13 & a14;
a13 ^= ~a14 & a10;
a14 ^= ~a10 & a11;
a10 = c0;
a11 = c1;
c0 = a15 ^ (~a16 & a17);
c1 = a16 ^ (~a17 & a18);
a17 ^= ~a18 & a19;
a18 ^= ~a19 & a15;
a19 ^= ~a15 & a16;
a15 = c0;
a16 = c1;
c0 = a20 ^ (~a21 & a22);
c1 = a21 ^ (~a22 & a23);
a22 ^= ~a23 & a24;
a23 ^= ~a24 & a20;
a24 ^= ~a20 & a21;
a20 = c0;
a21 = c1;
// iota
a00 ^= KeccakRoundConstants[i];
}
A[ 0] = a00; A[ 1] = a01; A[ 2] = a02; A[ 3] = a03; A[ 4] = a04;
A[ 5] = a05; A[ 6] = a06; A[ 7] = a07; A[ 8] = a08; A[ 9] = a09;
A[10] = a10; A[11] = a11; A[12] = a12; A[13] = a13; A[14] = a14;
A[15] = a15; A[16] = a16; A[17] = a17; A[18] = a18; A[19] = a19;
A[20] = a20; A[21] = a21; A[22] = a22; A[23] = a23; A[24] = a24;
}
protected CryptoServiceProperties cryptoServiceProperties()
{
return Utils.getDefaultProperties(this, getDigestSize() * 8, purpose);
}
}

View File

@ -0,0 +1,426 @@
package org.bouncycastle.crypto.digests;
import org.bouncycastle.crypto.CryptoServiceProperties;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.ExtendedDigest;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.util.Pack;
/**
* Base class for SHA-384 and SHA-512.
*/
public abstract class LongDigest
implements ExtendedDigest, Memoable, EncodableDigest
{
private static final int BYTE_LENGTH = 128;
protected final CryptoServicePurpose purpose;
private byte[] xBuf = new byte[8];
private int xBufOff;
private long byteCount1;
private long byteCount2;
protected long H1, H2, H3, H4, H5, H6, H7, H8;
private long[] W = new long[80];
private int wOff;
/**
* Constructor for variable length word
*/
protected LongDigest()
{
this(CryptoServicePurpose.ANY);
}
/**
* Constructor for variable length word
*/
protected LongDigest(CryptoServicePurpose purpose)
{
this.purpose = purpose;
xBufOff = 0;
reset();
}
/**
* Copy constructor. We are using copy constructors in place
* of the Object.clone() interface as this interface is not
* supported by J2ME.
*/
protected LongDigest(LongDigest t)
{
this.purpose = t.purpose;
copyIn(t);
}
protected void copyIn(LongDigest t)
{
System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
xBufOff = t.xBufOff;
byteCount1 = t.byteCount1;
byteCount2 = t.byteCount2;
H1 = t.H1;
H2 = t.H2;
H3 = t.H3;
H4 = t.H4;
H5 = t.H5;
H6 = t.H6;
H7 = t.H7;
H8 = t.H8;
System.arraycopy(t.W, 0, W, 0, t.W.length);
wOff = t.wOff;
}
protected void populateState(byte[] state)
{
System.arraycopy(xBuf, 0, state, 0, xBufOff);
Pack.intToBigEndian(xBufOff, state, 8);
Pack.longToBigEndian(byteCount1, state, 12);
Pack.longToBigEndian(byteCount2, state, 20);
Pack.longToBigEndian(H1, state, 28);
Pack.longToBigEndian(H2, state, 36);
Pack.longToBigEndian(H3, state, 44);
Pack.longToBigEndian(H4, state, 52);
Pack.longToBigEndian(H5, state, 60);
Pack.longToBigEndian(H6, state, 68);
Pack.longToBigEndian(H7, state, 76);
Pack.longToBigEndian(H8, state, 84);
Pack.intToBigEndian(wOff, state, 92);
for (int i = 0; i < wOff; i++)
{
Pack.longToBigEndian(W[i], state, 96 + (i * 8));
}
}
protected void restoreState(byte[] encodedState)
{
xBufOff = Pack.bigEndianToInt(encodedState, 8);
System.arraycopy(encodedState, 0, xBuf, 0, xBufOff);
byteCount1 = Pack.bigEndianToLong(encodedState, 12);
byteCount2 = Pack.bigEndianToLong(encodedState, 20);
H1 = Pack.bigEndianToLong(encodedState, 28);
H2 = Pack.bigEndianToLong(encodedState, 36);
H3 = Pack.bigEndianToLong(encodedState, 44);
H4 = Pack.bigEndianToLong(encodedState, 52);
H5 = Pack.bigEndianToLong(encodedState, 60);
H6 = Pack.bigEndianToLong(encodedState, 68);
H7 = Pack.bigEndianToLong(encodedState, 76);
H8 = Pack.bigEndianToLong(encodedState, 84);
wOff = Pack.bigEndianToInt(encodedState, 92);
for (int i = 0; i < wOff; i++)
{
W[i] = Pack.bigEndianToLong(encodedState, 96 + (i * 8));
}
}
protected int getEncodedStateSize()
{
return 96 + (wOff * 8);
}
public void update(
byte in)
{
xBuf[xBufOff++] = in;
if (xBufOff == xBuf.length)
{
processWord(xBuf, 0);
xBufOff = 0;
}
byteCount1++;
}
public void update(
byte[] in,
int inOff,
int len)
{
//
// fill the current word
//
while ((xBufOff != 0) && (len > 0))
{
update(in[inOff]);
inOff++;
len--;
}
//
// process whole words.
//
while (len >= xBuf.length)
{
processWord(in, inOff);
inOff += xBuf.length;
len -= xBuf.length;
byteCount1 += xBuf.length;
}
//
// load in the remainder.
//
while (len > 0)
{
update(in[inOff]);
inOff++;
len--;
}
}
public void finish()
{
adjustByteCounts();
long lowBitLength = byteCount1 << 3;
long hiBitLength = byteCount2;
//
// add the pad bytes.
//
update((byte)128);
while (xBufOff != 0)
{
update((byte)0);
}
processLength(lowBitLength, hiBitLength);
processBlock();
}
public void reset()
{
byteCount1 = 0;
byteCount2 = 0;
xBufOff = 0;
for (int i = 0; i < xBuf.length; i++)
{
xBuf[i] = 0;
}
wOff = 0;
for (int i = 0; i != W.length; i++)
{
W[i] = 0;
}
}
public int getByteLength()
{
return BYTE_LENGTH;
}
protected void processWord(
byte[] in,
int inOff)
{
W[wOff] = Pack.bigEndianToLong(in, inOff);
if (++wOff == 16)
{
processBlock();
}
}
/**
* adjust the byte counts so that byteCount2 represents the
* upper long (less 3 bits) word of the byte count.
*/
private void adjustByteCounts()
{
if (byteCount1 > 0x1fffffffffffffffL)
{
byteCount2 += (byteCount1 >>> 61);
byteCount1 &= 0x1fffffffffffffffL;
}
}
protected void processLength(
long lowW,
long hiW)
{
if (wOff > 14)
{
processBlock();
}
W[14] = hiW;
W[15] = lowW;
}
protected void processBlock()
{
adjustByteCounts();
//
// expand 16 word block into 80 word blocks.
//
for (int t = 16; t <= 79; t++)
{
W[t] = Sigma1(W[t - 2]) + W[t - 7] + Sigma0(W[t - 15]) + W[t - 16];
}
//
// set up working variables.
//
long a = H1;
long b = H2;
long c = H3;
long d = H4;
long e = H5;
long f = H6;
long g = H7;
long h = H8;
int t = 0;
for(int i = 0; i < 10; i ++)
{
// t = 8 * i
h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++];
d += h;
h += Sum0(a) + Maj(a, b, c);
// t = 8 * i + 1
g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++];
c += g;
g += Sum0(h) + Maj(h, a, b);
// t = 8 * i + 2
f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++];
b += f;
f += Sum0(g) + Maj(g, h, a);
// t = 8 * i + 3
e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++];
a += e;
e += Sum0(f) + Maj(f, g, h);
// t = 8 * i + 4
d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++];
h += d;
d += Sum0(e) + Maj(e, f, g);
// t = 8 * i + 5
c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++];
g += c;
c += Sum0(d) + Maj(d, e, f);
// t = 8 * i + 6
b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++];
f += b;
b += Sum0(c) + Maj(c, d, e);
// t = 8 * i + 7
a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++];
e += a;
a += Sum0(b) + Maj(b, c, d);
}
H1 += a;
H2 += b;
H3 += c;
H4 += d;
H5 += e;
H6 += f;
H7 += g;
H8 += h;
//
// reset the offset and clean out the word buffer.
//
wOff = 0;
for (int i = 0; i < 16; i++)
{
W[i] = 0;
}
}
/* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */
private long Ch(
long x,
long y,
long z)
{
return ((x & y) ^ ((~x) & z));
}
private long Maj(
long x,
long y,
long z)
{
return ((x & y) ^ (x & z) ^ (y & z));
}
private long Sum0(
long x)
{
return ((x << 36)|(x >>> 28)) ^ ((x << 30)|(x >>> 34)) ^ ((x << 25)|(x >>> 39));
}
private long Sum1(
long x)
{
return ((x << 50)|(x >>> 14)) ^ ((x << 46)|(x >>> 18)) ^ ((x << 23)|(x >>> 41));
}
private long Sigma0(
long x)
{
return ((x << 63)|(x >>> 1)) ^ ((x << 56)|(x >>> 8)) ^ (x >>> 7);
}
private long Sigma1(
long x)
{
return ((x << 45)|(x >>> 19)) ^ ((x << 3)|(x >>> 61)) ^ (x >>> 6);
}
/* SHA-384 and SHA-512 Constants
* (represent the first 64 bits of the fractional parts of the
* cube roots of the first sixty-four prime numbers)
*/
static final long K[] = {
0x428a2f98d728ae22L, 0x7137449123ef65cdL, 0xb5c0fbcfec4d3b2fL, 0xe9b5dba58189dbbcL,
0x3956c25bf348b538L, 0x59f111f1b605d019L, 0x923f82a4af194f9bL, 0xab1c5ed5da6d8118L,
0xd807aa98a3030242L, 0x12835b0145706fbeL, 0x243185be4ee4b28cL, 0x550c7dc3d5ffb4e2L,
0x72be5d74f27b896fL, 0x80deb1fe3b1696b1L, 0x9bdc06a725c71235L, 0xc19bf174cf692694L,
0xe49b69c19ef14ad2L, 0xefbe4786384f25e3L, 0x0fc19dc68b8cd5b5L, 0x240ca1cc77ac9c65L,
0x2de92c6f592b0275L, 0x4a7484aa6ea6e483L, 0x5cb0a9dcbd41fbd4L, 0x76f988da831153b5L,
0x983e5152ee66dfabL, 0xa831c66d2db43210L, 0xb00327c898fb213fL, 0xbf597fc7beef0ee4L,
0xc6e00bf33da88fc2L, 0xd5a79147930aa725L, 0x06ca6351e003826fL, 0x142929670a0e6e70L,
0x27b70a8546d22ffcL, 0x2e1b21385c26c926L, 0x4d2c6dfc5ac42aedL, 0x53380d139d95b3dfL,
0x650a73548baf63deL, 0x766a0abb3c77b2a8L, 0x81c2c92e47edaee6L, 0x92722c851482353bL,
0xa2bfe8a14cf10364L, 0xa81a664bbc423001L, 0xc24b8b70d0f89791L, 0xc76c51a30654be30L,
0xd192e819d6ef5218L, 0xd69906245565a910L, 0xf40e35855771202aL, 0x106aa07032bbd1b8L,
0x19a4c116b8d2d0c8L, 0x1e376c085141ab53L, 0x2748774cdf8eeb99L, 0x34b0bcb5e19b48a8L,
0x391c0cb3c5c95a63L, 0x4ed8aa4ae3418acbL, 0x5b9cca4f7763e373L, 0x682e6ff3d6b2b8a3L,
0x748f82ee5defb2fcL, 0x78a5636f43172f60L, 0x84c87814a1f0ab72L, 0x8cc702081a6439ecL,
0x90befffa23631e28L, 0xa4506cebde82bde9L, 0xbef9a3f7b2c67915L, 0xc67178f2e372532bL,
0xca273eceea26619cL, 0xd186b8c721c0c207L, 0xeada7dd6cde0eb1eL, 0xf57d4f7fee6ed178L,
0x06f067aa72176fbaL, 0x0a637dc5a2c898a6L, 0x113f9804bef90daeL, 0x1b710b35131c471bL,
0x28db77f523047d84L, 0x32caab7b40c72493L, 0x3c9ebe0a15c9bebcL, 0x431d67c49c100d4cL,
0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L
};
protected abstract CryptoServiceProperties cryptoServiceProperties();
}

View File

@ -0,0 +1,87 @@
package org.bouncycastle.crypto.digests;
import org.bouncycastle.crypto.CryptoServicePurpose;
/**
* implementation of SHA-3 based on following KeccakNISTInterface.c from https://keccak.noekeon.org/
* <p>
* Following the naming conventions used in the C source code to enable easy review of the implementation.
*/
public class SHA3Digest
extends KeccakDigest
{
private static int checkBitLength(int bitLength)
{
switch (bitLength)
{
case 224:
case 256:
case 384:
case 512:
return bitLength;
default:
throw new IllegalArgumentException("'bitLength' " + bitLength + " not supported for SHA-3");
}
}
public SHA3Digest()
{
this(256, CryptoServicePurpose.ANY);
}
public SHA3Digest(CryptoServicePurpose purpose)
{
this(256, purpose);
}
public SHA3Digest(int bitLength)
{
super(checkBitLength(bitLength), CryptoServicePurpose.ANY);
}
public SHA3Digest(int bitLength, CryptoServicePurpose purpose)
{
super(checkBitLength(bitLength), purpose);
}
public SHA3Digest(SHA3Digest source)
{
super(source);
}
public String getAlgorithmName()
{
return "SHA3-" + fixedOutputLength;
}
public int doFinal(byte[] out, int outOff)
{
absorbBits(0x02, 2);
return super.doFinal(out, outOff);
}
/*
* TODO Possible API change to support partial-byte suffixes.
*/
protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits)
{
if (partialBits < 0 || partialBits > 7)
{
throw new IllegalArgumentException("'partialBits' must be in the range [0,7]");
}
int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x02 << partialBits);
int finalBits = partialBits + 2;
if (finalBits >= 8)
{
absorb((byte)finalInput);
finalBits -= 8;
finalInput >>>= 8;
}
return super.doFinal(out, outOff, (byte)finalInput, finalBits);
}
}

View File

@ -0,0 +1,149 @@
package org.bouncycastle.crypto.digests;
import org.bouncycastle.crypto.CryptoServiceProperties;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.util.Pack;
/**
* FIPS 180-2 implementation of SHA-512.
*
* <pre>
* block word digest
* SHA-1 512 32 160
* SHA-256 512 32 256
* SHA-384 1024 64 384
* SHA-512 1024 64 512
* </pre>
*/
public class SHA512Digest
extends LongDigest
{
private static final int DIGEST_LENGTH = 64;
/**
* Standard constructor
*/
public SHA512Digest()
{
this(CryptoServicePurpose.ANY);
}
/**
* Standard constructor, with purpose
*/
public SHA512Digest(CryptoServicePurpose purpose)
{
super(purpose);
CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
reset();
}
/**
* Copy constructor. This will copy the state of the provided
* message digest.
*/
public SHA512Digest(SHA512Digest t)
{
super(t);
CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
}
/**
* State constructor - create a digest initialised with the state of a previous one.
*
* @param encodedState the encoded state from the originating digest.
*/
public SHA512Digest(byte[] encodedState)
{
super(CryptoServicePurpose.values()[encodedState[encodedState.length - 1]]);
restoreState(encodedState);
CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
}
public String getAlgorithmName()
{
return "SHA-512";
}
public int getDigestSize()
{
return DIGEST_LENGTH;
}
public int doFinal(
byte[] out,
int outOff)
{
finish();
Pack.longToBigEndian(H1, out, outOff);
Pack.longToBigEndian(H2, out, outOff + 8);
Pack.longToBigEndian(H3, out, outOff + 16);
Pack.longToBigEndian(H4, out, outOff + 24);
Pack.longToBigEndian(H5, out, outOff + 32);
Pack.longToBigEndian(H6, out, outOff + 40);
Pack.longToBigEndian(H7, out, outOff + 48);
Pack.longToBigEndian(H8, out, outOff + 56);
reset();
return DIGEST_LENGTH;
}
/**
* reset the chaining variables
*/
public void reset()
{
super.reset();
/* SHA-512 initial hash value
* The first 64 bits of the fractional parts of the square roots
* of the first eight prime numbers
*/
H1 = 0x6a09e667f3bcc908L;
H2 = 0xbb67ae8584caa73bL;
H3 = 0x3c6ef372fe94f82bL;
H4 = 0xa54ff53a5f1d36f1L;
H5 = 0x510e527fade682d1L;
H6 = 0x9b05688c2b3e6c1fL;
H7 = 0x1f83d9abfb41bd6bL;
H8 = 0x5be0cd19137e2179L;
}
public Memoable copy()
{
return new SHA512Digest(this);
}
public void reset(Memoable other)
{
SHA512Digest d = (SHA512Digest)other;
copyIn(d);
}
public byte[] getEncodedState()
{
byte[] encoded = new byte[getEncodedStateSize() + 1];
super.populateState(encoded);
encoded[encoded.length - 1] = (byte)purpose.ordinal();
return encoded;
}
protected CryptoServiceProperties cryptoServiceProperties()
{
return Utils.getDefaultProperties(this, 256, purpose);
}
}

View File

@ -0,0 +1,150 @@
package org.bouncycastle.crypto.digests;
import org.bouncycastle.crypto.CryptoServiceProperties;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.Xof;
/**
* implementation of SHAKE based on following KeccakNISTInterface.c from https://keccak.noekeon.org/
* <p>
* Following the naming conventions used in the C source code to enable easy review of the implementation.
*/
public class SHAKEDigest
extends KeccakDigest
implements Xof
{
private static int checkBitLength(int bitStrength)
{
switch (bitStrength)
{
case 128:
case 256:
return bitStrength;
default:
throw new IllegalArgumentException("'bitStrength' " + bitStrength + " not supported for SHAKE");
}
}
public SHAKEDigest()
{
this(128);
}
public SHAKEDigest(CryptoServicePurpose purpose)
{
this(128, purpose);
}
/**
* Base constructor.
*
* @param bitStrength the security strength in bits of the XOF.
*/
public SHAKEDigest(int bitStrength)
{
super(checkBitLength(bitStrength), CryptoServicePurpose.ANY);
}
/**
* Base constructor.
*
* @param bitStrength the security strength in bits of the XOF.
* @param purpose the purpose of the digest will be used for.
*/
public SHAKEDigest(int bitStrength, CryptoServicePurpose purpose)
{
super(checkBitLength(bitStrength), purpose);
}
/**
* Clone constructor
*
* @param source the other digest to be copied.
*/
public SHAKEDigest(SHAKEDigest source)
{
super(source);
}
public String getAlgorithmName()
{
return "SHAKE" + fixedOutputLength;
}
public int getDigestSize()
{
return fixedOutputLength / 4;
}
public int doFinal(byte[] out, int outOff)
{
return doFinal(out, outOff, getDigestSize());
}
public int doFinal(byte[] out, int outOff, int outLen)
{
int length = doOutput(out, outOff, outLen);
reset();
return length;
}
public int doOutput(byte[] out, int outOff, int outLen)
{
if (!squeezing)
{
absorbBits(0x0F, 4);
}
squeeze(out, outOff, ((long)outLen) * 8);
return outLen;
}
/*
* TODO Possible API change to support partial-byte suffixes.
*/
protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits)
{
return doFinal(out, outOff, getDigestSize(), partialByte, partialBits);
}
/*
* TODO Possible API change to support partial-byte suffixes.
*/
protected int doFinal(byte[] out, int outOff, int outLen, byte partialByte, int partialBits)
{
if (partialBits < 0 || partialBits > 7)
{
throw new IllegalArgumentException("'partialBits' must be in the range [0,7]");
}
int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x0F << partialBits);
int finalBits = partialBits + 4;
if (finalBits >= 8)
{
absorb((byte)finalInput);
finalBits -= 8;
finalInput >>>= 8;
}
if (finalBits > 0)
{
absorbBits(finalInput, finalBits);
}
squeeze(out, outOff, ((long)outLen) * 8);
reset();
return outLen;
}
protected CryptoServiceProperties cryptoServiceProperties()
{
return Utils.getDefaultProperties(this, purpose);
}
}

View File

@ -0,0 +1,4 @@
/**
* Message digest classes.
*/
package org.bouncycastle.crypto.digests;

View File

@ -0,0 +1,4 @@
/**
* Base classes for the lightweight API.
*/
package org.bouncycastle.crypto;

View File

@ -0,0 +1,20 @@
package org.bouncycastle.crypto.params;
import org.bouncycastle.crypto.CipherParameters;
public class AsymmetricKeyParameter
implements CipherParameters
{
boolean privateKey;
public AsymmetricKeyParameter(
boolean privateKey)
{
this.privateKey = privateKey;
}
public boolean isPrivate()
{
return privateKey;
}
}

View File

@ -0,0 +1,49 @@
package org.bouncycastle.crypto.params;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.util.Arrays;
public class ParametersWithContext
implements CipherParameters
{
private CipherParameters parameters;
private byte[] context;
public ParametersWithContext(
CipherParameters parameters,
byte[] context)
{
if (context == null)
{
throw new NullPointerException("'context' cannot be null");
}
this.parameters = parameters;
this.context = Arrays.clone(context);
}
public void copyContextTo(byte[] buf, int off, int len)
{
if (context.length != len)
{
throw new IllegalArgumentException("len");
}
System.arraycopy(context, 0, buf, off, len);
}
public byte[] getContext()
{
return Arrays.clone(context);
}
public int getContextLength()
{
return context.length;
}
public CipherParameters getParameters()
{
return parameters;
}
}

View File

@ -0,0 +1,37 @@
package org.bouncycastle.crypto.params;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
public class ParametersWithRandom
implements CipherParameters
{
private SecureRandom random;
private CipherParameters parameters;
public ParametersWithRandom(
CipherParameters parameters,
SecureRandom random)
{
this.random = CryptoServicesRegistrar.getSecureRandom(random);
this.parameters = parameters;
}
public ParametersWithRandom(
CipherParameters parameters)
{
this(parameters, null);
}
public SecureRandom getRandom()
{
return random;
}
public CipherParameters getParameters()
{
return parameters;
}
}

View File

@ -0,0 +1,4 @@
/**
* Classes for parameter objects for ciphers and generators.
*/
package org.bouncycastle.crypto.params;

View File

@ -0,0 +1,8 @@
package org.bouncycastle.pqc.crypto;
import org.bouncycastle.crypto.CipherParameters;
public interface KEMParameters
extends CipherParameters
{
}

View File

@ -0,0 +1,84 @@
package org.bouncycastle.pqc.crypto.mlkem;
final class CBD
{
public static void mlkemCBD(Poly r, byte[] bytes, int eta)
{
long t, d;
int a, b;
switch (eta)
{
case 3:
for (int i = 0; i < MLKEMEngine.KyberN / 4; i++)
{
t = convertByteTo24BitUnsignedInt(bytes, 3 * i);
d = t & 0x00249249;
d = d + ((t >> 1) & 0x00249249);
d = d + ((t >> 2) & 0x00249249);
for (int j = 0; j < 4; j++)
{
a = (short)((d >> (6 * j + 0)) & 0x7);
b = (short)((d >> (6 * j + 3)) & 0x7);
// System.out.printf("a = %d, b = %d\n", a, b);
r.setCoeffIndex(4 * i + j, (short)(a - b));
}
}
break;
default:
// Only for Kyber512 where eta = 2
for (int i = 0; i < MLKEMEngine.KyberN / 8; i++)
{
t = convertByteTo32BitUnsignedInt(bytes, 4 * i); // ? Problem
d = t & 0x55555555;
d = d + ((t >> 1) & 0x55555555);
for (int j = 0; j < 8; j++)
{
a = (short)((d >> (4 * j + 0)) & 0x3);
b = (short)((d >> (4 * j + eta)) & 0x3);
r.setCoeffIndex(8 * i + j, (short)(a - b));
}
}
}
}
/**
* Converts an Array of Bytes to a 32-bit Unsigned Integer
* Returns a 32-bit unsigned integer as a long
*
* @param x
* @return
*/
private static long convertByteTo32BitUnsignedInt(byte[] x, int offset)
{
// Convert first byte to an unsigned integer
// byte x & 0xFF allows us to grab the last 8 bits
long r = (long)(x[offset] & 0xFF);
// Perform the same operation then left bit shift to store the next 8 bits without
// altering the previous bits
r = r | (long)((long)(x[offset + 1] & 0xFF) << 8);
r = r | (long)((long)(x[offset + 2] & 0xFF) << 16);
r = r | (long)((long)(x[offset + 3] & 0xFF) << 24);
return r;
}
/**
* Converts an Array of Bytes to a 24-bit Unsigned Integer
* Returns a 24-bit unsigned integer as a long from byte x
*
* @param x
* @return
*/
private static long convertByteTo24BitUnsignedInt(byte[] x, int offset)
{
// Refer to convertByteTo32-BitUnsignedInt for explanation
long r = (long)(x[offset] & 0xFF);
r = r | (long)((long)(x[offset + 1] & 0xFF) << 8);
r = r | (long)((long)(x[offset + 2] & 0xFF) << 16);
return r;
}
}

View File

@ -0,0 +1,326 @@
package org.bouncycastle.pqc.crypto.mlkem;
import java.security.SecureRandom;
import org.bouncycastle.util.Arrays;
class MLKEMEngine
{
private SecureRandom random;
private MLKEMIndCpa indCpa;
// constant parameters
public final static int KyberN = 256;
public final static int KyberQ = 3329;
public final static int KyberQinv = 62209;
public final static int KyberSymBytes = 32; // Number of bytes for Hashes and Seeds
private final static int KyberSharedSecretBytes = 32; // Number of Bytes for Shared Secret
public final static int KyberPolyBytes = 384;
private final static int KyberEta2 = 2;
private final static int KyberIndCpaMsgBytes = KyberSymBytes;
// parameters for Kyber{k}
private final int KyberK;
private final int KyberPolyVecBytes;
private final int KyberPolyCompressedBytes;
private final int KyberPolyVecCompressedBytes;
private final int KyberEta1;
private final int KyberIndCpaPublicKeyBytes;
private final int KyberIndCpaSecretKeyBytes;
private final int KyberIndCpaBytes;
private final int KyberPublicKeyBytes;
private final int KyberSecretKeyBytes;
private final int KyberCipherTextBytes;
// Crypto
private final int CryptoBytes;
private final int CryptoSecretKeyBytes;
private final int CryptoPublicKeyBytes;
private final int CryptoCipherTextBytes;
private final int sessionKeyLength;
private final Symmetric symmetric;
public Symmetric getSymmetric()
{
return symmetric;
}
public static int getKyberEta2()
{
return KyberEta2;
}
public static int getKyberIndCpaMsgBytes()
{
return KyberIndCpaMsgBytes;
}
public int getCryptoCipherTextBytes()
{
return CryptoCipherTextBytes;
}
public int getCryptoPublicKeyBytes()
{
return CryptoPublicKeyBytes;
}
public int getCryptoSecretKeyBytes()
{
return CryptoSecretKeyBytes;
}
public int getCryptoBytes()
{
return CryptoBytes;
}
public int getKyberCipherTextBytes()
{
return KyberCipherTextBytes;
}
public int getKyberSecretKeyBytes()
{
return KyberSecretKeyBytes;
}
public int getKyberIndCpaPublicKeyBytes()
{
return KyberIndCpaPublicKeyBytes;
}
public int getKyberIndCpaSecretKeyBytes()
{
return KyberIndCpaSecretKeyBytes;
}
public int getKyberIndCpaBytes()
{
return KyberIndCpaBytes;
}
public int getKyberPublicKeyBytes()
{
return KyberPublicKeyBytes;
}
public int getKyberPolyCompressedBytes()
{
return KyberPolyCompressedBytes;
}
public int getKyberK()
{
return KyberK;
}
public int getKyberPolyVecBytes()
{
return KyberPolyVecBytes;
}
public int getKyberPolyVecCompressedBytes()
{
return KyberPolyVecCompressedBytes;
}
public int getKyberEta1()
{
return KyberEta1;
}
public MLKEMEngine(int k)
{
this.KyberK = k;
switch (k)
{
case 2:
KyberEta1 = 3;
KyberPolyCompressedBytes = 128;
KyberPolyVecCompressedBytes = k * 320;
sessionKeyLength = 32;
break;
case 3:
KyberEta1 = 2;
KyberPolyCompressedBytes = 128;
KyberPolyVecCompressedBytes = k * 320;
sessionKeyLength = 32;
break;
case 4:
KyberEta1 = 2;
KyberPolyCompressedBytes = 160;
KyberPolyVecCompressedBytes = k * 352;
sessionKeyLength = 32;
break;
default:
throw new IllegalArgumentException("K: " + k + " is not supported for Crystals Kyber");
}
this.KyberPolyVecBytes = k * KyberPolyBytes;
this.KyberIndCpaPublicKeyBytes = KyberPolyVecBytes + KyberSymBytes;
this.KyberIndCpaSecretKeyBytes = KyberPolyVecBytes;
this.KyberIndCpaBytes = KyberPolyVecCompressedBytes + KyberPolyCompressedBytes;
this.KyberPublicKeyBytes = KyberIndCpaPublicKeyBytes;
this.KyberSecretKeyBytes = KyberIndCpaSecretKeyBytes + KyberIndCpaPublicKeyBytes + 2 * KyberSymBytes;
this.KyberCipherTextBytes = KyberIndCpaBytes;
// Define Crypto Params
this.CryptoBytes = KyberSharedSecretBytes;
this.CryptoSecretKeyBytes = KyberSecretKeyBytes;
this.CryptoPublicKeyBytes = KyberPublicKeyBytes;
this.CryptoCipherTextBytes = KyberCipherTextBytes;
this.symmetric = new Symmetric.ShakeSymmetric();
this.indCpa = new MLKEMIndCpa(this);
}
public void init(SecureRandom random)
{
this.random = random;
}
public byte[][] generateKemKeyPair()
{
byte[] d = new byte[KyberSymBytes];
byte[] z = new byte[KyberSymBytes];
random.nextBytes(d);
random.nextBytes(z);
return generateKemKeyPairInternal(d, z);
}
//Internal functions are deterministic. No randomness is sampled inside them
public byte[][] generateKemKeyPairInternal(byte[] d, byte[] z)
{
byte[][] indCpaKeyPair = indCpa.generateKeyPair(d);
byte[] s = new byte[KyberIndCpaSecretKeyBytes];
System.arraycopy(indCpaKeyPair[1], 0, s, 0, KyberIndCpaSecretKeyBytes);
byte[] hashedPublicKey = new byte[32];
symmetric.hash_h(hashedPublicKey, indCpaKeyPair[0], 0);
byte[] outputPublicKey = new byte[KyberIndCpaPublicKeyBytes];
System.arraycopy(indCpaKeyPair[0], 0, outputPublicKey, 0, KyberIndCpaPublicKeyBytes);
return new byte[][]
{
Arrays.copyOfRange(outputPublicKey, 0, outputPublicKey.length - 32),
Arrays.copyOfRange(outputPublicKey, outputPublicKey.length - 32, outputPublicKey.length),
s,
hashedPublicKey,
z,
Arrays.concatenate(d, z)
};
}
public byte[][] kemEncryptInternal(byte[] publicKeyInput, byte[] randBytes)
{
byte[] outputCipherText;
byte[] buf = new byte[2 * KyberSymBytes];
byte[] kr = new byte[2 * KyberSymBytes];
System.arraycopy(randBytes, 0, buf, 0, KyberSymBytes);
// SHA3-256 Public Key
symmetric.hash_h(buf, publicKeyInput, KyberSymBytes);
// SHA3-512( SHA3-256(RandBytes) || SHA3-256(PublicKey) )
symmetric.hash_g(kr, buf);
// IndCpa Encryption
outputCipherText = indCpa.encrypt(publicKeyInput, Arrays.copyOfRange(buf, 0, KyberSymBytes), Arrays.copyOfRange(kr, 32, kr.length));
byte[] outputSharedSecret = new byte[sessionKeyLength];
System.arraycopy(kr, 0, outputSharedSecret, 0, outputSharedSecret.length);
byte[][] outBuf = new byte[2][];
outBuf[0] = outputSharedSecret;
outBuf[1] = outputCipherText;
return outBuf;
}
public byte[] kemDecryptInternal(byte[] secretKey, byte[] cipherText)
{
byte[] buf = new byte[2 * KyberSymBytes],
kr = new byte[2 * KyberSymBytes];
byte[] publicKey = Arrays.copyOfRange(secretKey, KyberIndCpaSecretKeyBytes, secretKey.length);
System.arraycopy(indCpa.decrypt(secretKey, cipherText), 0, buf, 0, KyberSymBytes);
System.arraycopy(secretKey, KyberSecretKeyBytes - 2 * KyberSymBytes, buf, KyberSymBytes, KyberSymBytes);
symmetric.hash_g(kr, buf);
byte[] implicit_rejection = new byte[KyberSymBytes + KyberCipherTextBytes];
System.arraycopy(secretKey, KyberSecretKeyBytes - KyberSymBytes, implicit_rejection, 0, KyberSymBytes);
System.arraycopy(cipherText, 0, implicit_rejection, KyberSymBytes, KyberCipherTextBytes);
symmetric.kdf(implicit_rejection, implicit_rejection ); // J(z||c)
byte[] cmp = indCpa.encrypt(publicKey, Arrays.copyOfRange(buf, 0, KyberSymBytes), Arrays.copyOfRange(kr, KyberSymBytes, kr.length));
boolean fail = !(Arrays.constantTimeAreEqual(cipherText, cmp));
cmov(kr, implicit_rejection, KyberSymBytes, fail);
return Arrays.copyOfRange(kr, 0, sessionKeyLength);
}
public byte[][] kemEncrypt(byte[] publicKeyInput, byte[] randBytes)
{
//TODO: do input validation elsewhere?
// Input validation (6.2 ML-KEM Encaps)
// Type Check
if (publicKeyInput.length != KyberIndCpaPublicKeyBytes)
{
throw new IllegalArgumentException("Input validation Error: Type check failed for ml-kem encapsulation");
}
// Modulus Check
PolyVec polyVec = new PolyVec(this);
byte[] seed = indCpa.unpackPublicKey(polyVec, publicKeyInput);
byte[] ek = indCpa.packPublicKey(polyVec, seed);
if (!Arrays.areEqual(ek, publicKeyInput))
{
throw new IllegalArgumentException("Input validation: Modulus check failed for ml-kem encapsulation");
}
return kemEncryptInternal(publicKeyInput, randBytes);
}
public byte[] kemDecrypt(byte[] secretKey, byte[] cipherText)
{
//TODO: do input validation
return kemDecryptInternal(secretKey, cipherText);
}
private void cmov(byte[] r, byte[] x, int xlen, boolean b)
{
if (b)
{
System.arraycopy(x, 0, r, 0, xlen);
}
else
{
System.arraycopy(r, 0, r, 0, xlen);
}
}
public void getRandomBytes(byte[] buf)
{
this.random.nextBytes(buf);
}
}

View File

@ -0,0 +1,31 @@
package org.bouncycastle.pqc.crypto.mlkem;
import org.bouncycastle.crypto.EncapsulatedSecretExtractor;
public class MLKEMExtractor
implements EncapsulatedSecretExtractor
{
private final MLKEMPrivateKeyParameters privateKey;
private final MLKEMEngine engine;
public MLKEMExtractor(MLKEMPrivateKeyParameters privateKey)
{
if (privateKey == null)
{
throw new NullPointerException("'privateKey' cannot be null");
}
this.privateKey = privateKey;
this.engine = privateKey.getParameters().getEngine();
}
public byte[] extractSecret(byte[] encapsulation)
{
return engine.kemDecrypt(privateKey.getEncoded(), encapsulation);
}
public int getEncapsulationLength()
{
return engine.getCryptoCipherTextBytes();
}
}

View File

@ -0,0 +1,42 @@
package org.bouncycastle.pqc.crypto.mlkem;
import java.security.SecureRandom;
import org.bouncycastle.crypto.EncapsulatedSecretGenerator;
import org.bouncycastle.crypto.SecretWithEncapsulation;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.pqc.crypto.util.SecretWithEncapsulationImpl;
public class MLKEMGenerator
implements EncapsulatedSecretGenerator
{
// the source of randomness
private final SecureRandom sr;
public MLKEMGenerator(SecureRandom random)
{
this.sr = random;
}
public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey)
{
MLKEMPublicKeyParameters key = (MLKEMPublicKeyParameters)recipientKey;
MLKEMEngine engine = key.getParameters().getEngine();
engine.init(sr);
byte[] randBytes = new byte[32];
engine.getRandomBytes(randBytes);
byte[][] kemEncrypt = engine.kemEncrypt(key.getEncoded(), randBytes);
return new SecretWithEncapsulationImpl(kemEncrypt[0], kemEncrypt[1]);
}
public SecretWithEncapsulation internalGenerateEncapsulated(AsymmetricKeyParameter recipientKey, byte[] randBytes)
{
MLKEMPublicKeyParameters key = (MLKEMPublicKeyParameters)recipientKey;
MLKEMEngine engine = key.getParameters().getEngine();
engine.init(sr);
byte[][] kemEncrypt = engine.kemEncryptInternal(key.getEncoded(), randBytes);
return new SecretWithEncapsulationImpl(kemEncrypt[0], kemEncrypt[1]);
}
}

View File

@ -0,0 +1,444 @@
package org.bouncycastle.pqc.crypto.mlkem;
import org.bouncycastle.util.Arrays;
class MLKEMIndCpa
{
private MLKEMEngine engine;
private int kyberK;
private int indCpaPublicKeyBytes;
private int polyVecBytes;
private int indCpaBytes;
private int polyVecCompressedBytes;
private int polyCompressedBytes;
private Symmetric symmetric;
public MLKEMIndCpa(MLKEMEngine engine)
{
this.engine = engine;
this.kyberK = engine.getKyberK();
this.indCpaPublicKeyBytes = engine.getKyberPublicKeyBytes();
this.polyVecBytes = engine.getKyberPolyVecBytes();
this.indCpaBytes = engine.getKyberIndCpaBytes();
this.polyVecCompressedBytes = engine.getKyberPolyVecCompressedBytes();
this.polyCompressedBytes = engine.getKyberPolyCompressedBytes();
this.symmetric = engine.getSymmetric();
KyberGenerateMatrixNBlocks =
(
(
12 * MLKEMEngine.KyberN
/ 8 * (1 << 12)
/ MLKEMEngine.KyberQ + symmetric.xofBlockBytes
)
/ symmetric.xofBlockBytes
);
}
/**
* Generates IndCpa Key Pair
*
* @return KeyPair where each key is represented as bytes
*/
byte[][] generateKeyPair(byte[] d)
{
PolyVec secretKey = new PolyVec(engine),
publicKey = new PolyVec(engine),
e = new PolyVec(engine);
// (p, sigma) <- G(d || k)
byte[] buf = new byte[64];
symmetric.hash_g(buf, Arrays.append(d, (byte)kyberK));
byte[] publicSeed = new byte[32]; // p in docs
byte[] noiseSeed = new byte[32]; // sigma in docs
System.arraycopy(buf, 0, publicSeed, 0, 32);
System.arraycopy(buf, 32, noiseSeed, 0, 32);
byte count = (byte)0;
// Helper.printByteArray(buf);
PolyVec[] aMatrix = new PolyVec[kyberK];
int i;
for (i = 0; i < kyberK; i++)
{
aMatrix[i] = new PolyVec(engine);
}
generateMatrix(aMatrix, publicSeed, false);
// System.out.println("aMatrix = ");
// for(i = 0; i < kyberK; i++) {
// System.out.print("[");
// for (int j = 0; j < kyberK; j++) {
// System.out.print("[");
// for (int k = 0; k < KyberEngine.KyberN; k++) {
// System.out.printf("%d ,", aMatrix[i].getVectorIndex(j).getCoeffIndex(k));
// }
// System.out.print("], \n");
// }
// System.out.print("]\n");
// }
for (i = 0; i < kyberK; i++)
{
secretKey.getVectorIndex(i).getEta1Noise(noiseSeed, count);
// System.out.print("SecretKeyPolyVec["+i+"] = [");
// for (int j =0; j < KyberEngine.KyberN; j++) {
// System.out.print(secretKey.getVectorIndex(i).getCoeffIndex(j) + ", ");
// }
// System.out.println("]");
count = (byte)(count + (byte)1);
}
for (i = 0; i < kyberK; i++)
{
e.getVectorIndex(i).getEta1Noise(noiseSeed, count);
count = (byte)(count + (byte)1);
}
secretKey.polyVecNtt();
// System.out.print("SecretKeyPolyVec = [");
// for (i = 0; i < kyberK; i++) {
// System.out.print("[");
// for (int j =0; j < KyberEngine.KyberN; j++) {
// System.out.print(secretKey.getVectorIndex(i).getCoeffIndex(j) + ", ");
// }
// System.out.println("],");
// }
// System.out.println("]");
e.polyVecNtt();
for (i = 0; i < kyberK; i++)
{
PolyVec.pointwiseAccountMontgomery(publicKey.getVectorIndex(i), aMatrix[i], secretKey, engine);
publicKey.getVectorIndex(i).convertToMont();
}
// System.out.print("PublicKey PolyVec = [");
// Helper.printPolyVec(publicKey, kyberK);
publicKey.addPoly(e);
publicKey.reducePoly();
return new byte[][]{packPublicKey(publicKey, publicSeed), packSecretKey(secretKey)};
}
public byte[] encrypt(byte[] publicKeyInput, byte[] msg, byte[] coins)
{
int i;
byte[] seed;
byte nonce = (byte)0;
PolyVec sp = new PolyVec(engine),
publicKeyPolyVec = new PolyVec(engine),
errorPolyVector = new PolyVec(engine),
bp = new PolyVec(engine);
PolyVec[] aMatrixTranspose = new PolyVec[engine.getKyberK()];
Poly errorPoly = new Poly(engine),
v = new Poly(engine),
k = new Poly(engine);
// System.out.print("publickeyinput = ");
// Helper.printByteArray(publicKeyInput);
// System.out.println();
seed = unpackPublicKey(publicKeyPolyVec, publicKeyInput);
// System.out.print("publickeyPolyVec = [");
// for (i = 0; i < kyberK; i++) {
// Helper.printShortArray(publicKeyPolyVec.getVectorIndex(i).getCoeffs());
// System.out.print("], \n");
// }
// System.out.println("]");
// System.out.print("seed = ");
// Helper.printByteArray(seed);
// System.out.println();
k.fromMsg(msg);
for (i = 0; i < kyberK; i++)
{
aMatrixTranspose[i] = new PolyVec(engine);
}
generateMatrix(aMatrixTranspose, seed, true);
// System.out.print("matrix transposed = ");
// for (i = 0; i < kyberK; i++) {
// System.out.print("[");
// for(int j = 0; j < kyberK; j++) {
// System.out.print("[");
// for (int l = 0; l < 256; l++) {
// System.out.printf("%d ,", aMatrixTranspose[i].getVectorIndex(j).getCoeffIndex(l));
// }
// System.out.print("] ,\n");
// }
// System.out.println("] ,");
// }
for (i = 0; i < kyberK; i++)
{
sp.getVectorIndex(i).getEta1Noise(coins, nonce);
nonce = (byte)(nonce + (byte)1);
}
for (i = 0; i < kyberK; i++)
{
errorPolyVector.getVectorIndex(i).getEta2Noise(coins, nonce);
nonce = (byte)(nonce + (byte)1);
}
errorPoly.getEta2Noise(coins, nonce);
sp.polyVecNtt();
// System.out.print("sp = [");
// for (i = 0; i < kyberK; i++) {
// Helper.printShortArray(sp.getVectorIndex(i).getCoeffs());
// System.out.print("], \n");
// }
// System.out.println("]");
// System.out.print("sp = [");
// for (i = 0; i < kyberK; i++) {
// Helper.printShortArray(sp.getVectorIndex(i).getCoeffs());
// System.out.print("], \n");
// }
// System.out.println("]");
for (i = 0; i < kyberK; i++)
{
PolyVec.pointwiseAccountMontgomery(bp.getVectorIndex(i), aMatrixTranspose[i], sp, engine);
}
// System.out.print("bp = [");
// for (i = 0; i < kyberK; i++) {
// Helper.printShortArray(bp.getVectorIndex(i).getCoeffs());
// System.out.print("], \n");
// }
// System.out.println("]");
PolyVec.pointwiseAccountMontgomery(v, publicKeyPolyVec, sp, engine);
bp.polyVecInverseNttToMont();
v.polyInverseNttToMont();
bp.addPoly(errorPolyVector);
v.addCoeffs(errorPoly);
v.addCoeffs(k);
bp.reducePoly();
v.reduce();
// System.out.print("bp = [");
// for (i = 0; i < kyberK; i++) {
// Helper.printShortArray(bp.getVectorIndex(i).getCoeffs());
// System.out.print("], \n");
// }
// System.out.println("]");
// System.out.print("v = ");
// Helper.printShortArray(v.getCoeffs());
// System.out.println();
byte[] outputCipherText = packCipherText(bp, v);
return outputCipherText;
}
private byte[] packCipherText(PolyVec b, Poly v)
{
byte[] outBuf = new byte[indCpaBytes];
System.arraycopy(b.compressPolyVec(), 0, outBuf, 0, polyVecCompressedBytes);
System.arraycopy(v.compressPoly(), 0, outBuf, polyVecCompressedBytes, polyCompressedBytes);
// System.out.print("outBuf = [");
// Helper.printByteArray(outBuf);
return outBuf;
}
private void unpackCipherText(PolyVec b, Poly v, byte[] cipherText)
{
byte[] compressedPolyVecCipherText = Arrays.copyOfRange(cipherText, 0, engine.getKyberPolyVecCompressedBytes());
b.decompressPolyVec(compressedPolyVecCipherText);
byte[] compressedPolyCipherText = Arrays.copyOfRange(cipherText, engine.getKyberPolyVecCompressedBytes(), cipherText.length);
v.decompressPoly(compressedPolyCipherText);
}
public byte[] packPublicKey(PolyVec publicKeyPolyVec, byte[] seed)
{
byte[] buf = new byte[indCpaPublicKeyBytes];
System.arraycopy(publicKeyPolyVec.toBytes(), 0, buf, 0, polyVecBytes);
System.arraycopy(seed, 0, buf, polyVecBytes, MLKEMEngine.KyberSymBytes);
return buf;
}
public byte[] unpackPublicKey(PolyVec publicKeyPolyVec, byte[] publicKey)
{
byte[] outputSeed = new byte[MLKEMEngine.KyberSymBytes];
publicKeyPolyVec.fromBytes(publicKey);
System.arraycopy(publicKey, polyVecBytes, outputSeed, 0, MLKEMEngine.KyberSymBytes);
return outputSeed;
}
public byte[] packSecretKey(PolyVec secretKeyPolyVec)
{
return secretKeyPolyVec.toBytes();
}
public void unpackSecretKey(PolyVec secretKeyPolyVec, byte[] secretKey)
{
secretKeyPolyVec.fromBytes(secretKey);
}
public final int KyberGenerateMatrixNBlocks;
public void generateMatrix(PolyVec[] aMatrix, byte[] seed, boolean transposed)
{
int i, j, k, ctr, off;
byte[] buf = new byte[KyberGenerateMatrixNBlocks * symmetric.xofBlockBytes + 2];
for (i = 0; i < kyberK; i++)
{
for (j = 0; j < kyberK; j++)
{
if (transposed)
{
symmetric.xofAbsorb(seed, (byte) i, (byte) j);
}
else
{
symmetric.xofAbsorb(seed, (byte) j, (byte) i);
}
symmetric.xofSqueezeBlocks(buf, 0, symmetric.xofBlockBytes * KyberGenerateMatrixNBlocks);
int buflen = KyberGenerateMatrixNBlocks * symmetric.xofBlockBytes;
ctr = rejectionSampling(aMatrix[i].getVectorIndex(j), 0, MLKEMEngine.KyberN, buf, buflen);
while (ctr < MLKEMEngine.KyberN)
{
off = buflen % 3;
for (k = 0; k < off; k++)
{
buf[k] = buf[buflen - off + k];
}
symmetric.xofSqueezeBlocks(buf, off, symmetric.xofBlockBytes * 2);
buflen = off + symmetric.xofBlockBytes;
// Error in code Section Unsure
ctr += rejectionSampling(aMatrix[i].getVectorIndex(j), ctr, MLKEMEngine.KyberN - ctr, buf, buflen);
}
}
}
}
private static int rejectionSampling(Poly outputBuffer, int coeffOff, int len, byte[] inpBuf, int inpBufLen)
{
int ctr, pos;
short val0, val1;
ctr = pos = 0;
while (ctr < len && pos + 3 <= inpBufLen)
{
val0 = (short)(((((short)(inpBuf[pos] & 0xFF)) >> 0) | (((short)(inpBuf[pos + 1] & 0xFF)) << 8)) & 0xFFF);
val1 = (short)(((((short)(inpBuf[pos + 1] & 0xFF)) >> 4) | (((short)(inpBuf[pos + 2] & 0xFF)) << 4)) & 0xFFF);
pos = pos + 3;
if (val0 < (short)MLKEMEngine.KyberQ)
{
outputBuffer.setCoeffIndex(coeffOff + ctr, (short)val0);
ctr++;
}
if (ctr < len && val1 < (short)MLKEMEngine.KyberQ)
{
outputBuffer.setCoeffIndex(coeffOff + ctr, (short)val1);
ctr++;
}
}
return ctr;
}
public byte[] decrypt(byte[] secretKey, byte[] cipherText)
{
byte[] outputMessage = new byte[MLKEMEngine.getKyberIndCpaMsgBytes()];
PolyVec bp = new PolyVec(engine), secretKeyPolyVec = new PolyVec(engine);
Poly v = new Poly(engine), mp = new Poly(engine);
unpackCipherText(bp, v, cipherText);
// System.out.print("bp = [");
// for (i = 0; i < kyberK; i++) {
// Helper.printShortArray(bp.getVectorIndex(i).getCoeffs());
// System.out.print("], \n");
// }
// System.out.println("]");
// System.out.print("v = ");
// Helper.printShortArray(v.getCoeffs());
// System.out.println();
unpackSecretKey(secretKeyPolyVec, secretKey);
// System.out.print("decrypt secretkey = ");;
// Helper.printByteArray(secretKey);
// System.out.print("SecretKeyPolyVec = [");
// for (i = 0; i < kyberK; i++) {
// System.out.print("[");
// for (int j =0; j < KyberEngine.KyberN; j++) {
// System.out.print(secretKeyPolyVec.getVectorIndex(i).getCoeffIndex(j) + ", ");
// }
// System.out.println("],");
// }
// System.out.println("]");
// System.out.print("bp before ntt = [");
// for (i = 0; i < kyberK; i++) {
// Helper.printShortArray(bp.getVectorIndex(i).getCoeffs());
// System.out.print("], \n");
// }
// System.out.println("]");
bp.polyVecNtt();
// System.out.print("bp after ntt = [");
// for (i = 0; i < kyberK; i++) {
// Helper.printShortArray(bp.getVectorIndex(i).getCoeffs());
// System.out.print("], \n");
// }
// System.out.println("]");
PolyVec.pointwiseAccountMontgomery(mp, secretKeyPolyVec, bp, engine);
mp.polyInverseNttToMont();
mp.polySubtract(v);
mp.reduce();
outputMessage = mp.toMsg();
return outputMessage;
}
}

View File

@ -0,0 +1,24 @@
package org.bouncycastle.pqc.crypto.mlkem;
import java.security.SecureRandom;
import org.bouncycastle.crypto.KeyGenerationParameters;
public class MLKEMKeyGenerationParameters
extends KeyGenerationParameters
{
private final MLKEMParameters params;
public MLKEMKeyGenerationParameters(
SecureRandom random,
MLKEMParameters mlkemParameters)
{
super(random, 256);
this.params = mlkemParameters;
}
public MLKEMParameters getParameters()
{
return params;
}
}

View File

@ -0,0 +1,57 @@
package org.bouncycastle.pqc.crypto.mlkem;
import java.security.SecureRandom;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
import org.bouncycastle.crypto.KeyGenerationParameters;
public class MLKEMKeyPairGenerator
implements AsymmetricCipherKeyPairGenerator
{
private MLKEMParameters mlkemParams;
private SecureRandom random;
private void initialize(
KeyGenerationParameters param)
{
this.mlkemParams = ((MLKEMKeyGenerationParameters)param).getParameters();
this.random = param.getRandom();
}
private AsymmetricCipherKeyPair genKeyPair()
{
MLKEMEngine engine = mlkemParams.getEngine();
engine.init(random);
byte[][] keyPair = engine.generateKemKeyPair();
MLKEMPublicKeyParameters pubKey = new MLKEMPublicKeyParameters(mlkemParams, keyPair[0], keyPair[1]);
MLKEMPrivateKeyParameters privKey = new MLKEMPrivateKeyParameters(mlkemParams, keyPair[2], keyPair[3], keyPair[4], keyPair[0], keyPair[1], keyPair[5]);
return new AsymmetricCipherKeyPair(pubKey, privKey);
}
public void init(KeyGenerationParameters param)
{
this.initialize(param);
}
public AsymmetricCipherKeyPair generateKeyPair()
{
return genKeyPair();
}
public AsymmetricCipherKeyPair internalGenerateKeyPair(byte[] d, byte[] z)
{
byte[][] keyPair = mlkemParams.getEngine().generateKemKeyPairInternal(d, z);
MLKEMPublicKeyParameters pubKey = new MLKEMPublicKeyParameters(mlkemParams, keyPair[0], keyPair[1]);
MLKEMPrivateKeyParameters privKey = new MLKEMPrivateKeyParameters(mlkemParams, keyPair[2], keyPair[3], keyPair[4], keyPair[0], keyPair[1], keyPair[5]);
return new AsymmetricCipherKeyPair(pubKey, privKey);
}
}

View File

@ -0,0 +1,23 @@
package org.bouncycastle.pqc.crypto.mlkem;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
public class MLKEMKeyParameters
extends AsymmetricKeyParameter
{
private MLKEMParameters params;
public MLKEMKeyParameters(
boolean isPrivate,
MLKEMParameters params)
{
super(isPrivate);
this.params = params;
}
public MLKEMParameters getParameters()
{
return params;
}
}

View File

@ -0,0 +1,37 @@
package org.bouncycastle.pqc.crypto.mlkem;
import org.bouncycastle.pqc.crypto.KEMParameters;
public class MLKEMParameters
implements KEMParameters
{
public static final MLKEMParameters ml_kem_512 = new MLKEMParameters("ML-KEM-512", 2, 256);
public static final MLKEMParameters ml_kem_768 = new MLKEMParameters("ML-KEM-768", 3, 256);
public static final MLKEMParameters ml_kem_1024 = new MLKEMParameters("ML-KEM-1024", 4, 256);
private final String name;
private final int k;
private final int sessionKeySize;
private MLKEMParameters(String name, int k, int sessionKeySize)
{
this.name = name;
this.k = k;
this.sessionKeySize = sessionKeySize;
}
public String getName()
{
return name;
}
public MLKEMEngine getEngine()
{
return new MLKEMEngine(k);
}
public int getSessionKeySize()
{
return sessionKeySize;
}
}

View File

@ -0,0 +1,109 @@
package org.bouncycastle.pqc.crypto.mlkem;
import org.bouncycastle.util.Arrays;
public class MLKEMPrivateKeyParameters
extends MLKEMKeyParameters
{
final byte[] s;
final byte[] hpk;
final byte[] nonce;
final byte[] t;
final byte[] rho;
final byte[] seed;
public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] s, byte[] hpk, byte[] nonce, byte[] t, byte[] rho)
{
this(params, s, hpk, nonce, t, rho, null);
}
public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] s, byte[] hpk, byte[] nonce, byte[] t, byte[] rho, byte[] seed)
{
super(true, params);
this.s = Arrays.clone(s);
this.hpk = Arrays.clone(hpk);
this.nonce = Arrays.clone(nonce);
this.t = Arrays.clone(t);
this.rho = Arrays.clone(rho);
this.seed = Arrays.clone(seed);
}
public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] encoding)
{
super(true, params);
MLKEMEngine eng = params.getEngine();
if (encoding.length == MLKEMEngine.KyberSymBytes * 2)
{
byte[][] keyData = eng.generateKemKeyPairInternal(
Arrays.copyOfRange(encoding, 0, MLKEMEngine.KyberSymBytes),
Arrays.copyOfRange(encoding, MLKEMEngine.KyberSymBytes, encoding.length));
this.s = keyData[2];
this.hpk = keyData[3];
this.nonce = keyData[4];
this.t = keyData[0];
this.rho = keyData[1];
this.seed = keyData[5];
}
else
{
int index = 0;
this.s = Arrays.copyOfRange(encoding, 0, eng.getKyberIndCpaSecretKeyBytes());
index += eng.getKyberIndCpaSecretKeyBytes();
this.t = Arrays.copyOfRange(encoding, index, index + eng.getKyberIndCpaPublicKeyBytes() - MLKEMEngine.KyberSymBytes);
index += eng.getKyberIndCpaPublicKeyBytes() - MLKEMEngine.KyberSymBytes;
this.rho = Arrays.copyOfRange(encoding, index, index + 32);
index += 32;
this.hpk = Arrays.copyOfRange(encoding, index, index + 32);
index += 32;
this.nonce = Arrays.copyOfRange(encoding, index, index + MLKEMEngine.KyberSymBytes);
this.seed = null;
}
}
public byte[] getEncoded()
{
return Arrays.concatenate(new byte[][]{ s, t, rho, hpk, nonce });
}
public byte[] getHPK()
{
return Arrays.clone(hpk);
}
public byte[] getNonce()
{
return Arrays.clone(nonce);
}
public byte[] getPublicKey()
{
return MLKEMPublicKeyParameters.getEncoded(t, rho);
}
public MLKEMPublicKeyParameters getPublicKeyParameters()
{
return new MLKEMPublicKeyParameters(getParameters(), t, rho);
}
public byte[] getRho()
{
return Arrays.clone(rho);
}
public byte[] getS()
{
return Arrays.clone(s);
}
public byte[] getT()
{
return Arrays.clone(t);
}
public byte[] getSeed()
{
return Arrays.clone(seed);
}
}

View File

@ -0,0 +1,44 @@
package org.bouncycastle.pqc.crypto.mlkem;
import org.bouncycastle.util.Arrays;
public class MLKEMPublicKeyParameters
extends MLKEMKeyParameters
{
static byte[] getEncoded(byte[] t, byte[] rho)
{
return Arrays.concatenate(t, rho);
}
final byte[] t;
final byte[] rho;
public MLKEMPublicKeyParameters(MLKEMParameters params, byte[] t, byte[] rho)
{
super(false, params);
this.t = Arrays.clone(t);
this.rho = Arrays.clone(rho);
}
public MLKEMPublicKeyParameters(MLKEMParameters params, byte[] encoding)
{
super(false, params);
this.t = Arrays.copyOfRange(encoding, 0, encoding.length - MLKEMEngine.KyberSymBytes);
this.rho = Arrays.copyOfRange(encoding, encoding.length - MLKEMEngine.KyberSymBytes, encoding.length);
}
public byte[] getEncoded()
{
return getEncoded(t, rho);
}
public byte[] getRho()
{
return Arrays.clone(rho);
}
public byte[] getT()
{
return Arrays.clone(t);
}
}

View File

@ -0,0 +1,100 @@
package org.bouncycastle.pqc.crypto.mlkem;
class Ntt
{
public static final short[] nttZetas = new short[]{
2285, 2571, 2970, 1812, 1493, 1422, 287, 202, 3158, 622, 1577, 182, 962,
2127, 1855, 1468, 573, 2004, 264, 383, 2500, 1458, 1727, 3199, 2648, 1017,
732, 608, 1787, 411, 3124, 1758, 1223, 652, 2777, 1015, 2036, 1491, 3047,
1785, 516, 3321, 3009, 2663, 1711, 2167, 126, 1469, 2476, 3239, 3058, 830,
107, 1908, 3082, 2378, 2931, 961, 1821, 2604, 448, 2264, 677, 2054, 2226,
430, 555, 843, 2078, 871, 1550, 105, 422, 587, 177, 3094, 3038, 2869, 1574,
1653, 3083, 778, 1159, 3182, 2552, 1483, 2727, 1119, 1739, 644, 2457, 349,
418, 329, 3173, 3254, 817, 1097, 603, 610, 1322, 2044, 1864, 384, 2114, 3193,
1218, 1994, 2455, 220, 2142, 1670, 2144, 1799, 2051, 794, 1819, 2475, 2459,
478, 3221, 3021, 996, 991, 958, 1869, 1522, 1628};
public static final short[] nttZetasInv = new short[]{
1701, 1807, 1460, 2371, 2338, 2333, 308, 108, 2851, 870, 854, 1510, 2535,
1278, 1530, 1185, 1659, 1187, 3109, 874, 1335, 2111, 136, 1215, 2945, 1465,
1285, 2007, 2719, 2726, 2232, 2512, 75, 156, 3000, 2911, 2980, 872, 2685,
1590, 2210, 602, 1846, 777, 147, 2170, 2551, 246, 1676, 1755, 460, 291, 235,
3152, 2742, 2907, 3224, 1779, 2458, 1251, 2486, 2774, 2899, 1103, 1275, 2652,
1065, 2881, 725, 1508, 2368, 398, 951, 247, 1421, 3222, 2499, 271, 90, 853,
1860, 3203, 1162, 1618, 666, 320, 8, 2813, 1544, 282, 1838, 1293, 2314, 552,
2677, 2106, 1571, 205, 2918, 1542, 2721, 2597, 2312, 681, 130, 1602, 1871,
829, 2946, 3065, 1325, 2756, 1861, 1474, 1202, 2367, 3147, 1752, 2707, 171,
3127, 3042, 1907, 1836, 1517, 359, 758, 1441};
public static short[] ntt(short[] inp)
{
short[] r = new short[MLKEMEngine.KyberN];
System.arraycopy(inp, 0, r, 0, r.length);
int len, start, j, k;
short t, zeta;
k = 1;
for (len = 128; len >= 2; len >>= 1)
{
for (start = 0; start < 256; start = j + len)
{
zeta = nttZetas[k++];
for (j = start; j < start + len; ++j)
{
t = factorQMulMont(zeta, r[j + len]);
r[j + len] = (short)(r[j] - t);
r[j] = (short)(r[j] + t);
}
}
}
return r;
}
public static short[] invNtt(short[] inp)
{
short[] r = new short[MLKEMEngine.KyberN];
System.arraycopy(inp, 0, r, 0, MLKEMEngine.KyberN);
int len, start, j, k;
short t, zeta;
k = 0;
for (len = 2; len <= 128; len <<= 1)
{
for (start = 0; start < 256; start = j + len)
{
zeta = nttZetasInv[k++];
for (j = start; j < start + len; ++j)
{
t = r[j];
r[j] = Reduce.barretReduce((short)(t + r[j + len]));
r[j + len] = (short)(t - r[j + len]);
r[j + len] = factorQMulMont(zeta, r[j + len]);
}
}
}
for (j = 0; j < 256; ++j)
{
r[j] = factorQMulMont(r[j], Ntt.nttZetasInv[127]);
}
return r;
}
public static short factorQMulMont(short a, short b)
{
return Reduce.montgomeryReduce((int)(a * b));
}
public static void baseMult(Poly outPoly, int outIndex, short a0, short a1, short b0, short b1, short zeta)
{
short outVal0 = factorQMulMont(a1, b1);
outVal0 = factorQMulMont(outVal0, zeta);
outVal0 += factorQMulMont(a0, b0);
outPoly.setCoeffIndex(outIndex, outVal0);
short outVal1 = factorQMulMont(a0, b1);
outVal1 += factorQMulMont(a1, b0);
outPoly.setCoeffIndex(outIndex + 1, outVal1);
}
}

View File

@ -0,0 +1,354 @@
package org.bouncycastle.pqc.crypto.mlkem;
class Poly
{
private short[] coeffs;
private MLKEMEngine engine;
private int polyCompressedBytes;
private int eta1;
private int eta2;
private Symmetric symmetric;
public Poly(MLKEMEngine engine)
{
this.coeffs = new short[MLKEMEngine.KyberN];
this.engine = engine;
polyCompressedBytes = engine.getKyberPolyCompressedBytes();
this.eta1 = engine.getKyberEta1();
this.eta2 = MLKEMEngine.getKyberEta2();
this.symmetric = engine.getSymmetric();
}
public short getCoeffIndex(int i)
{
return this.coeffs[i];
}
public short[] getCoeffs()
{
return this.coeffs;
}
public void setCoeffIndex(int i, short val)
{
this.coeffs[i] = val;
}
public void setCoeffs(short[] coeffs)
{
this.coeffs = coeffs;
}
public void polyNtt()
{
this.setCoeffs(Ntt.ntt(this.getCoeffs()));
this.reduce();
}
public void polyInverseNttToMont()
{
this.setCoeffs(Ntt.invNtt(this.getCoeffs()));
}
public void reduce()
{
int i;
for (i = 0; i < MLKEMEngine.KyberN; i++)
{
this.setCoeffIndex(i, Reduce.barretReduce(this.getCoeffIndex(i)));
}
}
public static void baseMultMontgomery(Poly r, Poly a, Poly b)
{
int i;
for (i = 0; i < MLKEMEngine.KyberN / 4; i++)
{
Ntt.baseMult(r, 4 * i,
a.getCoeffIndex(4 * i), a.getCoeffIndex(4 * i + 1),
b.getCoeffIndex(4 * i), b.getCoeffIndex(4 * i + 1),
Ntt.nttZetas[64 + i]);
Ntt.baseMult(r, 4 * i + 2,
a.getCoeffIndex(4 * i + 2), a.getCoeffIndex(4 * i + 3),
b.getCoeffIndex(4 * i + 2), b.getCoeffIndex(4 * i + 3),
(short)(-1 * Ntt.nttZetas[64 + i]));
}
}
public void addCoeffs(Poly b)
{
int i;
for (i = 0; i < MLKEMEngine.KyberN; i++)
{
this.setCoeffIndex(i, (short)(this.getCoeffIndex(i) + b.getCoeffIndex(i)));
}
}
public void convertToMont()
{
int i;
final short f = (short)(((long)1 << 32) % MLKEMEngine.KyberQ);
for (i = 0; i < MLKEMEngine.KyberN; i++)
{
this.setCoeffIndex(i, Reduce.montgomeryReduce(this.getCoeffIndex(i) * f));
}
}
public byte[] compressPoly()
{
int i, j;
byte[] t = new byte[8];
byte[] r = new byte[polyCompressedBytes];
int count = 0;
this.conditionalSubQ();
// System.out.print("v = [");
// Helper.printShortArray(this.coeffs);
// System.out.print("]\n");
if (polyCompressedBytes == 128)
{
for (i = 0; i < MLKEMEngine.KyberN / 8; i++)
{
for (j = 0; j < 8; j++)
{
/*t[j] =
(byte)((((((short)this.getCoeffIndex(8 * i + j)) << 4)
+
(KyberEngine.KyberQ / 2)
) / KyberEngine.KyberQ)
& 15);*/
// Fix for KyberSlash2: division by KyberQ above is not
// constant time.
int t_j = this.getCoeffIndex(8 * i + j);
t_j <<= 4;
t_j += 1665;
t_j *= 80635;
t_j >>= 28;
t_j &= 15;
t[j] = (byte)t_j;
}
r[count + 0] = (byte)(t[0] | (t[1] << 4));
r[count + 1] = (byte)(t[2] | (t[3] << 4));
r[count + 2] = (byte)(t[4] | (t[5] << 4));
r[count + 3] = (byte)(t[6] | (t[7] << 4));
count += 4;
}
}
else if (polyCompressedBytes == 160)
{
for (i = 0; i < MLKEMEngine.KyberN / 8; i++)
{
for (j = 0; j < 8; j++)
{
/*t[j] =
(byte)(((((this.getCoeffIndex(8 * i + j) << 5))
+
(KyberEngine.KyberQ / 2)
) / KyberEngine.KyberQ
) & 31
);*/
// Fix for KyberSlash2: division by KyberQ above is not
// constant time.
int t_j = this.getCoeffIndex(8 * i + j);
t_j <<= 5;
t_j += 1664;
t_j *= 40318;
t_j >>= 27;
t_j &= 31;
t[j] = (byte)t_j;
}
r[count + 0] = (byte)((t[0] >> 0) | (t[1] << 5));
r[count + 1] = (byte)((t[1] >> 3) | (t[2] << 2) | (t[3] << 7));
r[count + 2] = (byte)((t[3] >> 1) | (t[4] << 4));
r[count + 3] = (byte)((t[4] >> 4) | (t[5] << 1) | (t[6] << 6));
r[count + 4] = (byte)((t[6] >> 2) | (t[7] << 3));
count += 5;
}
}
else
{
throw new RuntimeException("PolyCompressedBytes is neither 128 or 160!");
}
// System.out.print("r = ");
// Helper.printByteArray(r);
// System.out.println();
return r;
}
public void decompressPoly(byte[] compressedPolyCipherText)
{
int i, count = 0;
if (engine.getKyberPolyCompressedBytes() == 128)
{
for (i = 0; i < MLKEMEngine.KyberN / 2; i++)
{
this.setCoeffIndex(2 * i + 0, (short)((((short)((compressedPolyCipherText[count] & 0xFF) & 15) * MLKEMEngine.KyberQ) + 8) >> 4));
this.setCoeffIndex(2 * i + 1, (short)((((short)((compressedPolyCipherText[count] & 0xFF) >> 4) * MLKEMEngine.KyberQ) + 8) >> 4));
count += 1;
}
}
else if (engine.getKyberPolyCompressedBytes() == 160)
{
int j;
byte[] t = new byte[8];
for (i = 0; i < MLKEMEngine.KyberN / 8; i++)
{
t[0] = (byte)((compressedPolyCipherText[count + 0] & 0xFF) >> 0);
t[1] = (byte)(((compressedPolyCipherText[count + 0] & 0xFF) >> 5) | ((compressedPolyCipherText[count + 1] & 0xFF) << 3));
t[2] = (byte)((compressedPolyCipherText[count + 1] & 0xFF) >> 2);
t[3] = (byte)(((compressedPolyCipherText[count + 1] & 0xFF) >> 7) | ((compressedPolyCipherText[count + 2] & 0xFF) << 1));
t[4] = (byte)(((compressedPolyCipherText[count + 2] & 0xFF) >> 4) | ((compressedPolyCipherText[count + 3] & 0xFF) << 4));
t[5] = (byte)((compressedPolyCipherText[count + 3] & 0xFF) >> 1);
t[6] = (byte)(((compressedPolyCipherText[count + 3] & 0xFF) >> 6) | ((compressedPolyCipherText[count + 4] & 0xFF) << 2));
t[7] = (byte)((compressedPolyCipherText[count + 4] & 0xFF) >> 3);
count += 5;
for (j = 0; j < 8; j++)
{
this.setCoeffIndex(8 * i + j, (short)(((t[j] & 31) * MLKEMEngine.KyberQ + 16) >> 5));
}
}
}
else
{
throw new RuntimeException("PolyCompressedBytes is neither 128 or 160!");
}
}
public byte[] toBytes()
{
byte[] r = new byte[MLKEMEngine.KyberPolyBytes];
short t0, t1;
this.conditionalSubQ();
for (int i = 0; i < MLKEMEngine.KyberN / 2; i++)
{
t0 = this.getCoeffIndex(2 * i);
t1 = this.getCoeffIndex(2 * i + 1);
r[3 * i] = (byte)(t0 >> 0);
r[3 * i + 1] = (byte)((t0 >> 8) | (t1 << 4));
r[3 * i + 2] = (byte)(t1 >> 4);
}
return r;
}
public void fromBytes(byte[] inpBytes)
{
int i;
for (i = 0; i < MLKEMEngine.KyberN / 2; i++)
{
this.setCoeffIndex(2 * i, (short)(
(
((inpBytes[3 * i + 0] & 0xFF) >> 0)
| ((inpBytes[3 * i + 1] & 0xFF) << 8)
) & 0xFFF)
);
this.setCoeffIndex(2 * i + 1, (short)(
(
((inpBytes[3 * i + 1] & 0xFF) >> 4)
| (long)((inpBytes[3 * i + 2] & 0xFF) << 4)
) & 0xFFF)
);
}
}
public byte[] toMsg()
{
int LOWER = MLKEMEngine.KyberQ >>> 2;
int UPPER = MLKEMEngine.KyberQ - LOWER;
byte[] outMsg = new byte[MLKEMEngine.getKyberIndCpaMsgBytes()];
this.conditionalSubQ();
for (int i = 0; i < MLKEMEngine.KyberN / 8; i++)
{
outMsg[i] = 0;
for (int j = 0; j < 8; j++)
{
int c_j = this.getCoeffIndex(8 * i + j);
// KyberSlash: division by Q is not constant time.
// int t = (((c_j << 1) + (KyberEngine.KyberQ / 2)) / KyberEngine.KyberQ) & 1;
int t = ((LOWER - c_j) & (c_j - UPPER)) >>> 31;
outMsg[i] |= (byte)(t << j);
}
}
return outMsg;
}
public void fromMsg(byte[] msg)
{
int i, j;
short mask;
if (msg.length != MLKEMEngine.KyberN / 8)
{
throw new RuntimeException("KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!");
}
for (i = 0; i < MLKEMEngine.KyberN / 8; i++)
{
for (j = 0; j < 8; j++)
{
mask = (short)((-1) * (short)(((msg[i] & 0xFF) >> j) & 1));
this.setCoeffIndex(8 * i + j, (short)(mask & (short)((MLKEMEngine.KyberQ + 1) / 2)));
}
}
}
public void conditionalSubQ()
{
int i;
for (i = 0; i < MLKEMEngine.KyberN; i++)
{
this.setCoeffIndex(i, Reduce.conditionalSubQ(this.getCoeffIndex(i)));
}
}
public void getEta1Noise(byte[] seed, byte nonce)
{
byte[] buf = new byte[MLKEMEngine.KyberN * eta1 / 4];
symmetric.prf(buf, seed, nonce);
CBD.mlkemCBD(this, buf, eta1);
}
public void getEta2Noise(byte[] seed, byte nonce)
{
byte[] buf = new byte[MLKEMEngine.KyberN * eta2 / 4];
symmetric.prf(buf, seed, nonce);
CBD.mlkemCBD(this, buf, eta2);
}
public void polySubtract(Poly b)
{
int i;
for (i = 0; i < MLKEMEngine.KyberN; i++)
{
this.setCoeffIndex(i, (short)(b.getCoeffIndex(i) - this.getCoeffIndex(i)));
}
}
public String toString()
{
StringBuffer out = new StringBuffer();
out.append("[");
for (int i = 0; i < coeffs.length; i++)
{
out.append(coeffs[i]);
if (i != coeffs.length - 1)
{
out.append(", ");
}
}
out.append("]");
return out.toString();
}
}

View File

@ -0,0 +1,272 @@
package org.bouncycastle.pqc.crypto.mlkem;
import org.bouncycastle.util.Arrays;
class PolyVec
{
Poly[] vec;
private MLKEMEngine engine;
private int kyberK;
private int polyVecBytes;
public PolyVec(MLKEMEngine engine)
{
this.engine = engine;
this.kyberK = engine.getKyberK();
this.polyVecBytes = engine.getKyberPolyVecBytes();
this.vec = new Poly[kyberK];
for (int i = 0; i < kyberK; i++)
{
vec[i] = new Poly(engine);
}
}
public PolyVec()
throws Exception
{
throw new Exception("Requires Parameter");
}
public Poly getVectorIndex(int i)
{
return vec[i];
}
public void polyVecNtt()
{
int i;
for (i = 0; i < kyberK; i++)
{
this.getVectorIndex(i).polyNtt();
}
}
public void polyVecInverseNttToMont()
{
for (int i = 0; i < kyberK; i++)
{
this.getVectorIndex(i).polyInverseNttToMont();
}
}
public byte[] compressPolyVec()
{
int i, j, k;
this.conditionalSubQ();
short[] t;
byte[] r = new byte[engine.getKyberPolyVecCompressedBytes()];
int count = 0;
if (engine.getKyberPolyVecCompressedBytes() == kyberK * 320)
{
t = new short[4];
for (i = 0; i < kyberK; i++)
{
for (j = 0; j < MLKEMEngine.KyberN / 4; j++)
{
for (k = 0; k < 4; k++)
{
/*t[k] = (short)
(
(
((this.getVectorIndex(i).getCoeffIndex(4 * j + k) << 10)
+ (KyberEngine.KyberQ / 2))
/ KyberEngine.KyberQ)
& 0x3ff);*/
// Fix for KyberSlash2: division by KyberQ above is not
// constant time.
long t_k = this.getVectorIndex(i).getCoeffIndex(4 * j + k);
t_k <<= 10;
t_k += 1665;
t_k *= 1290167;
t_k >>= 32;
t_k &= 0x3ff;
t[k] = (short)t_k;
}
r[count + 0] = (byte)(t[0] >> 0);
r[count + 1] = (byte)((t[0] >> 8) | (t[1] << 2));
r[count + 2] = (byte)((t[1] >> 6) | (t[2] << 4));
r[count + 3] = (byte)((t[2] >> 4) | (t[3] << 6));
r[count + 4] = (byte)((t[3] >> 2));
count += 5;
}
}
}
else if (engine.getKyberPolyVecCompressedBytes() == kyberK * 352)
{
t = new short[8];
for (i = 0; i < kyberK; i++)
{
for (j = 0; j < MLKEMEngine.KyberN / 8; j++)
{
for (k = 0; k < 8; k++)
{
/*t[k] = (short)
(
(
((this.getVectorIndex(i).getCoeffIndex(8 * j + k) << 11)
+ (KyberEngine.KyberQ / 2))
/ KyberEngine.KyberQ)
& 0x7ff);*/
// Fix for KyberSlash2: division by KyberQ above is not
// constant time.
long t_k = this.getVectorIndex(i).getCoeffIndex(8 * j + k);
t_k <<= 11;
t_k += 1664;
t_k *= 645084;
t_k >>= 31;
t_k &= 0x7ff;
t[k] = (short)t_k;
}
r[count + 0] = (byte)((t[0] >> 0));
r[count + 1] = (byte)((t[0] >> 8) | (t[1] << 3));
r[count + 2] = (byte)((t[1] >> 5) | (t[2] << 6));
r[count + 3] = (byte)((t[2] >> 2));
r[count + 4] = (byte)((t[2] >> 10) | (t[3] << 1));
r[count + 5] = (byte)((t[3] >> 7) | (t[4] << 4));
r[count + 6] = (byte)((t[4] >> 4) | (t[5] << 7));
r[count + 7] = (byte)((t[5] >> 1));
r[count + 8] = (byte)((t[5] >> 9) | (t[6] << 2));
r[count + 9] = (byte)((t[6] >> 6) | (t[7] << 5));
r[count + 10] = (byte)((t[7] >> 3));
count += 11;
}
}
}
else
{
throw new RuntimeException("Kyber PolyVecCompressedBytes neither 320 * KyberK or 352 * KyberK!");
}
return r;
}
public void decompressPolyVec(byte[] compressedPolyVecCipherText)
{
int i, j, k, count = 0;
if (engine.getKyberPolyVecCompressedBytes() == (kyberK * 320))
{
short[] t = new short[4];
for (i = 0; i < kyberK; i++)
{
for (j = 0; j < MLKEMEngine.KyberN / 4; j++)
{
t[0] = (short)(((compressedPolyVecCipherText[count] & 0xFF) >> 0) | (short)((compressedPolyVecCipherText[count + 1] & 0xFF) << 8));
t[1] = (short)(((compressedPolyVecCipherText[count + 1] & 0xFF) >> 2) | (short)((compressedPolyVecCipherText[count + 2] & 0xFF) << 6));
t[2] = (short)(((compressedPolyVecCipherText[count + 2] & 0xFF) >> 4) | (short)((compressedPolyVecCipherText[count + 3] & 0xFF) << 4));
t[3] = (short)(((compressedPolyVecCipherText[count + 3] & 0xFF) >> 6) | (short)((compressedPolyVecCipherText[count + 4] & 0xFF) << 2));
count += 5;
for (k = 0; k < 4; k++)
{
this.vec[i].setCoeffIndex(4 * j + k, (short)(((t[k] & 0x3FF) * MLKEMEngine.KyberQ + 512) >> 10));
}
}
}
}
else if (engine.getKyberPolyVecCompressedBytes() == (kyberK * 352))
{
short[] t = new short[8];
for (i = 0; i < kyberK; i++)
{
for (j = 0; j < MLKEMEngine.KyberN / 8; j++)
{
t[0] = (short)(((compressedPolyVecCipherText[count] & 0xFF) >> 0) | ((short)(compressedPolyVecCipherText[count + 1] & 0xFF) << 8));
t[1] = (short)(((compressedPolyVecCipherText[count + 1] & 0xFF) >> 3) | ((short)(compressedPolyVecCipherText[count + 2] & 0xFF) << 5));
t[2] = (short)(((compressedPolyVecCipherText[count + 2] & 0xFF) >> 6) | ((short)(compressedPolyVecCipherText[count + 3] & 0xFF) << 2) | ((short)((compressedPolyVecCipherText[count + 4] & 0xFF) << 10)));
t[3] = (short)(((compressedPolyVecCipherText[count + 4] & 0xFF) >> 1) | ((short)(compressedPolyVecCipherText[count + 5] & 0xFF) << 7));
t[4] = (short)(((compressedPolyVecCipherText[count + 5] & 0xFF) >> 4) | ((short)(compressedPolyVecCipherText[count + 6] & 0xFF) << 4));
t[5] = (short)(((compressedPolyVecCipherText[count + 6] & 0xFF) >> 7) | ((short)(compressedPolyVecCipherText[count + 7] & 0xFF) << 1) | ((short)((compressedPolyVecCipherText[count + 8] & 0xFF) << 9)));
t[6] = (short)(((compressedPolyVecCipherText[count + 8] & 0xFF) >> 2) | ((short)(compressedPolyVecCipherText[count + 9] & 0xFF) << 6));
t[7] = (short)(((compressedPolyVecCipherText[count + 9] & 0xFF) >> 5) | ((short)(compressedPolyVecCipherText[count + 10] & 0xFF) << 3));
count += 11;
for (k = 0; k < 8; k++)
{
this.vec[i].setCoeffIndex(8 * j + k, (short)(((t[k] & 0x7FF) * MLKEMEngine.KyberQ + 1024) >> 11));
}
}
}
}
else
{
throw new RuntimeException("Kyber PolyVecCompressedBytes neither 320 * KyberK or 352 * KyberK!");
}
}
public static void pointwiseAccountMontgomery(Poly out, PolyVec inp1, PolyVec inp2, MLKEMEngine engine)
{
int i;
Poly t = new Poly(engine);
Poly.baseMultMontgomery(out, inp1.getVectorIndex(0), inp2.getVectorIndex(0));
for (i = 1; i < engine.getKyberK(); i++)
{
Poly.baseMultMontgomery(t, inp1.getVectorIndex(i), inp2.getVectorIndex(i));
out.addCoeffs(t);
}
out.reduce();
}
public void reducePoly()
{
int i;
for (i = 0; i < kyberK; i++)
{
this.getVectorIndex(i).reduce();
}
}
public void addPoly(PolyVec b)
{
int i;
for (i = 0; i < kyberK; i++)
{
this.getVectorIndex(i).addCoeffs(b.getVectorIndex(i));
}
}
public byte[] toBytes()
{
byte[] r = new byte[polyVecBytes];
for (int i = 0; i < kyberK; i++)
{
System.arraycopy(this.vec[i].toBytes(), 0, r, i * MLKEMEngine.KyberPolyBytes, MLKEMEngine.KyberPolyBytes);
}
return r;
}
public void fromBytes(byte[] inputBytes)
{
for (int i = 0; i < kyberK; i++)
{
this.getVectorIndex(i).fromBytes(Arrays.copyOfRange(inputBytes, i * MLKEMEngine.KyberPolyBytes, (i + 1) * MLKEMEngine.KyberPolyBytes));
}
}
public void conditionalSubQ()
{
for (int i = 0; i < kyberK; i++)
{
this.getVectorIndex(i).conditionalSubQ();
}
}
public String toString()
{
StringBuffer out = new StringBuffer();
out.append("[");
for (int i = 0; i < kyberK; i++)
{
out.append(vec[i].toString());
if (i != kyberK - 1)
{
out.append(", ");
}
}
out.append("]");
return out.toString();
}
}

View File

@ -0,0 +1,35 @@
package org.bouncycastle.pqc.crypto.mlkem;
class Reduce
{
public static short montgomeryReduce(int a)
{
int t;
short u;
u = (short)(a * MLKEMEngine.KyberQinv);
t = (int)(u * MLKEMEngine.KyberQ);
t = a - t;
t >>= 16;
return (short)t;
}
public static short barretReduce(short a)
{
short t;
long shift = (((long)1) << 26);
short v = (short)((shift + (MLKEMEngine.KyberQ / 2)) / MLKEMEngine.KyberQ);
t = (short)((v * a) >> 26);
t = (short)(t * MLKEMEngine.KyberQ);
return (short)(a - t);
}
public static short conditionalSubQ(short a)
{
a -= MLKEMEngine.KyberQ;
a += (a >> 15) & MLKEMEngine.KyberQ;
return a;
}
}

View File

@ -0,0 +1,94 @@
package org.bouncycastle.pqc.crypto.mlkem;
import org.bouncycastle.crypto.digests.SHA3Digest;
import org.bouncycastle.crypto.digests.SHAKEDigest;
abstract class Symmetric
{
final int xofBlockBytes;
abstract void hash_h(byte[] out, byte[] in, int outOffset);
abstract void hash_g(byte[] out, byte[] in);
abstract void xofAbsorb(byte[] seed, byte x, byte y);
abstract void xofSqueezeBlocks(byte[] out, int outOffset, int outLen);
abstract void prf(byte[] out, byte[] key, byte nonce);
abstract void kdf(byte[] out, byte[] in);
Symmetric(int blockBytes)
{
this.xofBlockBytes = blockBytes;
}
static class ShakeSymmetric
extends Symmetric
{
private final SHAKEDigest xof;
private final SHA3Digest sha3Digest512;
private final SHA3Digest sha3Digest256;
private final SHAKEDigest shakeDigest;
ShakeSymmetric()
{
super(168);
this.xof = new SHAKEDigest(128);
this.shakeDigest = new SHAKEDigest(256);
this.sha3Digest256 = new SHA3Digest(256);
this.sha3Digest512 = new SHA3Digest(512);
}
@Override
void hash_h(byte[] out, byte[] in, int outOffset)
{
sha3Digest256.update(in, 0, in.length);
sha3Digest256.doFinal(out, outOffset);
}
@Override
void hash_g(byte[] out, byte[] in)
{
sha3Digest512.update(in, 0, in.length);
sha3Digest512.doFinal(out, 0);
}
@Override
void xofAbsorb(byte[] seed, byte a, byte b)
{
xof.reset();
byte[] buf = new byte[seed.length + 2];
System.arraycopy(seed, 0, buf, 0, seed.length);
buf[seed.length] = a;
buf[seed.length + 1] = b;
xof.update(buf, 0, seed.length + 2);
}
@Override
void xofSqueezeBlocks(byte[] out, int outOffset, int outLen)
{
xof.doOutput(out, outOffset, outLen);
}
@Override
void prf(byte[] out, byte[] seed, byte nonce)
{
byte[] extSeed = new byte[seed.length + 1];
System.arraycopy(seed, 0, extSeed, 0, seed.length);
extSeed[seed.length] = nonce;
shakeDigest.update(extSeed, 0, extSeed.length);
shakeDigest.doFinal(out, 0, out.length);
}
@Override
void kdf(byte[] out, byte[] in)
{
shakeDigest.update(in, 0, in.length);
shakeDigest.doFinal(out, 0, out.length);
}
}
}

View File

@ -0,0 +1,64 @@
package org.bouncycastle.pqc.crypto.util;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.security.auth.DestroyFailedException;
import org.bouncycastle.crypto.SecretWithEncapsulation;
import org.bouncycastle.util.Arrays;
public class SecretWithEncapsulationImpl
implements SecretWithEncapsulation
{
private final AtomicBoolean hasBeenDestroyed = new AtomicBoolean(false);
private final byte[] sessionKey;
private final byte[] cipher_text;
public SecretWithEncapsulationImpl(byte[] sessionKey, byte[] cipher_text)
{
this.sessionKey = sessionKey;
this.cipher_text = cipher_text;
}
public byte[] getSecret()
{
byte[] clone = Arrays.clone(sessionKey);
checkDestroyed();
return clone;
}
public byte[] getEncapsulation()
{
byte[] clone = Arrays.clone(cipher_text);
checkDestroyed();
return clone;
}
public void destroy()
throws DestroyFailedException
{
if (!hasBeenDestroyed.getAndSet(true))
{
Arrays.clear(sessionKey);
Arrays.clear(cipher_text);
}
}
public boolean isDestroyed()
{
return hasBeenDestroyed.get();
}
void checkDestroyed()
{
if (isDestroyed())
{
throw new IllegalStateException("data has been destroyed");
}
}
}

View File

@ -0,0 +1,27 @@
package org.bouncycastle.util;
/**
* Interface for Memoable objects. Memoable objects allow the taking of a snapshot of their internal state
* via the copy() method and then resetting the object back to that state later using the reset() method.
*/
public interface Memoable
{
/**
* Produce a copy of this object with its configuration and in its current state.
* <p>
* The returned object may be used simply to store the state, or may be used as a similar object
* starting from the copied state.
*/
Memoable copy();
/**
* Restore a copied object state into this object.
* <p>
* Implementations of this method <em>should</em> try to avoid or minimise memory allocation to perform the reset.
*
* @param other an object originally {@link #copy() copied} from an object of the same type as this instance.
* @throws ClassCastException if the provided object is not of the correct type.
* @throws MemoableResetException if the <b>other</b> parameter is in some other way invalid.
*/
void reset(Memoable other);
}

View File

@ -0,0 +1,419 @@
package org.bouncycastle.util;
/**
* Utility methods for converting byte arrays into ints and longs, and back again.
*/
public abstract class Pack
{
public static short bigEndianToShort(byte[] bs, int off)
{
int n = (bs[off] & 0xff) << 8;
n |= (bs[++off] & 0xff);
return (short)n;
}
public static int bigEndianToInt(byte[] bs, int off)
{
int n = bs[off] << 24;
n |= (bs[++off] & 0xff) << 16;
n |= (bs[++off] & 0xff) << 8;
n |= (bs[++off] & 0xff);
return n;
}
public static void bigEndianToInt(byte[] bs, int off, int[] ns)
{
for (int i = 0; i < ns.length; ++i)
{
ns[i] = bigEndianToInt(bs, off);
off += 4;
}
}
public static void bigEndianToInt(byte[] bs, int off, int[] ns, int nsOff, int nsLen)
{
for (int i = 0; i < nsLen; ++i)
{
ns[nsOff + i] = bigEndianToInt(bs, off);
off += 4;
}
}
public static byte[] intToBigEndian(int n)
{
byte[] bs = new byte[4];
intToBigEndian(n, bs, 0);
return bs;
}
public static void intToBigEndian(int n, byte[] bs, int off)
{
bs[off] = (byte)(n >>> 24);
bs[++off] = (byte)(n >>> 16);
bs[++off] = (byte)(n >>> 8);
bs[++off] = (byte)(n);
}
public static byte[] intToBigEndian(int[] ns)
{
byte[] bs = new byte[4 * ns.length];
intToBigEndian(ns, bs, 0);
return bs;
}
public static void intToBigEndian(int[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.length; ++i)
{
intToBigEndian(ns[i], bs, off);
off += 4;
}
}
public static void intToBigEndian(int[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
intToBigEndian(ns[nsOff + i], bs, bsOff);
bsOff += 4;
}
}
public static long bigEndianToLong(byte[] bs, int off)
{
int hi = bigEndianToInt(bs, off);
int lo = bigEndianToInt(bs, off + 4);
return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL);
}
public static void bigEndianToLong(byte[] bs, int off, long[] ns)
{
for (int i = 0; i < ns.length; ++i)
{
ns[i] = bigEndianToLong(bs, off);
off += 8;
}
}
public static void bigEndianToLong(byte[] bs, int bsOff, long[] ns, int nsOff, int nsLen)
{
for (int i = 0; i < nsLen; ++i)
{
ns[nsOff + i] = bigEndianToLong(bs, bsOff);
bsOff += 8;
}
}
public static long bigEndianToLong(byte[] bs, int off, int len)
{
long x = 0;
for (int i = 0; i < len; ++i)
{
x |= (bs[i + off] & 0xFFL) << ((7 - i) << 3);
}
return x;
}
public static byte[] longToBigEndian(long n)
{
byte[] bs = new byte[8];
longToBigEndian(n, bs, 0);
return bs;
}
public static void longToBigEndian(long n, byte[] bs, int off)
{
intToBigEndian((int)(n >>> 32), bs, off);
intToBigEndian((int)(n & 0xffffffffL), bs, off + 4);
}
public static byte[] longToBigEndian(long[] ns)
{
byte[] bs = new byte[8 * ns.length];
longToBigEndian(ns, bs, 0);
return bs;
}
public static void longToBigEndian(long[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.length; ++i)
{
longToBigEndian(ns[i], bs, off);
off += 8;
}
}
public static void longToBigEndian(long[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
longToBigEndian(ns[nsOff + i], bs, bsOff);
bsOff += 8;
}
}
/**
* @param value The number
* @param bs The target.
* @param off Position in target to start.
* @param bytes number of bytes to write.
* @deprecated Will be removed
*/
public static void longToBigEndian(long value, byte[] bs, int off, int bytes)
{
for (int i = bytes - 1; i >= 0; i--)
{
bs[i + off] = (byte)(value & 0xff);
value >>>= 8;
}
}
public static short littleEndianToShort(byte[] bs, int off)
{
int n = bs[off] & 0xff;
n |= (bs[++off] & 0xff) << 8;
return (short)n;
}
public static int littleEndianToInt(byte[] bs, int off)
{
int n = bs[off] & 0xff;
n |= (bs[++off] & 0xff) << 8;
n |= (bs[++off] & 0xff) << 16;
n |= bs[++off] << 24;
return n;
}
public static int littleEndianToInt_High(byte[] bs, int off, int len)
{
return littleEndianToInt_Low(bs, off, len) << ((4 - len) << 3);
}
public static int littleEndianToInt_Low(byte[] bs, int off, int len)
{
// assert 1 <= len && len <= 4;
int result = bs[off] & 0xff;
int pos = 0;
for (int i = 1; i < len; ++i)
{
pos += 8;
result |= (bs[off + i] & 0xff) << pos;
}
return result;
}
public static void littleEndianToInt(byte[] bs, int off, int[] ns)
{
for (int i = 0; i < ns.length; ++i)
{
ns[i] = littleEndianToInt(bs, off);
off += 4;
}
}
public static void littleEndianToInt(byte[] bs, int bOff, int[] ns, int nOff, int count)
{
for (int i = 0; i < count; ++i)
{
ns[nOff + i] = littleEndianToInt(bs, bOff);
bOff += 4;
}
}
public static int[] littleEndianToInt(byte[] bs, int off, int count)
{
int[] ns = new int[count];
for (int i = 0; i < ns.length; ++i)
{
ns[i] = littleEndianToInt(bs, off);
off += 4;
}
return ns;
}
public static byte[] shortToLittleEndian(short n)
{
byte[] bs = new byte[2];
shortToLittleEndian(n, bs, 0);
return bs;
}
public static void shortToLittleEndian(short n, byte[] bs, int off)
{
bs[off] = (byte)(n);
bs[++off] = (byte)(n >>> 8);
}
public static byte[] shortToBigEndian(short n)
{
byte[] r = new byte[2];
shortToBigEndian(n, r, 0);
return r;
}
public static void shortToBigEndian(short n, byte[] bs, int off)
{
bs[off] = (byte)(n >>> 8);
bs[++off] = (byte)(n);
}
public static byte[] intToLittleEndian(int n)
{
byte[] bs = new byte[4];
intToLittleEndian(n, bs, 0);
return bs;
}
public static void intToLittleEndian(int n, byte[] bs, int off)
{
bs[off] = (byte)(n);
bs[++off] = (byte)(n >>> 8);
bs[++off] = (byte)(n >>> 16);
bs[++off] = (byte)(n >>> 24);
}
public static byte[] intToLittleEndian(int[] ns)
{
byte[] bs = new byte[4 * ns.length];
intToLittleEndian(ns, bs, 0);
return bs;
}
public static void intToLittleEndian(int[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.length; ++i)
{
intToLittleEndian(ns[i], bs, off);
off += 4;
}
}
public static void intToLittleEndian(int[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
intToLittleEndian(ns[nsOff + i], bs, bsOff);
bsOff += 4;
}
}
public static long littleEndianToLong(byte[] bs, int off)
{
int lo = littleEndianToInt(bs, off);
int hi = littleEndianToInt(bs, off + 4);
return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL);
}
public static void littleEndianToLong(byte[] bs, int off, long[] ns)
{
for (int i = 0; i < ns.length; ++i)
{
ns[i] = littleEndianToLong(bs, off);
off += 8;
}
}
public static long littleEndianToLong(byte[] input, int off, int len)
{
long result = 0;
for (int i = 0; i < len; ++i)
{
result |= (input[off + i] & 0xFFL) << (i << 3);
}
return result;
}
public static void littleEndianToLong(byte[] bs, int bsOff, long[] ns, int nsOff, int nsLen)
{
for (int i = 0; i < nsLen; ++i)
{
ns[nsOff + i] = littleEndianToLong(bs, bsOff);
bsOff += 8;
}
}
public static void longToLittleEndian_High(long n, byte[] bs, int off, int len)
{
//Debug.Assert(1 <= len && len <= 8);
int pos = 56;
bs[off] = (byte)(n >>> pos);
for (int i = 1; i < len; ++i)
{
pos -= 8;
bs[off + i] = (byte)(n >>> pos);
}
}
public static void longToLittleEndian(long n, byte[] bs, int off, int len)
{
for (int i = 0; i < len; ++i)
{
bs[off + i] = (byte)(n >>> (i << 3));
}
}
// public static void longToLittleEndian_Low(long n, byte[] bs, int off, int len)
// {
// longToLittleEndian_High(n << ((8 - len) << 3), bs, off, len);
// }
public static long littleEndianToLong_High(byte[] bs, int off, int len)
{
return littleEndianToLong_Low(bs, off, len) << ((8 - len) << 3);
}
public static long littleEndianToLong_Low(byte[] bs, int off, int len)
{
//Debug.Assert(1 <= len && len <= 8);
long result = bs[off] & 0xFF;
for (int i = 1; i < len; ++i)
{
result <<= 8;
result |= bs[off + i] & 0xFF;
}
return result;
}
public static byte[] longToLittleEndian(long n)
{
byte[] bs = new byte[8];
longToLittleEndian(n, bs, 0);
return bs;
}
public static void longToLittleEndian(long n, byte[] bs, int off)
{
intToLittleEndian((int)(n & 0xffffffffL), bs, off);
intToLittleEndian((int)(n >>> 32), bs, off + 4);
}
public static byte[] longToLittleEndian(long[] ns)
{
byte[] bs = new byte[8 * ns.length];
longToLittleEndian(ns, bs, 0);
return bs;
}
public static void longToLittleEndian(long[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.length; ++i)
{
longToLittleEndian(ns[i], bs, off);
off += 8;
}
}
public static void longToLittleEndian(long[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
longToLittleEndian(ns[nsOff + i], bs, bsOff);
bsOff += 8;
}
}
}

View File

@ -0,0 +1,4 @@
/**
* General purpose utility classes used throughout the APIs.
*/
package org.bouncycastle.util;