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:
parent
4ede29d1f1
commit
08eb22b4cb
@ -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
|
||||
|
||||
|
@ -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') {
|
||||
|
56
app/src/main/java/org/bouncycastle/crypto/BlockCipher.java
Normal file
56
app/src/main/java/org/bouncycastle/crypto/BlockCipher.java
Normal 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();
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package org.bouncycastle.crypto;
|
||||
|
||||
/**
|
||||
* all parameter classes implement this.
|
||||
*/
|
||||
public interface CipherParameters
|
||||
{
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
71
app/src/main/java/org/bouncycastle/crypto/Mac.java
Normal file
71
app/src/main/java/org/bouncycastle/crypto/Mac.java
Normal 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();
|
||||
}
|
@ -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;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package org.bouncycastle.crypto;
|
||||
|
||||
public class OutputLengthException
|
||||
extends DataLengthException
|
||||
{
|
||||
public OutputLengthException(String msg)
|
||||
{
|
||||
super(msg);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package org.bouncycastle.crypto;
|
||||
|
||||
/**
|
||||
* General interface for a stream cipher that supports skipping.
|
||||
*/
|
||||
public interface SkippingStreamCipher
|
||||
extends StreamCipher, SkippingCipher
|
||||
{
|
||||
}
|
@ -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);
|
||||
}
|
54
app/src/main/java/org/bouncycastle/crypto/StreamCipher.java
Normal file
54
app/src/main/java/org/bouncycastle/crypto/StreamCipher.java
Normal 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();
|
||||
}
|
609
app/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
Normal file
609
app/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
138
app/src/main/java/org/bouncycastle/crypto/modes/AEADCipher.java
Normal file
138
app/src/main/java/org/bouncycastle/crypto/modes/AEADCipher.java
Normal 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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package org.bouncycastle.crypto.modes;
|
||||
|
||||
public interface CCMModeCipher
|
||||
extends AEADBlockCipher
|
||||
{
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
1227
app/src/main/java/org/bouncycastle/util/Arrays.java
Normal file
1227
app/src/main/java/org/bouncycastle/util/Arrays.java
Normal file
File diff suppressed because it is too large
Load Diff
14
app/src/main/java/org/bouncycastle/util/Objects.java
Normal file
14
app/src/main/java/org/bouncycastle/util/Objects.java
Normal 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();
|
||||
}
|
||||
}
|
40
app/src/main/java/org/bouncycastle/util/Pack.java
Normal file
40
app/src/main/java/org/bouncycastle/util/Pack.java
Normal 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);
|
||||
}
|
||||
}
|
38
app/src/main/java/org/bouncycastle/util/Strings.java
Normal file
38
app/src/main/java/org/bouncycastle/util/Strings.java
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user