/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) Martin Schoeberl <martin@jopdesign.com>
Thomas B. Preusser <thomas.preusser@tu-dresden.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package jembench.stream;
import jembench.StreamBenchmark;
import jembench.Runner;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.engines.AESLightEngine;
import java.util.Random;
/**
* Streaming benchmark with four processing stages:
*
* 1. Data Generation
* 2. Encryption
* 3. Decryption
* 4. Verification
*
* This benchmark can, by its nature, not scale beyond 4 independent Threads.
* The measured performance will typically scale even less due to
* platform-dependent imbalances of the computational complexities of the
* individual stages. Moreso, the serialized version running within a single
* Thread may even perform best on platforms with significant cache memory
* due to the improved locality. Thus, this benchmark also evaluates the
* performance of the inter-Thread communication.
*
* @author Thomas B. Preusser <thomas.preusser@tu-dresden.de>
* @author Martin Schoeberl <martin@jopdesign.com>
*/
public class AES extends StreamBenchmark {
private final static int BLOCK_SIZE = 128; // This MUST be a multiple of 16.
private final static int POOL_LENGTH = 8;
private final BufferQueue q1, q2, q3, free;
private final Source source;
private final Encrypt enc;
private final Decrypt dec;
private final Sink sink;
private final Runnable[] runners;
private int blockCnt;
private volatile boolean finished;
public AES() {
// Pool of Data Blocks
q1 = new BufferQueue(POOL_LENGTH);
q2 = new BufferQueue(POOL_LENGTH);
q3 = new BufferQueue(POOL_LENGTH);
free = new BufferQueue(POOL_LENGTH);
for (int i = 0; i < POOL_LENGTH; ++i) {
final byte[] block = new byte[BLOCK_SIZE];
free.checkedEnq(block);
}
final CipherParameters params; { // Encryption Parameters
final byte[] key = new byte[16];
final Random rnd = new Random(127);
for (int i = key.length; i > 0; key[--i] = (byte) rnd.nextInt());
params = new KeyParameter(key);
}
source = new Source();
enc = new Encrypt(params);
dec = new Decrypt(params);
sink = new Sink();
runners = new Runnable[] { source, enc, dec, sink };
}
public String toString() {
return "AES";
}
public Runnable[] getWorkers() {
return runners;
}
protected int getDepth() {
return 4;
}
public void reset(int cnt) {
finished = false;
source.reset();
enc .reset();
dec .reset();
sink .reset();
blockCnt = cnt;
}
public boolean isFinished() {
return finished;
}
private class Source implements Runnable {
private final Random rnd;
private int cnt;
public Source() {
rnd = new Random();
}
public void reset() {
rnd.setSeed(127);
cnt = 0;
}
public void run() {
if (cnt < blockCnt) {
if (!free.empty() && !q1.full()) {
final byte[] block = free.deq();
for (int i = 0; i < block.length; i++) {
block[i] = (byte)rnd.nextInt();
}
q1.enq(block);
++cnt;
}
}
}
}
private class Encrypt implements Runnable {
private final BlockCipher crypt;
private int cnt;
private byte[] ciph;
public Encrypt(CipherParameters params) {
crypt = new AESLightEngine();
crypt.init(true, params);
ciph = new byte[BLOCK_SIZE];
}
public void reset() {
crypt.reset();
cnt = 0;
}
public void run() {
if (cnt < blockCnt) {
if (!q1.empty() && !q2.full()) {
final byte[] block = q1.deq();
int ofs = BLOCK_SIZE;
do {
ofs -= crypt.getBlockSize();
crypt.processBlock(block, ofs, ciph, ofs);
}
while(ofs > 0);
q2.enq(ciph);
ciph = block;
++cnt;
}
}
}
}
private class Decrypt implements Runnable {
private final BlockCipher decrypt;
private int cnt;
private byte[] deciph;
public Decrypt(CipherParameters params) {
decrypt = new AESLightEngine();
decrypt.init(false, params);
deciph = new byte[BLOCK_SIZE];
}
public void reset() {
decrypt.reset();
cnt = 0;
}
public void run() {
if (cnt < blockCnt) {
if (!q2.empty() && !q3.full()) {
final byte[] block = q2.deq();
int ofs = BLOCK_SIZE;
do {
ofs -= decrypt.getBlockSize();
decrypt.processBlock(block, ofs, deciph, ofs);
}
while(ofs > 0);
q3.enq(deciph);
deciph = block;
++cnt;
}
}
}
}
private class Sink implements Runnable {
private final Random rnd;
private int cnt;
private boolean ok;
public Sink() {
rnd = new Random();
}
public void reset() {
rnd.setSeed(127);
cnt = 0;
ok = true;
}
public void run() {
if (cnt < blockCnt) {
if (!q3.empty() && !free.full()) {
final byte[] block = q3.deq();
for (int i = 0; i < block.length; i++) {
if(block[i] != (byte)rnd.nextInt()) ok = false;
}
free.enq(block);
++cnt;
}
} else {
finished = true;
Runner.stop();
if(!ok) System.err.println("AES failed.");
}
}
}
}