diff --git a/README.md b/README.md index 8fbef8cd7..af4612f27 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/app/build.gradle b/app/build.gradle index 37842e998..e7cd7c641 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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') { diff --git a/app/src/main/java/org/bouncycastle/crypto/BlockCipher.java b/app/src/main/java/org/bouncycastle/crypto/BlockCipher.java new file mode 100644 index 000000000..370225f65 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/BlockCipher.java @@ -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(); +} diff --git a/app/src/main/java/org/bouncycastle/crypto/CipherParameters.java b/app/src/main/java/org/bouncycastle/crypto/CipherParameters.java new file mode 100644 index 000000000..5be873047 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/CipherParameters.java @@ -0,0 +1,8 @@ +package org.bouncycastle.crypto; + +/** + * all parameter classes implement this. + */ +public interface CipherParameters +{ +} diff --git a/app/src/main/java/org/bouncycastle/crypto/CryptoException.java b/app/src/main/java/org/bouncycastle/crypto/CryptoException.java new file mode 100644 index 000000000..352c5569b --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/CryptoException.java @@ -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; + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/DataLengthException.java b/app/src/main/java/org/bouncycastle/crypto/DataLengthException.java new file mode 100644 index 000000000..fbf047cf5 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/DataLengthException.java @@ -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); + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java b/app/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java new file mode 100644 index 000000000..3bc565cf0 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java @@ -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; + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java b/app/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java new file mode 100644 index 000000000..21c150d96 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java @@ -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); + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/Mac.java b/app/src/main/java/org/bouncycastle/crypto/Mac.java new file mode 100644 index 000000000..c00cd58cc --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/Mac.java @@ -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. + *

+ * 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(); +} diff --git a/app/src/main/java/org/bouncycastle/crypto/MultiBlockCipher.java b/app/src/main/java/org/bouncycastle/crypto/MultiBlockCipher.java new file mode 100644 index 000000000..dad402036 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/MultiBlockCipher.java @@ -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; +} diff --git a/app/src/main/java/org/bouncycastle/crypto/OutputLengthException.java b/app/src/main/java/org/bouncycastle/crypto/OutputLengthException.java new file mode 100644 index 000000000..62811a2b5 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/OutputLengthException.java @@ -0,0 +1,10 @@ +package org.bouncycastle.crypto; + +public class OutputLengthException + extends DataLengthException +{ + public OutputLengthException(String msg) + { + super(msg); + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java b/app/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java new file mode 100644 index 000000000..c1572020b --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java @@ -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); + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/SkippingCipher.java b/app/src/main/java/org/bouncycastle/crypto/SkippingCipher.java new file mode 100644 index 000000000..f8cc648eb --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/SkippingCipher.java @@ -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(); +} diff --git a/app/src/main/java/org/bouncycastle/crypto/SkippingStreamCipher.java b/app/src/main/java/org/bouncycastle/crypto/SkippingStreamCipher.java new file mode 100644 index 000000000..a707a8108 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/SkippingStreamCipher.java @@ -0,0 +1,9 @@ +package org.bouncycastle.crypto; + +/** + * General interface for a stream cipher that supports skipping. + */ +public interface SkippingStreamCipher + extends StreamCipher, SkippingCipher +{ +} diff --git a/app/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java b/app/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java new file mode 100644 index 000000000..b1702fe7f --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java @@ -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); +} \ No newline at end of file diff --git a/app/src/main/java/org/bouncycastle/crypto/StreamCipher.java b/app/src/main/java/org/bouncycastle/crypto/StreamCipher.java new file mode 100644 index 000000000..c1255e941 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/StreamCipher.java @@ -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(); +} diff --git a/app/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java b/app/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java new file mode 100644 index 000000000..6b3690732 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java @@ -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. + *

+ * For further details see: https://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * https://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * 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. + *

+ * 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; + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java b/app/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java new file mode 100644 index 000000000..908780735 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java @@ -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. + *

+ * 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. + *

