1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-27 20:36:51 +01:00

Mi Band 8: Import Bouncy Castle 1.76 classes

This commit is contained in:
José Rebelo 2023-10-05 10:40:49 +01:00
parent 4ede29d1f1
commit 08eb22b4cb
34 changed files with 4188 additions and 4 deletions

View File

@ -23,7 +23,7 @@ vendor's servers.
* Gadgetbridge is licensed under the AGPLv3
* Files in app/src/main/java/net/osmand/ and app/src/main/aidl/net/osmand/ are licensed under the GPLv3 by OsmAnd BV
* Files in app/src/main/java/org/bouncycastle are licensed under the MIT license by The Legion of the Bouncy Castle Inc.
## Download

View File

@ -292,9 +292,9 @@ dependencies {
implementation 'com.google.protobuf:protobuf-javalite:3.21.7'
implementation 'com.android.volley:volley:1.2.1'
// TODO pull just the needed classes into GB?
implementation 'org.bouncycastle:bcpkix-jdk15to18:1.71'
implementation 'org.bouncycastle:bcprov-jdk15to18:1.71'
// Bouncy Castle is included directly in GB, to avoid pulling the entire dependency
//implementation 'org.bouncycastle:bcpkix-jdk15to18:1.76'
//implementation 'org.bouncycastle:bcprov-jdk15to18:1.76'
// NON-FOSS dependencies
// implementation('androidx.core:core-google-shortcuts:1.0.1') {

View File

@ -0,0 +1,56 @@
package org.bouncycastle.crypto;
/**
* Block cipher engines are expected to conform to this interface.
*/
public interface BlockCipher
{
/**
* Initialise the cipher.
*
* @param forEncryption if true the cipher is initialised for
* encryption, if false for decryption.
* @param params the key and other data required by the cipher.
* @exception IllegalArgumentException if the params argument is
* inappropriate.
*/
public void init(boolean forEncryption, CipherParameters params)
throws IllegalArgumentException;
/**
* Return the name of the algorithm the cipher implements.
*
* @return the name of the algorithm the cipher implements.
*/
public String getAlgorithmName();
/**
* Return the block size for this cipher (in bytes).
*
* @return the block size for this cipher in bytes.
*/
public int getBlockSize();
/**
* Process one block of input from the array in and write it to
* the out array.
*
* @param input the array containing the input data.
* @param inOff offset into the in array the data starts at.
* @param output the array the output data will be copied into.
* @param outOff the offset into the out array the output will start at.
* @exception DataLengthException if there isn't enough data in input , or
* space in out.
* @exception IllegalStateException if the cipher isn't initialised.
* @return the number of bytes processed and produced.
*/
public int processBlock(byte[] input, int inOff, byte[] output, int outOff)
throws DataLengthException, IllegalStateException;
/**
* Reset the cipher. After resetting the cipher is in the same state
* as it was after the last init (if there was one).
*/
public void reset();
}

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,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,33 @@
package org.bouncycastle.crypto;
public abstract class DefaultMultiBlockCipher
implements MultiBlockCipher
{
protected DefaultMultiBlockCipher()
{
}
public int getMultiBlockSize()
{
return this.getBlockSize();
}
public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
// TODO check if the underlying cipher supports the multiblock interface and call it directly?
int resultLen = 0;
int blockSize = this.getMultiBlockSize();
for (int i = 0; i != blockCount; i++)
{
resultLen += this.processBlock(in, inOff, out, outOff + resultLen);
inOff += blockSize;
}
return resultLen;
}
}

View File

