469 lines
16 KiB
Java
469 lines
16 KiB
Java
package org.bouncycastle.crypto.modes;
|
|
|
|
import com.google.common.primitives.UnsignedBytes;
|
|
import java.util.Vector;
|
|
import net.sf.scuba.smartcards.ISO7816;
|
|
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.params.AEADParameters;
|
|
import org.bouncycastle.crypto.params.KeyParameter;
|
|
import org.bouncycastle.crypto.params.ParametersWithIV;
|
|
import org.bouncycastle.util.Arrays;
|
|
|
|
/* loaded from: classes6.dex */
|
|
public class OCBBlockCipher implements AEADBlockCipher {
|
|
private static final int BLOCK_SIZE = 16;
|
|
private byte[] Checksum;
|
|
private Vector L;
|
|
private byte[] L_Asterisk;
|
|
private byte[] L_Dollar;
|
|
private byte[] OffsetHASH;
|
|
private byte[] Sum;
|
|
private boolean forEncryption;
|
|
private byte[] hashBlock;
|
|
private long hashBlockCount;
|
|
private int hashBlockPos;
|
|
private BlockCipher hashCipher;
|
|
private byte[] initialAssociatedText;
|
|
private byte[] macBlock;
|
|
private int macSize;
|
|
private byte[] mainBlock;
|
|
private long mainBlockCount;
|
|
private int mainBlockPos;
|
|
private BlockCipher mainCipher;
|
|
private byte[] KtopInput = null;
|
|
private byte[] Stretch = new byte[24];
|
|
private byte[] OffsetMAIN_0 = new byte[16];
|
|
private byte[] OffsetMAIN = new byte[16];
|
|
|
|
protected static int OCB_ntz(long j) {
|
|
if (j == 0) {
|
|
return 64;
|
|
}
|
|
int i = 0;
|
|
while ((1 & j) == 0) {
|
|
i++;
|
|
j >>>= 1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
protected void updateHASH(byte[] bArr) {
|
|
xor(this.OffsetHASH, bArr);
|
|
xor(this.hashBlock, this.OffsetHASH);
|
|
BlockCipher blockCipher = this.hashCipher;
|
|
byte[] bArr2 = this.hashBlock;
|
|
blockCipher.processBlock(bArr2, 0, bArr2, 0);
|
|
xor(this.Sum, this.hashBlock);
|
|
}
|
|
|
|
protected void reset(boolean z) {
|
|
this.hashCipher.reset();
|
|
this.mainCipher.reset();
|
|
clear(this.hashBlock);
|
|
clear(this.mainBlock);
|
|
this.hashBlockPos = 0;
|
|
this.mainBlockPos = 0;
|
|
this.hashBlockCount = 0L;
|
|
this.mainBlockCount = 0L;
|
|
clear(this.OffsetHASH);
|
|
clear(this.Sum);
|
|
System.arraycopy(this.OffsetMAIN_0, 0, this.OffsetMAIN, 0, 16);
|
|
clear(this.Checksum);
|
|
if (z) {
|
|
this.macBlock = null;
|
|
}
|
|
byte[] bArr = this.initialAssociatedText;
|
|
if (bArr != null) {
|
|
processAADBytes(bArr, 0, bArr.length);
|
|
}
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public void reset() {
|
|
reset(true);
|
|
}
|
|
|
|
protected int processNonce(byte[] bArr) {
|
|
byte[] bArr2 = new byte[16];
|
|
int i = 0;
|
|
System.arraycopy(bArr, 0, bArr2, 16 - bArr.length, bArr.length);
|
|
bArr2[0] = (byte) (this.macSize << 4);
|
|
int length = 15 - bArr.length;
|
|
bArr2[length] = (byte) (bArr2[length] | 1);
|
|
byte b = bArr2[15];
|
|
bArr2[15] = (byte) (b & ISO7816.INS_GET_RESPONSE);
|
|
byte[] bArr3 = this.KtopInput;
|
|
if (bArr3 == null || !Arrays.areEqual(bArr2, bArr3)) {
|
|
byte[] bArr4 = new byte[16];
|
|
this.KtopInput = bArr2;
|
|
this.hashCipher.processBlock(bArr2, 0, bArr4, 0);
|
|
System.arraycopy(bArr4, 0, this.Stretch, 0, 16);
|
|
while (i < 8) {
|
|
int i2 = i + 1;
|
|
this.Stretch[i + 16] = (byte) (bArr4[i] ^ bArr4[i2]);
|
|
i = i2;
|
|
}
|
|
}
|
|
return b & 63;
|
|
}
|
|
|
|
protected void processMainBlock(byte[] bArr, int i) {
|
|
if (bArr.length < i + 16) {
|
|
throw new OutputLengthException("Output buffer too short");
|
|
}
|
|
if (this.forEncryption) {
|
|
xor(this.Checksum, this.mainBlock);
|
|
this.mainBlockPos = 0;
|
|
}
|
|
byte[] bArr2 = this.OffsetMAIN;
|
|
long j = this.mainBlockCount + 1;
|
|
this.mainBlockCount = j;
|
|
xor(bArr2, getLSub(OCB_ntz(j)));
|
|
xor(this.mainBlock, this.OffsetMAIN);
|
|
BlockCipher blockCipher = this.mainCipher;
|
|
byte[] bArr3 = this.mainBlock;
|
|
blockCipher.processBlock(bArr3, 0, bArr3, 0);
|
|
xor(this.mainBlock, this.OffsetMAIN);
|
|
System.arraycopy(this.mainBlock, 0, bArr, i, 16);
|
|
if (this.forEncryption) {
|
|
return;
|
|
}
|
|
xor(this.Checksum, this.mainBlock);
|
|
byte[] bArr4 = this.mainBlock;
|
|
System.arraycopy(bArr4, 16, bArr4, 0, this.macSize);
|
|
this.mainBlockPos = this.macSize;
|
|
}
|
|
|
|
protected void processHashBlock() {
|
|
long j = this.hashBlockCount + 1;
|
|
this.hashBlockCount = j;
|
|
updateHASH(getLSub(OCB_ntz(j)));
|
|
this.hashBlockPos = 0;
|
|
}
|
|
|
|
@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.mainBlock;
|
|
int i6 = this.mainBlockPos;
|
|
bArr3[i6] = bArr[i + i5];
|
|
int i7 = i6 + 1;
|
|
this.mainBlockPos = i7;
|
|
if (i7 == bArr3.length) {
|
|
processMainBlock(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.mainBlock;
|
|
int i2 = this.mainBlockPos;
|
|
bArr2[i2] = b;
|
|
int i3 = i2 + 1;
|
|
this.mainBlockPos = i3;
|
|
if (i3 != bArr2.length) {
|
|
return 0;
|
|
}
|
|
processMainBlock(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.hashBlock;
|
|
int i4 = this.hashBlockPos;
|
|
bArr2[i4] = bArr[i + i3];
|
|
int i5 = i4 + 1;
|
|
this.hashBlockPos = i5;
|
|
if (i5 == bArr2.length) {
|
|
processHashBlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public void processAADByte(byte b) {
|
|
byte[] bArr = this.hashBlock;
|
|
int i = this.hashBlockPos;
|
|
bArr[i] = b;
|
|
int i2 = i + 1;
|
|
this.hashBlockPos = i2;
|
|
if (i2 == bArr.length) {
|
|
processHashBlock();
|
|
}
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public void init(boolean z, CipherParameters cipherParameters) throws IllegalArgumentException {
|
|
byte[] iv;
|
|
KeyParameter keyParameter;
|
|
boolean z2 = this.forEncryption;
|
|
this.forEncryption = z;
|
|
this.macBlock = null;
|
|
if (cipherParameters instanceof AEADParameters) {
|
|
AEADParameters aEADParameters = (AEADParameters) cipherParameters;
|
|
iv = aEADParameters.getNonce();
|
|
this.initialAssociatedText = aEADParameters.getAssociatedText();
|
|
int macSize = aEADParameters.getMacSize();
|
|
if (macSize < 64 || 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 OCB");
|
|
}
|
|
ParametersWithIV parametersWithIV = (ParametersWithIV) cipherParameters;
|
|
iv = parametersWithIV.getIV();
|
|
this.initialAssociatedText = null;
|
|
this.macSize = 16;
|
|
keyParameter = (KeyParameter) parametersWithIV.getParameters();
|
|
}
|
|
this.hashBlock = new byte[16];
|
|
this.mainBlock = new byte[z ? 16 : this.macSize + 16];
|
|
if (iv == null) {
|
|
iv = new byte[0];
|
|
}
|
|
if (iv.length > 15) {
|
|
throw new IllegalArgumentException("IV must be no more than 15 bytes");
|
|
}
|
|
if (keyParameter != null) {
|
|
this.hashCipher.init(true, keyParameter);
|
|
this.mainCipher.init(z, keyParameter);
|
|
this.KtopInput = null;
|
|
} else if (z2 != z) {
|
|
throw new IllegalArgumentException("cannot change encrypting state without providing key.");
|
|
}
|
|
byte[] bArr = new byte[16];
|
|
this.L_Asterisk = bArr;
|
|
this.hashCipher.processBlock(bArr, 0, bArr, 0);
|
|
this.L_Dollar = OCB_double(this.L_Asterisk);
|
|
Vector vector = new Vector();
|
|
this.L = vector;
|
|
vector.addElement(OCB_double(this.L_Dollar));
|
|
int processNonce = processNonce(iv);
|
|
int i = processNonce % 8;
|
|
int i2 = processNonce / 8;
|
|
if (i == 0) {
|
|
System.arraycopy(this.Stretch, i2, this.OffsetMAIN_0, 0, 16);
|
|
} else {
|
|
for (int i3 = 0; i3 < 16; i3++) {
|
|
byte[] bArr2 = this.Stretch;
|
|
byte b = bArr2[i2];
|
|
i2++;
|
|
this.OffsetMAIN_0[i3] = (byte) (((bArr2[i2] & UnsignedBytes.MAX_VALUE) >>> (8 - i)) | ((b & UnsignedBytes.MAX_VALUE) << i));
|
|
}
|
|
}
|
|
this.hashBlockPos = 0;
|
|
this.mainBlockPos = 0;
|
|
this.hashBlockCount = 0L;
|
|
this.mainBlockCount = 0L;
|
|
this.OffsetHASH = new byte[16];
|
|
this.Sum = new byte[16];
|
|
System.arraycopy(this.OffsetMAIN_0, 0, this.OffsetMAIN, 0, 16);
|
|
this.Checksum = new byte[16];
|
|
byte[] bArr3 = this.initialAssociatedText;
|
|
if (bArr3 != null) {
|
|
processAADBytes(bArr3, 0, bArr3.length);
|
|
}
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public int getUpdateOutputSize(int i) {
|
|
int i2 = i + this.mainBlockPos;
|
|
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.mainCipher;
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public int getOutputSize(int i) {
|
|
int i2 = i + this.mainBlockPos;
|
|
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);
|
|
}
|
|
|
|
protected byte[] getLSub(int i) {
|
|
while (i >= this.L.size()) {
|
|
Vector vector = this.L;
|
|
vector.addElement(OCB_double((byte[]) vector.lastElement()));
|
|
}
|
|
return (byte[]) this.L.elementAt(i);
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public String getAlgorithmName() {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(this.mainCipher.getAlgorithmName());
|
|
sb.append("/OCB");
|
|
return sb.toString();
|
|
}
|
|
|
|
@Override // org.bouncycastle.crypto.modes.AEADBlockCipher
|
|
public int doFinal(byte[] bArr, int i) throws IllegalStateException, InvalidCipherTextException {
|
|
byte[] bArr2;
|
|
if (this.forEncryption) {
|
|
bArr2 = null;
|
|
} else {
|
|
int i2 = this.mainBlockPos;
|
|
int i3 = this.macSize;
|
|
if (i2 < i3) {
|
|
throw new InvalidCipherTextException("data too short");
|
|
}
|
|
int i4 = i2 - i3;
|
|
this.mainBlockPos = i4;
|
|
bArr2 = new byte[i3];
|
|
System.arraycopy(this.mainBlock, i4, bArr2, 0, i3);
|
|
}
|
|
int i5 = this.hashBlockPos;
|
|
if (i5 > 0) {
|
|
OCB_extend(this.hashBlock, i5);
|
|
updateHASH(this.L_Asterisk);
|
|
}
|
|
int i6 = this.mainBlockPos;
|
|
if (i6 > 0) {
|
|
if (this.forEncryption) {
|
|
OCB_extend(this.mainBlock, i6);
|
|
xor(this.Checksum, this.mainBlock);
|
|
}
|
|
xor(this.OffsetMAIN, this.L_Asterisk);
|
|
byte[] bArr3 = new byte[16];
|
|
this.hashCipher.processBlock(this.OffsetMAIN, 0, bArr3, 0);
|
|
xor(this.mainBlock, bArr3);
|
|
int length = bArr.length;
|
|
int i7 = this.mainBlockPos;
|
|
if (length < i + i7) {
|
|
throw new OutputLengthException("Output buffer too short");
|
|
}
|
|
System.arraycopy(this.mainBlock, 0, bArr, i, i7);
|
|
if (!this.forEncryption) {
|
|
OCB_extend(this.mainBlock, this.mainBlockPos);
|
|
xor(this.Checksum, this.mainBlock);
|
|
}
|
|
}
|
|
xor(this.Checksum, this.OffsetMAIN);
|
|
xor(this.Checksum, this.L_Dollar);
|
|
BlockCipher blockCipher = this.hashCipher;
|
|
byte[] bArr4 = this.Checksum;
|
|
blockCipher.processBlock(bArr4, 0, bArr4, 0);
|
|
xor(this.Checksum, this.Sum);
|
|
int i8 = this.macSize;
|
|
byte[] bArr5 = new byte[i8];
|
|
this.macBlock = bArr5;
|
|
System.arraycopy(this.Checksum, 0, bArr5, 0, i8);
|
|
int i9 = this.mainBlockPos;
|
|
if (this.forEncryption) {
|
|
int length2 = bArr.length;
|
|
int i10 = i + i9;
|
|
int i11 = this.macSize;
|
|
if (length2 < i10 + i11) {
|
|
throw new OutputLengthException("Output buffer too short");
|
|
}
|
|
System.arraycopy(this.macBlock, 0, bArr, i10, i11);
|
|
i9 += this.macSize;
|
|
} else if (!Arrays.constantTimeAreEqual(this.macBlock, bArr2)) {
|
|
throw new InvalidCipherTextException("mac check in OCB failed");
|
|
}
|
|
reset(false);
|
|
return i9;
|
|
}
|
|
|
|
protected void clear(byte[] bArr) {
|
|
if (bArr != null) {
|
|
Arrays.fill(bArr, (byte) 0);
|
|
}
|
|
}
|
|
|
|
protected static void xor(byte[] bArr, byte[] bArr2) {
|
|
for (int i = 15; i >= 0; i--) {
|
|
bArr[i] = (byte) (bArr[i] ^ bArr2[i]);
|
|
}
|
|
}
|
|
|
|
protected static int shiftLeft(byte[] bArr, byte[] bArr2) {
|
|
int i = 16;
|
|
int i2 = 0;
|
|
while (true) {
|
|
i--;
|
|
if (i < 0) {
|
|
return i2;
|
|
}
|
|
int i3 = bArr[i] & UnsignedBytes.MAX_VALUE;
|
|
bArr2[i] = (byte) (i2 | (i3 << 1));
|
|
i2 = (i3 >>> 7) & 1;
|
|
}
|
|
}
|
|
|
|
protected static void OCB_extend(byte[] bArr, int i) {
|
|
bArr[i] = Byte.MIN_VALUE;
|
|
while (true) {
|
|
i++;
|
|
if (i >= 16) {
|
|
return;
|
|
} else {
|
|
bArr[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static byte[] OCB_double(byte[] bArr) {
|
|
byte[] bArr2 = new byte[16];
|
|
bArr2[15] = (byte) ((135 >>> ((1 - shiftLeft(bArr, bArr2)) << 3)) ^ bArr2[15]);
|
|
return bArr2;
|
|
}
|
|
|
|
public OCBBlockCipher(BlockCipher blockCipher, BlockCipher blockCipher2) {
|
|
if (blockCipher == null) {
|
|
throw new IllegalArgumentException("'hashCipher' cannot be null");
|
|
}
|
|
if (blockCipher.getBlockSize() != 16) {
|
|
throw new IllegalArgumentException("'hashCipher' must have a block size of 16");
|
|
}
|
|
if (blockCipher2 == null) {
|
|
throw new IllegalArgumentException("'mainCipher' cannot be null");
|
|
}
|
|
if (blockCipher2.getBlockSize() != 16) {
|
|
throw new IllegalArgumentException("'mainCipher' must have a block size of 16");
|
|
}
|
|
if (!blockCipher.getAlgorithmName().equals(blockCipher2.getAlgorithmName())) {
|
|
throw new IllegalArgumentException("'hashCipher' and 'mainCipher' must be the same algorithm");
|
|
}
|
|
this.hashCipher = blockCipher;
|
|
this.mainCipher = blockCipher2;
|
|
}
|
|
}
|