Improved encapsulation of thread synchronisation as follows

- replaced use of Object instance mutex with a private final Lock object
- replaced Object signaling with specific condition signalling
This commit is contained in:
Abraham Kiggundu
2014-12-23 23:55:56 +03:00
parent 276dcb1038
commit b074978472
19 changed files with 1001 additions and 478 deletions

View File

@@ -1,5 +1,8 @@
package org.briarproject.crypto;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.briarproject.api.crypto.MessageDigest;
import org.spongycastle.crypto.BlockCipher;
import org.spongycastle.crypto.digests.SHA256Digest;
@@ -23,61 +26,89 @@ class FortunaGenerator {
private final byte[] counter = new byte[BLOCK_BYTES];
private final byte[] buffer = new byte[BLOCK_BYTES];
private final byte[] newKey = new byte[KEY_BYTES];
private final Lock synchLock = new ReentrantLock();
FortunaGenerator(byte[] seed) {
reseed(seed);
}
synchronized void reseed(byte[] seed) {
digest.update(key);
digest.update(seed);
digest.digest(key, 0, KEY_BYTES);
incrementCounter();
void reseed(byte[] seed) {
synchLock.lock();
try{
digest.update(key);
digest.update(seed);
digest.digest(key, 0, KEY_BYTES);
incrementCounter();
}
finally{
synchLock.unlock();
}
}
// Package access for testing
synchronized void incrementCounter() {
counter[0]++;
for(int i = 0; counter[i] == 0; i++) {
if(i + 1 == BLOCK_BYTES)
throw new RuntimeException("Counter exhausted");
counter[i + 1]++;
void incrementCounter() {
synchLock.lock();
try{
counter[0]++;
for(int i = 0; counter[i] == 0; i++) {
if(i + 1 == BLOCK_BYTES)
throw new RuntimeException("Counter exhausted");
counter[i + 1]++;
}
}
}
finally{
synchLock.unlock();
}
}
// Package access for testing
synchronized byte[] getCounter() {
return counter;
byte[] getCounter() {
synchLock.lock();
try{
return counter;
}
finally{
synchLock.unlock();
}
}
synchronized int nextBytes(byte[] dest, int off, int len) {
// Don't write more than the maximum number of bytes in one request
if(len > MAX_BYTES_PER_REQUEST) len = MAX_BYTES_PER_REQUEST;
cipher.init(true, new KeyParameter(key));
// Generate full blocks directly into the output buffer
int fullBlocks = len / BLOCK_BYTES;
for(int i = 0; i < fullBlocks; i++) {
cipher.processBlock(counter, 0, dest, off + i * BLOCK_BYTES);
incrementCounter();
int nextBytes(byte[] dest, int off, int len) {
synchLock.lock();
try{
// Don't write more than the maximum number of bytes in one request
if(len > MAX_BYTES_PER_REQUEST) len = MAX_BYTES_PER_REQUEST;
cipher.init(true, new KeyParameter(key));
// Generate full blocks directly into the output buffer
int fullBlocks = len / BLOCK_BYTES;
for(int i = 0; i < fullBlocks; i++) {
cipher.processBlock(counter, 0, dest, off + i * BLOCK_BYTES);
incrementCounter();
}
// Generate a partial block if needed
int done = fullBlocks * BLOCK_BYTES, remaining = len - done;
assert remaining < BLOCK_BYTES;
if(remaining > 0) {
cipher.processBlock(counter, 0, buffer, 0);
incrementCounter();
// Copy the partial block to the output buffer and erase our copy
System.arraycopy(buffer, 0, dest, off + done, remaining);
for(int i = 0; i < BLOCK_BYTES; i++) buffer[i] = 0;
}
// Generate a new key
for(int i = 0; i < KEY_BYTES / BLOCK_BYTES; i++) {
cipher.processBlock(counter, 0, newKey, i * BLOCK_BYTES);
incrementCounter();
}
System.arraycopy(newKey, 0, key, 0, KEY_BYTES);
for(int i = 0; i < KEY_BYTES; i++) newKey[i] = 0;
// Return the number of bytes written
return len;
}
// Generate a partial block if needed
int done = fullBlocks * BLOCK_BYTES, remaining = len - done;
assert remaining < BLOCK_BYTES;
if(remaining > 0) {
cipher.processBlock(counter, 0, buffer, 0);
incrementCounter();
// Copy the partial block to the output buffer and erase our copy
System.arraycopy(buffer, 0, dest, off + done, remaining);
for(int i = 0; i < BLOCK_BYTES; i++) buffer[i] = 0;
finally{
synchLock.unlock();
}
// Generate a new key
for(int i = 0; i < KEY_BYTES / BLOCK_BYTES; i++) {
cipher.processBlock(counter, 0, newKey, i * BLOCK_BYTES);
incrementCounter();
}
System.arraycopy(newKey, 0, key, 0, KEY_BYTES);
for(int i = 0; i < KEY_BYTES; i++) newKey[i] = 0;
// Return the number of bytes written
return len;
}
}

View File

@@ -1,5 +1,8 @@
package org.briarproject.crypto;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.briarproject.api.crypto.MessageDigest;
import org.briarproject.api.crypto.PseudoRandom;
import org.briarproject.util.ByteUtils;
@@ -10,6 +13,8 @@ class PseudoRandomImpl implements PseudoRandom {
private byte[] state;
private int offset;
private final Lock synchLock = new ReentrantLock();
PseudoRandomImpl(MessageDigest messageDigest, int seed1, int seed2) {
this.messageDigest = messageDigest;
@@ -21,21 +26,27 @@ class PseudoRandomImpl implements PseudoRandom {
offset = 0;
}
public synchronized byte[] nextBytes(int bytes) {
byte[] b = new byte[bytes];
int half = state.length / 2;
int off = 0, len = b.length, available = half - offset;
while(available < len) {
System.arraycopy(state, offset, b, off, available);
off += available;
len -= available;
messageDigest.update(state, half, half);
state = messageDigest.digest();
offset = 0;
available = half;
public byte[] nextBytes(int bytes) {
synchLock.lock();
try{
byte[] b = new byte[bytes];
int half = state.length / 2;
int off = 0, len = b.length, available = half - offset;
while(available < len) {
System.arraycopy(state, offset, b, off, available);
off += available;
len -= available;
messageDigest.update(state, half, half);
state = messageDigest.digest();
offset = 0;
available = half;
}
System.arraycopy(state, offset, b, off, len);
offset += len;
return b;
}
finally{
synchLock.unlock();
}
System.arraycopy(state, offset, b, off, len);
offset += len;
return b;
}
}

View File

@@ -1,5 +1,8 @@
package org.briarproject.crypto;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.util.ByteUtils;
@@ -8,23 +11,38 @@ class SecretKeyImpl implements SecretKey {
private final byte[] key;
private boolean erased = false; // Locking: this
private final Lock synchLock = new ReentrantLock();
SecretKeyImpl(byte[] key) {
this.key = key;
}
public synchronized byte[] getEncoded() {
if(erased) throw new IllegalStateException();
return key;
public byte[] getEncoded() {
synchLock.lock();
try{
if(erased) throw new IllegalStateException();
return key;
}
finally{
synchLock.unlock();
}
}
public SecretKey copy() {
return new SecretKeyImpl(key.clone());
}
public synchronized void erase() {
if(erased) throw new IllegalStateException();
ByteUtils.erase(key);
erased = true;
public void erase() {
synchLock.lock();
try{
if(erased) throw new IllegalStateException();
ByteUtils.erase(key);
erased = true;
}
finally{
synchLock.unlock();
}
}
}