+ * 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(); + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java b/app/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java new file mode 100644 index 000000000..b8c7ad512 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java @@ -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(); +} diff --git a/app/src/main/java/org/bouncycastle/crypto/modes/AEADCipher.java b/app/src/main/java/org/bouncycastle/crypto/modes/AEADCipher.java new file mode 100644 index 000000000..4e49a5a98 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/modes/AEADCipher.java @@ -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. + *

+ * 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)}. + *

+ * 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. + *
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. + *
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. + *

+ * 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. + *

+ * + * @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. + *

+ * 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)}. + *

+ * @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(); +} diff --git a/app/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/app/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java new file mode 100644 index 000000000..42cf511bc --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java @@ -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; + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/modes/CBCModeCipher.java b/app/src/main/java/org/bouncycastle/crypto/modes/CBCModeCipher.java new file mode 100644 index 000000000..682b5807c --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/modes/CBCModeCipher.java @@ -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(); +} diff --git a/app/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/app/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java new file mode 100644 index 000000000..1ac255bb8 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java @@ -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. + *

+ * Note: 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; + } + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java b/app/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java new file mode 100644 index 000000000..d96ac05ae --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java @@ -0,0 +1,6 @@ +package org.bouncycastle.crypto.modes; + +public interface CCMModeCipher + extends AEADBlockCipher +{ +} diff --git a/app/src/main/java/org/bouncycastle/crypto/modes/CTRModeCipher.java b/app/src/main/java/org/bouncycastle/crypto/modes/CTRModeCipher.java new file mode 100644 index 000000000..13fd97e74 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/modes/CTRModeCipher.java @@ -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(); +} diff --git a/app/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java b/app/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java new file mode 100644 index 000000000..8b2013635 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java @@ -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; + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java b/app/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java new file mode 100644 index 000000000..7c4f0aee3 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java @@ -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. + *

+ * 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. + *

+ */ + 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; +} diff --git a/app/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java b/app/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java new file mode 100644 index 000000000..c06481591 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java @@ -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); + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java b/app/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java new file mode 100644 index 000000000..e16399595 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java @@ -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; + } +} diff --git a/app/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java b/app/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java new file mode 100644 index 000000000..4a1e6e9a3 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java @@ -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; + } +} diff --git a/app/src/main/java/org/bouncycastle/util/Arrays.java b/app/src/main/java/org/bouncycastle/util/Arrays.java new file mode 100644 index 000000000..230520eaf --- /dev/null +++ b/app/src/main/java/org/bouncycastle/util/Arrays.java @@ -0,0 +1,1227 @@ +package org.bouncycastle.util; + +import java.math.BigInteger; +import java.util.NoSuchElementException; + +/** + * General array utilities. + */ +public final class Arrays +{ + private Arrays() + { + // static class, hide constructor + } + + public static boolean areAllZeroes(byte[] buf, int off, int len) + { + int bits = 0; + for (int i = 0; i < len; ++i) + { + bits |= buf[off + i]; + } + return bits == 0; + } + + public static boolean areEqual(boolean[] a, boolean[] b) + { + return java.util.Arrays.equals(a, b); + } + + public static boolean areEqual(byte[] a, byte[] b) + { + return java.util.Arrays.equals(a, b); + } + + public static boolean areEqual(byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex) + { + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + + if (aLength != bLength) + { + return false; + } + + for (int i = 0; i < aLength; ++i) + { + if (a[aFromIndex + i] != b[bFromIndex + i]) + { + return false; + } + } + + return true; + } + + public static boolean areEqual(char[] a, char[] b) + { + return java.util.Arrays.equals(a, b); + } + + public static boolean areEqual(int[] a, int[] b) + { + return java.util.Arrays.equals(a, b); + } + + public static boolean areEqual(long[] a, long[] b) + { + return java.util.Arrays.equals(a, b); + } + + public static boolean areEqual(Object[] a, Object[] b) + { + return java.util.Arrays.equals(a, b); + } + + public static boolean areEqual(short[] a, short[] b) + { + return java.util.Arrays.equals(a, b); + } + + /** + * A constant time equals comparison - does not terminate early if + * test will fail. For best results always pass the expected value + * as the first parameter. + * + * @param expected first array + * @param supplied second array + * @return true if arrays equal, false otherwise. + */ + public static boolean constantTimeAreEqual( + byte[] expected, + byte[] supplied) + { + if (expected == null || supplied == null) + { + return false; + } + + if (expected == supplied) + { + return true; + } + + int len = (expected.length < supplied.length) ? expected.length : supplied.length; + + int nonEqual = expected.length ^ supplied.length; + + for (int i = 0; i != len; i++) + { + nonEqual |= (expected[i] ^ supplied[i]); + } + for (int i = len; i < supplied.length; i++) + { + nonEqual |= (supplied[i] ^ ~supplied[i]); + } + + return nonEqual == 0; + } + + public static boolean constantTimeAreEqual(int len, byte[] a, int aOff, byte[] b, int bOff) + { + if (null == a) + { + throw new NullPointerException("'a' cannot be null"); + } + if (null == b) + { + throw new NullPointerException("'b' cannot be null"); + } + if (len < 0) + { + throw new IllegalArgumentException("'len' cannot be negative"); + } + if (aOff > (a.length - len)) + { + throw new IndexOutOfBoundsException("'aOff' value invalid for specified length"); + } + if (bOff > (b.length - len)) + { + throw new IndexOutOfBoundsException("'bOff' value invalid for specified length"); + } + + int d = 0; + for (int i = 0; i < len; ++i) + { + d |= (a[aOff + i] ^ b[bOff + i]); + } + return 0 == d; + } + + /** + * A constant time equals comparison - does not terminate early if + * comparison fails. For best results always pass the expected value + * as the first parameter. + * + * @param expected first array + * @param supplied second array + * @return true if arrays equal, false otherwise. + */ + public static boolean constantTimeAreEqual( + char[] expected, + char[] supplied) + { + if (expected == null || supplied == null) + { + return false; + } + + if (expected == supplied) + { + return true; + } + + int len = Math.min(expected.length, supplied.length); + + int nonEqual = expected.length ^ supplied.length; + + // do the char-wise comparison + for (int i = 0; i != len; i++) + { + nonEqual |= (expected[i] ^ supplied[i]); + } + // If supplied is longer than expected, iterate over rest of supplied with NOPs + for (int i = len; i < supplied.length; i++) + { + nonEqual |= ((byte)supplied[i] ^ (byte)~supplied[i]); + } + + return nonEqual == 0; + } + + public static int compareUnsigned(byte[] a, byte[] b) + { + if (a == b) + { + return 0; + } + if (a == null) + { + return -1; + } + if (b == null) + { + return 1; + } + int minLen = Math.min(a.length, b.length); + for (int i = 0; i < minLen; ++i) + { + int aVal = a[i] & 0xFF, bVal = b[i] & 0xFF; + if (aVal < bVal) + { + return -1; + } + if (aVal > bVal) + { + return 1; + } + } + if (a.length < b.length) + { + return -1; + } + if (a.length > b.length) + { + return 1; + } + return 0; + } + + public static boolean contains(boolean[] a, boolean val) + { + for (int i = 0; i < a.length; ++i) + { + if (a[i] == val) + { + return true; + } + } + return false; + } + + public static boolean contains(byte[] a, byte val) + { + for (int i = 0; i < a.length; ++i) + { + if (a[i] == val) + { + return true; + } + } + return false; + } + + public static boolean contains(char[] a, char val) + { + for (int i = 0; i < a.length; ++i) + { + if (a[i] == val) + { + return true; + } + } + return false; + } + + public static boolean contains(int[] a, int val) + { + for (int i = 0; i < a.length; ++i) + { + if (a[i] == val) + { + return true; + } + } + return false; + } + + public static boolean contains(long[] a, long val) + { + for (int i = 0; i < a.length; ++i) + { + if (a[i] == val) + { + return true; + } + } + return false; + } + + public static boolean contains(short[] a, short val) + { + for (int i = 0; i < a.length; ++i) + { + if (a[i] == val) + { + return true; + } + } + return false; + } + + public static void fill(boolean[] a, boolean val) + { + java.util.Arrays.fill(a, val); + } + + public static void fill(boolean[] a, int fromIndex, int toIndex, boolean val) + { + java.util.Arrays.fill(a, fromIndex, toIndex, val); + } + + public static void fill(byte[] a, byte val) + { + java.util.Arrays.fill(a, val); + } + + public static void fill(byte[] a, int fromIndex, int toIndex, byte val) + { + java.util.Arrays.fill(a, fromIndex, toIndex, val); + } + + public static void fill(char[] a, char val) + { + java.util.Arrays.fill(a, val); + } + + public static void fill(char[] a, int fromIndex, int toIndex, char val) + { + java.util.Arrays.fill(a, fromIndex, toIndex, val); + } + + public static void fill(int[] a, int val) + { + java.util.Arrays.fill(a, val); + } + + public static void fill(int[] a, int fromIndex, int toIndex, int val) + { + java.util.Arrays.fill(a, fromIndex, toIndex, val); + } + + public static void fill(long[] a, long val) + { + java.util.Arrays.fill(a, val); + } + + public static void fill(long[] a, int fromIndex, int toIndex, long val) + { + java.util.Arrays.fill(a, fromIndex, toIndex, val); + } + + public static void fill(Object[] a, Object val) + { + java.util.Arrays.fill(a, val); + } + + public static void fill(Object[] a, int fromIndex, int toIndex, Object val) + { + java.util.Arrays.fill(a, fromIndex, toIndex, val); + } + + public static void fill(short[] a, short val) + { + java.util.Arrays.fill(a, val); + } + + public static void fill(short[] a, int fromIndex, int toIndex, short val) + { + java.util.Arrays.fill(a, fromIndex, toIndex, val); + } + + public static int hashCode(byte[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i]; + } + + return hc; + } + + public static int hashCode(byte[] data, int off, int len) + { + if (data == null) + { + return 0; + } + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[off + i]; + } + + return hc; + } + + public static int hashCode(char[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i]; + } + + return hc; + } + + public static int hashCode(int[][] ints) + { + int hc = 0; + + for (int i = 0; i != ints.length; i++) + { + hc = hc * 257 + hashCode(ints[i]); + } + + return hc; + } + + public static int hashCode(int[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i]; + } + + return hc; + } + + public static int hashCode(int[] data, int off, int len) + { + if (data == null) + { + return 0; + } + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[off + i]; + } + + return hc; + } + + public static int hashCode(long[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + long di = data[i]; + hc *= 257; + hc ^= (int)di; + hc *= 257; + hc ^= (int)(di >>> 32); + } + + return hc; + } + + public static int hashCode(long[] data, int off, int len) + { + if (data == null) + { + return 0; + } + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + long di = data[off + i]; + hc *= 257; + hc ^= (int)di; + hc *= 257; + hc ^= (int)(di >>> 32); + } + + return hc; + } + + public static int hashCode(short[][][] shorts) + { + int hc = 0; + + for (int i = 0; i != shorts.length; i++) + { + hc = hc * 257 + hashCode(shorts[i]); + } + + return hc; + } + + public static int hashCode(short[][] shorts) + { + int hc = 0; + + for (int i = 0; i != shorts.length; i++) + { + hc = hc * 257 + hashCode(shorts[i]); + } + + return hc; + } + + public static int hashCode(short[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= (data[i] & 0xff); + } + + return hc; + } + + public static int hashCode(Object[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= Objects.hashCode(data[i]); + } + + return hc; + } + + public static boolean[] clone(boolean[] data) + { + return null == data ? null : data.clone(); + } + + public static byte[] clone(byte[] data) + { + return null == data ? null : data.clone(); + } + + public static char[] clone(char[] data) + { + return null == data ? null : data.clone(); + } + + public static int[] clone(int[] data) + { + return null == data ? null : data.clone(); + } + + public static long[] clone(long[] data) + { + return null == data ? null : data.clone(); + } + + public static short[] clone(short[] data) + { + return null == data ? null : data.clone(); + } + + public static BigInteger[] clone(BigInteger[] data) + { + return null == data ? null : data.clone(); + } + + public static byte[] clone(byte[] data, byte[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.length != data.length)) + { + return clone(data); + } + System.arraycopy(data, 0, existing, 0, existing.length); + return existing; + } + + public static long[] clone(long[] data, long[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.length != data.length)) + { + return clone(data); + } + System.arraycopy(data, 0, existing, 0, existing.length); + return existing; + } + + public static byte[][] clone(byte[][] data) + { + if (data == null) + { + return null; + } + + byte[][] copy = new byte[data.length][]; + + for (int i = 0; i != copy.length; i++) + { + copy[i] = clone(data[i]); + } + + return copy; + } + + public static byte[][][] clone(byte[][][] data) + { + if (data == null) + { + return null; + } + + byte[][][] copy = new byte[data.length][][]; + + for (int i = 0; i != copy.length; i++) + { + copy[i] = clone(data[i]); + } + + return copy; + } + + public static boolean[] copyOf(boolean[] original, int newLength) + { + boolean[] copy = new boolean[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + public static byte[] copyOf(byte[] original, int newLength) + { + byte[] copy = new byte[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + public static char[] copyOf(char[] original, int newLength) + { + char[] copy = new char[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + public static int[] copyOf(int[] original, int newLength) + { + int[] copy = new int[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + public static long[] copyOf(long[] original, int newLength) + { + long[] copy = new long[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + public static short[] copyOf(short[] original, int newLength) + { + short[] copy = new short[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + public static BigInteger[] copyOf(BigInteger[] original, int newLength) + { + BigInteger[] copy = new BigInteger[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + public static boolean[] copyOfRange(boolean[] original, int from, int to) + { + int newLength = getLength(from, to); + boolean[] copy = new boolean[newLength]; + System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); + return copy; + } + + /** + * Make a copy of a range of bytes from the passed in array. The range can extend beyond the end + * of the input array, in which case the returned array will be padded with zeroes. + * + * @param original + * the array from which the data is to be copied. + * @param from + * the start index at which the copying should take place. + * @param to + * the final index of the range (exclusive). + * + * @return a new byte array containing the range given. + */ + public static byte[] copyOfRange(byte[] original, int from, int to) + { + int newLength = getLength(from, to); + byte[] copy = new byte[newLength]; + System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); + return copy; + } + + public static char[] copyOfRange(char[] original, int from, int to) + { + int newLength = getLength(from, to); + char[] copy = new char[newLength]; + System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); + return copy; + } + + public static int[] copyOfRange(int[] original, int from, int to) + { + int newLength = getLength(from, to); + int[] copy = new int[newLength]; + System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); + return copy; + } + + public static long[] copyOfRange(long[] original, int from, int to) + { + int newLength = getLength(from, to); + long[] copy = new long[newLength]; + System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); + return copy; + } + + public static short[] copyOfRange(short[] original, int from, int to) + { + int newLength = getLength(from, to); + short[] copy = new short[newLength]; + System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); + return copy; + } + + public static BigInteger[] copyOfRange(BigInteger[] original, int from, int to) + { + int newLength = getLength(from, to); + BigInteger[] copy = new BigInteger[newLength]; + System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); + return copy; + } + + private static int getLength(int from, int to) + { + int newLength = to - from; + if (newLength < 0) + { + StringBuffer sb = new StringBuffer(from); + sb.append(" > ").append(to); + throw new IllegalArgumentException(sb.toString()); + } + return newLength; + } + + public static byte[] append(byte[] a, byte b) + { + if (a == null) + { + return new byte[]{ b }; + } + + int length = a.length; + byte[] result = new byte[length + 1]; + System.arraycopy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static short[] append(short[] a, short b) + { + if (a == null) + { + return new short[]{ b }; + } + + int length = a.length; + short[] result = new short[length + 1]; + System.arraycopy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static int[] append(int[] a, int b) + { + if (a == null) + { + return new int[]{ b }; + } + + int length = a.length; + int[] result = new int[length + 1]; + System.arraycopy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static String[] append(String[] a, String b) + { + if (a == null) + { + return new String[]{ b }; + } + + int length = a.length; + String[] result = new String[length + 1]; + System.arraycopy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static byte[] concatenate(byte[] a, byte[] b) + { + if (null == a) + { + // b might also be null + return clone(b); + } + if (null == b) + { + // a might also be null + return clone(a); + } + + byte[] r = new byte[a.length + b.length]; + System.arraycopy(a, 0, r, 0, a.length); + System.arraycopy(b, 0, r, a.length, b.length); + return r; + } + + public static short[] concatenate(short[] a, short[] b) + { + if (null == a) + { + // b might also be null + return clone(b); + } + if (null == b) + { + // a might also be null + return clone(a); + } + + short[] r = new short[a.length + b.length]; + System.arraycopy(a, 0, r, 0, a.length); + System.arraycopy(b, 0, r, a.length, b.length); + return r; + } + + public static byte[] concatenate(byte[] a, byte[] b, byte[] c) + { + if (null == a) + { + return concatenate(b, c); + } + if (null == b) + { + return concatenate(a, c); + } + if (null == c) + { + return concatenate(a, b); + } + + byte[] r = new byte[a.length + b.length + c.length]; + int pos = 0; + System.arraycopy(a, 0, r, pos, a.length); pos += a.length; + System.arraycopy(b, 0, r, pos, b.length); pos += b.length; + System.arraycopy(c, 0, r, pos, c.length); + return r; + } + + public static byte[] concatenate(byte[] a, byte[] b, byte[] c, byte[] d) + { + if (null == a) + { + return concatenate(b, c, d); + } + if (null == b) + { + return concatenate(a, c, d); + } + if (null == c) + { + return concatenate(a, b, d); + } + if (null == d) + { + return concatenate(a, b, c); + } + + byte[] r = new byte[a.length + b.length + c.length + d.length]; + int pos = 0; + System.arraycopy(a, 0, r, pos, a.length); pos += a.length; + System.arraycopy(b, 0, r, pos, b.length); pos += b.length; + System.arraycopy(c, 0, r, pos, c.length); pos += c.length; + System.arraycopy(d, 0, r, pos, d.length); + return r; + } + + public static byte[] concatenate(byte[][] arrays) + { + int size = 0; + for (int i = 0; i != arrays.length; i++) + { + size += arrays[i].length; + } + + byte[] rv = new byte[size]; + + int offSet = 0; + for (int i = 0; i != arrays.length; i++) + { + System.arraycopy(arrays[i], 0, rv, offSet, arrays[i].length); + offSet += arrays[i].length; + } + + return rv; + } + + public static int[] concatenate(int[] a, int[] b) + { + if (null == a) + { + // b might also be null + return clone(b); + } + if (null == b) + { + // a might also be null + return clone(a); + } + + int[] r = new int[a.length + b.length]; + System.arraycopy(a, 0, r, 0, a.length); + System.arraycopy(b, 0, r, a.length, b.length); + return r; + } + + public static byte[] prepend(byte[] a, byte b) + { + if (a == null) + { + return new byte[]{ b }; + } + + int length = a.length; + byte[] result = new byte[length + 1]; + System.arraycopy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static short[] prepend(short[] a, short b) + { + if (a == null) + { + return new short[]{ b }; + } + + int length = a.length; + short[] result = new short[length + 1]; + System.arraycopy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static int[] prepend(int[] a, int b) + { + if (a == null) + { + return new int[]{ b }; + } + + int length = a.length; + int[] result = new int[length + 1]; + System.arraycopy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static byte[] reverse(byte[] a) + { + if (a == null) + { + return null; + } + + int p1 = 0, p2 = a.length; + byte[] result = new byte[p2]; + + while (--p2 >= 0) + { + result[p2] = a[p1++]; + } + + return result; + } + + public static int[] reverse(int[] a) + { + if (a == null) + { + return null; + } + + int p1 = 0, p2 = a.length; + int[] result = new int[p2]; + + while (--p2 >= 0) + { + result[p2] = a[p1++]; + } + + return result; + } + + public static void reverse(byte[] input, byte[] output) + { + int last = input.length - 1; + for (int i = 0; i <= last; ++i) + { + output[i] = input[last - i]; + } + } + + public static byte[] reverseInPlace(byte[] a) + { + if (null == a) + { + return null; + } + + int p1 = 0, p2 = a.length - 1; + while (p1 < p2) + { + byte t1 = a[p1], t2 = a[p2]; + a[p1++] = t2; + a[p2--] = t1; + } + + return a; + } + + public static void reverseInPlace(byte[] a, int aOff, int aLen) + { + int p1 = aOff, p2 = aOff + aLen - 1; + while (p1 < p2) + { + byte t1 = a[p1], t2 = a[p2]; + a[p1++] = t2; + a[p2--] = t1; + } + } + + public static int[] reverseInPlace(int[] a) + { + if (null == a) + { + return null; + } + + int p1 = 0, p2 = a.length - 1; + while (p1 < p2) + { + int t1 = a[p1], t2 = a[p2]; + a[p1++] = t2; + a[p2--] = t1; + } + + return a; + } + + /** + * Iterator backed by a specific array. + */ + public static class Iterator + implements java.util.Iterator + { + private final T[] dataArray; + + private int position = 0; + + /** + * Base constructor. + *

+ * Note: the array is not cloned, changes to it will affect the values returned by next(). + *

+ * + * @param dataArray array backing the iterator. + */ + public Iterator(T[] dataArray) + { + this.dataArray = dataArray; + } + + public boolean hasNext() + { + return position < dataArray.length; + } + + public T next() + { + if (position == dataArray.length) + { + throw new NoSuchElementException("Out of elements: " + position); + } + + return dataArray[position++]; + } + + public void remove() + { + throw new UnsupportedOperationException("Cannot remove element from an Array."); + } + } + + /** + * Fill input array by zeros + * + * @param data input array + */ + public static void clear(byte[] data) + { + if (null != data) + { + java.util.Arrays.fill(data, (byte)0x00); + } + } + + public static void clear(int[] data) + { + if (null != data) + { + java.util.Arrays.fill(data, 0); + } + } + + public static boolean isNullOrContainsNull(Object[] array) + { + if (null == array) + { + return true; + } + int count = array.length; + for (int i = 0; i < count; ++i) + { + if (null == array[i]) + { + return true; + } + } + return false; + } + + public static boolean isNullOrEmpty(byte[] array) + { + return null == array || array.length < 1; + } + + public static boolean isNullOrEmpty(int[] array) + { + return null == array || array.length < 1; + } + + public static boolean isNullOrEmpty(Object[] array) + { + return null == array || array.length < 1; + } +} diff --git a/app/src/main/java/org/bouncycastle/util/Objects.java b/app/src/main/java/org/bouncycastle/util/Objects.java new file mode 100644 index 000000000..9ea2ff36c --- /dev/null +++ b/app/src/main/java/org/bouncycastle/util/Objects.java @@ -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(); + } +} diff --git a/app/src/main/java/org/bouncycastle/util/Pack.java b/app/src/main/java/org/bouncycastle/util/Pack.java new file mode 100644 index 000000000..525f1e940 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/util/Pack.java @@ -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); + } +} diff --git a/app/src/main/java/org/bouncycastle/util/Strings.java b/app/src/main/java/org/bouncycastle/util/Strings.java new file mode 100644 index 000000000..7f2d81b66 --- /dev/null +++ b/app/src/main/java/org/bouncycastle/util/Strings.java @@ -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() { // 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; + } +}