package org.bouncycastle.crypto.engines;
import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.util.Pack;
public class SM2Engine {
private final Digest digest;
private final SM2Engine.Mode mode;
private boolean forEncryption;
private ECKeyParameters ecKey;
private ECDomainParameters ecParams;
private int curveLength;
private SecureRandom random;
public SM2Engine() {
this((Digest)(new SM3Digest()));
}
public SM2Engine(SM2Engine.Mode var1) {
this(new SM3Digest(), var1);
}
public SM2Engine(Digest var1) {
this(var1, SM2Engine.Mode.C1C2C3);
}
public SM2Engine(Digest var1, SM2Engine.Mode var2) {
if (var2 == null) {
throw new IllegalArgumentException("mode cannot be NULL");
} else {
this.digest = var1;
this.mode = var2;
}
}
public void init(boolean var1, CipherParameters var2) {
this.forEncryption = var1;
if (var1) {
ParametersWithRandom var3 = (ParametersWithRandom)var2;
this.ecKey = (ECKeyParameters)var3.getParameters();
this.ecParams = this.ecKey.getParameters();
ECPoint var4 = ((ECPublicKeyParameters)this.ecKey).getQ().multiply(this.ecParams.getH());
if (var4.isInfinity()) {
throw new IllegalArgumentException("invalid key: [h]Q at infinity");
}
this.random = var3.getRandom();
} else {
this.ecKey = (ECKeyParameters)var2;
this.ecParams = this.ecKey.getParameters();
}
this.curveLength = (this.ecParams.getCurve().getFieldSize() + 7) / 8;
}
public byte[] processBlock(byte[] var1, int var2, int var3) throws InvalidCipherTextException {
return this.forEncryption ? this.encrypt(var1, var2, var3) : this.decrypt(var1, var2, var3);
}
public int getOutputSize(int var1) {
return 1 + 2 * this.curveLength + var1 + this.digest.getDigestSize();
}
protected ECMultiplier createBasePointMultiplier() {
return new FixedPointCombMultiplier();
}
private byte[] encrypt(byte[] var1, int var2, int var3) throws InvalidCipherTextException {
byte[] var4 = new byte[var3];
System.arraycopy(var1, var2, var4, 0, var4.length);
ECMultiplier var5 = this.createBasePointMultiplier();
byte[] var6;
ECPoint var7;
do {
BigInteger var8 = this.nextK();
ECPoint var9 = var5.multiply(this.ecParams.getG(), var8).normalize();
var6 = var9.getEncoded(false);
var7 = ((ECPublicKeyParameters)this.ecKey).getQ().multiply(var8).normalize();
this.kdf(this.digest, var7, var4);
} while(this.notEncrypted(var4, var1, var2));
byte[] var10 = new byte[this.digest.getDigestSize()];
this.addFieldElement(this.digest, var7.getAffineXCoord());
this.digest.update(var1, var2, var3);
this.addFieldElement(this.digest, var7.getAffineYCoord());
this.digest.doFinal(var10, 0);
switch(this.mode) {
case C1C3C2:
return Arrays.concatenate(var6, var10, var4);
default:
return Arrays.concatenate(var6, var4, var10);
}
}
private byte[] decrypt(byte[] var1, int var2, int var3) throws InvalidCipherTextException {
byte[] var4 = new byte[this.curveLength * 2 + 1];
System.arraycopy(var1, var2, var4, 0, var4.length);
ECPoint var5 = this.ecParams.getCurve().decodePoint(var4);
ECPoint var6 = var5.multiply(this.ecParams.getH());
if (var6.isInfinity()) {
throw new InvalidCipherTextException("[h]C1 at infinity");
} else {
var5 = var5.multiply(((ECPrivateKeyParameters)this.ecKey).getD()).normalize();
int var7 = this.digest.getDigestSize();
byte[] var8 = new byte[var3 - var4.length - var7];
if (this.mode == SM2Engine.Mode.C1C3C2) {
System.arraycopy(var1, var2 + var4.length + var7, var8, 0, var8.length);
} else {
System.arraycopy(var1, var2 + var4.length, var8, 0, var8.length);
}
this.kdf(this.digest, var5, var8);
byte[] var9 = new byte[this.digest.getDigestSize()];
this.addFieldElement(this.digest, var5.getAffineXCoord());
this.digest.update(var8, 0, var8.length);
this.addFieldElement(this.digest, var5.getAffineYCoord());
this.digest.doFinal(var9, 0);
int var10 = 0;
int var11;
if (this.mode == SM2Engine.Mode.C1C3C2) {
for(var11 = 0; var11 != var9.length; ++var11) {
var10 |= var9[var11] ^ var1[var2 + var4.length + var11];
}
} else {
for(var11 = 0; var11 != var9.length; ++var11) {
var10 |= var9[var11] ^ var1[var2 + var4.length + var8.length + var11];
}
}
Arrays.fill(var4, (byte)0);
Arrays.fill(var9, (byte)0);
if (var10 != 0) {
Arrays.fill(var8, (byte)0);
throw new InvalidCipherTextException("invalid cipher text");
} else {
return var8;
}
}
}
private boolean notEncrypted(byte[] var1, byte[] var2, int var3) {
for(int var4 = 0; var4 != var1.length; ++var4) {
if (var1[var4] != var2[var3 + var4]) {
return false;
}
}
return true;
}
private void kdf(Digest var1, ECPoint var2, byte[] var3) {
int var4 = var1.getDigestSize();
byte[] var5 = new byte[Math.max(4, var4)];
int var6 = 0;
Memoable var7 = null;
Memoable var8 = null;
if (var1 instanceof Memoable) {
this.addFieldElement(var1, var2.getAffineXCoord());
this.addFieldElement(var1, var2.getAffineYCoord());
var7 = (Memoable)var1;
var8 = var7.copy();
}
int var10;
for(int var9 = 0; var6 < var3.length; var6 += var10) {
if (var7 != null) {
var7.reset(var8);
} else {
this.addFieldElement(var1, var2.getAffineXCoord());
this.addFieldElement(var1, var2.getAffineYCoord());
}
++var9;
Pack.intToBigEndian(var9, var5, 0);
var1.update(var5, 0, 4);
var1.doFinal(var5, 0);
var10 = Math.min(var4, var3.length - var6);
this.xor(var3, var5, var6, var10);
}
}
private void xor(byte[] var1, byte[] var2, int var3, int var4) {
for(int var5 = 0; var5 != var4; ++var5) {
var1[var3 + var5] ^= var2[var5];
}
}
private BigInteger nextK() {
int var1 = this.ecParams.getN().bitLength();
BigInteger var2;
do {
do {
var2 = BigIntegers.createRandomBigInteger(var1, this.random);
} while(var2.equals(BigIntegers.ZERO));
} while(var2.compareTo(this.ecParams.getN()) >= 0);
return var2;
}
private void addFieldElement(Digest var1, ECFieldElement var2) {
byte[] var3 = BigIntegers.asUnsignedByteArray(this.curveLength, var2.toBigInteger());
var1.update(var3, 0, var3.length);
}
public static enum Mode {
C1C2C3,
C1C3C2;
private Mode() {
}
}
}
package org.bouncycastle.crypto.engines;
import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.util.Pack;
public class SM2Engine {
private final Digest digest;
private final SM2Engine.Mode mode;
private boolean forEncryption;
private ECKeyParameters ecKey;
private ECDomainParameters ecParams;
private int curveLength;
private SecureRandom random;
public SM2Engine() {
this((Digest)(new SM3Digest()));
}
public SM2Engine(SM2Engine.Mode var1) {
this(new SM3Digest(), var1);
}
public SM2Engine(Digest var1) {
this(var1, SM2Engine.Mode.C1C2C3);
}
public SM2Engine(Digest var1, SM2Engine.Mode var2) {
if (var2 == null) {
throw new IllegalArgumentException("mode cannot be NULL");
} else {
this.digest = var1;
this.mode = var2;
}
}
public void init(boolean var1, CipherParameters var2) {
this.forEncryption = var1;
if (var1) {
ParametersWithRandom var3 = (ParametersWithRandom)var2;
this.ecKey = (ECKeyParameters)var3.getParameters();
this.ecParams = this.ecKey.getParameters();
ECPoint var4 = ((ECPublicKeyParameters)this.ecKey).getQ().multiply(this.ecParams.getH());
if (var4.isInfinity()) {
throw new IllegalArgumentException("invalid key: [h]Q at infinity");
}
this.random = var3.getRandom();
} else {
this.ecKey = (ECKeyParameters)var2;
this.ecParams = this.ecKey.getParameters();
}
this.curveLength = (this.ecParams.getCurve().getFieldSize() + 7) / 8;
}
public byte[] processBlock(byte[] var1, int var2, int var3) throws InvalidCipherTextException {
return this.forEncryption ? this.encrypt(var1, var2, var3) : this.decrypt(var1, var2, var3);
}
public int getOutputSize(int var1) {
return 1 + 2 * this.curveLength + var1 + this.digest.getDigestSize();
}
protected ECMultiplier createBasePointMultiplier() {
return new FixedPointCombMultiplier();
}
private byte[] encrypt(byte[] var1, int var2, int var3) throws InvalidCipherTextException {
byte[] var4 = new byte[var3];
System.arraycopy(var1, var2, var4, 0, var4.length);
ECMultiplier var5 = this.createBasePointMultiplier();
byte[] var6;
ECPoint var7;
do {
BigInteger var8 = this.nextK();
ECPoint var9 = var5.multiply(this.ecParams.getG(), var8).normalize();
var6 = var9.getEncoded(false);
var7 = ((ECPublicKeyParameters)this.ecKey).getQ().multiply(var8).normalize();
this.kdf(this.digest, var7, var4);
} while(this.notEncrypted(var4, var1, var2));
byte[] var10 = new byte[this.digest.getDigestSize()];
this.addFieldElement(this.digest, var7.getAffineXCoord());
this.digest.update(var1, var2, var3);
this.addFieldElement(this.digest, var7.getAffineYCoord());
this.digest.doFinal(var10, 0);
switch(this.mode) {
case C1C3C2:
return Arrays.concatenate(var6, var10, var4);
default:
return Arrays.concatenate(var6, var4, var10);
}
}
private byte[] decrypt(byte[] var1, int var2, int var3) throws InvalidCipherTextException {
byte[] var4 = new byte[this.curveLength * 2 + 1];
System.arraycopy(var1, var2, var4, 0, var4.length);
ECPoint var5 = this.ecParams.getCurve().decodePoint(var4);
ECPoint var6 = var5.multiply(this.ecParams.getH());
if (var6.isInfinity()) {
throw new InvalidCipherTextException("[h]C1 at infinity");
} else {
var5 = var5.multiply(((ECPrivateKeyParameters)this.ecKey).getD()).normalize();
int var7 = this.digest.getDigestSize();
byte[] var8 = new byte[var3 - var4.length - var7];
if (this.mode == SM2Engine.Mode.C1C3C2) {
System.arraycopy(var1, var2 + var4.length + var7, var8, 0, var8.length);
} else {
System.arraycopy(var1, var2 + var4.length, var8, 0, var8.length);
}
this.kdf(this.digest, var5, var8);
byte[] var9 = new byte[this.digest.getDigestSize()];
this.addFieldElement(this.digest, var5.getAffineXCoord());
this.digest.update(var8, 0, var8.length);
this.addFieldElement(this.digest, var5.getAffineYCoord());
this.digest.doFinal(var9, 0);
int var10 = 0;
int var11;
if (this.mode == SM2Engine.Mode.C1C3C2) {
for(var11 = 0; var11 != var9.length; ++var11) {
var10 |= var9[var11] ^ var1[var2 + var4.length + var11];
}
} else {
for(var11 = 0; var11 != var9.length; ++var11) {
var10 |= var9[var11] ^ var1[var2 + var4.length + var8.length + var11];
}
}
Arrays.fill(var4, (byte)0);
Arrays.fill(var9, (byte)0);
if (var10 != 0) {
Arrays.fill(var8, (byte)0);
throw new InvalidCipherTextException("invalid cipher text");
} else {
return var8;
}
}
}
private boolean notEncrypted(byte[] var1, byte[] var2, int var3) {
for(int var4 = 0; var4 != var1.length; ++var4) {
if (var1[var4] != var2[var3 + var4]) {
return false;
}
}
return true;
}
private void kdf(Digest var1, ECPoint var2, byte[] var3) {
int var4 = var1.getDigestSize();
byte[] var5 = new byte[Math.max(4, var4)];
int var6 = 0;
Memoable var7 = null;
Memoable var8 = null;
if (var1 instanceof Memoable) {
this.addFieldElement(var1, var2.getAffineXCoord());
this.addFieldElement(var1, var2.getAffineYCoord());
var7 = (Memoable)var1;
var8 = var7.copy();
}
int var10;
for(int var9 = 0; var6 < var3.length; var6 += var10) {
if (var7 != null) {
var7.reset(var8);
} else {
this.addFieldElement(var1, var2.getAffineXCoord());
this.addFieldElement(var1, var2.getAffineYCoord());
}
++var9;
Pack.intToBigEndian(var9, var5, 0);
var1.update(var5, 0, 4);
var1.doFinal(var5, 0);
var10 = Math.min(var4, var3.length - var6);
this.xor(var3, var5, var6, var10);
}
}
private void xor(byte[] var1, byte[] var2, int var3, int var4) {
for(int var5 = 0; var5 != var4; ++var5) {
var1[var3 + var5] ^= var2[var5];
}
}
private BigInteger nextK() {
int var1 = this.ecParams.getN().bitLength();
BigInteger var2;
do {
do {
var2 = BigIntegers.createRandomBigInteger(var1, this.random);
} while(var2.equals(BigIntegers.ZERO));
} while(var2.compareTo(this.ecParams.getN()) >= 0);
return var2;
}
private void addFieldElement(Digest var1, ECFieldElement var2) {
byte[] var3 = BigIntegers.asUnsignedByteArray(this.curveLength, var2.toBigInteger());
var1.update(var3, 0, var3.length);
}
public static enum Mode {
C1C2C3,
C1C3C2;
private Mode() {
}
}
}
package org.bouncycastle.util;
import java.math.BigInteger;
import java.security.SecureRandom;
public final class BigIntegers {
public static final BigInteger ZERO = BigInteger.valueOf(0L);
public static final BigInteger ONE = BigInteger.valueOf(1L);
private static final BigInteger TWO = BigInteger.valueOf(2L);
private static final BigInteger THREE = BigInteger.valueOf(3L);
private static final int MAX_ITERATIONS = 1000;
private static final BigInteger SMALL_PRIMES_PRODUCT = new BigInteger("8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f73d893fa424cd2edc8636a6c3285e022b0e3866a565ae8108eed8591cd4fe8d2ce86165a978d719ebf647f362d33fca29cd179fb42401cbaf3df0c614056f9c8f3cfd51e474afb6bc6974f78db8aba8e9e517fded658591ab7502bd41849462f", 16);
private static final int MAX_SMALL = BigInteger.valueOf(743L).bitLength();
public BigIntegers() {
}
public static byte[] asUnsignedByteArray(BigInteger var0) {
byte[] var1 = var0.toByteArray();
if (var1[0] == 0) {
byte[] var2 = new byte[var1.length - 1];
System.arraycopy(var1, 1, var2, 0, var2.length);
return var2;
} else {
return var1;
}
}
public static byte[] asUnsignedByteArray(int var0, BigInteger var1) {
byte[] var2 = var1.toByteArray();
if (var2.length == var0) {
return var2;
} else {
int var3 = var2[0] == 0 ? 1 : 0;
int var4 = var2.length - var3;
if (var4 > var0) {
throw new IllegalArgumentException("standard length exceeded for value");
} else {
byte[] var5 = new byte[var0];
System.arraycopy(var2, var3, var5, var5.length - var4, var4);
return var5;
}
}
}
public static void asUnsignedByteArray(BigInteger var0, byte[] var1, int var2, int var3) {
byte[] var4 = var0.toByteArray();
if (var4.length == var3) {
System.arraycopy(var4, 0, var1, var2, var3);
} else {
int var5 = var4[0] == 0 ? 1 : 0;
int var6 = var4.length - var5;
if (var6 > var3) {
throw new IllegalArgumentException("standard length exceeded for value");
} else {
int var7 = var3 - var6;
Arrays.fill(var1, var2, var2 + var7, (byte)0);
System.arraycopy(var4, var5, var1, var2 + var7, var6);
}
}
}
public static BigInteger createRandomInRange(BigInteger var0, BigInteger var1, SecureRandom var2) {
int var3 = var0.compareTo(var1);
if (var3 >= 0) {
if (var3 > 0) {
throw new IllegalArgumentException("'min' may not be greater than 'max'");
} else {
return var0;
}
} else if (var0.bitLength() > var1.bitLength() / 2) {
return createRandomInRange(ZERO, var1.subtract(var0), var2).add(var0);
} else {
for(int var4 = 0; var4 < 1000; ++var4) {
BigInteger var5 = createRandomBigInteger(var1.bitLength(), var2);
if (var5.compareTo(var0) >= 0 && var5.compareTo(var1) <= 0) {
return var5;
}
}
return createRandomBigInteger(var1.subtract(var0).bitLength() - 1, var2).add(var0);
}
}
public static BigInteger fromUnsignedByteArray(byte[] var0) {
return new BigInteger(1, var0);
}
public static BigInteger fromUnsignedByteArray(byte[] var0, int var1, int var2) {
byte[] var3 = var0;
if (var1 != 0 || var2 != var0.length) {
var3 = new byte[var2];
System.arraycopy(var0, var1, var3, 0, var2);
}
return new BigInteger(1, var3);
}
public static int intValueExact(BigInteger var0) {
if (var0.bitLength() > 31) {
throw new ArithmeticException("BigInteger out of int range");
} else {
return var0.intValue();
}
}
public static long longValueExact(BigInteger var0) {
if (var0.bitLength() > 63) {
throw new ArithmeticException("BigInteger out of long range");
} else {
return var0.longValue();
}
}
public static int getUnsignedByteLength(BigInteger var0) {
return (var0.bitLength() + 7) / 8;
}
public static BigInteger createRandomBigInteger(int var0, SecureRandom var1) {
return new BigInteger(1, createRandom(var0, var1));
}
public static BigInteger createRandomPrime(int var0, int var1, SecureRandom var2) {
if (var0 < 2) {
throw new IllegalArgumentException("bitLength < 2");
} else if (var0 == 2) {
return var2.nextInt() < 0 ? TWO : THREE;
} else {
BigInteger var3;
do {
byte[] var4 = createRandom(var0, var2);
int var5 = 8 * var4.length - var0;
byte var6 = (byte)(1 << 7 - var5);
var4[0] |= var6;
var4[var4.length - 1] = (byte)(var4[var4.length - 1] | 1);
var3 = new BigInteger(1, var4);
if (var0 > MAX_SMALL) {
while(!var3.gcd(SMALL_PRIMES_PRODUCT).equals(ONE)) {
var3 = var3.add(TWO);
}
}
} while(!var3.isProbablePrime(var1));
return var3;
}
}
private static byte[] createRandom(int var0, SecureRandom var1) throws IllegalArgumentException {
if (var0 < 1) {
throw new IllegalArgumentException("bitLength must be at least 1");
} else {
int var2 = (var0 + 7) / 8;
byte[] var3 = new byte[var2];
var1.nextBytes(var3);
int var4 = 8 * var2 - var0;
var3[0] &= (byte)(255 >>> var4);
return var3;
}
}
}
public static ECCurve convertCurve(EllipticCurve var0) {
ECField var1 = var0.getField();
BigInteger var2 = var0.getA();
BigInteger var3 = var0.getB();
if (var1 instanceof ECFieldFp) {
Fp var7 = new Fp(((ECFieldFp)var1).getP(), var2, var3);
return (ECCurve)(customCurves.containsKey(var7) ? (ECCurve)customCurves.get(var7) : var7);
} else {
ECFieldF2m var4 = (ECFieldF2m)var1;
int var5 = var4.getM();
int[] var6 = ECUtil.convertMidTerms(var4.getMidTermsOfReductionPolynomial());
return new F2m(var5, var6[0], var6[1], var6[2], var2, var3);
}
}