All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.pqc.legacy.crypto.mceliece.McElieceKobaraImaiCipher Maven / Gradle / Ivy

Go to download

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;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy