
com.github.jinahya.kisa.aria.ARIAEngineProxy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kisa-aria Show documentation
Show all versions of kisa-aria Show documentation
ARIA from seed.kisa.or.kr
The newest version!
package com.github.jinahya.kisa.aria;
import javax.crypto.Cipher;
import javax.crypto.ShortBufferException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
public class ARIAEngineProxy {
private static final String NAME = "kr.re.nsri.aria.ARIAEngine";
private static final Class> CLASS;
private static final Constructor> CONSTRUCTOR;
private static final Method SET_KEY;
private static final Method SETUP_ROUND_KEYS;
private static final Method ENCRYPT;
private static final Method DECRYPT;
static {
try {
CLASS = Class.forName(NAME);
try {
CONSTRUCTOR = CLASS.getConstructor(int.class);
if (!CONSTRUCTOR.isAccessible()) {
CONSTRUCTOR.setAccessible(true);
}
} catch (final NoSuchMethodException nsme) {
throw new ExceptionInInitializerError(nsme);
}
try {
SET_KEY = CLASS.getDeclaredMethod("setKey", byte[].class);
if (!SET_KEY.isAccessible()) {
SET_KEY.setAccessible(true);
}
} catch (final NoSuchMethodException nsme) {
throw new ExceptionInInitializerError("unable to find setKey([B) from " + CLASS);
}
try {
SETUP_ROUND_KEYS = CLASS.getDeclaredMethod("setupRoundKeys");
if (!SETUP_ROUND_KEYS.isAccessible()) {
SETUP_ROUND_KEYS.setAccessible(true);
}
} catch (final NoSuchMethodException nsme) {
throw new ExceptionInInitializerError("unable to find setupRoundKeys() from " + CLASS);
}
try {
ENCRYPT = CLASS.getDeclaredMethod("encrypt", byte[].class, int.class, byte[].class, int.class);
if (!ENCRYPT.isAccessible()) {
ENCRYPT.setAccessible(true);
}
} catch (final NoSuchMethodException nsme) {
throw new ExceptionInInitializerError("unable to find encrypt([B, I, [B, I) from " + CLASS);
}
try {
DECRYPT = CLASS.getDeclaredMethod("decrypt", byte[].class, int.class, byte[].class, int.class);
if (!DECRYPT.isAccessible()) {
DECRYPT.setAccessible(true);
}
} catch (final NoSuchMethodException nsme) {
throw new ExceptionInInitializerError("unable to find decrypt([B, I, [B, I) from " + CLASS);
}
} catch (final ClassNotFoundException cnfe) {
throw new ExceptionInInitializerError(cnfe);
}
}
// -----------------------------------------------------------------------------------------------------------------
private static final Method BUFFER_POSITION;
static {
Method bufferPosition;
try {
bufferPosition = ByteBuffer.class.getMethod("position", int.class);
} catch (final NoSuchMethodException nsme) {
bufferPosition = null;
}
BUFFER_POSITION = bufferPosition;
}
// -----------------------------------------------------------------------------------------------------------------
static final int BLOCK_SIZE = 128;
static final int BLOCK_BYTES = BLOCK_SIZE / Byte.SIZE;
// -----------------------------------------------------------------------------------------------------------------
private static ARIAEngineProxy newInstance(final int mode, final byte[] key) throws InvalidKeyException {
if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE) {
throw new IllegalArgumentException(
"invalid mode: " + mode + ";" +
" not Cipher.ENCRYPT_MODE(" + Cipher.ENCRYPT_MODE + ")" +
" nor Cipher.DECRYPT_MODE(" + Cipher.DECRYPT_MODE + ")"
);
}
if (key == null) {
throw new NullPointerException("key is null");
}
final Object engine;
try {
engine = CONSTRUCTOR.newInstance(key.length * Byte.SIZE);
SET_KEY.invoke(engine, key);
SETUP_ROUND_KEYS.invoke(engine);
} catch (final InstantiationException ie) {
throw new RuntimeException(ie);
} catch (final IllegalAccessException iae) {
throw new RuntimeException(iae);
} catch (final InvocationTargetException ite) {
final Throwable cause = ite.getCause();
if (cause instanceof InvalidKeyException) {
throw (InvalidKeyException) cause;
}
throw new RuntimeException(ite);
}
if (mode == Cipher.ENCRYPT_MODE) {
return new ARIAEngineProxy(engine) {
@Override
public int decrypt(final byte[] input, final int inputOffset, final byte[] output,
final int outputOffset)
throws ShortBufferException {
throw new IllegalStateException("not initialized for decryption");
}
};
}
assert mode == Cipher.DECRYPT_MODE;
return new ARIAEngineProxy(engine) {
@Override
public int encrypt(final byte[] input, final int inputOffset, final byte[] output, final int outputOffset)
throws ShortBufferException {
throw new IllegalStateException("not initialized for encryption");
}
};
}
/**
* Creates a new instance, for encryption, with specified key.
*
* @param key the key.
* @return a new instance for encryption.
* @throws InvalidKeyException if {@code key}'s length is not {@code 128}, {@code 192}, nor {@code 256};
*/
public static ARIAEngineProxy newInstanceForEncryption(final byte[] key) throws InvalidKeyException {
return newInstance(Cipher.ENCRYPT_MODE, key);
}
/**
* Creates a new instance, for decryption, with specified key.
*
* @param key the key.
* @return a new instance for decryption.
* @throws InvalidKeyException if {@code key}'s length is not {@code 128}, {@code 192}, nor {@code 256};
*/
public static ARIAEngineProxy newInstanceForDecryption(final byte[] key) throws InvalidKeyException {
return newInstance(Cipher.DECRYPT_MODE, key);
}
// -----------------------------------------------------------------------------------------------------------------
private ARIAEngineProxy(final Object engine) {
super();
if (engine == null) {
throw new NullPointerException("engine is null");
}
this.engine = engine;
}
// -----------------------------------------------------------------------------------------------------------------
private void check(final byte[] input, final int inputOffset, final byte[] output, final int outputOffset)
throws ShortBufferException {
if (input == null) {
throw new NullPointerException("input is null");
}
if (inputOffset < 0) {
throw new IllegalArgumentException("inputOffset(" + inputOffset + ") is negative");
}
if (inputOffset > input.length) {
throw new IllegalArgumentException(
"inputOffset(" + inputOffset + ") > input.length(" + input.length + ")"
);
}
final int inputLength = input.length - inputOffset;
final int requiredBytes = inputLength / BLOCK_BYTES * BLOCK_BYTES;
if (output == null) {
throw new NullPointerException("output is null");
}
if (outputOffset < 0) {
throw new IllegalArgumentException("outputOffset(" + outputOffset + ") is negative");
}
if (outputOffset > output.length) {
throw new IllegalArgumentException(
"outputOffset(" + outputOffset + ") > output.length(" + output.length + ")"
);
}
final int outputLength = output.length - outputOffset;
if (outputLength < requiredBytes) {
throw new ShortBufferException("outputLength(" + outputLength + " < " + requiredBytes);
}
}
/**
* Encrypts bytes of specified input, starting at specified index, sets result on specified output, starting at
* specified index, and returns the number of bytes set.
*
* @param input the input to encrypt.
* @param inputOffset the starting index of the {@code input}.
* @param output the output on which results are set.
* @param outputOffset the starting index of the {@code output}.
* @return the number of bytes set on the {@code output} which is same as the number of bytes processed in
* {@code input}.
* @throws ShortBufferException when there is not enough space on the {@code output}.
*/
public int encrypt(final byte[] input, int inputOffset, final byte[] output, int outputOffset)
throws ShortBufferException {
check(input, inputOffset, output, outputOffset);
final int blocks = (input.length - inputOffset) / BLOCK_BYTES;
for (int b = 0; b < blocks; b++) {
try {
ENCRYPT.invoke(engine, input, inputOffset, output, outputOffset);
inputOffset += BLOCK_BYTES;
outputOffset += BLOCK_BYTES;
} catch (final IllegalAccessException iae) {
throw new RuntimeException("unable to encrypt", iae);
} catch (final InvocationTargetException ite) {
throw new RuntimeException("unable to encrypt", ite.getCause());
}
}
return blocks * BLOCK_BYTES;
}
/**
* Encrypts remaining bytes on specified input buffer, and puts result to specified output buffer.
*
* @param input the input buffer whose remaining bytes are encrypted.
* @param output the output buffer on which encrypted bytes are put.
* @throws ShortBufferException if there is no enough remaining on the {@code output}.
*/
public void encrypt(final ByteBuffer input, ByteBuffer output)
throws ShortBufferException {
if (input == null) {
throw new NullPointerException("input is null");
}
if (output == null) {
throw new NullPointerException("output is null");
}
if (false && BUFFER_POSITION != null && input.hasArray() && output.hasArray()) { // animal-sniffer
final int bytes = encrypt(input.array(), input.arrayOffset() + input.position(),
output.array(), output.arrayOffset() + output.position());
input.position(input.position() + bytes);
output.position(output.position() + bytes);
return;
}
final int blocks = input.remaining() / BLOCK_BYTES;
if (output.remaining() < blocks * BLOCK_BYTES) {
throw new ShortBufferException("not enough remaining on the output");
}
final byte[] i = new byte[BLOCK_BYTES];
final byte[] o = new byte[BLOCK_BYTES];
for (int b = 0; b < blocks; b++) {
input.get(i);
encrypt(i, 0, o, 0);
output.put(o);
}
}
/**
* Decrypts bytes of specified input, starting at specified index, sets result on specified output, starting at
* specified index, and returns the number of bytes set.
*
* @param input the input to encrypt.
* @param inputOffset the starting index of the {@code input}.
* @param output the output on which results are set.
* @param outputOffset the starting index of the {@code output}.
* @return the number of bytes set on the {@code output} which is same as the number of bytes processed in
* {@code input}.
* @throws ShortBufferException when there is not enough space on the {@code output}.
*/
public int decrypt(final byte[] input, int inputOffset, final byte[] output, int outputOffset)
throws ShortBufferException {
check(input, inputOffset, output, outputOffset);
final int blocks = (input.length - inputOffset) / BLOCK_BYTES;
for (int b = 0; b < blocks; b++) {
try {
DECRYPT.invoke(engine, input, inputOffset, output, outputOffset);
inputOffset += BLOCK_BYTES;
outputOffset += BLOCK_BYTES;
} catch (final IllegalAccessException iae) {
throw new RuntimeException("unable to decrypt", iae);
} catch (final InvocationTargetException ite) {
throw new RuntimeException("unable to decrypt", ite.getCause());
}
}
return blocks * BLOCK_BYTES;
}
/**
* Decrypts remaining bytes on specified input buffer, and puts result to specified output buffer.
*
* @param input the input buffer whose remaining bytes are decrypted.
* @param output the output buffer on which decrypted bytes are put.
* @throws ShortBufferException if there is no enough remaining on the {@code output}.
*/
public void decrypt(final ByteBuffer input, ByteBuffer output)
throws ShortBufferException {
if (input == null) {
throw new NullPointerException("input is null");
}
if (output == null) {
throw new NullPointerException("output is null");
}
if (false && BUFFER_POSITION != null && input.hasArray() && output.hasArray()) { // animal-sniffer
final int bytes = decrypt(input.array(), input.arrayOffset() + input.position(),
output.array(), output.arrayOffset() + output.position());
input.position(input.position() + bytes);
output.position(output.position() + bytes);
return;
}
final int blocks = input.remaining() / BLOCK_BYTES;
if (output.remaining() < blocks * BLOCK_BYTES) {
throw new ShortBufferException("not enough remaining on the output");
}
final byte[] i = new byte[BLOCK_BYTES];
final byte[] o = new byte[BLOCK_BYTES];
for (int b = 0; b < blocks; b++) {
input.get(i);
decrypt(i, 0, o, 0);
output.put(o);
}
}
// -----------------------------------------------------------------------------------------------------------------
private final Object engine;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy