![JAR search and dependency download from the Maven repository](/logo.png)
org.bouncycastle.crypto.fips.FipsAES Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bc-fips-debug Show documentation
Show all versions of bc-fips-debug Show documentation
The FIPS 140-2 Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms certified to FIPS 140-2 level 1. This jar contains the debug version JCE provider and low-level API for the BC-FJA version 1.0.2.3, FIPS Certificate #3514. Please note the debug jar is not certified.
package org.bouncycastle.crypto.fips;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import org.bouncycastle.crypto.Algorithm;
import org.bouncycastle.crypto.AuthenticationParametersWithIV;
import org.bouncycastle.crypto.CipherOutputStream;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.IllegalKeyException;
import org.bouncycastle.crypto.InvalidWrappingException;
import org.bouncycastle.crypto.OperatorUsingSecureRandom;
import org.bouncycastle.crypto.OutputEncryptor;
import org.bouncycastle.crypto.PlainInputProcessingException;
import org.bouncycastle.crypto.SymmetricKey;
import org.bouncycastle.crypto.SymmetricSecretKey;
import org.bouncycastle.crypto.UpdateOutputStream;
import org.bouncycastle.crypto.general.FipsRegister;
import org.bouncycastle.crypto.internal.BlockCipher;
import org.bouncycastle.crypto.internal.BufferedBlockCipher;
import org.bouncycastle.crypto.internal.CipherParameters;
import org.bouncycastle.crypto.internal.InvalidCipherTextException;
import org.bouncycastle.crypto.internal.KeyGenerationParameters;
import org.bouncycastle.crypto.internal.Mac;
import org.bouncycastle.crypto.internal.MultiBlockCipher;
import org.bouncycastle.crypto.internal.StreamCipher;
import org.bouncycastle.crypto.internal.ValidatedSymmetricKey;
import org.bouncycastle.crypto.internal.Wrapper;
import org.bouncycastle.crypto.internal.fpe.SP80038G;
import org.bouncycastle.crypto.internal.io.CipherInputStream;
import org.bouncycastle.crypto.internal.io.CipherOutputStreamImpl;
import org.bouncycastle.crypto.internal.macs.AEADCipherMac;
import org.bouncycastle.crypto.internal.macs.CMac;
import org.bouncycastle.crypto.internal.macs.GMac;
import org.bouncycastle.crypto.internal.modes.AEADBlockCipher;
import org.bouncycastle.crypto.internal.modes.CCMBlockCipher;
import org.bouncycastle.crypto.internal.modes.GCMBlockCipher;
import org.bouncycastle.crypto.internal.params.KeyParameter;
import org.bouncycastle.crypto.internal.params.KeyParameterImpl;
import org.bouncycastle.crypto.internal.test.BasicKatTest;
import org.bouncycastle.crypto.internal.wrappers.SP80038FWrapEngine;
import org.bouncycastle.crypto.internal.wrappers.SP80038FWrapWithPaddingEngine;
import org.bouncycastle.crypto.util.RadixConverter;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.Properties;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.io.Streams;
/**
* Source class for approved implementations of AES based algorithms
*/
public final class FipsAES
{
private static final String FPE_DISABLED = "org.bouncycastle.fpe.disable";
private static final String FF1_FPE_DISABLED = "org.bouncycastle.fpe.disable_ff1";
private FipsAES()
{
}
static final FipsEngineProvider ENGINE_PROVIDER;
/**
* Raw AES algorithm, can be used for creating general purpose AES keys.
*/
public static final FipsAlgorithm ALGORITHM = new FipsAlgorithm("AES");
/**
* AES in electronic code book (ECB) mode.
*/
public static final Parameters ECB = new Parameters(new FipsAlgorithm(ALGORITHM, Mode.ECB));
/**
* AES in electronic code book mode with PKCS#7/PKCS#5 padding.
*/
public static final Parameters ECBwithPKCS7 = new Parameters(new FipsAlgorithm(ALGORITHM, Mode.ECB, Padding.PKCS7));
/**
* AES in electronic code book mode with ISO10126-2 padding.
*/
public static final Parameters ECBwithISO10126_2 = new Parameters(new FipsAlgorithm(ALGORITHM, Mode.ECB, Padding.ISO10126_2));
/**
* AES in electronic code book mode with X9.23 padding.
*/
public static final Parameters ECBwithX923 = new Parameters(new FipsAlgorithm(ALGORITHM, Mode.ECB, Padding.X923));
/**
* AES in electronic code book mode with ISO7816-4 padding.
*/
public static final Parameters ECBwithISO7816_4 = new Parameters(new FipsAlgorithm(ALGORITHM, Mode.ECB, Padding.ISO7816_4));
/**
* AES in electronic code book mode with trailing bit complement (TBC) padding.
*/
public static final Parameters ECBwithTBC = new Parameters(new FipsAlgorithm(ALGORITHM, Mode.ECB, Padding.TBC));
/**
* AES in cipher block chaining (CBC) mode.
*/
public static final ParametersWithIV CBC = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CBC));
/**
* AES in cipher block chaining mode with PKCS#7/PKCS#5 padding.
*/
public static final ParametersWithIV CBCwithPKCS7 = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CBC, Padding.PKCS7));
/**
* AES in cipher block chaining mode with ISO10126-2 padding.
*/
public static final ParametersWithIV CBCwithISO10126_2 = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CBC, Padding.ISO10126_2));
/**
* AES in cipher block chaining mode with X9.23 padding.
*/
public static final ParametersWithIV CBCwithX923 = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CBC, Padding.X923));
/**
* AES in cipher block chaining mode with ISO7816-4 padding.
*/
public static final ParametersWithIV CBCwithISO7816_4 = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CBC, Padding.ISO7816_4));
/**
* AES in cipher block chaining mode with trailing bit complement (TBC) padding.
*/
public static final ParametersWithIV CBCwithTBC = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CBC, Padding.TBC));
/**
* AES in cipher block chaining mode cipher text stealing type 1.
*/
public static final ParametersWithIV CBCwithCS1 = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CBC, Padding.CS1));
/**
* AES in cipher block chaining mode cipher text stealing type 2.
*/
public static final ParametersWithIV CBCwithCS2 = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CBC, Padding.CS2));
/**
* AES in cipher block chaining mode cipher text stealing type 3.
*/
public static final ParametersWithIV CBCwithCS3 = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CBC, Padding.CS3));
/**
* AES in cipher feedback (CFB) mode, 8 bit block size.
*/
public static final ParametersWithIV CFB8 = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CFB8));
/**
* AES in cipher feedback (CFB) mode, 128 bit block size.
*/
public static final ParametersWithIV CFB128 = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CFB128));
/**
* AES in output feedback (OFB) mode - 128 bit block size.
*/
public static final ParametersWithIV OFB = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.OFB128));
/**
* AES in counter (CTR) mode.
*/
public static final ParametersWithIV CTR = new ParametersWithIV(new FipsAlgorithm(ALGORITHM, Mode.CTR));
/**
* AES in Galois/Counter Mode (GCM).
*/
public static final AuthParameters GCM = new AuthParameters(new FipsAlgorithm(ALGORITHM, Mode.GCM));
/**
* AES in counter with CBC-MAC (CCM).
*/
public static final AuthParameters CCM = new AuthParameters(new FipsAlgorithm(ALGORITHM, Mode.CCM));
/**
* AES cipher-based CMAC algorithm.
*/
public static final AuthParameters CMAC = new AuthParameters(new FipsAlgorithm(ALGORITHM, Mode.CMAC));
/**
* AES cipher-based GMAC algorithm.
*/
public static final AuthParameters GMAC = new AuthParameters(new FipsAlgorithm(ALGORITHM, Mode.GMAC));
/**
* AES as a FIPS SP800-38F/RFC 3394 key wrapper.
*/
public static final WrapParameters KW = new WrapParameters(new FipsAlgorithm(ALGORITHM, Mode.WRAP));
/**
* AES as a FIPS SP800-38F key wrapper with padding.
*/
public static final WrapParameters KWP = new WrapParameters(new FipsAlgorithm(ALGORITHM, Mode.WRAPPAD));
/**
* AES as in Format Preserving Encryption - FF1 mode.
*/
public static final FPEParameters FF1 = new FPEParameters(new FipsAlgorithm(ALGORITHM, Mode.FF1));
/**
* AES as in Format Preserving Encryption - FF3 mode (now obsolete)
*/
// public static final FPEParameters FF3 = new FPEParameters(new FipsAlgorithm(ALGORITHM, Mode.FF3));
/**
* AES as in Format Preserving Encryption - FF3-1 mode.
*/
public static final FPEParameters FF3_1 = new FPEParameters(new FipsAlgorithm(ALGORITHM, Mode.FF3_1));
static
{
EngineProvider provider = new EngineProvider();
// FSM_STATE:5.AES.0,"AES ENCRYPT DECRYPT KAT","The module is performing AES encrypt and decrypt KAT self-test"
// FSM_TRANS:5.AES.0.0,"CONDITIONAL TEST","AES ENCRYPT DECRYPT KAT","Invoke AES Encrypt/Decrypt KAT self-test"
provider.createEngine();
// FSM_TRANS:5.AES.0.1,"AES ENCRYPT DECRYPT KAT","CONDITIONAL TEST","AES Encrypt / Decrypt KAT self-test successful completion"
// FSM_TRANS:5.AES.0.2,"AES ENCRYPT DECRYPT KAT","SOFT ERROR","AES Encrypt / Decrypt KAT self-test failed"
// FSM_STATE:5.AES.1,"CCM GENERATE VERIFY KAT","The module is performing AES CCM generate and verify KAT self-test"
// FSM_TRANS:5.AES.1.0,"CONDITIONAL TEST","CCM GENERATE VERIFY KAT","Invoke CCM Generate/Verify KAT self-test"
ccmStartUpTest(provider);
// FSM_TRANS:5.AES.1.1, "CCM GENERATE VERIFY KAT","CONDITIONAL TEST","CCM Generate/Verify KAT self-test successful completion"
// FSM_TRANS:5.AES.1.2, "CCM GENERATE VERIFY KAT","SOFT ERROR","CCM Generate/Verify KAT self-test failed"
// FSM_STATE:5.AES.2,"AES-CMAC GENERATE VERIFY KAT","The module is performing AES-CMAC generate and verify KAT self-test"
// FSM_TRANS:5.AES.2.0,"CONDITIONAL TEST","AES-CMAC GENERATE VERIFY KAT","Invoke CMAC Generate/Verify KAT self-test"
cmacStartUpTest(provider);
// FSM_TRANS:5.AES.2.1,"AES-CMAC GENERATE VERIFY KAT", "CONDITIONAL TEST", "CMAC Generate/Verify KAT self-test successful completion"
// FSM_TRANS:5.AES.2.2,"AES-CMAC GENERATE VERIFY KAT", "SOFT ERROR", "CMAC Generate/Verify KAT self-test failed"
// FSM_STATE:5.AES.3,"GCM GMAC GENERATE VERIFY KAT","The module is performing GCM/GMAC generate and verify KAT self-test"
// FSM_TRANS:5.AES.3.0,"CONDITIONAL TEST","GCM GMAC GENERATE VERIFY KAT","Invoke GCM Generate/Verify KAT self-test"
gcmStartUpTest(provider);
// FSM_TRANS:5.AES.3.1,"GCM GMAC GENERATE VERIFY KAT","CONDITIONAL TEST","GCM Generate/Verify KAT self-test successful completion"
// FSM_TRANS:5.AES.3.2,"GCM GMAC GENERATE VERIFY KAT","SOFT ERROR","GCM Generate/Verify KAT self-test failed"
ENGINE_PROVIDER = provider;
FipsRegister.registerEngineProvider(ALGORITHM, provider);
}
/**
* General AES operator parameters.
*/
public static class Parameters
extends FipsParameters
{
Parameters(FipsAlgorithm algorithm)
{
super(algorithm);
}
}
/**
* General AES operator parameters with IV
*/
public static final class ParametersWithIV
extends Parameters
implements org.bouncycastle.crypto.ParametersWithIV
{
private final byte[] iv;
ParametersWithIV(FipsAlgorithm algorithm)
{
this(algorithm, null);
}
private ParametersWithIV(FipsAlgorithm algorithm, byte[] iv)
{
super(algorithm);
((Mode)algorithm.basicVariation()).checkIv(iv, 16);
this.iv = iv;
}
public ParametersWithIV withIV(byte[] iv)
{
return new ParametersWithIV(this.getAlgorithm(), Arrays.clone(iv));
}
public ParametersWithIV withIV(SecureRandom random)
{
return new ParametersWithIV(this.getAlgorithm(), this.getAlgorithm().createDefaultIvIfNecessary(16, random));
}
public byte[] getIV()
{
return Arrays.clone(iv);
}
}
public static final class FPEParameters
extends Parameters
{
private final RadixConverter radixConverter;
private final byte[] tweak;
private final boolean useInverse;
FPEParameters(FipsAlgorithm algorithm)
{
this(algorithm, null, null, false);
}
private FPEParameters(FipsAlgorithm algorithm, RadixConverter radixConverter, byte[] tweak, boolean useInverse)
{
super(algorithm);
this.radixConverter = radixConverter;
this.tweak = tweak;
this.useInverse = useInverse;
}
public int getRadix()
{
return radixConverter == null ? 0 : radixConverter.getRadix();
}
public byte[] getTweak()
{
return Arrays.clone(tweak);
}
public boolean isUsingInverseFunction()
{
return useInverse;
}
public FPEParameters withRadix(int radix)
{
return new FPEParameters(getAlgorithm(), new RadixConverter(radix), this.tweak, this.useInverse);
}
public FPEParameters withRadixConverter(RadixConverter radixConverter)
{
return new FPEParameters(getAlgorithm(), radixConverter, this.tweak, this.useInverse);
}
public FPEParameters withTweak(byte[] tweak)
{
return new FPEParameters(getAlgorithm(), this.radixConverter, Arrays.clone(tweak), this.useInverse);
}
public FPEParameters withUsingInverseFunction(boolean useInverse)
{
return new FPEParameters(getAlgorithm(), this.radixConverter, this.tweak, useInverse);
}
}
/**
* Parameters for AES key wrap operators.
*/
public static final class WrapParameters
extends FipsParameters
{
private final boolean useInverse;
WrapParameters(FipsAlgorithm algorithm)
{
this(algorithm, false);
}
private WrapParameters(FipsAlgorithm algorithm, boolean useInverse)
{
super(algorithm);
this.useInverse = useInverse;
}
public boolean isUsingInverseFunction()
{
return useInverse;
}
public WrapParameters withUsingInverseFunction(boolean useInverse)
{
return new WrapParameters(getAlgorithm(), useInverse);
}
}
/**
* Parameters for AES AEAD and MAC modes..
*/
public static final class AuthParameters
extends FipsParameters
implements AuthenticationParametersWithIV
{
private final byte[] iv;
private final int macLenInBits;
/**
* Base constructor - the algorithm, null IV.
* In this case the tag length defaults to the 128 for GCM, CMAC, or GMAC, 64 bits otherwise.
*
* @param algorithm algorithm mode.
*/
AuthParameters(FipsAlgorithm algorithm)
{
this(algorithm, null, Utils.getDefaultMacSize(algorithm, 128)); // tag full blocksize or half
}
private AuthParameters(FipsAlgorithm algorithm, byte[] iv, int macLenInBits)
{
super(algorithm);
this.iv = iv;
this.macLenInBits = macLenInBits;
}
public int getMACSizeInBits()
{
return macLenInBits;
}
public byte[] getIV()
{
return Arrays.clone(iv);
}
public AuthParameters withIV(byte[] iv)
{
return new AuthParameters(this.getAlgorithm(), Arrays.clone(iv), this.macLenInBits);
}
public AuthParameters withIV(SecureRandom random)
{
if (CryptoServicesRegistrar.isInApprovedOnlyMode())
{
if (getAlgorithm().equals(GCM.getAlgorithm()))
{
Utils.validateRandom(random, GCM.getAlgorithm(), "GCM IV can only be generated by an approved DRGB");
}
}
if (getAlgorithm().equals(GCM.getAlgorithm()))
{
return new AuthParameters(this.getAlgorithm(), this.getAlgorithm().createDefaultIvIfNecessary(12, random), this.macLenInBits);
}
else
{
return new AuthParameters(this.getAlgorithm(), this.getAlgorithm().createDefaultIvIfNecessary(16, random), this.macLenInBits);
}
}
/**
* @param random source of randomness for iv (nonce)
* @param ivLen length of the iv (nonce) in bytes to use with the algorithm.
*/
public AuthParameters withIV(SecureRandom random, int ivLen)
{
if (CryptoServicesRegistrar.isInApprovedOnlyMode())
{
if (getAlgorithm().equals(GCM.getAlgorithm()))
{
Utils.validateRandom(random, GCM.getAlgorithm(), "GCM IV can only be generated by an approved DRGB");
if (ivLen < 12)
{
throw new FipsUnapprovedOperationError("GCM IV must be at least 96 bits", GCM.getAlgorithm());
}
}
}
return new AuthParameters(this.getAlgorithm(), this.getAlgorithm().createIvIfNecessary(ivLen, random), this.macLenInBits);
}
/**
* Return a new set of parameters specifying a specific mac size.
*
* @param macSizeInBits bit length of the MAC length.
* @return a new set of AuthParameters for the MAC size.
*/
public AuthParameters withMACSize(int macSizeInBits)
{
return new AuthParameters(this.getAlgorithm(), Arrays.clone(iv), macSizeInBits);
}
}
/**
* AES key generator.
*/
public static final class KeyGenerator
extends FipsSymmetricKeyGenerator
{
private final FipsAlgorithm algorithm;
private final int keySizeInBits;
private final SecureRandom random;
/**
* Constructor to generate a general purpose AES key.
*
* @param keySizeInBits size of the key in bits.
* @param random secure random to use in key construction.
*/
public KeyGenerator(int keySizeInBits, SecureRandom random)
{
this(ALGORITHM, keySizeInBits, random);
}
/**
* Constructor to generate a specific purpose AES key for an algorithm in a particular parameter set.
*
* @param parameterSet FIPS algorithm key is for,
* @param keySizeInBits size of the key in bits.
* @param random secure random to use in key construction.
*/
public KeyGenerator(FipsParameters parameterSet, int keySizeInBits, SecureRandom random)
{
this(parameterSet.getAlgorithm(), keySizeInBits, random);
}
private KeyGenerator(FipsAlgorithm algorithm, int keySizeInBits, SecureRandom random)
{
if (CryptoServicesRegistrar.isInApprovedOnlyMode())
{
Utils.validateKeyGenRandom(random, keySizeInBits, algorithm);
}
if (keySizeInBits != 128 && keySizeInBits != 192 && keySizeInBits != 256)
{
throw new IllegalArgumentException("Attempt to create key with invalid key size [" + keySizeInBits + "]: " + algorithm.getName());
}
this.algorithm = algorithm;
this.keySizeInBits = keySizeInBits;
this.random = random;
}
/**
* Generate a key,
*
* @return an AES key.
*/
public SymmetricSecretKey generateKey()
{
CipherKeyGenerator cipherKeyGenerator = new CipherKeyGenerator();
cipherKeyGenerator.init(new KeyGenerationParameters(random, keySizeInBits));
return new SymmetricSecretKey(algorithm, cipherKeyGenerator.generateKey());
}
}
/**
* Factory for basic AES encryption/decryption operators.
*/
public static final class OperatorFactory
extends FipsSymmetricOperatorFactory
{
private final SecureRandom random;
public OperatorFactory()
{
this(null);
}
private OperatorFactory(SecureRandom random)
{
this.random = random;
}
@Override
public FipsOutputEncryptor createOutputEncryptor(final SymmetricKey key, final Parameters parameters)
{
final ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
if (parameters instanceof FPEParameters)
{
return new FpeOutProcessor(sKey, (FPEParameters)parameters, true);
}
return new OutEncryptor(sKey, parameters, null);
}
public FipsOutputEncryptor createOutputEncryptor(final SymmetricKey key, final FPEParameters parameters)
{
final ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
return new FpeOutProcessor(sKey, parameters, true);
}
@Override
public FipsOutputDecryptor createOutputDecryptor(final SymmetricKey key, final Parameters parameters)
{
ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
if (parameters instanceof FPEParameters)
{
return new FpeOutDecProcessor(sKey, (FPEParameters)parameters, false);
}
final BufferedBlockCipher cipher = BlockCipherUtils.createStandardCipher(false, sKey, ENGINE_PROVIDER, parameters, random);
return new FipsOutputDecryptor()
{
@Override
public Parameters getParameters()
{
return parameters;
}
public int getMaxOutputSize(int inputLen)
{
return cipher.getOutputSize(inputLen);
}
public int getUpdateOutputSize(int inputLen)
{
return cipher.getUpdateOutputSize(inputLen);
}
@Override
public CipherOutputStream getDecryptingStream(OutputStream out)
{
if (cipher.getUnderlyingCipher() instanceof StreamCipher)
{
return CipherOutputStreamImpl.getInstance(out, (StreamCipher)cipher.getUnderlyingCipher());
}
return CipherOutputStreamImpl.getInstance(out, cipher);
}
@Override
public String toString()
{
return "FipsOutputDecryptor(" + cipher.getUnderlyingCipher().toString() + ")";
}
};
}
@Override
public FipsInputDecryptor createInputDecryptor(final SymmetricKey key, final Parameters parameters)
{
if (parameters instanceof FPEParameters)
{
return createInputDecryptor(key, (FPEParameters)parameters);
}
ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
final BufferedBlockCipher cipher = BlockCipherUtils.createStandardCipher(false, sKey, ENGINE_PROVIDER, parameters, random);
return new FipsInputDecryptor()
{
@Override
public Parameters getParameters()
{
return parameters;
}
@Override
public InputStream getDecryptingStream(InputStream in)
{
if (cipher.getUnderlyingCipher() instanceof StreamCipher)
{
return new CipherInputStream(in, (StreamCipher)cipher.getUnderlyingCipher());
}
return new CipherInputStream(in, cipher);
}
@Override
public String toString()
{
return "FipsInputDecryptor(" + cipher.getUnderlyingCipher().toString() + ")";
}
};
}
public FipsInputDecryptor createInputDecryptor(final SymmetricKey key, final FPEParameters parameters)
{
ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
final BlockCipher cipher = ENGINE_PROVIDER.createEngine();
boolean isFF1 = FF1.getAlgorithm().equals(parameters.getAlgorithm());
if (isFF1)
{
if (Properties.isOverrideSet(FPE_DISABLED) || Properties.isOverrideSet(FF1_FPE_DISABLED))
{
throw new UnsupportedOperationException("FF1 encryption disabled");
}
cipher.init(!parameters.isUsingInverseFunction(), new KeyParameterImpl(sKey.getKeyBytes()));
}
else
{
if (Properties.isOverrideSet(FPE_DISABLED))
{
throw new UnsupportedOperationException("FPE disabled");
}
cipher.init(!parameters.isUsingInverseFunction(), new KeyParameterImpl(Arrays.reverse(sKey.getKeyBytes())));
}
return new FipsInputDecryptor()
{
@Override
public FPEParameters getParameters()
{
return parameters;
}
@Override
public InputStream getDecryptingStream(InputStream in)
{
return new LocalInputStream(cipher, parameters, in);
}
@Override
public String toString()
{
return "FipsInputDecryptor(" + cipher.toString() + ")";
}
};
}
private static class OutEncryptor
extends FipsOutputEncryptor
implements OperatorUsingSecureRandom>
{
private final Parameters parameters;
private final ValidatedSymmetricKey key;
private final BufferedBlockCipher cipher;
public OutEncryptor(ValidatedSymmetricKey key, Parameters parameters, SecureRandom random)
{
this.key = key;
this.parameters = parameters;
cipher = BlockCipherUtils.createStandardCipher(true, key, ENGINE_PROVIDER, parameters, random);
}
public CipherOutputStream getEncryptingStream(OutputStream out)
{
if (cipher.getUnderlyingCipher() instanceof StreamCipher)
{
return CipherOutputStreamImpl.getInstance(out, (StreamCipher)cipher.getUnderlyingCipher());
}
return CipherOutputStreamImpl.getInstance(out, cipher);
}
public OutputEncryptor withSecureRandom(SecureRandom random)
{
return new OutEncryptor(key, parameters, random);
}
public Parameters getParameters()
{
return parameters;
}
public int getMaxOutputSize(int inputLen)
{
return cipher.getOutputSize(inputLen);
}
public int getUpdateOutputSize(int inputLen)
{
return cipher.getUpdateOutputSize(inputLen);
}
@Override
public String toString()
{
return "OutputEncryptor(" + cipher.toString() + ")";
}
}
private class LocalInputStream
extends InputStream
{
private final BlockCipher cipher;
private final FPEParameters parameters;
private final InputStream in;
private final boolean isFF1;
private volatile ByteArrayInputStream source;
public LocalInputStream(BlockCipher cipher, FPEParameters parameters, InputStream in)
{
this.cipher = cipher;
this.parameters = parameters;
this.isFF1 = FF1.getAlgorithm().equals(parameters.getAlgorithm());
this.in = in;
}
@Override
public int read()
throws IOException
{
if (source == null)
{
byte[] data = Streams.readAll(in);
if (isFF1)
{
if (parameters.radixConverter.getRadix() > 256)
{
this.source = new ByteArrayInputStream(toByteArray(SP80038G.decryptFF1w(cipher, parameters.radixConverter, parameters.tweak, toShortArray(data, data.length), 0, data.length / 2)));
}
else
{
this.source = new ByteArrayInputStream(SP80038G.decryptFF1(cipher, parameters.radixConverter, parameters.tweak, data, 0, data.length));
}
}
else
{
if (parameters.radixConverter.getRadix() > 256)
{
this.source = new ByteArrayInputStream(toByteArray(SP80038G.decryptFF3_1w(cipher, parameters.radixConverter, parameters.tweak, toShortArray(data, data.length), 0, data.length / 2)));
}
else
{
this.source = new ByteArrayInputStream(SP80038G.decryptFF3_1(cipher, parameters.radixConverter, parameters.tweak, data, 0, data.length));
}
}
}
return source.read();
}
}
private static class FpeOutProcessor
extends FipsOutputEncryptor
{
private final FPEParameters parameters;
private final BlockCipher cipher;
private final OutputingStream localOutputStream;
public FpeOutProcessor(ValidatedSymmetricKey key, FPEParameters parameters, boolean forEncryption)
{
this.parameters = parameters;
cipher = ENGINE_PROVIDER.createEngine();
boolean isFF1 = FF1.getAlgorithm().equals(parameters.getAlgorithm());
if (isFF1)
{
if (Properties.isOverrideSet(FPE_DISABLED) || Properties.isOverrideSet(FF1_FPE_DISABLED))
{
throw new UnsupportedOperationException("FF1 encryption disabled");
}
cipher.init(!parameters.isUsingInverseFunction(), new KeyParameterImpl(key.getKeyBytes()));
}
else
{
if (Properties.isOverrideSet(FPE_DISABLED) || isApprovedMode())
{
throw new UnsupportedOperationException("FPE disabled");
}
cipher.init(!parameters.isUsingInverseFunction(), new KeyParameterImpl(Arrays.reverse(key.getKeyBytes())));
}
this.localOutputStream = forEncryption ? new EncryptingOutputStream(isFF1, cipher, parameters) : new DecryptingOutputStream(isFF1, cipher, parameters);
}
public CipherOutputStream getEncryptingStream(OutputStream out)
{
localOutputStream.init(out);
return new CipherOutputStream()
{
@Override
public void write(int b)
throws IOException
{
localOutputStream.write(b);
}
@Override
public void close()
throws IOException
{
localOutputStream.close();
}
};
}
public FPEParameters getParameters()
{
return parameters;
}
public int getMaxOutputSize(int inputLen)
{
return localOutputStream.size() + inputLen;
}
public int getUpdateOutputSize(int inputLen)
{
return localOutputStream.size() + inputLen;
}
}
private static class FpeOutDecProcessor
extends FipsOutputDecryptor
{
private final FPEParameters parameters;
private final BlockCipher cipher;
private final OutputingStream localOutputStream;
public FpeOutDecProcessor(ValidatedSymmetricKey key, FPEParameters parameters, boolean forEncryption)
{
this.parameters = parameters;
cipher = ENGINE_PROVIDER.createEngine();
boolean isFF1 = FF1.getAlgorithm().equals(parameters.getAlgorithm());
if (isFF1)
{
if (Properties.isOverrideSet(FPE_DISABLED) || Properties.isOverrideSet(FF1_FPE_DISABLED))
{
throw new UnsupportedOperationException("FF1 encryption disabled");
}
cipher.init(!parameters.isUsingInverseFunction(), new KeyParameterImpl(key.getKeyBytes()));
}
else
{
if (Properties.isOverrideSet(FPE_DISABLED) || isApprovedMode())
{
throw new UnsupportedOperationException("FPE disabled");
}
cipher.init(!parameters.isUsingInverseFunction(), new KeyParameterImpl(Arrays.reverse(key.getKeyBytes())));
}
this.localOutputStream = forEncryption ? new EncryptingOutputStream(isFF1, cipher, parameters) : new DecryptingOutputStream(isFF1, cipher, parameters);
}
public CipherOutputStream getDecryptingStream(OutputStream out)
{
localOutputStream.init(out);
return new CipherOutputStream()
{
@Override
public void write(int b)
throws IOException
{
localOutputStream.write(b);
}
@Override
public void close()
throws IOException
{
localOutputStream.close();
}
};
}
public FPEParameters getParameters()
{
return parameters;
}
public int getMaxOutputSize(int inputLen)
{
return localOutputStream.size() + inputLen;
}
public int getUpdateOutputSize(int inputLen)
{
return localOutputStream.size() + inputLen;
}
}
private static interface OutputingStream
{
void init(OutputStream out);
void write(int b)
throws IOException;
void close()
throws IOException;
int size();
}
private static class DecryptingOutputStream
extends ErasableByteArrayOutputStream
implements OutputingStream
{
private final boolean isFF1;
private final BlockCipher cipher;
private final FPEParameters parameters;
private OutputStream output;
DecryptingOutputStream(boolean isFF1, BlockCipher cipher, FPEParameters parameters)
{
this.isFF1 = isFF1;
this.cipher = cipher;
this.parameters = parameters;
}
public void init(OutputStream output)
{
this.output = output;
}
public void close()
throws IOException
{
super.close();
try
{
if (isFF1)
{
if (parameters.radixConverter.getRadix() > 256)
{
output.write(FipsAES.toByteArray(SP80038G.decryptFF1w(cipher, parameters.radixConverter, parameters.getTweak(), toShortArray(this.buf, size()), 0, size() / 2)));
}
else
{
output.write(SP80038G.decryptFF1(cipher, parameters.radixConverter, parameters.getTweak(), this.buf, 0, size()));
}
}
else
{
if (parameters.radixConverter.getRadix() > 256)
{
output.write(FipsAES.toByteArray(SP80038G.decryptFF3_1w(cipher, parameters.radixConverter, parameters.getTweak(), toShortArray(buf, size()), 0, size() / 2)));
}
else
{
output.write(SP80038G.decryptFF3_1(cipher, parameters.radixConverter, parameters.getTweak(), buf, 0, size()));
}
}
output.flush();
}
catch (IllegalArgumentException e)
{
throw new IOException(e.getMessage());
}
finally
{
erase();
}
}
}
private static class EncryptingOutputStream
extends ErasableByteArrayOutputStream
implements OutputingStream
{
private final boolean isFF1;
private final BlockCipher cipher;
private final FPEParameters parameters;
private OutputStream output;
EncryptingOutputStream(boolean isFF1, BlockCipher cipher, FPEParameters parameters)
{
this.isFF1 = isFF1;
this.cipher = cipher;
this.parameters = parameters;
}
public void init(OutputStream output)
{
this.output = output;
}
public void close()
throws IOException
{
super.close();
try
{
if (isFF1)
{
if (parameters.radixConverter.getRadix() > 256)
{
output.write(FipsAES.toByteArray(SP80038G.encryptFF1w(cipher, parameters.radixConverter, parameters.getTweak(), toShortArray(this.buf, size()), 0, size() / 2)));
}
else
{
output.write(SP80038G.encryptFF1(cipher, parameters.radixConverter, parameters.getTweak(), this.buf, 0, size()));
}
}
else
{
if (parameters.radixConverter.getRadix() > 256)
{
output.write(FipsAES.toByteArray(SP80038G.encryptFF3_1w(cipher, parameters.radixConverter, parameters.getTweak(), toShortArray(this.buf, size()), 0, size() / 2)));
}
else
{
output.write(SP80038G.encryptFF3_1(cipher, parameters.radixConverter, parameters.getTweak(), this.buf, 0, size()));
}
}
output.flush();
}
catch (IllegalArgumentException e)
{
throw new IOException(e.getMessage());
}
finally
{
erase();
}
}
}
}
/**
* Factory for producing FIPS AES MAC calculators.
*/
public static final class MACOperatorFactory
extends FipsMACOperatorFactory
{
@Override
protected int calculateMACSize(AuthParameters parameters)
{
return makeMAC(parameters).getMacSize();
}
@Override
protected Mac createMAC(SymmetricKey key, final AuthParameters parameters)
{
final Mac mac = makeMAC(parameters);
ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
if (parameters.getIV() != null)
{
mac.init(Utils.getParametersWithIV(sKey, parameters.getIV()));
}
else
{
mac.init(Utils.getKeyParameter(sKey));
}
return mac;
}
}
static FipsEngineProvider getMacProvider(final FipsAlgorithm algorithm)
{
final FipsEngineProvider macProvider;
switch (((Mode)algorithm.basicVariation()))
{
case CMAC:
macProvider = new FipsEngineProvider()
{
public Mac createEngine()
{
return new CMac(ENGINE_PROVIDER.createEngine());
}
};
break;
case GMAC:
macProvider = new FipsEngineProvider()
{
public Mac createEngine()
{
AEADBlockCipher gcm;
if (NativeLoader.hasNativeService(FipsNativeServices.AES_GCM))
{
gcm = AESNativeGCM.newInstance();
}
else
{
gcm = new GCMBlockCipher(ENGINE_PROVIDER.createEngine());
}
return new GMac(gcm);
}
};
break;
default:
throw new IllegalArgumentException("Unknown algorithm passed to FipsAES MAC Provider: " + algorithm);
}
return macProvider;
}
static Mac makeMAC(final AuthParameters parameters)
{
final Mac mac;
switch (((Mode)parameters.getAlgorithm().basicVariation()))
{
case CCM:
mac = new AEADCipherMac(new CCMBlockCipher(ENGINE_PROVIDER.createEngine()), parameters.macLenInBits);
break;
case CMAC:
mac = new CMac(ENGINE_PROVIDER.createEngine(), parameters.macLenInBits);
break;
case GMAC:
AEADBlockCipher gcm;
if (NativeLoader.hasNativeService(FipsNativeServices.AES_GCM))
{
gcm = AESNativeGCM.newInstance();
}
else
{
gcm = new GCMBlockCipher(ENGINE_PROVIDER.createEngine());
}
mac = new GMac(gcm, parameters.macLenInBits);
break;
default:
throw new IllegalArgumentException("Unknown algorithm passed to FipsAES.OperatorFactory.createMACCalculator: " + parameters.getAlgorithm());
}
return mac;
}
/**
* Factory for producing FIPS AES key wrap/unwrap operators.
*/
public static final class KeyWrapOperatorFactory
extends FipsKeyWrapOperatorFactory
{
private Wrapper createWrapper(FipsAlgorithm algorithm, boolean useInverse)
{
Wrapper cipher;
switch (((Mode)algorithm.basicVariation()))
{
case WRAP:
cipher = new SP80038FWrapEngine(ENGINE_PROVIDER.createEngine(), useInverse);
break;
case WRAPPAD:
cipher = new SP80038FWrapWithPaddingEngine(ENGINE_PROVIDER.createEngine(), useInverse);
break;
default:
throw new IllegalArgumentException("Unknown algorithm passed to FipsAES.KeyWrapOperatorFactory: " + algorithm.getName());
}
return cipher;
}
@Override
public FipsKeyWrapper createKeyWrapper(SymmetricKey key, final WrapParameters parameters)
{
ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
final Wrapper wrapper = createWrapper(parameters.getAlgorithm(), parameters.useInverse);
wrapper.init(true, Utils.getKeyParameter(sKey));
return new FipsKeyWrapper()
{
public WrapParameters getParameters()
{
return parameters;
}
public byte[] wrap(byte[] in, int inOff, int inLen)
throws PlainInputProcessingException
{
try
{
return wrapper.wrap(in, inOff, inLen);
}
catch (Exception e)
{
throw new PlainInputProcessingException("Unable to wrap key: " + e.getMessage(), e);
}
}
};
}
@Override
public FipsKeyUnwrapper createKeyUnwrapper(SymmetricKey key, final WrapParameters parameters)
{
ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
final Wrapper wrapper = createWrapper(parameters.getAlgorithm(), parameters.useInverse);
wrapper.init(false, Utils.getKeyParameter(sKey));
return new FipsKeyUnwrapper()
{
public WrapParameters getParameters()
{
return parameters;
}
@Override
public byte[] unwrap(byte[] in, int inOff, int inLen)
throws InvalidWrappingException
{
try
{
return wrapper.unwrap(in, inOff, inLen);
}
catch (InvalidCipherTextException e)
{
throw new InvalidWrappingException("Unable to unwrap key: " + e.getMessage(), e);
}
}
};
}
}
/**
* Factory for AEAD encryption/decryption operations.
*/
public static final class AEADOperatorFactory
extends FipsAEADOperatorFactory
{
@Override
public FipsOutputAEADEncryptor createOutputAEADEncryptor(SymmetricKey key, final AuthParameters parameter)
{
ValidatedSymmetricKey sKey = validateKey(key, parameter.getAlgorithm());
return new OutEncryptor(sKey, parameter);
}
@Override
public FipsOutputAEADDecryptor createOutputAEADDecryptor(SymmetricKey key, final AuthParameters parameters)
{
ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
final AEADBlockCipher cipher = BlockCipherUtils.createAEADCipher(parameters.getAlgorithm(), ENGINE_PROVIDER);
if (parameters.iv != null)
{
cipher.init(false, Utils.getAEADParameters(sKey, parameters.iv, parameters.macLenInBits));
}
else
{
throw new IllegalArgumentException("AEAD decryption requires an iv/nonce to be provided.");
}
return new FipsOutputAEADDecryptor()
{
@Override
public AuthParameters getParameters()
{
return parameters;
}
public int getMaxOutputSize(int inputLen)
{
return cipher.getOutputSize(inputLen);
}
public int getUpdateOutputSize(int inputLen)
{
return cipher.getUpdateOutputSize(inputLen);
}
@Override
public UpdateOutputStream getAADStream()
{
return new AADStream(cipher);
}
@Override
public CipherOutputStream getDecryptingStream(final OutputStream out)
{
return CipherOutputStreamImpl.getInstance(out, cipher);
}
@Override
public byte[] getMAC()
{
return cipher.getMac();
}
public String toString()
{
return "FipsOutputAEADDecryptor(" + cipher.toString() + ")";
}
};
}
@Override
public FipsInputAEADDecryptor createInputAEADDecryptor(SymmetricKey key, final AuthParameters parameters)
{
ValidatedSymmetricKey sKey = validateKey(key, parameters.getAlgorithm());
final AEADBlockCipher cipher = BlockCipherUtils.createAEADCipher(parameters.getAlgorithm(), ENGINE_PROVIDER);
if (parameters.iv != null)
{
cipher.init(false, Utils.getAEADParameters(sKey, parameters.iv, parameters.macLenInBits));
}
else
{
throw new IllegalArgumentException("AEAD decryption requires an iv/nonce to be provided.");
}
return new FipsInputAEADDecryptor()
{
@Override
public AuthParameters getParameters()
{
return parameters;
}
@Override
public UpdateOutputStream getAADStream()
{
return new AADStream(cipher);
}
@Override
public InputStream getDecryptingStream(InputStream in)
{
return new CipherInputStream(in, cipher);
}
@Override
public byte[] getMAC()
{
return cipher.getMac();
}
};
}
private static class OutEncryptor
extends FipsOutputAEADEncryptor
{
private final AuthParameters parameters;
private final AEADBlockCipher cipher;
public OutEncryptor(ValidatedSymmetricKey key, AuthParameters parameters)
{
this.parameters = parameters;
this.cipher = BlockCipherUtils.createAEADCipher(parameters.getAlgorithm(), ENGINE_PROVIDER);
if (parameters.iv != null)
{
cipher.init(true, Utils.getAEADParameters(key, parameters.iv, parameters.macLenInBits));
}
else
{
throw new IllegalArgumentException("AEAD encryption requires an iv/nonce to be provided.");
}
}
public AuthParameters getParameters()
{
return parameters;
}
public int getMaxOutputSize(int inputLen)
{
return cipher.getOutputSize(inputLen);
}
public int getUpdateOutputSize(int inputLen)
{
return cipher.getUpdateOutputSize(inputLen);
}
@Override
public UpdateOutputStream getAADStream()
{
return new AADStream(cipher);
}
@Override
public CipherOutputStream getEncryptingStream(final OutputStream out)
{
return CipherOutputStreamImpl.getInstance(out, cipher);
}
@Override
public byte[] getMAC()
{
return cipher.getMac();
}
@Override
public String toString()
{
return "OutEncryptor(" + cipher.toString() + ")";
}
}
private static class AADStream
extends UpdateOutputStream
{
private AEADBlockCipher cipher;
public AADStream(AEADBlockCipher cipher)
{
this.cipher = cipher;
}
@Override
public void write(byte[] buf, int off, int len)
throws IOException
{
cipher.processAADBytes(buf, off, len);
}
@Override
public void write(int b)
throws IOException
{
cipher.processAADByte((byte)b);
}
}
}
private static short[] toShortArray(byte[] buf, int length)
{
if ((length & 1) != 0)
{
throw new IllegalArgumentException("data must be an even number of bytes for a wide radix");
}
short[] rv = new short[length / 2];
for (int i = 0; i != rv.length; i++)
{
rv[i] = Pack.bigEndianToShort(buf, i * 2);
}
return rv;
}
private static byte[] toByteArray(short[] buf)
{
byte[] rv = new byte[buf.length * 2];
for (int i = 0; i != buf.length; i++)
{
Pack.shortToBigEndian(buf[i], rv, i * 2);
}
return rv;
}
private static ValidatedSymmetricKey validateKey(SymmetricKey key, FipsAlgorithm fipsAlgorithm)
{
// FSM_STATE:5.AES.4,"AES KEY VALIDITY TEST", "The module is validating the size and purpose of an AES key"
// FSM_TRANS:5.AES.4.0,"CONDITIONAL TEST", "AES KEY VALIDITY TEST", "Invoke Validity test on AES key"
ValidatedSymmetricKey vKey = PrivilegedUtils.getValidatedKey(key);
int keyLength = vKey.getKeySizeInBits();
if (keyLength != 128 && keyLength != 192 && keyLength != 256)
{
// FSM_TRANS:5.AES.4.2,"AES KEY VALIDITY TEST", "USER COMMAND REJECTED", "Validity test on AES key failed"
throw new IllegalKeyException("AES key must be of length 128, 192, or 256");
}
Algorithm algorithm = vKey.getAlgorithm();
if (!algorithm.equals(ALGORITHM))
{
if (!algorithm.equals(fipsAlgorithm))
{
// FSM_TRANS:5.AES.4.2,"AES KEY VALIDITY TEST", "USER COMMAND REJECTED", "Validity test on AES key failed"
throw new IllegalKeyException("FIPS Key not for specified algorithm");
}
}
// FSM_TRANS:5.AES.4.1,"AES KEY VALIDITY TEST", "CONDITIONAL TEST", "Validity test on AES key successful"
return vKey;
}
private static final class EngineProvider
extends FipsEngineProvider
{
private static final byte[] input = Hex.decode("00112233445566778899aabbccddeeff");
private static final byte[] output = Hex.decode("69c4e0d86a7b0430d8cdb78070b4c55a");
private static final byte[] keyBytes = Hex.decode("000102030405060708090a0b0c0d0e0f");
public MultiBlockCipher createEngine()
{
if (NativeLoader.hasNativeService(FipsNativeServices.AES_ECB))
{
return SelfTestExecutor.validate(ALGORITHM, new AESNativeEngine(), new VariantKatTest()
{
public void evaluate(AESNativeEngine aesEngine)
{
byte[] tmp = new byte[input.length];
KeyParameter key = new KeyParameterImpl(keyBytes);
aesEngine.init(true, key);
aesEngine.processBlock(input, 0, tmp, 0);
if (!Arrays.areEqual(output, tmp))
{
fail("Failed self test on encryption");
}
aesEngine.init(false, key);
aesEngine.processBlock(tmp, 0, tmp, 0);
if (!Arrays.areEqual(input, tmp))
{
fail("Failed self test on decryption");
}
}
});
}
return SelfTestExecutor.validate(ALGORITHM, new AESEngine(), new VariantKatTest()
{
public void evaluate(AESEngine aesEngine)
{
byte[] tmp = new byte[input.length];
KeyParameter key = new KeyParameterImpl(keyBytes);
aesEngine.init(true, key);
aesEngine.processBlock(input, 0, tmp, 0);
if (!Arrays.areEqual(output, tmp))
{
fail("Failed self test on encryption");
}
aesEngine.init(false, key);
aesEngine.processBlock(tmp, 0, tmp, 0);
if (!Arrays.areEqual(input, tmp))
{
fail("Failed self test on decryption");
}
}
});
}
}
private static void ccmStartUpTest(EngineProvider provider)
{
SelfTestExecutor.validate(CCM.getAlgorithm(), provider, new VariantKatTest()
{
public void evaluate(EngineProvider provider)
throws Exception
{
byte[] K = Hex.decode("404142434445464748494a4b4c4d4e4f");
byte[] N = Hex.decode("10111213141516");
byte[] A = Hex.decode("0001020304050607");
byte[] P = Hex.decode("20212223");
byte[] C = Hex.decode("7162015b4dac255d");
byte[] T = Hex.decode("6084341b");
BlockCipher aesCipher = provider.createEngine();
CCMBlockCipher encCipher = new CCMBlockCipher(aesCipher);
int macSize = T.length * 8;
KeyParameter keyParam = new KeyParameterImpl(K);
encCipher.init(true, new org.bouncycastle.crypto.internal.params.AEADParameters(keyParam, macSize, N, A));
byte[] enc = new byte[C.length];
int len = encCipher.processBytes(P, 0, P.length, enc, 0);
encCipher.doFinal(enc, len);
if (!Arrays.areEqual(C, enc))
{
fail("Encrypted stream fails to match in self test");
}
if (!Arrays.areEqual(T, encCipher.getMac()))
{
fail("MAC fails to match in self test encrypt");
}
CCMBlockCipher decCipher = new CCMBlockCipher(aesCipher);
decCipher.init(false, new org.bouncycastle.crypto.internal.params.AEADParameters(keyParam, macSize, N, A));
byte[] tmp = new byte[enc.length];
len = decCipher.processBytes(enc, 0, enc.length, tmp, 0);
len += decCipher.doFinal(tmp, len);
byte[] dec = new byte[len];
System.arraycopy(tmp, 0, dec, 0, len);
if (!Arrays.areEqual(P, dec))
{
fail("Decrypted stream fails to match in self test");
}
if (!Arrays.areEqual(T, decCipher.getMac()))
{
fail("MAC fails to match in self test");
}
}
});
}
private static void cmacStartUpTest(final EngineProvider provider)
{
SelfTestExecutor.validate(CMAC.getAlgorithm(), provider, new BasicKatTest()
{
public boolean hasTestPassed(EngineProvider engine)
{
byte[] keyBytes128 = Hex.decode("2b7e151628aed2a6abf7158809cf4f3c");
byte[] input16 = Hex.decode("6bc1bee22e409f96e93d7e117393172a");
byte[] output_k128_m16 = Hex.decode("070a16b46b4d4144f79bdd9dd04a287c");
Mac mac = new CMac(provider.createEngine(), 128);
//128 bytes key
KeyParameter key = new KeyParameterImpl(keyBytes128);
// 0 bytes message - 128 bytes key
mac.init(key);
mac.update(input16, 0, input16.length);
byte[] out = new byte[16];
mac.doFinal(out, 0);
return Arrays.areEqual(out, output_k128_m16);
}
});
}
private static void gcmStartUpTest(EngineProvider provider)
{
if (NativeLoader.hasNativeService(FipsNativeServices.AES_GCM))
{
// newInstance will launch the startup test.
AESNativeGCM.newInstance();
}
else
{
SelfTestExecutor.validate(GCM.getAlgorithm(), provider, new VariantKatTest()
{
public void evaluate(EngineProvider provider)
throws Exception
{
AEADBlockCipher encCipher;
BlockCipher aesCipher = provider.createEngine();
encCipher = new GCMBlockCipher(aesCipher);
byte[] K = Hex.decode("feffe9928665731c6d6a8f9467308308");
byte[] P = Hex.decode("d9313225f88406e5a55909c5aff5269a"
+ "86a7a9531534f7da2e4c303d8a318a72"
+ "1c3c0c95956809532fcf0e2449a6b525"
+ "b16aedf5aa0de657ba637b39");
byte[] A = Hex.decode("feedfacedeadbeeffeedfacedeadbeef"
+ "abaddad2");
byte[] IV = Hex.decode("cafebabefacedbaddecaf888");
byte[] C = Hex.decode("42831ec2217774244b7221b784d0d49c"
+ "e3aa212f2c02a4e035c17e2329aca12e"
+ "21d514b25466931c7d8f6a5aac84aa05"
+ "1ba30b396a0aac973d58e091");
byte[] T = Hex.decode("5bc94fbc3221a5db94fae95ae7121a47");
CipherParameters params = new org.bouncycastle.crypto.internal.params.AEADParameters(new KeyParameterImpl(K), T.length * 8, IV, A);
encCipher.init(true, params);
byte[] enc = new byte[encCipher.getOutputSize(P.length)];
int len = encCipher.processBytes(P, 0, P.length, enc, 0);
len += encCipher.doFinal(enc, len);
if (enc.length != len)
{
fail("Encryption reported incorrect length");
}
byte[] mac = encCipher.getMac();
byte[] data = new byte[P.length];
System.arraycopy(enc, 0, data, 0, data.length);
byte[] tail = new byte[enc.length - P.length];
System.arraycopy(enc, P.length, tail, 0, tail.length);
if (!Arrays.areEqual(C, data))
{
fail("Incorrect encrypt");
}
if (!Arrays.areEqual(T, mac))
{
fail("getMac() returned wrong MAC");
}
if (!Arrays.areEqual(T, tail))
{
fail("Stream contained wrong MAC");
}
GCMBlockCipher decCipher = new GCMBlockCipher(aesCipher);
decCipher.init(false, params);
byte[] dec = new byte[decCipher.getOutputSize(enc.length)];
len = decCipher.processBytes(enc, 0, enc.length, dec, 0);
decCipher.doFinal(dec, len);
mac = decCipher.getMac();
data = new byte[C.length];
System.arraycopy(dec, 0, data, 0, data.length);
if (!Arrays.areEqual(P, data))
{
fail("Incorrect decrypt");
}
if (!Arrays.areEqual(T, mac))
{
fail("Incorrect MAC on decrypt");
}
}
});
}
}
private static class ErasableByteArrayOutputStream
extends ByteArrayOutputStream
{
public void erase()
{
Arrays.clear(buf);
this.reset();
}
}
private static boolean isApprovedMode()
{
if (CryptoServicesRegistrar.isInApprovedOnlyMode())
{
throw new FipsUnapprovedOperationError("FF3-1 not supported in approved-only mode");
}
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy