org.bouncycastle.pqc.legacy.crypto.mceliece.McElieceKobaraImaiCipher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-ext-debug-jdk18on Show documentation
Show all versions of bcprov-ext-debug-jdk18on Show documentation
The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for Java 1.8 and later with debug enabled.
The newest version!
package org.bouncycastle.pqc.legacy.crypto.mceliece;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.prng.DigestRandomGenerator;
import org.bouncycastle.pqc.crypto.MessageEncryptor;
import org.bouncycastle.pqc.legacy.math.linearalgebra.ByteUtils;
import org.bouncycastle.pqc.legacy.math.linearalgebra.GF2Vector;
import org.bouncycastle.pqc.legacy.math.linearalgebra.IntegerFunctions;
/**
* This class implements the Kobara/Imai conversion of the McEliecePKCS. This is
* a conversion of the McEliecePKCS which is CCA2-secure. For details, see D.
* Engelbert, R. Overbeck, A. Schmidt, "A Summary of McEliece-Type Cryptosystems and their Security", technical report.
* https://www.degruyter.com/document/doi/10.1515/JMC.2007.009/html
*/
public class McElieceKobaraImaiCipher
implements MessageEncryptor
{
/**
* The OID of the algorithm.
*/
public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.3";
private static final String DEFAULT_PRNG_NAME = "SHA1PRNG";
/**
* A predetermined public constant.
*/
public static final byte[] PUBLIC_CONSTANT = "a predetermined public constant"
.getBytes();
private Digest messDigest;
private SecureRandom sr;
McElieceCCA2KeyParameters key;
/**
* The McEliece main parameters
*/
private int n, k, t;
private boolean forEncryption;
public void init(boolean forEncryption,
CipherParameters param)
{
this.forEncryption = forEncryption;
if (forEncryption)
{
if (param instanceof ParametersWithRandom)
{
ParametersWithRandom rParam = (ParametersWithRandom)param;
this.sr = rParam.getRandom();
this.key = (McElieceCCA2PublicKeyParameters)rParam.getParameters();
this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
}
else
{
this.sr = CryptoServicesRegistrar.getSecureRandom();
this.key = (McElieceCCA2PublicKeyParameters)param;
this.initCipherEncrypt((McElieceCCA2PublicKeyParameters)key);
}
}
else
{
this.key = (McElieceCCA2PrivateKeyParameters)param;
this.initCipherDecrypt((McElieceCCA2PrivateKeyParameters)key);
}
}
/**
* Return the key size of the given key object.
*
* @param key the McElieceCCA2KeyParameters object
* @return the key size of the given key object
*/
public int getKeySize(McElieceCCA2KeyParameters key)
{
if (key instanceof McElieceCCA2PublicKeyParameters)
{
return ((McElieceCCA2PublicKeyParameters)key).getN();
}
if (key instanceof McElieceCCA2PrivateKeyParameters)
{
return ((McElieceCCA2PrivateKeyParameters)key).getN();
}
throw new IllegalArgumentException("unsupported type");
}
private void initCipherEncrypt(McElieceCCA2PublicKeyParameters pubKey)
{
this.messDigest = Utils.getDigest(pubKey.getDigest());
n = pubKey.getN();
k = pubKey.getK();
t = pubKey.getT();
}
private void initCipherDecrypt(McElieceCCA2PrivateKeyParameters privKey)
{
this.messDigest = Utils.getDigest(privKey.getDigest());
n = privKey.getN();
k = privKey.getK();
t = privKey.getT();
}
public byte[] messageEncrypt(byte[] input)
{
if (!forEncryption)
{
throw new IllegalStateException("cipher initialised for decryption");
}
int c2Len = messDigest.getDigestSize();
int c4Len = k >> 3;
int c5Len = (IntegerFunctions.binomial(n, t).bitLength() - 1) >> 3;
int mLen = c4Len + c5Len - c2Len - PUBLIC_CONSTANT.length;
if (input.length > mLen)
{
mLen = input.length;
}
int c1Len = mLen + PUBLIC_CONSTANT.length;
int c6Len = c1Len + c2Len - c4Len - c5Len;
// compute (m||const)
byte[] mConst = new byte[c1Len];
System.arraycopy(input, 0, mConst, 0, input.length);
System.arraycopy(PUBLIC_CONSTANT, 0, mConst, mLen,
PUBLIC_CONSTANT.length);
// generate random r of length c2Len bytes
byte[] r = new byte[c2Len];
sr.nextBytes(r);
// get PRNG object
// get PRNG object
DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
// seed PRNG with r'
sr0.addSeedMaterial(r);
// generate random sequence ...
byte[] c1 = new byte[c1Len];
sr0.nextBytes(c1);
// ... and XOR with (m||const) to obtain c1
for (int i = c1Len - 1; i >= 0; i--)
{
c1[i] ^= mConst[i];
}
// compute H(c1) ...
byte[] c2 = new byte[messDigest.getDigestSize()];
messDigest.update(c1, 0, c1.length);
messDigest.doFinal(c2, 0);
// ... and XOR with r
for (int i = c2Len - 1; i >= 0; i--)
{
c2[i] ^= r[i];
}
// compute (c2||c1)
byte[] c2c1 = ByteUtils.concatenate(c2, c1);
// split (c2||c1) into (c6||c5||c4), where c4Len is k/8 bytes, c5Len is
// floor[log(n|t)]/8 bytes, and c6Len is c1Len+c2Len-c4Len-c5Len (may be
// 0).
byte[] c6 = new byte[0];
if (c6Len > 0)
{
c6 = new byte[c6Len];
System.arraycopy(c2c1, 0, c6, 0, c6Len);
}
byte[] c5 = new byte[c5Len];
System.arraycopy(c2c1, c6Len, c5, 0, c5Len);
byte[] c4 = new byte[c4Len];
System.arraycopy(c2c1, c6Len + c5Len, c4, 0, c4Len);
// convert c4 to vector over GF(2)
GF2Vector c4Vec = GF2Vector.OS2VP(k, c4);
// convert c5 to error vector z
GF2Vector z = Conversions.encode(n, t, c5);
// compute encC4 = E(c4, z)
byte[] encC4 = McElieceCCA2Primitives.encryptionPrimitive((McElieceCCA2PublicKeyParameters)key,
c4Vec, z).getEncoded();
// if c6Len > 0
if (c6Len > 0)
{
// return (c6||encC4)
return ByteUtils.concatenate(c6, encC4);
}
// else, return encC4
return encC4;
}
public byte[] messageDecrypt(byte[] input)
throws InvalidCipherTextException
{
if (forEncryption)
{
throw new IllegalStateException("cipher initialised for decryption");
}
int nDiv8 = n >> 3;
if (input.length < nDiv8)
{
throw new InvalidCipherTextException("Bad Padding: Ciphertext too short.");
}
int c2Len = messDigest.getDigestSize();
int c4Len = k >> 3;
int c5Len = (IntegerFunctions.binomial(n, t).bitLength() - 1) >> 3;
int c6Len = input.length - nDiv8;
// split cipher text (c6||encC4), where c6 may be empty
byte[] c6, encC4;
if (c6Len > 0)
{
byte[][] c6EncC4 = ByteUtils.split(input, c6Len);
c6 = c6EncC4[0];
encC4 = c6EncC4[1];
}
else
{
c6 = new byte[0];
encC4 = input;
}
// convert encC4 into vector over GF(2)
GF2Vector encC4Vec = GF2Vector.OS2VP(n, encC4);
// decrypt encC4Vec to obtain c4 and error vector z
GF2Vector[] c4z = McElieceCCA2Primitives.decryptionPrimitive((McElieceCCA2PrivateKeyParameters)key,
encC4Vec);
byte[] c4 = c4z[0].getEncoded();
GF2Vector z = c4z[1];
// if length of c4 is greater than c4Len (because of padding) ...
if (c4.length > c4Len)
{
// ... truncate the padding bytes
c4 = ByteUtils.subArray(c4, 0, c4Len);
}
// compute c5 = Conv^-1(z)
byte[] c5 = Conversions.decode(n, t, z);
// if c5 is shorter than expected, pad with leading zeroes
if (c5.length < c5Len)
{
byte[] paddedC5 = new byte[c5Len];
System.arraycopy(c5, 0, paddedC5, c5Len - c5.length, c5.length);
c5 = paddedC5;
}
// compute (c6||c5||c4)
byte[] c6c5c4 = ByteUtils.concatenate(c6, c5);
c6c5c4 = ByteUtils.concatenate(c6c5c4, c4);
// split (c6||c5||c4) into (c2||c1), where c2Len = mdLen and c1Len =
// input.length-c2Len bytes.
int c1Len = c6c5c4.length - c2Len;
byte[][] c2c1 = ByteUtils.split(c6c5c4, c2Len);
byte[] c2 = c2c1[0];
byte[] c1 = c2c1[1];
// compute H(c1) ...
byte[] rPrime = new byte[messDigest.getDigestSize()];
messDigest.update(c1, 0, c1.length);
messDigest.doFinal(rPrime, 0);
// ... and XOR with c2 to obtain r'
for (int i = c2Len - 1; i >= 0; i--)
{
rPrime[i] ^= c2[i];
}
// get PRNG object
DigestRandomGenerator sr0 = new DigestRandomGenerator(new SHA1Digest());
// seed PRNG with r'
sr0.addSeedMaterial(rPrime);
// generate random sequence R(r') ...
byte[] mConstPrime = new byte[c1Len];
sr0.nextBytes(mConstPrime);
// ... and XOR with c1 to obtain (m||const')
for (int i = c1Len - 1; i >= 0; i--)
{
mConstPrime[i] ^= c1[i];
}
if (mConstPrime.length < c1Len)
{
throw new InvalidCipherTextException("Bad Padding: invalid ciphertext");
}
byte[][] temp = ByteUtils.split(mConstPrime, c1Len
- PUBLIC_CONSTANT.length);
byte[] mr = temp[0];
byte[] constPrime = temp[1];
if (!ByteUtils.equals(constPrime, PUBLIC_CONSTANT))
{
throw new InvalidCipherTextException("Bad Padding: invalid ciphertext");
}
return mr;
}
}