UUIDs.java
Home
/
api /
src /
main /
java /
org /
killbill /
billing /
util /
UUIDs.java
/*
* Copyright 2015 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.killbill.billing.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.UUID;
/**
* UUIDs helper.
*
* @author kares
*/
public abstract class UUIDs {
public static UUID randomUUID() { return rndUUIDv4(); }
private static UUID rndUUIDv4() {
// ~ return UUID.randomUUID() :
final Random random = threadRandom.get();
final byte[] uuid = new byte[16];
random.nextBytes(uuid);
uuid[6] &= 0x0f; /* clear version */
uuid[6] |= 0x40; /* set to version 4 */
uuid[8] &= 0x3f; /* clear variant */
uuid[8] |= 0x80; /* set to IETF variant */
long msb = 0;
msb = (msb << 8) | (uuid[0] & 0xff);
msb = (msb << 8) | (uuid[1] & 0xff);
msb = (msb << 8) | (uuid[2] & 0xff);
msb = (msb << 8) | (uuid[3] & 0xff);
msb = (msb << 8) | (uuid[4] & 0xff);
msb = (msb << 8) | (uuid[5] & 0xff);
msb = (msb << 8) | (uuid[6] & 0xff);
msb = (msb << 8) | (uuid[7] & 0xff);
long lsb = 0;
lsb = (lsb << 8) | (uuid[8] & 0xff);
lsb = (lsb << 8) | (uuid[9] & 0xff);
lsb = (lsb << 8) | (uuid[10] & 0xff);
lsb = (lsb << 8) | (uuid[11] & 0xff);
lsb = (lsb << 8) | (uuid[12] & 0xff);
lsb = (lsb << 8) | (uuid[13] & 0xff);
lsb = (lsb << 8) | (uuid[14] & 0xff);
lsb = (lsb << 8) | (uuid[15] & 0xff);
return new UUID(msb, lsb);
}
private static final ThreadLocal<Random> threadRandom =
new ThreadLocal<Random>() {
protected Random initialValue() {
return new LightSecureRandom(); // new SecureRandom();
}
};
/**
* An implementation of SecureRandom inspired by bouncy-castle's own for
* light-weight APIs (JDK 1.0, and J2ME).
*
* Random generation is based on the traditional SHA1 with
* counter. Calling setSeed will always increase the entropy of the hash.
*/
// NOTE: assumes non-concurrent use (generator calls should be synchronized)
private static class LightSecureRandom extends Random {
private static abstract class SeederHolder {
static final SecureRandom seeder = new SecureRandom();
}
private final DigestRandomGenerator generator;
LightSecureRandom() {
this( sha1Generator() );
setSeed( SeederHolder.seeder.generateSeed( generator.getDigestLength() ) );
}
LightSecureRandom(final byte[] inSeed) {
this( sha1Generator() );
setSeed(inSeed);
}
private LightSecureRandom(DigestRandomGenerator generator) {
super(0);
this.generator = generator;
}
private static DigestRandomGenerator sha1Generator() {
try {
return new DigestRandomGenerator(MessageDigest.getInstance("SHA-1"));
}
catch (NoSuchAlgorithmException ex) {
throw new AssertionError("unexpeced missing SHA-1 digest", ex);
}
}
@Override
public void setSeed(final long seed) {
if ( seed != 0 ) { // to avoid problems with Random calling setSeed in construction
generator.addSeedMaterial(seed);
}
}
public void setSeed(final byte[] seed) {
generator.addSeedMaterial(seed);
}
@Override
public void nextBytes(byte[] bytes) {
generator.nextBytes(bytes);
}
@Override
public int nextInt() {
final byte[] intBytes = new byte[4];
nextBytes(intBytes);
int result = 0;
for ( int i = 0; i < 4; i++ ) {
result = (result << 8) + (intBytes[i] & 0xff);
}
return result;
}
@Override
protected final int next(int numBits) {
int size = (numBits + 7) / 8;
byte[] bytes = new byte[size];
nextBytes(bytes);
int result = 0;
for (int i = 0; i < size; i++)
{
result = (result << 8) + (bytes[i] & 0xff);
}
return result & ((1 << numBits) - 1);
}
}
private static class DigestRandomGenerator {
private static final long CYCLE_COUNT = 10;
private long stateCounter;
private long seedCounter;
private final MessageDigest digest;
private byte[] state;
private byte[] seed;
private DigestRandomGenerator(MessageDigest digest) {
this.digest = digest;
this.seed = new byte[digest.getDigestLength()];
this.seedCounter = 1;
this.state = new byte[digest.getDigestLength()];
this.stateCounter = 1;
}
int getDigestLength() { return digest.getDigestLength(); }
// NOTE: requires external synchronization
final void addSeedMaterial(byte[] inSeed) {
//synchronized (this) {
digestUpdate(inSeed);
digestUpdate(seed);
seed = digest.digest(); // digestDoFinal(seed);
//}
}
// NOTE: requires external synchronization
final void addSeedMaterial(long rSeed) {
//synchronized (this) {
digestAddCounter(rSeed);
digestUpdate(seed);
seed = digest.digest(); // digestDoFinal(seed);
//}
}
void nextBytes(byte[] bytes) {
nextBytes(bytes, 0, bytes.length);
}
// NOTE: requires external synchronization
final void nextBytes(byte[] bytes, int start, int len) {
//synchronized (this) {
int stateOff = 0;
generateState();
int end = start + len;
for (int i = start; i != end; i++) {
if (stateOff == state.length) {
generateState();
stateOff = 0;
}
bytes[i] = state[stateOff++];
}
//}
}
private void cycleSeed() {
digestUpdate(seed);
digestAddCounter(seedCounter++);
seed = digest.digest(); // digestDoFinal(seed);
}
private void generateState() {
digestAddCounter(stateCounter++);
digestUpdate(state);
digestUpdate(seed);
state = digest.digest(); // digestDoFinal(state);
if ((stateCounter % CYCLE_COUNT) == 0) {
cycleSeed();
}
}
private void digestAddCounter(long seed) {
for (int i = 0; i != 8; i++) {
digest.update((byte) seed);
seed >>>= 8;
}
}
private void digestUpdate(byte[] inSeed) {
digest.update(inSeed, 0, inSeed.length);
}
//private void digestDoFinal(byte[] result) {
// digest.doFinal(result, 0);
//}
}
}