summaryrefslogtreecommitdiffstats
path: root/bcprov/src/main/java/org/bouncycastle/pqc/crypto/mceliece/McEliecePKCSCipher.java
blob: b4eecfb39ac0ee337a8fb71b50b41cef50af963e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package org.bouncycastle.pqc.crypto.mceliece;

import java.security.SecureRandom;

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.crypto.MessageEncryptor;
import org.bouncycastle.pqc.math.linearalgebra.GF2Matrix;
import org.bouncycastle.pqc.math.linearalgebra.GF2Vector;
import org.bouncycastle.pqc.math.linearalgebra.GF2mField;
import org.bouncycastle.pqc.math.linearalgebra.GoppaCode;
import org.bouncycastle.pqc.math.linearalgebra.Permutation;
import org.bouncycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM;
import org.bouncycastle.pqc.math.linearalgebra.Vector;

/**
 * This class implements the McEliece Public Key cryptosystem (McEliecePKCS). It
 * was first described in R.J. McEliece, "A public key cryptosystem based on
 * algebraic coding theory", DSN progress report, 42-44:114-116, 1978. The
 * McEliecePKCS is the first cryptosystem which is based on error correcting
 * codes. The trapdoor for the McEliece cryptosystem using Goppa codes is the
 * knowledge of the Goppa polynomial used to generate the code.
 */
public class McEliecePKCSCipher
    implements MessageEncryptor
{

    /**
     * The OID of the algorithm.
     */
    public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.1";


    // the source of randomness
    private SecureRandom sr;

    // the McEliece main parameters
    private int n, k, t;

    // The maximum number of bytes the cipher can decrypt
    public int maxPlainTextSize;

    // The maximum number of bytes the cipher can encrypt
    public int cipherTextSize;

    McElieceKeyParameters key;


    public void init(boolean forSigning,
                     CipherParameters param)
    {

        if (forSigning)
        {
            if (param instanceof ParametersWithRandom)
            {
                ParametersWithRandom rParam = (ParametersWithRandom)param;

                this.sr = rParam.getRandom();
                this.key = (McEliecePublicKeyParameters)rParam.getParameters();
                this.initCipherEncrypt((McEliecePublicKeyParameters)key);

            }
            else
            {
                this.sr = new SecureRandom();
                this.key = (McEliecePublicKeyParameters)param;
                this.initCipherEncrypt((McEliecePublicKeyParameters)key);
            }
        }
        else
        {
            this.key = (McEliecePrivateKeyParameters)param;
            this.initCipherDecrypt((McEliecePrivateKeyParameters)key);
        }

    }


    /**
     * Return the key size of the given key object.
     *
     * @param key the McElieceKeyParameters object
     * @return the keysize of the given key object
     */

    public int getKeySize(McElieceKeyParameters key)
    {

        if (key instanceof McEliecePublicKeyParameters)
        {
            return ((McEliecePublicKeyParameters)key).getN();

        }
        if (key instanceof McEliecePrivateKeyParameters)
        {
            return ((McEliecePrivateKeyParameters)key).getN();
        }
        throw new IllegalArgumentException("unsupported type");

    }


    public void initCipherEncrypt(McEliecePublicKeyParameters pubKey)
    {
        this.sr = sr != null ? sr : new SecureRandom();
        n = pubKey.getN();
        k = pubKey.getK();
        t = pubKey.getT();
        cipherTextSize = n >> 3;
        maxPlainTextSize = (k >> 3);
    }


    public void initCipherDecrypt(McEliecePrivateKeyParameters privKey)
    {
        n = privKey.getN();
        k = privKey.getK();

        maxPlainTextSize = (k >> 3);
        cipherTextSize = n >> 3;
    }

    /**
     * Encrypt a plain text.
     *
     * @param input the plain text
     * @return the cipher text
     */
    public byte[] messageEncrypt(byte[] input)
    {
        GF2Vector m = computeMessageRepresentative(input);
        GF2Vector z = new GF2Vector(n, t, sr);

        GF2Matrix g = ((McEliecePublicKeyParameters)key).getG();
        Vector mG = g.leftMultiply(m);
        GF2Vector mGZ = (GF2Vector)mG.add(z);

        return mGZ.getEncoded();
    }

    private GF2Vector computeMessageRepresentative(byte[] input)
    {
        byte[] data = new byte[maxPlainTextSize + ((k & 0x07) != 0 ? 1 : 0)];
        System.arraycopy(input, 0, data, 0, input.length);
        data[input.length] = 0x01;
        return GF2Vector.OS2VP(k, data);
    }

    /**
     * Decrypt a cipher text.
     *
     * @param input the cipher text
     * @return the plain text
     * @throws Exception if the cipher text is invalid.
     */
    public byte[] messageDecrypt(byte[] input)
        throws Exception
    {
        GF2Vector vec = GF2Vector.OS2VP(n, input);
        McEliecePrivateKeyParameters privKey = (McEliecePrivateKeyParameters)key;
        GF2mField field = privKey.getField();
        PolynomialGF2mSmallM gp = privKey.getGoppaPoly();
        GF2Matrix sInv = privKey.getSInv();
        Permutation p1 = privKey.getP1();
        Permutation p2 = privKey.getP2();
        GF2Matrix h = privKey.getH();
        PolynomialGF2mSmallM[] qInv = privKey.getQInv();

        // compute permutation P = P1 * P2
        Permutation p = p1.rightMultiply(p2);

        // compute P^-1
        Permutation pInv = p.computeInverse();

        // compute c P^-1
        GF2Vector cPInv = (GF2Vector)vec.multiply(pInv);

        // compute syndrome of c P^-1
        GF2Vector syndrome = (GF2Vector)h.rightMultiply(cPInv);

        // decode syndrome
        GF2Vector z = GoppaCode.syndromeDecode(syndrome, field, gp, qInv);
        GF2Vector mSG = (GF2Vector)cPInv.add(z);

        // multiply codeword with P1 and error vector with P
        mSG = (GF2Vector)mSG.multiply(p1);
        z = (GF2Vector)z.multiply(p);

        // extract mS (last k columns of mSG)
        GF2Vector mS = mSG.extractRightVector(k);

        // compute plaintext vector
        GF2Vector mVec = (GF2Vector)sInv.leftMultiply(mS);

        // compute and return plaintext
        return computeMessage(mVec);
    }

    private byte[] computeMessage(GF2Vector mr)
        throws Exception
    {
        byte[] mrBytes = mr.getEncoded();
        // find first non-zero byte
        int index;
        for (index = mrBytes.length - 1; index >= 0 && mrBytes[index] == 0; index--)
        {
            ;
        }

        // check if padding byte is valid
        if (index<0 || mrBytes[index] != 0x01)
        {
            throw new Exception("Bad Padding: invalid ciphertext");
        }

        // extract and return message
        byte[] mBytes = new byte[index];
        System.arraycopy(mrBytes, 0, mBytes, 0, index);
        return mBytes;
    }


}