src/share/classes/com/sun/crypto/provider/GHASH.java
Print this page
rev 10474 : com.sun.crypto.provider.GHASH performance fix
*** 26,38 ****
* (C) Copyright IBM Corp. 2013
*/
package com.sun.crypto.provider;
! import java.util.Arrays;
! import java.security.*;
! import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
/**
* This class represents the GHASH function defined in NIST 800-38D
* under section 6.4. It needs to be constructed w/ a hash subkey, i.e.
* block H. Given input of 128-bit blocks, it will process and output
--- 26,36 ----
* (C) Copyright IBM Corp. 2013
*/
package com.sun.crypto.provider;
! import java.security.ProviderException;
/**
* This class represents the GHASH function defined in NIST 800-38D
* under section 6.4. It needs to be constructed w/ a hash subkey, i.e.
* block H. Given input of 128-bit blocks, it will process and output
*** 42,107 ****
*
* @since 1.8
*/
final class GHASH {
! private static final byte P128 = (byte) 0xe1; //reduction polynomial
! private static boolean getBit(byte[] b, int pos) {
! int p = pos / 8;
! pos %= 8;
! int i = (b[p] >>> (7 - pos)) & 1;
! return i != 0;
! }
!
! private static void shift(byte[] b) {
! byte temp, temp2;
! temp2 = 0;
! for (int i = 0; i < b.length; i++) {
! temp = (byte) ((b[i] & 0x01) << 7);
! b[i] = (byte) ((b[i] & 0xff) >>> 1);
! b[i] = (byte) (b[i] | temp2);
! temp2 = temp;
}
}
! // Given block X and Y, returns the muliplication of X * Y
! private static byte[] blockMult(byte[] x, byte[] y) {
! if (x.length != AES_BLOCK_SIZE || y.length != AES_BLOCK_SIZE) {
! throw new RuntimeException("illegal input sizes");
! }
! byte[] z = new byte[AES_BLOCK_SIZE];
! byte[] v = y.clone();
! // calculate Z1-Z127 and V1-V127
! for (int i = 0; i < 127; i++) {
// Zi+1 = Zi if bit i of x is 0
! if (getBit(x, i)) {
! for (int n = 0; n < z.length; n++) {
! z[n] ^= v[n];
! }
}
! boolean lastBitOfV = getBit(v, 127);
! shift(v);
! if (lastBitOfV) v[0] ^= P128;
}
// calculate Z128
! if (getBit(x, 127)) {
! for (int n = 0; n < z.length; n++) {
! z[n] ^= v[n];
! }
! }
! return z;
}
// hash subkey H; should not change after the object has been constructed
! private final byte[] subkeyH;
// buffer for storing hash
! private byte[] state;
// variables for save/restore calls
! private byte[] stateSave = null;
/**
* Initializes the cipher in the specified mode with the given key
* and iv.
*
--- 40,133 ----
*
* @since 1.8
*/
final class GHASH {
! private static long getLong(byte[] buffer, int offset) {
! long result = 0;
! int end = offset + 8;
! for (int i = offset; i < end; ++i) {
! result = (result << 8) + (buffer[i] & 0xFF);
! }
! return result;
! }
! private static void putLong(byte[] buffer, int offset, long value) {
! int end = offset + 8;
! for (int i = end - 1; i >= offset; --i) {
! buffer[i] = (byte) value;
! value >>= 8;
}
}
! private static final int AES_BLOCK_SIZE = 16;
!
! // Multiplies state0, state1 by V0, V1.
! private void blockMult(long V0, long V1) {
! long Z0 = 0;
! long Z1 = 0;
! long X;
!
! // Separate loops for processing state0 and state1.
! X = state0;
! for (int i = 0; i < 64; i++) {
// Zi+1 = Zi if bit i of x is 0
! long mask = X >> 63;
! Z0 ^= V0 & mask;
! Z1 ^= V1 & mask;
!
! // Save mask for conditional reduction below.
! mask = (V1 << 63) >> 63;
!
! // V = rightshift(V)
! long carry = V0 & 1;
! V0 = V0 >>> 1;
! V1 = (V1 >>> 1) | (carry << 63);
!
! // Conditional reduction modulo P128.
! V0 ^= 0xe100000000000000L & mask;
! X <<= 1;
}
!
! X = state1;
! for (int i = 64; i < 127; i++) {
! // Zi+1 = Zi if bit i of x is 0
! long mask = X >> 63;
! Z0 ^= V0 & mask;
! Z1 ^= V1 & mask;
!
! // Save mask for conditional reduction below.
! mask = (V1 << 63) >> 63;
!
! // V = rightshift(V)
! long carry = V0 & 1;
! V0 = V0 >>> 1;
! V1 = (V1 >>> 1) | (carry << 63);
!
! // Conditional reduction.
! V0 ^= 0xe100000000000000L & mask;
! X <<= 1;
}
+
// calculate Z128
! long mask = X >> 63;
! Z0 ^= V0 & mask;
! Z1 ^= V1 & mask;
!
! // Save result.
! state0 = Z0;
! state1 = Z1;
}
// hash subkey H; should not change after the object has been constructed
! private final long subkeyH0, subkeyH1;
// buffer for storing hash
! private long state0, state1;
// variables for save/restore calls
! private long stateSave0, stateSave1;
/**
* Initializes the cipher in the specified mode with the given key
* and iv.
*
*** 112,156 ****
*/
GHASH(byte[] subkeyH) throws ProviderException {
if ((subkeyH == null) || subkeyH.length != AES_BLOCK_SIZE) {
throw new ProviderException("Internal error");
}
! this.subkeyH = subkeyH;
! this.state = new byte[AES_BLOCK_SIZE];
}
/**
* Resets the GHASH object to its original state, i.e. blank w/
* the same subkey H. Used after digest() is called and to re-use
* this object for different data w/ the same H.
*/
void reset() {
! Arrays.fill(state, (byte) 0);
}
/**
* Save the current snapshot of this GHASH object.
*/
void save() {
! stateSave = state.clone();
}
/**
* Restores this object using the saved snapshot.
*/
void restore() {
! state = stateSave;
}
private void processBlock(byte[] data, int ofs) {
if (data.length - ofs < AES_BLOCK_SIZE) {
throw new RuntimeException("need complete block");
}
! for (int n = 0; n < state.length; n++) {
! state[n] ^= data[ofs + n];
! }
! state = blockMult(state, subkeyH);
}
void update(byte[] in) {
update(in, 0, in.length);
}
--- 138,184 ----
*/
GHASH(byte[] subkeyH) throws ProviderException {
if ((subkeyH == null) || subkeyH.length != AES_BLOCK_SIZE) {
throw new ProviderException("Internal error");
}
! this.subkeyH0 = getLong(subkeyH, 0);
! this.subkeyH1 = getLong(subkeyH, 8);
}
/**
* Resets the GHASH object to its original state, i.e. blank w/
* the same subkey H. Used after digest() is called and to re-use
* this object for different data w/ the same H.
*/
void reset() {
! state0 = 0;
! state1 = 0;
}
/**
* Save the current snapshot of this GHASH object.
*/
void save() {
! stateSave0 = state0;
! stateSave1 = state1;
}
/**
* Restores this object using the saved snapshot.
*/
void restore() {
! state0 = stateSave0;
! state1 = stateSave1;
}
private void processBlock(byte[] data, int ofs) {
if (data.length - ofs < AES_BLOCK_SIZE) {
throw new RuntimeException("need complete block");
}
! state0 ^= getLong(data, ofs);
! state1 ^= getLong(data, ofs + 8);
! blockMult(subkeyH0, subkeyH1);
}
void update(byte[] in) {
update(in, 0, in.length);
}
*** 167,178 ****
processBlock(in, i);
}
}
byte[] digest() {
! try {
! return state.clone();
! } finally {
reset();
! }
}
}
--- 195,206 ----
processBlock(in, i);
}
}
byte[] digest() {
! byte[] result = new byte[AES_BLOCK_SIZE];
! putLong(result, 0, state0);
! putLong(result, 8, state1);
reset();
! return result;
}
}