@ -0,0 +1,40 @@
package org.bouncycastle.crypto;
/**
* this exception is thrown whenever we find something we don't expect in a
* message.
*/
public class InvalidCipherTextException
extends CryptoException
{
/**
* base constructor.
*/
public InvalidCipherTextException()
{
}
/**
* create a InvalidCipherTextException with the given message.
*
* @param message the message to be carried with the exception.
*/
public InvalidCipherTextException(
String message)
{
super(message);
}
/**
* create a InvalidCipherTextException with the given message.
*
* @param message the message to be carried with the exception.
* @param cause the root cause of the exception.
*/
public InvalidCipherTextException(
String message,
Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,71 @@
package org.bouncycastle.crypto;
/**
* The base interface for implementations of message authentication codes (MACs).
*/
public interface Mac
{
/**
* Initialise the MAC.
*
* @param params the key and other data required by the MAC.
* @exception IllegalArgumentException if the params argument is
* inappropriate.
*/
public void init(CipherParameters params)
throws IllegalArgumentException;
/**
* Return the name of the algorithm the MAC implements.
*
* @return the name of the algorithm the MAC implements.
*/
public String getAlgorithmName();
/**
* Return the block size for this MAC (in bytes).
*
* @return the block size for this MAC in bytes.
*/
public int getMacSize();
/**
* add a single byte to the mac for processing.
*
* @param in the byte to be processed.
* @exception IllegalStateException if the MAC is not initialised.
*/
public void update(byte in)
throws IllegalStateException;
/**
* @param in the array containing the input.
* @param inOff the index in the array the data begins at.
* @param len the length of the input starting at inOff.
* @exception IllegalStateException if the MAC is not initialised.
* @exception DataLengthException if there isn't enough data in in.
*/
public void update(byte[] in, int inOff, int len)
throws DataLengthException, IllegalStateException;
/**
* Compute the final stage of the MAC writing the output to the out
* parameter.
* <p>
* doFinal leaves the MAC in the same state it was after the last init.
*
* @param out the array the MAC is to be output to.
* @param outOff the offset into the out buffer the output is to start at.
* @exception DataLengthException if there isn't enough space in out.
* @exception IllegalStateException if the MAC is not initialised.
*/
public int doFinal(byte[] out, int outOff)
throws DataLengthException, IllegalStateException;
/**
* Reset the MAC. At the end of resetting the MAC should be in the
* in the same state it was after the last init (if there was one).
*/
public void reset();
}

View File

@ -0,0 +1,31 @@
package org.bouncycastle.crypto;
/**
* Base interface for a cipher engine capable of processing multiple blocks at a time.
*/
public interface MultiBlockCipher
extends BlockCipher
{
/**
* Return the multi-block size for this cipher (in bytes).
*
* @return the multi-block size for this cipher in bytes.
*/
int getMultiBlockSize();
/**
* Process blockCount blocks from input in offset inOff and place the output in
* out from offset outOff.
*
* @param in input data array.
* @param inOff start of input data in in.
* @param blockCount number of blocks to be processed.
* @param out output data array.
* @param outOff start position for output data.
* @return number of bytes written to out.
* @throws DataLengthException
* @throws IllegalStateException
*/
int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff)
throws DataLengthException, IllegalStateException;
}

View File

@ -0,0 +1,10 @@
package org.bouncycastle.crypto;
public class OutputLengthException
extends DataLengthException
{
public OutputLengthException(String msg)
{
super(msg);
}
}

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,31 @@
package org.bouncycastle.crypto;
/**
* Ciphers producing a key stream which can be reset to particular points in the stream implement this.
*/
public interface SkippingCipher
{
/**
* Skip numberOfBytes forwards, or backwards.
*
* @param numberOfBytes the number of bytes to skip (positive forward, negative backwards).
* @return the number of bytes actually skipped.
* @throws java.lang.IllegalArgumentException if numberOfBytes is an invalid value.
*/
long skip(long numberOfBytes);
/**
* Reset the cipher and then skip forward to a given position.
*
* @param position the number of bytes in to set the cipher state to.
* @return the byte position moved to.
*/
long seekTo(long position);
/**
* Return the current "position" of the cipher
*
* @return the current byte position.
*/
long getPosition();
}

View File

@ -0,0 +1,9 @@
package org.bouncycastle.crypto;
/**
* General interface for a stream cipher that supports skipping.
*/
public interface SkippingStreamCipher
extends StreamCipher, SkippingCipher
{
}

View File

@ -0,0 +1,58 @@
package org.bouncycastle.crypto;
/**
* A parent class for block cipher modes that do not require block aligned data to be processed, but can function in
* a streaming mode.
*/
public abstract class StreamBlockCipher
extends DefaultMultiBlockCipher
implements StreamCipher
{
private final BlockCipher cipher;
protected StreamBlockCipher(BlockCipher cipher)
{
this.cipher = cipher;
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public BlockCipher getUnderlyingCipher()
{
return cipher;
}
public final byte returnByte(byte in)
{
return calculateByte(in);
}
public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
throws DataLengthException
{
if (inOff + len > in.length)
{
throw new DataLengthException("input buffer too small");
}
if (outOff + len > out.length)
{
throw new OutputLengthException("output buffer too short");
}
int inStart = inOff;
int inEnd = inOff + len;
int outStart = outOff;
while (inStart < inEnd)
{
out[outStart++] = calculateByte(in[inStart++]);
}
return len;
}
protected abstract byte calculateByte(byte b);
}

View File

@ -0,0 +1,54 @@
package org.bouncycastle.crypto;
/**
* the interface stream ciphers conform to.
*/
public interface StreamCipher
{
/**
* Initialise the cipher.
*
* @param forEncryption if true the cipher is initialised for
* encryption, if false for decryption.
* @param params the key and other data required by the cipher.
* @exception IllegalArgumentException if the params argument is
* inappropriate.
*/
public void init(boolean forEncryption, CipherParameters params)
throws IllegalArgumentException;
/**
* Return the name of the algorithm the cipher implements.
*
* @return the name of the algorithm the cipher implements.
*/
public String getAlgorithmName();
/**
* encrypt/decrypt a single byte returning the result.
*
* @param in the byte to be processed.
* @return the result of processing the input byte.
*/
public byte returnByte(byte in);
/**
* process a block of bytes from in putting the result into out.
*
* @param in the input byte array.
* @param inOff the offset into the in array where the data to be processed starts.
* @param len the number of bytes to be processed.
* @param out the output buffer the processed bytes go into.
* @param outOff the offset into the output byte array the processed data starts at.
* @return the number of bytes produced - should always be len.
* @exception DataLengthException if the output buffer is too small.
*/
public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
throws DataLengthException;
/**
* reset the cipher. This leaves it in the same state
* it was at after the last init (if there was one).
*/
public void reset();
}

View File

@ -0,0 +1,609 @@
package org.bouncycastle.crypto.engines;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.DefaultMultiBlockCipher;
import org.bouncycastle.crypto.MultiBlockCipher;
import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;
/**
* an implementation of the AES (Rijndael), from FIPS-197.
* <p>
* For further details see: <a href="https://csrc.nist.gov/encryption/aes/">https://csrc.nist.gov/encryption/aes/</a>.
*
* This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
* <a href="https://fp.gladman.plus.com/cryptography_technology/rijndael/">https://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
*
* There are three levels of tradeoff of speed vs memory
* Because java has no preprocessor, they are written as three separate classes from which to choose
*
* The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
* and 4 for decryption.
*
* The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
* adding 12 rotate operations per round to compute the values contained in the other tables from
* the contents of the first.
*
* The slowest version uses no static tables at all and computes the values in each round.
* <p>
* This file contains the middle performance version with 2Kbytes of static tables for round precomputation.
*
*/
public class AESEngine
extends DefaultMultiBlockCipher
{
// The S box
private static final byte[] S = {
(byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197,
(byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
(byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240,
(byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
(byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204,
(byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
(byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154,
(byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
(byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160,
(byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
(byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91,
(byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
(byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133,
(byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
(byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245,
(byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
(byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23,
(byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
(byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136,
(byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
(byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92,
(byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
(byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169,
(byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
(byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198,
(byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
(byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14,
(byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
(byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148,
(byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
(byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104,
(byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
};
// The inverse S-box
private static final byte[] Si = {
(byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56,
(byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
(byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135,
(byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
(byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61,
(byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78,
(byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178,
(byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37,
(byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22,
(byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146,
(byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218,
(byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
(byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10,
(byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6,
(byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2,
(byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107,
(byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234,
(byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
(byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133,
(byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110,
(byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137,
(byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27,
(byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32,
(byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244,
(byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49,
(byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95,
(byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13,
(byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
(byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176,
(byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97,
(byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38,
(byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125,
};
// vector used in calculating key schedule (powers of x in GF(256))
private static final int[] rcon = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
// precomputation tables of calculations for rounds
private static final int[] T0 =
{
0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
0x3a16162c};
private static final int[] Tinv0 =
{
0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
0x4257b8d0};
private static int shift(int r, int shift)
{
return (r >>> shift) | (r << -shift);
}
/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
private static final int m1 = 0x80808080;
private static final int m2 = 0x7f7f7f7f;
private static final int m3 = 0x0000001b;
private static final int m4 = 0xC0C0C0C0;
private static final int m5 = 0x3f3f3f3f;
private static int FFmulX(int x)
{
return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
}
private static int FFmulX2(int x)
{
int t0 = (x & m5) << 2;
int t1 = (x & m4);
t1 ^= (t1 >>> 1);
return t0 ^ (t1 >>> 2) ^ (t1 >>> 5);
}
/*
The following defines provide alternative definitions of FFmulX that might
give improved performance if a fast 32-bit multiply is not available.
private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
private static final int m4 = 0x1b1b1b1b;
private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
*/
private static int inv_mcol(int x)
{
int t0, t1;
t0 = x;
t1 = t0 ^ shift(t0, 8);
t0 ^= FFmulX(t1);
t1 ^= FFmulX2(t0);
t0 ^= t1 ^ shift(t1, 16);
return t0;
}
private static int subWord(int x)
{
return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24);
}
/**
* Calculate the necessary round keys
* The number of calculations depends on key size and block size
* AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
* This code is written assuming those are the only possible values
*/
private int[][] generateWorkingKey(byte[] key, boolean forEncryption)
{
int keyLen = key.length;
if (keyLen < 16 || keyLen > 32 || (keyLen & 7) != 0)
{
throw new IllegalArgumentException("Key length not 128/192/256 bits.");
}
int KC = keyLen >>> 2;
ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
int[][] W = new int[ROUNDS+1][4]; // 4 words in a block
switch (KC)
{
case 4:
{
int col0 = Pack.littleEndianToInt(key, 0); W[0][0] = col0;
int col1 = Pack.littleEndianToInt(key, 4); W[0][1] = col1;
int col2 = Pack.littleEndianToInt(key, 8); W[0][2] = col2;
int col3 = Pack.littleEndianToInt(key, 12); W[0][3] = col3;
for (int i = 1; i <= 10; ++i)
{
int colx = subWord(shift(col3, 8)) ^ rcon[i - 1];
col0 ^= colx; W[i][0] = col0;
col1 ^= col0; W[i][1] = col1;
col2 ^= col1; W[i][2] = col2;
col3 ^= col2; W[i][3] = col3;
}
break;
}
case 6:
{
int col0 = Pack.littleEndianToInt(key, 0); W[0][0] = col0;
int col1 = Pack.littleEndianToInt(key, 4); W[0][1] = col1;
int col2 = Pack.littleEndianToInt(key, 8); W[0][2] = col2;
int col3 = Pack.littleEndianToInt(key, 12); W[0][3] = col3;
int col4 = Pack.littleEndianToInt(key, 16);
int col5 = Pack.littleEndianToInt(key, 20);
int i = 1, rcon = 1, colx;
for (;;)
{
W[i ][0] = col4;
W[i ][1] = col5;
colx = subWord(shift(col5, 8)) ^ rcon; rcon <<= 1;
col0 ^= colx; W[i ][2] = col0;
col1 ^= col0; W[i ][3] = col1;
col2 ^= col1; W[i + 1][0] = col2;
col3 ^= col2; W[i + 1][1] = col3;
col4 ^= col3; W[i + 1][2] = col4;
col5 ^= col4; W[i + 1][3] = col5;
colx = subWord(shift(col5, 8)) ^ rcon; rcon <<= 1;
col0 ^= colx; W[i + 2][0] = col0;
col1 ^= col0; W[i + 2][1] = col1;
col2 ^= col1; W[i + 2][2] = col2;
col3 ^= col2; W[i + 2][3] = col3;
if ((i += 3) >= 13)
{
break;
}
col4 ^= col3;
col5 ^= col4;
}
break;
}
case 8:
{
int col0 = Pack.littleEndianToInt(key, 0); W[0][0] = col0;
int col1 = Pack.littleEndianToInt(key, 4); W[0][1] = col1;
int col2 = Pack.littleEndianToInt(key, 8); W[0][2] = col2;
int col3 = Pack.littleEndianToInt(key, 12); W[0][3] = col3;
int col4 = Pack.littleEndianToInt(key, 16); W[1][0] = col4;
int col5 = Pack.littleEndianToInt(key, 20); W[1][1] = col5;
int col6 = Pack.littleEndianToInt(key, 24); W[1][2] = col6;
int col7 = Pack.littleEndianToInt(key, 28); W[1][3] = col7;
int i = 2, rcon = 1, colx;
for (;;)
{
colx = subWord(shift(col7, 8)) ^ rcon; rcon <<= 1;
col0 ^= colx; W[i][0] = col0;
col1 ^= col0; W[i][1] = col1;
col2 ^= col1; W[i][2] = col2;
col3 ^= col2; W[i][3] = col3;
++i;
if (i >= 15)
{
break;
}
colx = subWord(col3);
col4 ^= colx; W[i][0] = col4;
col5 ^= col4; W[i][1] = col5;
col6 ^= col5; W[i][2] = col6;
col7 ^= col6; W[i][3] = col7;
++i;
}
break;
}
default:
{
throw new IllegalStateException("Should never get here");
}
}
if (!forEncryption)
{
for (int j = 1; j < ROUNDS; j++)
{
for (int i = 0; i < 4; i++)
{
W[j][i] = inv_mcol(W[j][i]);
}
}
}
return W;
}
private int ROUNDS;
private int[][] WorkingKey = null;
private boolean forEncryption;
private byte[] s;
private static final int BLOCK_SIZE = 16;
/**
* Return an AESEngine.
*
* @return an AES ECB mode cipher.
*/
public static MultiBlockCipher newInstance()
{
return new AESEngine();
}
/**
* default constructor - 128 bit block size.
* @deprecated use AESEngine.newInstance()
*/
public AESEngine()
{
// CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
}
/**
* initialise an AES cipher.
*
* @param forEncryption whether or not we are for encryption.
* @param params the parameters required to set up the cipher.
* @exception IllegalArgumentException if the params argument is
* inappropriate.
*/
public void init(
boolean forEncryption,
CipherParameters params)
{
if (params instanceof KeyParameter)
{
WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
this.forEncryption = forEncryption;
if (forEncryption)
{
s = Arrays.clone(S);
}
else
{
s = Arrays.clone(Si);
}
//CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
return;
}
throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName());
}
public String getAlgorithmName()
{
return "AES";
}
public int getBlockSize()
{
return BLOCK_SIZE;
}
public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
{
if (WorkingKey == null)
{
throw new IllegalStateException("AES engine not initialised");
}
if (inOff > (in.length - BLOCK_SIZE))
{
throw new DataLengthException("input buffer too short");
}
if (outOff > (out.length - BLOCK_SIZE))
{
throw new OutputLengthException("output buffer too short");
}
if (forEncryption)
{
encryptBlock(in, inOff, out, outOff, WorkingKey);
}
else
{
decryptBlock(in, inOff, out, outOff, WorkingKey);
}
return BLOCK_SIZE;
}
public void reset()
{
}
private void encryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
{
int C0 = Pack.littleEndianToInt(in, inOff + 0);
int C1 = Pack.littleEndianToInt(in, inOff + 4);
int C2 = Pack.littleEndianToInt(in, inOff + 8);
int C3 = Pack.littleEndianToInt(in, inOff + 12);
int t0 = C0 ^ KW[0][0];
int t1 = C1 ^ KW[0][1];
int t2 = C2 ^ KW[0][2];
int r = 1, r0, r1, r2, r3 = C3 ^ KW[0][3];
while (r < ROUNDS - 1)
{
r0 = T0[t0&255] ^ shift(T0[(t1>>8)&255], 24) ^ shift(T0[(t2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
r1 = T0[t1&255] ^ shift(T0[(t2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(t0>>24)&255], 8) ^ KW[r][1];
r2 = T0[t2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(t0>>16)&255], 16) ^ shift(T0[(t1>>24)&255], 8) ^ KW[r][2];
r3 = T0[r3&255] ^ shift(T0[(t0>>8)&255], 24) ^ shift(T0[(t1>>16)&255], 16) ^ shift(T0[(t2>>24)&255], 8) ^ KW[r++][3];
t0 = T0[r0&255] ^ shift(T0[(r1>>8)&255], 24) ^ shift(T0[(r2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
t1 = T0[r1&255] ^ shift(T0[(r2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(r0>>24)&255], 8) ^ KW[r][1];
t2 = T0[r2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(r0>>16)&255], 16) ^ shift(T0[(r1>>24)&255], 8) ^ KW[r][2];
r3 = T0[r3&255] ^ shift(T0[(r0>>8)&255], 24) ^ shift(T0[(r1>>16)&255], 16) ^ shift(T0[(r2>>24)&255], 8) ^ KW[r++][3];
}
r0 = T0[t0&255] ^ shift(T0[(t1>>8)&255], 24) ^ shift(T0[(t2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
r1 = T0[t1&255] ^ shift(T0[(t2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(t0>>24)&255], 8) ^ KW[r][1];
r2 = T0[t2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(t0>>16)&255], 16) ^ shift(T0[(t1>>24)&255], 8) ^ KW[r][2];
r3 = T0[r3&255] ^ shift(T0[(t0>>8)&255], 24) ^ shift(T0[(t1>>16)&255], 16) ^ shift(T0[(t2>>24)&255], 8) ^ KW[r++][3];
// the final round's table is a simple function of S so we don't use a whole other four tables for it
C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[r][0];
C1 = (s[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[r][1];
C2 = (s[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
C3 = (s[r3&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
Pack.intToLittleEndian(C0, out, outOff + 0);
Pack.intToLittleEndian(C1, out, outOff + 4);
Pack.intToLittleEndian(C2, out, outOff + 8);
Pack.intToLittleEndian(C3, out, outOff + 12);
}
private void decryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
{
int C0 = Pack.littleEndianToInt(in, inOff + 0);
int C1 = Pack.littleEndianToInt(in, inOff + 4);
int C2 = Pack.littleEndianToInt(in, inOff + 8);
int C3 = Pack.littleEndianToInt(in, inOff + 12);
int t0 = C0 ^ KW[ROUNDS][0];
int t1 = C1 ^ KW[ROUNDS][1];
int t2 = C2 ^ KW[ROUNDS][2];
int r = ROUNDS - 1, r0, r1, r2, r3 = C3 ^ KW[ROUNDS][3];
while (r > 1)
{
r0 = Tinv0[t0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(t2>>16)&255], 16) ^ shift(Tinv0[(t1>>24)&255], 8) ^ KW[r][0];
r1 = Tinv0[t1&255] ^ shift(Tinv0[(t0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(t2>>24)&255], 8) ^ KW[r][1];
r2 = Tinv0[t2&255] ^ shift(Tinv0[(t1>>8)&255], 24) ^ shift(Tinv0[(t0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
r3 = Tinv0[r3&255] ^ shift(Tinv0[(t2>>8)&255], 24) ^ shift(Tinv0[(t1>>16)&255], 16) ^ shift(Tinv0[(t0>>24)&255], 8) ^ KW[r--][3];
t0 = Tinv0[r0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(r2>>16)&255], 16) ^ shift(Tinv0[(r1>>24)&255], 8) ^ KW[r][0];
t1 = Tinv0[r1&255] ^ shift(Tinv0[(r0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(r2>>24)&255], 8) ^ KW[r][1];
t2 = Tinv0[r2&255] ^ shift(Tinv0[(r1>>8)&255], 24) ^ shift(Tinv0[(r0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
r3 = Tinv0[r3&255] ^ shift(Tinv0[(r2>>8)&255], 24) ^ shift(Tinv0[(r1>>16)&255], 16) ^ shift(Tinv0[(r0>>24)&255], 8) ^ KW[r--][3];
}
r0 = Tinv0[t0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(t2>>16)&255], 16) ^ shift(Tinv0[(t1>>24)&255], 8) ^ KW[r][0];
r1 = Tinv0[t1&255] ^ shift(Tinv0[(t0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(t2>>24)&255], 8) ^ KW[r][1];
r2 = Tinv0[t2&255] ^ shift(Tinv0[(t1>>8)&255], 24) ^ shift(Tinv0[(t0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
r3 = Tinv0[r3&255] ^ shift(Tinv0[(t2>>8)&255], 24) ^ shift(Tinv0[(t1>>16)&255], 16) ^ shift(Tinv0[(t0>>24)&255], 8) ^ KW[r][3];
// the final round's table is a simple function of Si so we don't use a whole other four tables for it
C0 = (Si[r0&255]&255) ^ ((s[(r3>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
C1 = (s[r1&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (s[(r2>>24)&255]<<24) ^ KW[0][1];
C2 = (s[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[0][2];
C3 = (Si[r3&255]&255) ^ ((s[(r2>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[0][3];
Pack.intToLittleEndian(C0, out, outOff + 0);
Pack.intToLittleEndian(C1, out, outOff + 4);
Pack.intToLittleEndian(C2, out, outOff + 8);
Pack.intToLittleEndian(C3, out, outOff + 12);
}
private int bitsOfSecurity()
{
if (WorkingKey == null)
{
return 256;
}
return (WorkingKey.length - 7) << 5;
}
}

View File

@ -0,0 +1,229 @@
package org.bouncycastle.crypto.macs;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.BlockCipherPadding;
/**
* standard CBC Block Cipher MAC - if no padding is specified the default of
* pad of zeroes is used.
*/
public class CBCBlockCipherMac
implements Mac
{
private byte[] mac;
private byte[] buf;
private int bufOff;
private BlockCipher cipher;
private BlockCipherPadding padding;
private int macSize;
/**
* create a standard MAC based on a CBC block cipher. This will produce an
* authentication code half the length of the block size of the cipher.
*
* @param cipher the cipher to be used as the basis of the MAC generation.
*/
public CBCBlockCipherMac(
BlockCipher cipher)
{
this(cipher, (cipher.getBlockSize() * 8) / 2, null);
}
/**
* create a standard MAC based on a CBC block cipher. This will produce an
* authentication code half the length of the block size of the cipher.
*
* @param cipher the cipher to be used as the basis of the MAC generation.
* @param padding the padding to be used to complete the last block.
*/
public CBCBlockCipherMac(
BlockCipher cipher,
BlockCipherPadding padding)
{
this(cipher, (cipher.getBlockSize() * 8) / 2, padding);
}
/**
* create a standard MAC based on a block cipher with the size of the
* MAC been given in bits. This class uses CBC mode as the basis for the
* MAC generation.
* <p>
* Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
* or 16 bits if being used as a data authenticator (FIPS Publication 113),
* and in general should be less than the size of the block cipher as it reduces
* the chance of an exhaustive attack (see Handbook of Applied Cryptography).
*
* @param cipher the cipher to be used as the basis of the MAC generation.
* @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
*/
public CBCBlockCipherMac(
BlockCipher cipher,
int macSizeInBits)
{
this(cipher, macSizeInBits, null);
}
/**
* create a standard MAC based on a block cipher with the size of the
* MAC been given in bits. This class uses CBC mode as the basis for the
* MAC generation.
* <p>
* Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
* or 16 bits if being used as a data authenticator (FIPS Publication 113),
* and in general should be less than the size of the block cipher as it reduces
* the chance of an exhaustive attack (see Handbook of Applied Cryptography).
*
* @param cipher the cipher to be used as the basis of the MAC generation.
* @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
* @param padding the padding to be used to complete the last block.
*/
public CBCBlockCipherMac(
BlockCipher cipher,
int macSizeInBits,
BlockCipherPadding padding)
{
if ((macSizeInBits % 8) != 0)
{
throw new IllegalArgumentException("MAC size must be multiple of 8");
}
this.cipher = CBCBlockCipher.newInstance(cipher);
this.padding = padding;
this.macSize = macSizeInBits / 8;
mac = new byte[cipher.getBlockSize()];
buf = new byte[cipher.getBlockSize()];
bufOff = 0;
}
public String getAlgorithmName()
{
return cipher.getAlgorithmName();
}
public void init(
CipherParameters params)
{
reset();
cipher.init(true, params);
}
public int getMacSize()
{
return macSize;
}
public void update(
byte in)
{
if (bufOff == buf.length)
{
cipher.processBlock(buf, 0, mac, 0);
bufOff = 0;
}
buf[bufOff++] = in;
}
public void update(
byte[] in,
int inOff,
int len)
{
if (len < 0)
{
throw new IllegalArgumentException("Can't have a negative input length!");
}
int blockSize = cipher.getBlockSize();
int gapLen = blockSize - bufOff;
if (len > gapLen)
{
System.arraycopy(in, inOff, buf, bufOff, gapLen);
cipher.processBlock(buf, 0, mac, 0);
bufOff = 0;
len -= gapLen;
inOff += gapLen;
while (len > blockSize)
{
cipher.processBlock(in, inOff, mac, 0);
len -= blockSize;
inOff += blockSize;
}
}
System.arraycopy(in, inOff, buf, bufOff, len);
bufOff += len;
}
public int doFinal(
byte[] out,
int outOff)
{
int blockSize = cipher.getBlockSize();
if (padding == null)
{
//
// pad with zeroes
//
while (bufOff < blockSize)
{
buf[bufOff] = 0;
bufOff++;
}
}
else
{
if (bufOff == blockSize)
{
cipher.processBlock(buf, 0, mac, 0);
bufOff = 0;
}
padding.addPadding(buf, bufOff);
}
cipher.processBlock(buf, 0, mac, 0);
System.arraycopy(mac, 0, out, outOff, macSize);
reset();
return macSize;
}
/**
* Reset the mac generator.
*/
public void reset()
{
/*
* clean the buffer.
*/
for (int i = 0; i < buf.length; i++)
{
buf[i] = 0;
}
bufOff = 0;
/*
* reset the underlying cipher.
*/
cipher.reset();
}
}

View File

@ -0,0 +1,17 @@
package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
/**
* An {@link AEADCipher} based on a {@link BlockCipher}.
*/
public interface AEADBlockCipher
extends AEADCipher
{
/**
* return the {@link BlockCipher} this object wraps.
*
* @return the {@link BlockCipher} this object wraps.
*/
public BlockCipher getUnderlyingCipher();
}

View File

@ -0,0 +1,138 @@
package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
/**
* A cipher mode that includes authenticated encryption with a streaming mode and optional associated data.
* <p>
* Implementations of this interface may operate in a packet mode (where all input data is buffered and
* processed during the call to {@link #doFinal(byte[], int)}), or in a streaming mode (where output data is
* incrementally produced with each call to {@link #processByte(byte, byte[], int)} or
* {@link #processBytes(byte[], int, int, byte[], int)}.
* </p>
* This is important to consider during decryption: in a streaming mode, unauthenticated plaintext data
* may be output prior to the call to {@link #doFinal(byte[], int)} that results in an authentication
* failure. The higher level protocol utilising this cipher must ensure the plaintext data is handled
* appropriately until the end of data is reached and the entire ciphertext is authenticated.
* @see org.bouncycastle.crypto.params.AEADParameters
*/
public interface AEADCipher
{
/**
* initialise the underlying cipher. Parameter can either be an AEADParameters or a ParametersWithIV object.
*
* @param forEncryption true if we are setting up for encryption, false otherwise.
* @param params the necessary parameters for the underlying cipher to be initialised.
* @exception IllegalArgumentException if the params argument is inappropriate.
*/
public void init(boolean forEncryption, CipherParameters params)
throws IllegalArgumentException;
/**
* Return the name of the algorithm.
*
* @return the algorithm name.
*/
public String getAlgorithmName();
/**
* Add a single byte to the associated data check.
* <br>If the implementation supports it, this will be an online operation and will not retain the associated data.
*
* @param in the byte to be processed.
*/
public void processAADByte(byte in);
/**
* Add a sequence of bytes to the associated data check.
* <br>If the implementation supports it, this will be an online operation and will not retain the associated data.
*
* @param in the input byte array.
* @param inOff the offset into the in array where the data to be processed starts.
* @param len the number of bytes to be processed.
*/
public void processAADBytes(byte[] in, int inOff, int len);
/**
* encrypt/decrypt a single byte.
*
* @param in the byte to be processed.
* @param out the output buffer the processed byte goes into.
* @param outOff the offset into the output byte array the processed data starts at.
* @return the number of bytes written to out.
* @exception DataLengthException if the output buffer is too small.
*/
public int processByte(byte in, byte[] out, int outOff)
throws DataLengthException;
/**
* process a block of bytes from in putting the result into out.
*
* @param in the input byte array.
* @param inOff the offset into the in array where the data to be processed starts.
* @param len the number of bytes to be processed.
* @param out the output buffer the processed bytes go into.
* @param outOff the offset into the output byte array the processed data starts at.
* @return the number of bytes written to out.
* @exception DataLengthException if the output buffer is too small.
*/
public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
throws DataLengthException;
/**
* Finish the operation either appending or verifying the MAC at the end of the data.
*
* @param out space for any resulting output data.
* @param outOff offset into out to start copying the data at.
* @return number of bytes written into out.
* @throws IllegalStateException if the cipher is in an inappropriate state.
* @throws org.bouncycastle.crypto.InvalidCipherTextException if the MAC fails to match.
*/
public int doFinal(byte[] out, int outOff)
throws IllegalStateException, InvalidCipherTextException;
/**
* Return the value of the MAC associated with the last stream processed.
*
* @return MAC for plaintext data.
*/
public byte[] getMac();
/**
* return the size of the output buffer required for a processBytes
* an input of len bytes.
* <p>
* The returned size may be dependent on the initialisation of this cipher
* and may not be accurate once subsequent input data is processed - this method
* should be invoked immediately prior to input data being processed.
* </p>
*
* @param len the length of the input.
* @return the space required to accommodate a call to processBytes
* with len bytes of input.
*/
public int getUpdateOutputSize(int len);
/**
* return the size of the output buffer required for a processBytes plus a
* doFinal with an input of len bytes.
* <p>
* The returned size may be dependent on the initialisation of this cipher
* and may not be accurate once subsequent input data is processed - this method
* should be invoked immediately prior to a call to final processing of input data
* and a call to {@link #doFinal(byte[], int)}.
* </p>
* @param len the length of the input.
* @return the space required to accommodate a call to processBytes and doFinal
* with len bytes of input.
*/
public int getOutputSize(int len);
/**
* Reset the cipher. After resetting the cipher is in the same state
* as it was after the last init (if there was one).
*/
public void reset();
}

View File

@ -0,0 +1,266 @@
package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.DefaultMultiBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
/**
* implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
*/
public class CBCBlockCipher
extends DefaultMultiBlockCipher
implements CBCModeCipher
{
private byte[] IV;
private byte[] cbcV;
private byte[] cbcNextV;
private int blockSize;
private BlockCipher cipher = null;
private boolean encrypting;
/**
* Return a new CBC mode cipher based on the passed in base cipher
*
* @param cipher the base cipher for the CBC mode.
*/
public static CBCModeCipher newInstance(BlockCipher cipher)
{
return new CBCBlockCipher(cipher);
}
/**
* Basic constructor.
*
* @param cipher the block cipher to be used as the basis of chaining.
* @deprecated use the CBCBlockCipher.newInstance() static method.
*/
public CBCBlockCipher(
BlockCipher cipher)
{
this.cipher = cipher;
this.blockSize = cipher.getBlockSize();
this.IV = new byte[blockSize];
this.cbcV = new byte[blockSize];
this.cbcNextV = new byte[blockSize];
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public BlockCipher getUnderlyingCipher()
{
return cipher;
}
/**
* Initialise the cipher and, possibly, the initialisation vector (IV).
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
*
* @param encrypting if true the cipher is initialised for
* encryption, if false for decryption.
* @param params the key and other data required by the cipher.
* @exception IllegalArgumentException if the params argument is
* inappropriate.
*/
public void init(
boolean encrypting,
CipherParameters params)
throws IllegalArgumentException
{
boolean oldEncrypting = this.encrypting;
this.encrypting = encrypting;
if (params instanceof ParametersWithIV)
{
ParametersWithIV ivParam = (ParametersWithIV)params;
byte[] iv = ivParam.getIV();
if (iv.length != blockSize)
{
throw new IllegalArgumentException("initialisation vector must be the same length as block size");
}
System.arraycopy(iv, 0, IV, 0, iv.length);
reset();
// if null it's an IV changed only.
if (ivParam.getParameters() != null)
{
cipher.init(encrypting, ivParam.getParameters());
}
else if (oldEncrypting != encrypting)
{
throw new IllegalArgumentException("cannot change encrypting state without providing key.");
}
}
else
{
reset();
// if it's null, key is to be reused.
if (params != null)
{
cipher.init(encrypting, params);
}
else if (oldEncrypting != encrypting)
{
throw new IllegalArgumentException("cannot change encrypting state without providing key.");
}
}
}
/**
* return the algorithm name and mode.
*
* @return the name of the underlying algorithm followed by "/CBC".
*/
public String getAlgorithmName()
{
return cipher.getAlgorithmName() + "/CBC";
}
/**
* return the block size of the underlying cipher.
*
* @return the block size of the underlying cipher.
*/
public int getBlockSize()
{
return cipher.getBlockSize();
}
/**
* Process one block of input from the array in and write it to
* the out array.
*
* @param in the array containing the input data.
* @param inOff offset into the in array the data starts at.
* @param out the array the output data will be copied into.
* @param outOff the offset into the out array the output will start at.
* @exception DataLengthException if there isn't enough data in in, or
* space in out.
* @exception IllegalStateException if the cipher isn't initialised.
* @return the number of bytes processed and produced.
*/
public int processBlock(
byte[] in,
int inOff,
byte[] out,
int outOff)
throws DataLengthException, IllegalStateException
{
return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
}
/**
* reset the chaining vector back to the IV and reset the underlying
* cipher.
*/
public void reset()
{
System.arraycopy(IV, 0, cbcV, 0, IV.length);
Arrays.fill(cbcNextV, (byte)0);
cipher.reset();
}
/**
* Do the appropriate chaining step for CBC mode encryption.
*
* @param in the array containing the data to be encrypted.
* @param inOff offset into the in array the data starts at.
* @param out the array the encrypted data will be copied into.
* @param outOff the offset into the out array the output will start at.
* @exception DataLengthException if there isn't enough data in in, or
* space in out.
* @exception IllegalStateException if the cipher isn't initialised.
* @return the number of bytes processed and produced.
*/
private int encryptBlock(
byte[] in,
int inOff,
byte[] out,
int outOff)
throws DataLengthException, IllegalStateException
{
if ((inOff + blockSize) > in.length)
{
throw new DataLengthException("input buffer too short");
}
/*
* XOR the cbcV and the input,
* then encrypt the cbcV
*/
for (int i = 0; i < blockSize; i++)
{
cbcV[i] ^= in[inOff + i];
}
int length = cipher.processBlock(cbcV, 0, out, outOff);
/*
* copy ciphertext to cbcV
*/
System.arraycopy(out, outOff, cbcV, 0, cbcV.length);
return length;
}
/**
* Do the appropriate chaining step for CBC mode decryption.
*
* @param in the array containing the data to be decrypted.
* @param inOff offset into the in array the data starts at.
* @param out the array the decrypted data will be copied into.
* @param outOff the offset into the out array the output will start at.
* @exception DataLengthException if there isn't enough data in in, or
* space in out.
* @exception IllegalStateException if the cipher isn't initialised.
* @return the number of bytes processed and produced.
*/
private int decryptBlock(
byte[] in,
int inOff,
byte[] out,
int outOff)
throws DataLengthException, IllegalStateException
{
if ((inOff + blockSize) > in.length)
{
throw new DataLengthException("input buffer too short");
}
System.arraycopy(in, inOff, cbcNextV, 0, blockSize);
int length = cipher.processBlock(in, inOff, out, outOff);
/*
* XOR the cbcV and the output
*/
for (int i = 0; i < blockSize; i++)
{
out[outOff + i] ^= cbcV[i];
}
/*
* swap the back up buffer into next position
*/
byte[] tmp;
tmp = cbcV;
cbcV = cbcNextV;
cbcNextV = tmp;
return length;
}
}

View File

@ -0,0 +1,15 @@
package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.MultiBlockCipher;
public interface CBCModeCipher
extends MultiBlockCipher
{
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
BlockCipher getUnderlyingCipher();
}

View File

@ -0,0 +1,480 @@
package org.bouncycastle.crypto.modes;
import java.io.ByteArrayOutputStream;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
/**
* Implements the Counter with Cipher Block Chaining mode (CCM) detailed in
* NIST Special Publication 800-38C.
* <p>
* <b>Note</b>: this mode is a packet mode - it needs all the data up front.
*/
public class CCMBlockCipher
implements CCMModeCipher
{
private BlockCipher cipher;
private int blockSize;
private boolean forEncryption;
private byte[] nonce;
private byte[] initialAssociatedText;
private int macSize;
private CipherParameters keyParam;
private byte[] macBlock;
private ExposedByteArrayOutputStream associatedText = new ExposedByteArrayOutputStream();
private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream();
/**
* Return a new CCM mode cipher based on the passed in base cipher
*
* @param cipher the base cipher for the CCM mode.
*/
public static CCMModeCipher newInstance(BlockCipher cipher)
{
return new CCMBlockCipher(cipher);
}
/**
* Basic constructor.
*
* @param c the block cipher to be used.
* @deprecated use the CCMBlockCipher.newInstance() static method.
*/
public CCMBlockCipher(BlockCipher c)
{
this.cipher = c;
this.blockSize = c.getBlockSize();
this.macBlock = new byte[blockSize];
if (blockSize != 16)
{
throw new IllegalArgumentException("cipher required with a block size of 16.");
}
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public BlockCipher getUnderlyingCipher()
{
return cipher;
}
public void init(boolean forEncryption, CipherParameters params)
throws IllegalArgumentException
{
this.forEncryption = forEncryption;
CipherParameters cipherParameters;
if (params instanceof AEADParameters)
{
AEADParameters param = (AEADParameters)params;
nonce = param.getNonce();
initialAssociatedText = param.getAssociatedText();
macSize = getMacSize(forEncryption, param.getMacSize());
cipherParameters = param.getKey();
}
else if (params instanceof ParametersWithIV)
{
ParametersWithIV param = (ParametersWithIV)params;
nonce = param.getIV();
initialAssociatedText = null;
macSize = getMacSize(forEncryption, 64);
cipherParameters = param.getParameters();
}
else
{
throw new IllegalArgumentException("invalid parameters passed to CCM: " + params.getClass().getName());
}
// NOTE: Very basic support for key re-use, but no performance gain from it
if (cipherParameters != null)
{
keyParam = cipherParameters;
}
if (nonce == null || nonce.length < 7 || nonce.length > 13)
{
throw new IllegalArgumentException("nonce must have length from 7 to 13 octets");
}
reset();
}
public String getAlgorithmName()
{
return cipher.getAlgorithmName() + "/CCM";
}
public void processAADByte(byte in)
{
associatedText.write(in);
}
public void processAADBytes(byte[] in, int inOff, int len)
{
// TODO: Process AAD online
associatedText.write(in, inOff, len);
}
public int processByte(byte in, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
data.write(in);
return 0;
}
public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
if (in.length < (inOff + inLen))
{
throw new DataLengthException("Input buffer too short");
}
data.write(in, inOff, inLen);
return 0;
}
public int doFinal(byte[] out, int outOff)
throws IllegalStateException, InvalidCipherTextException
{
int len = processPacket(data.getBuffer(), 0, data.size(), out, outOff);
reset();
return len;
}
public void reset()
{
cipher.reset();
associatedText.reset();
data.reset();
}
/**
* Returns a byte array containing the mac calculated as part of the
* last encrypt or decrypt operation.
*
* @return the last mac calculated.
*/
public byte[] getMac()
{
byte[] mac = new byte[macSize];
System.arraycopy(macBlock, 0, mac, 0, mac.length);
return mac;
}
public int getUpdateOutputSize(int len)
{
return 0;
}
public int getOutputSize(int len)
{
int totalData = len + data.size();
if (forEncryption)
{
return totalData + macSize;
}
return totalData < macSize ? 0 : totalData - macSize;
}
/**
* Process a packet of data for either CCM decryption or encryption.
*
* @param in data for processing.
* @param inOff offset at which data starts in the input array.
* @param inLen length of the data in the input array.
* @return a byte array containing the processed input..
* @throws IllegalStateException if the cipher is not appropriately set up.
* @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
*/
public byte[] processPacket(byte[] in, int inOff, int inLen)
throws IllegalStateException, InvalidCipherTextException
{
byte[] output;
if (forEncryption)
{
output = new byte[inLen + macSize];
}
else
{
if (inLen < macSize)
{
throw new InvalidCipherTextException("data too short");
}
output = new byte[inLen - macSize];
}
processPacket(in, inOff, inLen, output, 0);
return output;
}
/**
* Process a packet of data for either CCM decryption or encryption.
*
* @param in data for processing.
* @param inOff offset at which data starts in the input array.
* @param inLen length of the data in the input array.
* @param output output array.
* @param outOff offset into output array to start putting processed bytes.
* @return the number of bytes added to output.
* @throws IllegalStateException if the cipher is not appropriately set up.
* @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
* @throws DataLengthException if output buffer too short.
*/
public int processPacket(byte[] in, int inOff, int inLen, byte[] output, int outOff)
throws IllegalStateException, InvalidCipherTextException, DataLengthException
{
// TODO: handle null keyParam (e.g. via RepeatedKeySpec)
// Need to keep the CTR and CBC Mac parts around and reset
if (keyParam == null)
{
throw new IllegalStateException("CCM cipher unitialized.");
}
int n = nonce.length;
int q = 15 - n;
if (q < 4)
{
int limitLen = 1 << (8 * q);
if (inLen >= limitLen)
{
throw new IllegalStateException("CCM packet too large for choice of q.");
}
}
byte[] iv = new byte[blockSize];
iv[0] = (byte)((q - 1) & 0x7);
System.arraycopy(nonce, 0, iv, 1, nonce.length);
BlockCipher ctrCipher = SICBlockCipher.newInstance(cipher);
ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
int outputLen;
int inIndex = inOff;
int outIndex = outOff;
if (forEncryption)
{
outputLen = inLen + macSize;
if (output.length < (outputLen + outOff))
{
throw new OutputLengthException("Output buffer too short.");
}
calculateMac(in, inOff, inLen, macBlock);
byte[] encMac = new byte[blockSize];
ctrCipher.processBlock(macBlock, 0, encMac, 0); // S0
while (inIndex < (inOff + inLen - blockSize)) // S1...
{
ctrCipher.processBlock(in, inIndex, output, outIndex);
outIndex += blockSize;
inIndex += blockSize;
}
byte[] block = new byte[blockSize];
System.arraycopy(in, inIndex, block, 0, inLen + inOff - inIndex);
ctrCipher.processBlock(block, 0, block, 0);
System.arraycopy(block, 0, output, outIndex, inLen + inOff - inIndex);
System.arraycopy(encMac, 0, output, outOff + inLen, macSize);
}
else
{
if (inLen < macSize)
{
throw new InvalidCipherTextException("data too short");
}
outputLen = inLen - macSize;
if (output.length < (outputLen + outOff))
{
throw new OutputLengthException("Output buffer too short.");
}
System.arraycopy(in, inOff + outputLen, macBlock, 0, macSize);
ctrCipher.processBlock(macBlock, 0, macBlock, 0);
for (int i = macSize; i != macBlock.length; i++)
{
macBlock[i] = 0;
}
while (inIndex < (inOff + outputLen - blockSize))
{
ctrCipher.processBlock(in, inIndex, output, outIndex);
outIndex += blockSize;
inIndex += blockSize;
}
byte[] block = new byte[blockSize];
System.arraycopy(in, inIndex, block, 0, outputLen - (inIndex - inOff));
ctrCipher.processBlock(block, 0, block, 0);
System.arraycopy(block, 0, output, outIndex, outputLen - (inIndex - inOff));
byte[] calculatedMacBlock = new byte[blockSize];
calculateMac(output, outOff, outputLen, calculatedMacBlock);
if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock))
{
throw new InvalidCipherTextException("mac check in CCM failed");
}
}
return outputLen;
}
private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
{
Mac cMac = new CBCBlockCipherMac(cipher, macSize * 8);
cMac.init(keyParam);
//
// build b0
//
byte[] b0 = new byte[16];
if (hasAssociatedText())
{
b0[0] |= 0x40;
}
b0[0] |= (((cMac.getMacSize() - 2) / 2) & 0x7) << 3;
b0[0] |= ((15 - nonce.length) - 1) & 0x7;
System.arraycopy(nonce, 0, b0, 1, nonce.length);
int q = dataLen;
int count = 1;
while (q > 0)
{
b0[b0.length - count] = (byte)(q & 0xff);
q >>>= 8;
count++;
}
cMac.update(b0, 0, b0.length);
//
// process associated text
//
if (hasAssociatedText())
{
int extra;
int textLength = getAssociatedTextLength();
if (textLength < ((1 << 16) - (1 << 8)))
{
cMac.update((byte)(textLength >> 8));
cMac.update((byte)textLength);
extra = 2;
}
else // can't go any higher than 2^32
{
cMac.update((byte)0xff);
cMac.update((byte)0xfe);
cMac.update((byte)(textLength >> 24));
cMac.update((byte)(textLength >> 16));
cMac.update((byte)(textLength >> 8));
cMac.update((byte)textLength);
extra = 6;
}
if (initialAssociatedText != null)
{
cMac.update(initialAssociatedText, 0, initialAssociatedText.length);
}
if (associatedText.size() > 0)
{
cMac.update(associatedText.getBuffer(), 0, associatedText.size());
}
extra = (extra + textLength) % 16;
if (extra != 0)
{
for (int i = extra; i != 16; i++)
{
cMac.update((byte)0x00);
}
}
}
//
// add the text
//
cMac.update(data, dataOff, dataLen);
return cMac.doFinal(macBlock, 0);
}
private int getMacSize(boolean forEncryption, int requestedMacBits)
{
if (forEncryption && (requestedMacBits < 32 || requestedMacBits > 128 || 0 != (requestedMacBits & 15)))
{
throw new IllegalArgumentException("tag length in octets must be one of {4,6,8,10,12,14,16}");
}
return requestedMacBits >>> 3;
}
private int getAssociatedTextLength()
{
return associatedText.size() + ((initialAssociatedText == null) ? 0 : initialAssociatedText.length);
}
private boolean hasAssociatedText()
{
return getAssociatedTextLength() > 0;
}
private static class ExposedByteArrayOutputStream
extends ByteArrayOutputStream
{
public ExposedByteArrayOutputStream()
{
}
public byte[] getBuffer()
{
return this.buf;
}
}
}

View File

@ -0,0 +1,6 @@
package org.bouncycastle.crypto.modes;
public interface CCMModeCipher
extends AEADBlockCipher
{
}

View File

@ -0,0 +1,16 @@
package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.MultiBlockCipher;
import org.bouncycastle.crypto.SkippingStreamCipher;
public interface CTRModeCipher
extends MultiBlockCipher, SkippingStreamCipher
{
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
BlockCipher getUnderlyingCipher();
}

View File

@ -0,0 +1,381 @@
package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;
/**
* Implements the Segmented Integer Counter (SIC) mode on top of a simple
* block cipher. This mode is also known as CTR mode.
*/
public class SICBlockCipher
extends StreamBlockCipher
implements CTRModeCipher
{
private final BlockCipher cipher;
private final int blockSize;
private byte[] IV;
private byte[] counter;
private byte[] counterOut;
private int byteCount;
/**
* Return a new SIC/CTR mode cipher based on the passed in base cipher
*
* @param cipher the base cipher for the SIC/CTR mode.
*/
public static CTRModeCipher newInstance(BlockCipher cipher)
{
return new SICBlockCipher(cipher);
}
/**
* Basic constructor.
*
* @param c the block cipher to be used.
* @deprecated use newInstance() method.
*/
public SICBlockCipher(BlockCipher c)
{
super(c);
this.cipher = c;
this.blockSize = cipher.getBlockSize();
this.IV = new byte[blockSize];
this.counter = new byte[blockSize];
this.counterOut = new byte[blockSize];
this.byteCount = 0;
}
public void init(
boolean forEncryption, //ignored by this CTR mode
CipherParameters params)
throws IllegalArgumentException
{
if (params instanceof ParametersWithIV)
{
ParametersWithIV ivParam = (ParametersWithIV)params;
this.IV = Arrays.clone(ivParam.getIV());
if (blockSize < IV.length)
{
throw new IllegalArgumentException("CTR/SIC mode requires IV no greater than: " + blockSize + " bytes.");
}
int maxCounterSize = (8 > blockSize / 2) ? blockSize / 2 : 8;
if (blockSize - IV.length > maxCounterSize)
{
throw new IllegalArgumentException("CTR/SIC mode requires IV of at least: " + (blockSize - maxCounterSize) + " bytes.");
}
// if null it's an IV changed only.
if (ivParam.getParameters() != null)
{
cipher.init(true, ivParam.getParameters());
}
reset();
}
else
{
throw new IllegalArgumentException("CTR/SIC mode requires ParametersWithIV");
}
}
public String getAlgorithmName()
{
return cipher.getAlgorithmName() + "/SIC";
}
public int getBlockSize()
{
return cipher.getBlockSize();
}
public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
if (byteCount != 0)
{
processBytes(in, inOff, blockSize, out, outOff);
return blockSize;
}
if (inOff + blockSize > in.length)
{
throw new DataLengthException("input buffer too small");
}
if (outOff + blockSize > out.length)
{
throw new OutputLengthException("output buffer too short");
}
cipher.processBlock(counter, 0, counterOut, 0);
for (int i = 0; i < blockSize; ++i)
{
out[outOff + i] = (byte)(in[inOff + i] ^ counterOut[i]);
}
incrementCounter();
return blockSize;
}
public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
throws DataLengthException
{
if (inOff + len > in.length)
{
throw new DataLengthException("input buffer too small");
}
if (outOff + len > out.length)
{
throw new OutputLengthException("output buffer too short");
}
for (int i = 0; i < len; ++i)
{
byte next;
if (byteCount == 0)
{
checkLastIncrement();
cipher.processBlock(counter, 0, counterOut, 0);
next = (byte)(in[inOff + i] ^ counterOut[byteCount++]);
}
else
{
next = (byte)(in[inOff + i] ^ counterOut[byteCount++]);
if (byteCount == counter.length)
{
byteCount = 0;
incrementCounter();
}
}
out[outOff + i] = next;
}
return len;
}
protected byte calculateByte(byte in)
throws DataLengthException, IllegalStateException
{
if (byteCount == 0)
{
checkLastIncrement();
cipher.processBlock(counter, 0, counterOut, 0);
return (byte)(counterOut[byteCount++] ^ in);
}
byte rv = (byte)(counterOut[byteCount++] ^ in);
if (byteCount == counter.length)
{
byteCount = 0;
incrementCounter();
}
return rv;
}
private void checkCounter()
{
// if the IV is the same as the blocksize we assume the user knows what they are doing
if (IV.length < blockSize)
{
for (int i = IV.length - 1; i >= 0; i--)
{
if (counter[i] != IV[i])
{
throw new IllegalStateException("Counter in CTR/SIC mode out of range.");
}
}
}
}
private void checkLastIncrement()
{
// if the IV is the same as the blocksize we assume the user knows what they are doing
if (IV.length < blockSize)
{
if (counter[IV.length - 1] != IV[IV.length - 1])
{
throw new IllegalStateException("Counter in CTR/SIC mode out of range.");
}
}
}
private void incrementCounter()
{
int i = counter.length;
while (--i >= 0)
{
if (++counter[i] != 0)
{
break;
}
}
}
private void incrementCounterAt(int pos)
{
int i = counter.length - pos;
while (--i >= 0)
{
if (++counter[i] != 0)
{
break;
}
}
}
private void incrementCounter(int offSet)
{
byte old = counter[counter.length - 1];
counter[counter.length - 1] += offSet;
if (old != 0 && counter[counter.length - 1] < old)
{
incrementCounterAt(1);
}
}
private void decrementCounterAt(int pos)
{
int i = counter.length - pos;
while (--i >= 0)
{
if (--counter[i] != -1)
{
return;
}
}
}
private void adjustCounter(long n)
{
if (n >= 0)
{
long numBlocks = (n + byteCount) / blockSize;
long rem = numBlocks;
if (rem > 255)
{
for (int i = 5; i >= 1; i--)
{
long diff = 1L << (8 * i);
while (rem >= diff)
{
incrementCounterAt(i);
rem -= diff;
}
}
}
incrementCounter((int)rem);
byteCount = (int)((n + byteCount) - (blockSize * numBlocks));
}
else
{
long numBlocks = (-n - byteCount) / blockSize;
long rem = numBlocks;
if (rem > 255)
{
for (int i = 5; i >= 1; i--)
{
long diff = 1L << (8 * i);
while (rem > diff)
{
decrementCounterAt(i);
rem -= diff;
}
}
}
for (long i = 0; i != rem; i++)
{
decrementCounterAt(0);
}
int gap = (int)(byteCount + n + (blockSize * numBlocks));
if (gap >= 0)
{
byteCount = 0;
}
else
{
decrementCounterAt(0);
byteCount = blockSize + gap;
}
}
}
public void reset()
{
Arrays.fill(counter, (byte)0);
System.arraycopy(IV, 0, counter, 0, IV.length);
cipher.reset();
this.byteCount = 0;
}
public long skip(long numberOfBytes)
{
adjustCounter(numberOfBytes);
checkCounter();
cipher.processBlock(counter, 0, counterOut, 0);
return numberOfBytes;
}
public long seekTo(long position)
{
reset();
return skip(position);
}
public long getPosition()
{
byte[] res = new byte[counter.length];
System.arraycopy(counter, 0, res, 0, res.length);
for (int i = res.length - 1; i >= 1; i--)
{
int v;
if (i < IV.length)
{
v = (res[i] & 0xff) - (IV[i] & 0xff);
}
else
{
v = (res[i] & 0xff);
}
if (v < 0)
{
res[i - 1]--;
v += 256;
}
res[i] = (byte)v;
}
return Pack.bigEndianToLong(res, res.length - 8) * blockSize + byteCount;
}
}

View File

@ -0,0 +1,48 @@
package org.bouncycastle.crypto.paddings;
import java.security.SecureRandom;
import org.bouncycastle.crypto.InvalidCipherTextException;
/**
* Block cipher padders are expected to conform to this interface
*/
public interface BlockCipherPadding
{
/**
* Initialise the padder.
*
* @param random the source of randomness for the padding, if required.
*/
public void init(SecureRandom random)
throws IllegalArgumentException;
/**
* Return the name of the algorithm the cipher implements.
*
* @return the name of the algorithm the cipher implements.
*/
public String getPaddingName();
/**
* add the pad bytes to the passed in block, returning the
* number of bytes added.
* <p>
* Note: this assumes that the last block of plain text is always
* passed to it inside in. i.e. if inOff is zero, indicating the
* entire block is to be overwritten with padding the value of in
* should be the same as the last block of plain text. The reason
* for this is that some modes such as "trailing bit compliment"
* base the padding on the last byte of plain text.
* </p>
*/
public int addPadding(byte[] in, int inOff);
/**
* return the number of pad bytes present in the block.
* @exception InvalidCipherTextException if the padding is badly formed
* or invalid.
*/
public int padCount(byte[] in)
throws InvalidCipherTextException;
}

View File

@ -0,0 +1,61 @@
package org.bouncycastle.crypto.params;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.util.Arrays;
public class AEADParameters
implements CipherParameters
{
private byte[] associatedText;
private byte[] nonce;
private KeyParameter key;
private int macSize;
/**
* Base constructor.
*
* @param key key to be used by underlying cipher
* @param macSize macSize in bits
* @param nonce nonce to be used
*/
public AEADParameters(KeyParameter key, int macSize, byte[] nonce)
{
this(key, macSize, nonce, null);
}
/**
* Base constructor.
*
* @param key key to be used by underlying cipher
* @param macSize macSize in bits
* @param nonce nonce to be used
* @param associatedText initial associated text, if any
*/
public AEADParameters(KeyParameter key, int macSize, byte[] nonce, byte[] associatedText)
{
this.key = key;
this.nonce = Arrays.clone(nonce);
this.macSize = macSize;
this.associatedText = Arrays.clone(associatedText);
}
public KeyParameter getKey()
{
return key;
}
public int getMacSize()
{
return macSize;
}
public byte[] getAssociatedText()
{
return Arrays.clone(associatedText);
}
public byte[] getNonce()
{
return Arrays.clone(nonce);
}
}

View File

@ -0,0 +1,56 @@
package org.bouncycastle.crypto.params;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.util.Arrays;
public class KeyParameter
implements CipherParameters
{
private byte[] key;
public KeyParameter(
byte[] key)
{
this(key, 0, key.length);
}
public KeyParameter(
byte[] key,
int keyOff,
int keyLen)
{
this(keyLen);
System.arraycopy(key, keyOff, this.key, 0, keyLen);
}
private KeyParameter(int length)
{
this.key = new byte[length];
}
public void copyTo(byte[] buf, int off, int len)
{
if (key.length != len)
throw new IllegalArgumentException("len");
System.arraycopy(key, 0, buf, off, len);
}
public byte[] getKey()
{
return key;
}
public int getKeyLength()
{
return key.length;
}
public KeyParameter reverse()
{
KeyParameter reversed = new KeyParameter(key.length);
Arrays.reverse(this.key, reversed.key);
return reversed;
}
}

View File

@ -0,0 +1,39 @@
package org.bouncycastle.crypto.params;
import org.bouncycastle.crypto.CipherParameters;
public class ParametersWithIV
implements CipherParameters
{
private byte[] iv;
private CipherParameters parameters;
public ParametersWithIV(
CipherParameters parameters,
byte[] iv)
{
this(parameters, iv, 0, iv.length);
}
public ParametersWithIV(
CipherParameters parameters,
byte[] iv,
int ivOff,
int ivLen)
{
this.iv = new byte[ivLen];
this.parameters = parameters;
System.arraycopy(iv, ivOff, this.iv, 0, ivLen);
}
public byte[] getIV()
{
return iv;
}
public CipherParameters getParameters()
{
return parameters;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
package org.bouncycastle.util;
public class Objects
{
public static boolean areEqual(Object a, Object b)
{
return a == b || (null != a && null != b && a.equals(b));
}
public static int hashCode(Object obj)
{
return null == obj ? 0 : obj.hashCode();
}
}

View File

@ -0,0 +1,40 @@
package org.bouncycastle.util;
/**
* Utility methods for converting byte arrays into ints and longs, and back again.
*/
public abstract class Pack
{
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 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 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 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);
}
}

View File

@ -0,0 +1,38 @@
package org.bouncycastle.util;
import java.security.AccessController;
import java.security.PrivilegedAction;
/* loaded from: classes.dex */
public final class Strings {
private static String LINE_SEPARATOR;
static {
try {
LINE_SEPARATOR = (String) AccessController.doPrivileged(new PrivilegedAction<String>() { // from class: org.bouncycastle.util.Strings.1
@Override // java.security.PrivilegedAction
public String run() {
return System.getProperty("line.separator");
}
});
} catch (Exception e) {
try {
LINE_SEPARATOR = String.format("%n", new Object[0]);
} catch (Exception e2) {
LINE_SEPARATOR = "\n";
}
}
}
public static String toLowerCase(String str) {
char[] charArray = str.toCharArray();
boolean z = false;
for (int i = 0; i != charArray.length; i++) {
char c = charArray[i];
if ('A' <= c && 'Z' >= c) {
charArray[i] = (char) ((c - 'A') + 97);
z = true;
}
}
return z ? new String(charArray) : str;
}
}