427 lines
15 KiB
Java
427 lines
15 KiB
Java
package org.bouncycastle.crypto.modes;
|
|
|
|
import com.google.common.primitives.UnsignedBytes;
|
|
import org.bouncycastle.crypto.BlockCipher;
|
|
import org.bouncycastle.crypto.CipherParameters;
|
|
import org.bouncycastle.crypto.DataLengthException;
|
|
import org.bouncycastle.crypto.InvalidCipherTextException;
|
|
import org.bouncycastle.crypto.OutputLengthException;
|
|
import org.bouncycastle.crypto.modes.gcm.GCMExponentiator;
|
|
import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
|
|
import org.bouncycastle.crypto.modes.gcm.GCMUtil;
|
|
import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
|
|
import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
|
|
import org.bouncycastle.crypto.params.AEADParameters;
|
|
import org.bouncycastle.crypto.params.KeyParameter;
|
|
import org.bouncycastle.crypto.params.ParametersWithIV;
|
|
import org.bouncycastle.util.Arrays;
|
|
import org.bouncycastle.util.Pack;
|
|
|
|
/* loaded from: classes6.dex */
|
|
public class GCMBlockCipher implements AEADBlockCipher {
|
|
private static final int BLOCK_SIZE = 16;
|
|
private byte[] H;
|
|
private byte[] J0;
|
|
private byte[] S;
|
|
private byte[] S_at;
|
|
private byte[] S_atPre;
|
|
private byte[] atBlock;
|
|
private int atBlockPos;
|
|
private long atLength;
|
|
private long atLengthPre;
|
|
private int blocksRemaining;
|
|
private byte[] bufBlock;
|
|
private int bufOff;
|
|
private BlockCipher cipher;
|
|
private byte[] counter;
|
|
private GCMExponentiator exp;
|
|
private boolean forEncryption;
|
|
private byte[] initialAssociatedText;
|
|
private byte[] macBlock;
|
|
private int macSize;
|
|
private GCMMultiplier multiplier;
|
|
private byte[] nonce;
|
|
private long totalLength;
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public void reset() {
|
|
reset(true);
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public int processBytes(byte[] bArr, int i, int i2, byte[] bArr2, int i3) throws DataLengthException {
|
|
if (bArr.length < i + i2) {
|
|
throw new DataLengthException("Input buffer too short");
|
|
}
|
|
int i4 = 0;
|
|
for (int i5 = 0; i5 < i2; i5++) {
|
|
byte[] bArr3 = this.bufBlock;
|
|
int i6 = this.bufOff;
|
|
bArr3[i6] = bArr[i + i5];
|
|
int i7 = i6 + 1;
|
|
this.bufOff = i7;
|
|
if (i7 == bArr3.length) {
|
|
outputBlock(bArr2, i3 + i4);
|
|
i4 += 16;
|
|
}
|
|
}
|
|
return i4;
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public int processByte(byte b, byte[] bArr, int i) throws DataLengthException {
|
|
byte[] bArr2 = this.bufBlock;
|
|
int i2 = this.bufOff;
|
|
bArr2[i2] = b;
|
|
int i3 = i2 + 1;
|
|
this.bufOff = i3;
|
|
if (i3 != bArr2.length) {
|
|
return 0;
|
|
}
|
|
outputBlock(bArr, i);
|
|
return 16;
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public void processAADBytes(byte[] bArr, int i, int i2) {
|
|
for (int i3 = 0; i3 < i2; i3++) {
|
|
byte[] bArr2 = this.atBlock;
|
|
int i4 = this.atBlockPos;
|
|
bArr2[i4] = bArr[i + i3];
|
|
int i5 = i4 + 1;
|
|
this.atBlockPos = i5;
|
|
if (i5 == 16) {
|
|
gHASHBlock(this.S_at, bArr2);
|
|
this.atBlockPos = 0;
|
|
this.atLength += 16;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public void processAADByte(byte b) {
|
|
byte[] bArr = this.atBlock;
|
|
int i = this.atBlockPos;
|
|
bArr[i] = b;
|
|
int i2 = i + 1;
|
|
this.atBlockPos = i2;
|
|
if (i2 == 16) {
|
|
gHASHBlock(this.S_at, bArr);
|
|
this.atBlockPos = 0;
|
|
this.atLength += 16;
|
|
}
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public void init(boolean z, CipherParameters cipherParameters) throws IllegalArgumentException {
|
|
KeyParameter keyParameter;
|
|
this.forEncryption = z;
|
|
this.macBlock = null;
|
|
if (cipherParameters instanceof AEADParameters) {
|
|
AEADParameters aEADParameters = (AEADParameters) cipherParameters;
|
|
this.nonce = aEADParameters.getNonce();
|
|
this.initialAssociatedText = aEADParameters.getAssociatedText();
|
|
int macSize = aEADParameters.getMacSize();
|
|
if (macSize < 32 || macSize > 128 || macSize % 8 != 0) {
|
|
throw new IllegalArgumentException("Invalid value for MAC size: ".concat(String.valueOf(macSize)));
|
|
}
|
|
this.macSize = macSize / 8;
|
|
keyParameter = aEADParameters.getKey();
|
|
} else {
|
|
if (!(cipherParameters instanceof ParametersWithIV)) {
|
|
throw new IllegalArgumentException("invalid parameters passed to GCM");
|
|
}
|
|
ParametersWithIV parametersWithIV = (ParametersWithIV) cipherParameters;
|
|
this.nonce = parametersWithIV.getIV();
|
|
this.initialAssociatedText = null;
|
|
this.macSize = 16;
|
|
keyParameter = (KeyParameter) parametersWithIV.getParameters();
|
|
}
|
|
this.bufBlock = new byte[z ? 16 : this.macSize + 16];
|
|
byte[] bArr = this.nonce;
|
|
if (bArr == null || bArr.length <= 0) {
|
|
throw new IllegalArgumentException("IV must be at least 1 byte");
|
|
}
|
|
if (keyParameter != null) {
|
|
this.cipher.init(true, keyParameter);
|
|
byte[] bArr2 = new byte[16];
|
|
this.H = bArr2;
|
|
this.cipher.processBlock(bArr2, 0, bArr2, 0);
|
|
this.multiplier.init(this.H);
|
|
this.exp = null;
|
|
} else if (this.H == null) {
|
|
throw new IllegalArgumentException("Key must be specified in initial init");
|
|
}
|
|
byte[] bArr3 = new byte[16];
|
|
this.J0 = bArr3;
|
|
byte[] bArr4 = this.nonce;
|
|
if (bArr4.length == 12) {
|
|
System.arraycopy(bArr4, 0, bArr3, 0, bArr4.length);
|
|
this.J0[15] = 1;
|
|
} else {
|
|
gHASH(bArr3, bArr4, bArr4.length);
|
|
byte[] bArr5 = new byte[16];
|
|
Pack.longToBigEndian(this.nonce.length << 3, bArr5, 8);
|
|
gHASHBlock(this.J0, bArr5);
|
|
}
|
|
this.S = new byte[16];
|
|
this.S_at = new byte[16];
|
|
this.S_atPre = new byte[16];
|
|
this.atBlock = new byte[16];
|
|
this.atBlockPos = 0;
|
|
this.atLength = 0L;
|
|
this.atLengthPre = 0L;
|
|
this.counter = Arrays.clone(this.J0);
|
|
this.blocksRemaining = -2;
|
|
this.bufOff = 0;
|
|
this.totalLength = 0L;
|
|
byte[] bArr6 = this.initialAssociatedText;
|
|
if (bArr6 != null) {
|
|
processAADBytes(bArr6, 0, bArr6.length);
|
|
}
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public int getUpdateOutputSize(int i) {
|
|
int i2 = i + this.bufOff;
|
|
if (!this.forEncryption) {
|
|
int i3 = this.macSize;
|
|
if (i2 < i3) {
|
|
return 0;
|
|
}
|
|
i2 -= i3;
|
|
}
|
|
return i2 - (i2 % 16);
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public BlockCipher getUnderlyingCipher() {
|
|
return this.cipher;
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public int getOutputSize(int i) {
|
|
int i2 = i + this.bufOff;
|
|
if (this.forEncryption) {
|
|
return i2 + this.macSize;
|
|
}
|
|
int i3 = this.macSize;
|
|
if (i2 < i3) {
|
|
return 0;
|
|
}
|
|
return i2 - i3;
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public byte[] getMac() {
|
|
return Arrays.clone(this.macBlock);
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public String getAlgorithmName() {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(this.cipher.getAlgorithmName());
|
|
sb.append("/GCM");
|
|
return sb.toString();
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public int doFinal(byte[] bArr, int i) throws IllegalStateException, InvalidCipherTextException {
|
|
if (this.totalLength == 0) {
|
|
initCipher();
|
|
}
|
|
int i2 = this.bufOff;
|
|
if (!this.forEncryption) {
|
|
int i3 = this.macSize;
|
|
if (i2 < i3) {
|
|
throw new InvalidCipherTextException("data too short");
|
|
}
|
|
i2 -= i3;
|
|
if (bArr.length < i + i2) {
|
|
throw new OutputLengthException("Output buffer too short");
|
|
}
|
|
} else if (bArr.length < i + i2 + this.macSize) {
|
|
throw new OutputLengthException("Output buffer too short");
|
|
}
|
|
if (i2 > 0) {
|
|
gCTRPartial(this.bufBlock, 0, i2, bArr, i);
|
|
}
|
|
long j = this.atLength;
|
|
int i4 = this.atBlockPos;
|
|
long j2 = j + i4;
|
|
this.atLength = j2;
|
|
if (j2 > this.atLengthPre) {
|
|
if (i4 > 0) {
|
|
gHASHPartial(this.S_at, this.atBlock, 0, i4);
|
|
}
|
|
if (this.atLengthPre > 0) {
|
|
GCMUtil.xor(this.S_at, this.S_atPre);
|
|
}
|
|
long j3 = this.totalLength;
|
|
byte[] bArr2 = new byte[16];
|
|
if (this.exp == null) {
|
|
Tables1kGCMExponentiator tables1kGCMExponentiator = new Tables1kGCMExponentiator();
|
|
this.exp = tables1kGCMExponentiator;
|
|
tables1kGCMExponentiator.init(this.H);
|
|
}
|
|
this.exp.exponentiateX(((j3 << 3) + 127) >>> 7, bArr2);
|
|
GCMUtil.multiply(this.S_at, bArr2);
|
|
GCMUtil.xor(this.S, this.S_at);
|
|
}
|
|
byte[] bArr3 = new byte[16];
|
|
Pack.longToBigEndian(this.atLength << 3, bArr3, 0);
|
|
Pack.longToBigEndian(this.totalLength << 3, bArr3, 8);
|
|
gHASHBlock(this.S, bArr3);
|
|
byte[] bArr4 = new byte[16];
|
|
this.cipher.processBlock(this.J0, 0, bArr4, 0);
|
|
GCMUtil.xor(bArr4, this.S);
|
|
int i5 = this.macSize;
|
|
byte[] bArr5 = new byte[i5];
|
|
this.macBlock = bArr5;
|
|
System.arraycopy(bArr4, 0, bArr5, 0, i5);
|
|
if (this.forEncryption) {
|
|
System.arraycopy(this.macBlock, 0, bArr, i + this.bufOff, this.macSize);
|
|
i2 += this.macSize;
|
|
} else {
|
|
int i6 = this.macSize;
|
|
byte[] bArr6 = new byte[i6];
|
|
System.arraycopy(this.bufBlock, i2, bArr6, 0, i6);
|
|
if (!Arrays.constantTimeAreEqual(this.macBlock, bArr6)) {
|
|
throw new InvalidCipherTextException("mac check in GCM failed");
|
|
}
|
|
}
|
|
reset(false);
|
|
return i2;
|
|
}
|
|
|
|
private void reset(boolean z) {
|
|
this.cipher.reset();
|
|
this.S = new byte[16];
|
|
this.S_at = new byte[16];
|
|
this.S_atPre = new byte[16];
|
|
this.atBlock = new byte[16];
|
|
this.atBlockPos = 0;
|
|
this.atLength = 0L;
|
|
this.atLengthPre = 0L;
|
|
this.counter = Arrays.clone(this.J0);
|
|
this.blocksRemaining = -2;
|
|
this.bufOff = 0;
|
|
this.totalLength = 0L;
|
|
byte[] bArr = this.bufBlock;
|
|
if (bArr != null) {
|
|
Arrays.fill(bArr, (byte) 0);
|
|
}
|
|
if (z) {
|
|
this.macBlock = null;
|
|
}
|
|
byte[] bArr2 = this.initialAssociatedText;
|
|
if (bArr2 != null) {
|
|
processAADBytes(bArr2, 0, bArr2.length);
|
|
}
|
|
}
|
|
|
|
private void outputBlock(byte[] bArr, int i) {
|
|
if (bArr.length < i + 16) {
|
|
throw new OutputLengthException("Output buffer too short");
|
|
}
|
|
if (this.totalLength == 0) {
|
|
initCipher();
|
|
}
|
|
gCTRBlock(this.bufBlock, bArr, i);
|
|
if (this.forEncryption) {
|
|
this.bufOff = 0;
|
|
return;
|
|
}
|
|
byte[] bArr2 = this.bufBlock;
|
|
System.arraycopy(bArr2, 16, bArr2, 0, this.macSize);
|
|
this.bufOff = this.macSize;
|
|
}
|
|
|
|
private void initCipher() {
|
|
if (this.atLength > 0) {
|
|
System.arraycopy(this.S_at, 0, this.S_atPre, 0, 16);
|
|
this.atLengthPre = this.atLength;
|
|
}
|
|
int i = this.atBlockPos;
|
|
if (i > 0) {
|
|
gHASHPartial(this.S_atPre, this.atBlock, 0, i);
|
|
this.atLengthPre += this.atBlockPos;
|
|
}
|
|
if (this.atLengthPre > 0) {
|
|
System.arraycopy(this.S_atPre, 0, this.S, 0, 16);
|
|
}
|
|
}
|
|
|
|
private byte[] getNextCounterBlock() {
|
|
int i = this.blocksRemaining;
|
|
if (i == 0) {
|
|
throw new IllegalStateException("Attempt to process too many blocks");
|
|
}
|
|
this.blocksRemaining = i - 1;
|
|
byte[] bArr = this.counter;
|
|
int i2 = (bArr[15] & UnsignedBytes.MAX_VALUE) + 1;
|
|
bArr[15] = (byte) i2;
|
|
int i3 = (i2 >>> 8) + (bArr[14] & UnsignedBytes.MAX_VALUE);
|
|
bArr[14] = (byte) i3;
|
|
int i4 = (i3 >>> 8) + (bArr[13] & UnsignedBytes.MAX_VALUE);
|
|
bArr[13] = (byte) i4;
|
|
bArr[12] = (byte) ((i4 >>> 8) + (bArr[12] & UnsignedBytes.MAX_VALUE));
|
|
byte[] bArr2 = new byte[16];
|
|
this.cipher.processBlock(bArr, 0, bArr2, 0);
|
|
return bArr2;
|
|
}
|
|
|
|
private void gHASHPartial(byte[] bArr, byte[] bArr2, int i, int i2) {
|
|
GCMUtil.xor(bArr, bArr2, i, i2);
|
|
this.multiplier.multiplyH(bArr);
|
|
}
|
|
|
|
private void gHASHBlock(byte[] bArr, byte[] bArr2) {
|
|
GCMUtil.xor(bArr, bArr2);
|
|
this.multiplier.multiplyH(bArr);
|
|
}
|
|
|
|
private void gHASH(byte[] bArr, byte[] bArr2, int i) {
|
|
for (int i2 = 0; i2 < i; i2 += 16) {
|
|
gHASHPartial(bArr, bArr2, i2, Math.min(i - i2, 16));
|
|
}
|
|
}
|
|
|
|
private void gCTRPartial(byte[] bArr, int i, int i2, byte[] bArr2, int i3) {
|
|
byte[] nextCounterBlock = getNextCounterBlock();
|
|
GCMUtil.xor(nextCounterBlock, bArr, i, i2);
|
|
System.arraycopy(nextCounterBlock, 0, bArr2, i3, i2);
|
|
byte[] bArr3 = this.S;
|
|
if (this.forEncryption) {
|
|
bArr = nextCounterBlock;
|
|
}
|
|
gHASHPartial(bArr3, bArr, 0, i2);
|
|
this.totalLength += i2;
|
|
}
|
|
|
|
private void gCTRBlock(byte[] bArr, byte[] bArr2, int i) {
|
|
byte[] nextCounterBlock = getNextCounterBlock();
|
|
GCMUtil.xor(nextCounterBlock, bArr);
|
|
System.arraycopy(nextCounterBlock, 0, bArr2, i, 16);
|
|
byte[] bArr3 = this.S;
|
|
if (this.forEncryption) {
|
|
bArr = nextCounterBlock;
|
|
}
|
|
gHASHBlock(bArr3, bArr);
|
|
this.totalLength += 16;
|
|
}
|
|
|
|
public GCMBlockCipher(BlockCipher blockCipher, GCMMultiplier gCMMultiplier) {
|
|
if (blockCipher.getBlockSize() != 16) {
|
|
throw new IllegalArgumentException("cipher required with a block size of 16.");
|
|
}
|
|
gCMMultiplier = gCMMultiplier == null ? new Tables8kGCMMultiplier() : gCMMultiplier;
|
|
this.cipher = blockCipher;
|
|
this.multiplier = gCMMultiplier;
|
|
}
|
|
|
|
public GCMBlockCipher(BlockCipher blockCipher) {
|
|
this(blockCipher, null);
|
|
}
|
|
}
|