Skip to content

Commit

Permalink
Merge pull request #703 from KostasTsiounis/gcm_perf
Browse files Browse the repository at this point in the history
Optimize AES/GCM cipher and IV initialization and improve array cleanup code
  • Loading branch information
keithc-ca committed Sep 13, 2023
2 parents 5e99d55 + 20affef commit 31bc73a
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 304 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,26 @@
*/
/*
* ===========================================================================
* (c) Copyright IBM Corp. 2018, 2021 All Rights Reserved
* (c) Copyright IBM Corp. 2018, 2023 All Rights Reserved
* ===========================================================================
*/

package com.sun.crypto.provider;

import java.util.Arrays;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
import com.sun.crypto.provider.AESCrypt;
import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;

import java.io.ByteArrayOutputStream;
import java.lang.ref.Cleaner;
import java.security.InvalidKeyException;
import java.security.ProviderException;

import javax.crypto.AEADBadTagException;
import javax.crypto.ShortBufferException;
import javax.crypto.IllegalBlockSizeException;

import jdk.crypto.jniprovider.NativeCrypto;
import jdk.internal.ref.CleanerFactory;

/**
* This class represents ciphers in GaloisCounter (GCM) mode.
Expand All @@ -54,9 +60,11 @@
*/
final class NativeGaloisCounterMode extends FeedbackCipher {

private static final byte[] EMPTY_BUF = new byte[0];

private byte[] key;
private boolean decrypting;
private static final byte[] emptyAAD = new byte[0];
private final long context;

static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;
static int DEFAULT_IV_LEN = 12; // in bytes
Expand Down Expand Up @@ -92,10 +100,34 @@ final class NativeGaloisCounterMode extends FeedbackCipher {
private byte[] ibufferSave = null;
private byte[] ibufferSave_enc = null;

private static NativeCrypto nativeCrypto;
private byte[] lastKey = EMPTY_BUF;
private byte[] lastIv = EMPTY_BUF;

private boolean newIVLen;
private boolean newKeyLen;

private static final NativeCrypto nativeCrypto = NativeCrypto.getNativeCrypto();
private static final Cleaner contextCleaner = CleanerFactory.cleaner();

private static final class GCMCleanerRunnable implements Runnable {
private final long nativeContext;

public GCMCleanerRunnable(long nativeContext) {
this.nativeContext = nativeContext;
}

static {
nativeCrypto = NativeCrypto.getNativeCrypto();
@Override
public void run() {
/*
* Release the GCM context.
*/
synchronized (NativeGaloisCounterMode.class) {
long ret = nativeCrypto.DestroyContext(nativeContext);
if (ret == -1) {
throw new ProviderException("Error in destroying context in NativeGaloisCounterMode.");
}
}
}
}

private static void checkDataLength(int processed, int len) {
Expand All @@ -111,6 +143,12 @@ private static void checkDataLength(int processed, int len) {
NativeGaloisCounterMode(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
aadBuffer = new ByteArrayOutputStream();

context = nativeCrypto.CreateContext();
if (context == -1) {
throw new ProviderException("Error in creating context for NativeGaloisCounterMode.");
}
contextCleaner.register(this, new GCMCleanerRunnable(context));
}

/**
Expand Down Expand Up @@ -244,6 +282,22 @@ void init(boolean decrypting, String algorithm, byte[] keyValue,
} else {
ibuffer_enc = new ByteArrayOutputStream();
}

/*
* Check whether cipher and IV need to be set,
* whether because something changed here or
* a call to set them in context hasn't been
* made yet.
*/
if (lastIv.length != this.iv.length) {
newIVLen = true;
}
if (lastKey.length != this.key.length) {
newKeyLen = true;
}

lastKey = keyValue;
lastIv = iv;
}
}

Expand Down Expand Up @@ -354,18 +408,26 @@ int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
len = in.length;
ibuffer_enc.reset();

byte[] aad = (((aadBuffer == null) || (aadBuffer.size() == 0)) ? emptyAAD : aadBuffer.toByteArray());
byte[] aad = (((aadBuffer == null) || (aadBuffer.size() == 0)) ? EMPTY_BUF : aadBuffer.toByteArray());

ret = nativeCrypto.GCMEncrypt(key, key.length,
ret = nativeCrypto.GCMEncrypt(context,
key, key.length,
iv, iv.length,
in, inOfs, len,
out, outOfs,
aad, aad.length, localTagLenBytes);
aad, aad.length,
localTagLenBytes,
newIVLen,
newKeyLen);
}
if (ret == -1) {
throw new ProviderException("Error in Native GaloisCounterMode");
}

/* Cipher and IV length were set, since call to GCMEncrypt succeeded. */
newKeyLen = false;
newIVLen = false;

return (len + localTagLenBytes);
}

Expand Down Expand Up @@ -442,7 +504,7 @@ int decryptFinal(byte[] in, int inOfs, int len,
}

byte[] aad = (((aadBuffer == null) || (aadBuffer.size() == 0)) ?
emptyAAD : aadBuffer.toByteArray());
EMPTY_BUF : aadBuffer.toByteArray());

aadBuffer = null;

Expand All @@ -456,18 +518,26 @@ int decryptFinal(byte[] in, int inOfs, int len,
len = in.length;
ibuffer.reset();

ret = nativeCrypto.GCMDecrypt(key, key.length,
ret = nativeCrypto.GCMDecrypt(context,
key, key.length,
iv, iv.length,
in, inOfs, len,
out, outOfs,
aad, aad.length, localTagLenBytes);
aad, aad.length,
localTagLenBytes,
newIVLen,
newKeyLen);
}
if (ret == -2) {
throw new AEADBadTagException("Tag mismatch!");
} else if (ret == -1) {
throw new ProviderException("Error in Native GaloisCounterMode");
}

/* Cipher and IV length were set, since call to GCMDecrypt succeeded. */
newKeyLen = false;
newIVLen = false;

return ret;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ public final native int DigestComputeAndReset(long context,

public final native int DigestReset(long context);

/* Native interfaces shared by CBC and ChaCha20 */
/* Native interfaces shared by CBC, ChaCha20 and GCM. */

public final native long CreateContext();

Expand Down Expand Up @@ -252,7 +252,8 @@ public final native int CBCFinalEncrypt(long context,

/* Native GCM interfaces */

public final native int GCMEncrypt(byte[] key,
public final native int GCMEncrypt(long context,
byte[] key,
int keylen,
byte[] iv,
int ivlen,
Expand All @@ -263,9 +264,12 @@ public final native int GCMEncrypt(byte[] key,
int outOffset,
byte[] aad,
int aadLen,
int tagLen);
int tagLen,
boolean newIVLen,
boolean newKeyLen);

public final native int GCMDecrypt(byte[] key,
public final native int GCMDecrypt(long context,
byte[] key,
int keylen,
byte[] iv,
int ivlen,
Expand All @@ -276,7 +280,9 @@ public final native int GCMDecrypt(byte[] key,
int outOffset,
byte[] aad,
int aadLen,
int tagLen);
int tagLen,
boolean newIVLen,
boolean newKeyLen);

/* Native RSA interfaces */

Expand Down
Loading

0 comments on commit 31bc73a

Please sign in to comment.