1package org.bouncycastle.crypto.modes;
2
3import org.bouncycastle.crypto.BlockCipher;
4import org.bouncycastle.crypto.CipherParameters;
5import org.bouncycastle.crypto.DataLengthException;
6import org.bouncycastle.crypto.InvalidCipherTextException;
7import org.bouncycastle.crypto.OutputLengthException;
8import org.bouncycastle.crypto.modes.gcm.GCMExponentiator;
9import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
10import org.bouncycastle.crypto.modes.gcm.GCMUtil;
11import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
12import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
13import org.bouncycastle.crypto.params.AEADParameters;
14import org.bouncycastle.crypto.params.KeyParameter;
15import org.bouncycastle.crypto.params.ParametersWithIV;
16import org.bouncycastle.util.Arrays;
17import org.bouncycastle.util.Pack;
18
19/**
20 * Implements the Galois/Counter mode (GCM) detailed in
21 * NIST Special Publication 800-38D.
22 */
23public class GCMBlockCipher
24    implements AEADBlockCipher
25{
26    private static final int BLOCK_SIZE = 16;
27    // BEGIN android-added
28    // 2^36-32 : limitation imposed by NIST GCM as otherwise the counter is wrapped and it can leak
29    // plaintext and authentication key
30    private static final long MAX_INPUT_SIZE = 68719476704L;
31    // END android-added
32
33    // not final due to a compiler bug
34    private BlockCipher   cipher;
35    private GCMMultiplier multiplier;
36    private GCMExponentiator exp;
37
38    // These fields are set by init and not modified by processing
39    private boolean             forEncryption;
40    private int                 macSize;
41    private byte[]              lastKey;
42    private byte[]              nonce;
43    private byte[]              initialAssociatedText;
44    private byte[]              H;
45    private byte[]              J0;
46
47    // These fields are modified during processing
48    private byte[]      bufBlock;
49    private byte[]      macBlock;
50    private byte[]      S, S_at, S_atPre;
51    private byte[]      counter;
52    private int         blocksRemaining;
53    private int         bufOff;
54    private long        totalLength;
55    private byte[]      atBlock;
56    private int         atBlockPos;
57    private long        atLength;
58    private long        atLengthPre;
59
60    public GCMBlockCipher(BlockCipher c)
61    {
62        this(c, null);
63    }
64
65    public GCMBlockCipher(BlockCipher c, GCMMultiplier m)
66    {
67        if (c.getBlockSize() != BLOCK_SIZE)
68        {
69            throw new IllegalArgumentException(
70                "cipher required with a block size of " + BLOCK_SIZE + ".");
71        }
72
73        if (m == null)
74        {
75            // TODO Consider a static property specifying default multiplier
76            m = new Tables8kGCMMultiplier();
77        }
78
79        this.cipher = c;
80        this.multiplier = m;
81    }
82
83    public BlockCipher getUnderlyingCipher()
84    {
85        return cipher;
86    }
87
88    public String getAlgorithmName()
89    {
90        return cipher.getAlgorithmName() + "/GCM";
91    }
92
93    /**
94     * NOTE: MAC sizes from 32 bits to 128 bits (must be a multiple of 8) are supported. The default is 128 bits.
95     * Sizes less than 96 are not recommended, but are supported for specialized applications.
96     */
97    public void init(boolean forEncryption, CipherParameters params)
98        throws IllegalArgumentException
99    {
100        this.forEncryption = forEncryption;
101        this.macBlock = null;
102
103        KeyParameter keyParam;
104        byte[] newNonce = null;
105
106        if (params instanceof AEADParameters)
107        {
108            AEADParameters param = (AEADParameters)params;
109
110            newNonce = param.getNonce();
111            initialAssociatedText = param.getAssociatedText();
112
113            int macSizeBits = param.getMacSize();
114            if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0)
115            {
116                throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
117            }
118
119            macSize = macSizeBits / 8;
120            keyParam = param.getKey();
121        }
122        else if (params instanceof ParametersWithIV)
123        {
124            ParametersWithIV param = (ParametersWithIV)params;
125
126            newNonce = param.getIV();
127            initialAssociatedText  = null;
128            macSize = 16;
129            keyParam = (KeyParameter)param.getParameters();
130        }
131        else
132        {
133            throw new IllegalArgumentException("invalid parameters passed to GCM");
134        }
135
136        int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize);
137        this.bufBlock = new byte[bufLength];
138
139        if (newNonce == null || newNonce.length < 1)
140        {
141            throw new IllegalArgumentException("IV must be at least 1 byte");
142        }
143
144        if (forEncryption)
145        {
146            if (nonce != null && Arrays.areEqual(nonce, newNonce))
147            {
148                if (keyParam == null)
149                {
150                    throw new IllegalArgumentException("cannot reuse nonce for GCM encryption");
151                }
152                if (lastKey != null && Arrays.areEqual(lastKey, keyParam.getKey()))
153                {
154                    throw new IllegalArgumentException("cannot reuse nonce for GCM encryption");
155                }
156            }
157        }
158
159        nonce = newNonce;
160        if (keyParam != null)
161        {
162            lastKey = keyParam.getKey();
163        }
164
165        // TODO Restrict macSize to 16 if nonce length not 12?
166
167        // Cipher always used in forward mode
168        // if keyParam is null we're reusing the last key.
169        if (keyParam != null)
170        {
171            cipher.init(true, keyParam);
172
173            this.H = new byte[BLOCK_SIZE];
174            cipher.processBlock(H, 0, H, 0);
175
176            // GCMMultiplier tables don't change unless the key changes (and are expensive to init)
177            multiplier.init(H);
178            exp = null;
179        }
180        else if (this.H == null)
181        {
182            throw new IllegalArgumentException("Key must be specified in initial init");
183        }
184
185        this.J0 = new byte[BLOCK_SIZE];
186
187        if (nonce.length == 12)
188        {
189            System.arraycopy(nonce, 0, J0, 0, nonce.length);
190            this.J0[BLOCK_SIZE - 1] = 0x01;
191        }
192        else
193        {
194            gHASH(J0, nonce, nonce.length);
195            byte[] X = new byte[BLOCK_SIZE];
196            Pack.longToBigEndian((long)nonce.length * 8, X, 8);
197            gHASHBlock(J0, X);
198        }
199
200        this.S = new byte[BLOCK_SIZE];
201        this.S_at = new byte[BLOCK_SIZE];
202        this.S_atPre = new byte[BLOCK_SIZE];
203        this.atBlock = new byte[BLOCK_SIZE];
204        this.atBlockPos = 0;
205        this.atLength = 0;
206        this.atLengthPre = 0;
207        this.counter = Arrays.clone(J0);
208        this.blocksRemaining = -2;      // page 8, len(P) <= 2^39 - 256, 1 block used by tag but done on J0
209        this.bufOff = 0;
210        this.totalLength = 0;
211
212        if (initialAssociatedText != null)
213        {
214            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
215        }
216    }
217
218    public byte[] getMac()
219    {
220        if (macBlock == null)
221        {
222            return new byte[macSize];
223        }
224        return Arrays.clone(macBlock);
225    }
226
227    public int getOutputSize(int len)
228    {
229        int totalData = len + bufOff;
230
231        if (forEncryption)
232        {
233            return totalData + macSize;
234        }
235
236        return totalData < macSize ? 0 : totalData - macSize;
237    }
238
239    // BEGIN android-added
240    /** Helper used to ensure that {@link #MAX_INPUT_SIZE} is not exceeded. */
241    private long getTotalInputSizeAfterNewInput(int newInputLen)
242    {
243        return totalLength + newInputLen + bufOff;
244    }
245    // END android-added
246
247    public int getUpdateOutputSize(int len)
248    {
249        int totalData = len + bufOff;
250        if (!forEncryption)
251        {
252            if (totalData < macSize)
253            {
254                return 0;
255            }
256            totalData -= macSize;
257        }
258        return totalData - totalData % BLOCK_SIZE;
259    }
260
261    public void processAADByte(byte in)
262    {
263        // BEGIN android-added
264        if (getTotalInputSizeAfterNewInput(1) > MAX_INPUT_SIZE) {
265            throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes");
266        }
267        // END android-added
268        atBlock[atBlockPos] = in;
269        if (++atBlockPos == BLOCK_SIZE)
270        {
271            // Hash each block as it fills
272            gHASHBlock(S_at, atBlock);
273            atBlockPos = 0;
274            atLength += BLOCK_SIZE;
275        }
276    }
277
278    public void processAADBytes(byte[] in, int inOff, int len)
279    {
280        // BEGIN android-added
281        if (getTotalInputSizeAfterNewInput(len) > MAX_INPUT_SIZE) {
282            throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes");
283        }
284        // END android-added
285        for (int i = 0; i < len; ++i)
286        {
287            atBlock[atBlockPos] = in[inOff + i];
288            if (++atBlockPos == BLOCK_SIZE)
289            {
290                // Hash each block as it fills
291                gHASHBlock(S_at, atBlock);
292                atBlockPos = 0;
293                atLength += BLOCK_SIZE;
294            }
295        }
296    }
297
298    private void initCipher()
299    {
300        if (atLength > 0)
301        {
302            System.arraycopy(S_at, 0, S_atPre, 0, BLOCK_SIZE);
303            atLengthPre = atLength;
304        }
305
306        // Finish hash for partial AAD block
307        if (atBlockPos > 0)
308        {
309            gHASHPartial(S_atPre, atBlock, 0, atBlockPos);
310            atLengthPre += atBlockPos;
311        }
312
313        if (atLengthPre > 0)
314        {
315            System.arraycopy(S_atPre, 0, S, 0, BLOCK_SIZE);
316        }
317    }
318
319    public int processByte(byte in, byte[] out, int outOff)
320        throws DataLengthException
321    {
322        // BEGIN android-added
323        if (getTotalInputSizeAfterNewInput(1) > MAX_INPUT_SIZE) {
324            throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes");
325        }
326        // END android-added
327        bufBlock[bufOff] = in;
328        if (++bufOff == bufBlock.length)
329        {
330            outputBlock(out, outOff);
331            return BLOCK_SIZE;
332        }
333        return 0;
334    }
335
336    public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
337        throws DataLengthException
338    {
339        // BEGIN android-added
340        if (getTotalInputSizeAfterNewInput(len) > MAX_INPUT_SIZE) {
341            throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes");
342        }
343        // END android-added
344        if (in.length < (inOff + len))
345        {
346            throw new DataLengthException("Input buffer too short");
347        }
348        int resultLen = 0;
349
350        for (int i = 0; i < len; ++i)
351        {
352            bufBlock[bufOff] = in[inOff + i];
353            if (++bufOff == bufBlock.length)
354            {
355                outputBlock(out, outOff + resultLen);
356                resultLen += BLOCK_SIZE;
357            }
358        }
359
360        return resultLen;
361    }
362
363    private void outputBlock(byte[] output, int offset)
364    {
365        if (output.length < (offset + BLOCK_SIZE))
366        {
367            throw new OutputLengthException("Output buffer too short");
368        }
369        if (totalLength == 0)
370        {
371            initCipher();
372        }
373        gCTRBlock(bufBlock, output, offset);
374        if (forEncryption)
375        {
376            bufOff = 0;
377        }
378        else
379        {
380            System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
381            bufOff = macSize;
382        }
383    }
384
385    public int doFinal(byte[] out, int outOff)
386        throws IllegalStateException, InvalidCipherTextException
387    {
388        if (totalLength == 0)
389        {
390            initCipher();
391        }
392
393        int extra = bufOff;
394
395        if (forEncryption)
396        {
397            if (out.length < (outOff + extra + macSize))
398            {
399                throw new OutputLengthException("Output buffer too short");
400            }
401        }
402        else
403        {
404            if (extra < macSize)
405            {
406                throw new InvalidCipherTextException("data too short");
407            }
408            extra -= macSize;
409
410            if (out.length < (outOff + extra))
411            {
412                throw new OutputLengthException("Output buffer too short");
413            }
414        }
415
416        if (extra > 0)
417        {
418            gCTRPartial(bufBlock, 0, extra, out, outOff);
419        }
420
421        atLength += atBlockPos;
422
423        if (atLength > atLengthPre)
424        {
425            /*
426             *  Some AAD was sent after the cipher started. We determine the difference b/w the hash value
427             *  we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at).
428             *  Then we carry this difference forward by multiplying by H^c, where c is the number of (full or
429             *  partial) cipher-text blocks produced, and adjust the current hash.
430             */
431
432            // Finish hash for partial AAD block
433            if (atBlockPos > 0)
434            {
435                gHASHPartial(S_at, atBlock, 0, atBlockPos);
436            }
437
438            // Find the difference between the AAD hashes
439            if (atLengthPre > 0)
440            {
441                GCMUtil.xor(S_at, S_atPre);
442            }
443
444            // Number of cipher-text blocks produced
445            long c = ((totalLength * 8) + 127) >>> 7;
446
447            // Calculate the adjustment factor
448            byte[] H_c = new byte[16];
449            if (exp == null)
450            {
451                exp = new Tables1kGCMExponentiator();
452                exp.init(H);
453            }
454            exp.exponentiateX(c, H_c);
455
456            // Carry the difference forward
457            GCMUtil.multiply(S_at, H_c);
458
459            // Adjust the current hash
460            GCMUtil.xor(S, S_at);
461        }
462
463        // Final gHASH
464        byte[] X = new byte[BLOCK_SIZE];
465        Pack.longToBigEndian(atLength * 8, X, 0);
466        Pack.longToBigEndian(totalLength * 8, X, 8);
467
468        gHASHBlock(S, X);
469
470        // T = MSBt(GCTRk(J0,S))
471        byte[] tag = new byte[BLOCK_SIZE];
472        cipher.processBlock(J0, 0, tag, 0);
473        GCMUtil.xor(tag, S);
474
475        int resultLen = extra;
476
477        // We place into macBlock our calculated value for T
478        this.macBlock = new byte[macSize];
479        System.arraycopy(tag, 0, macBlock, 0, macSize);
480
481        if (forEncryption)
482        {
483            // Append T to the message
484            System.arraycopy(macBlock, 0, out, outOff + bufOff, macSize);
485            resultLen += macSize;
486        }
487        else
488        {
489            // Retrieve the T value from the message and compare to calculated one
490            byte[] msgMac = new byte[macSize];
491            System.arraycopy(bufBlock, extra, msgMac, 0, macSize);
492            if (!Arrays.constantTimeAreEqual(this.macBlock, msgMac))
493            {
494                throw new InvalidCipherTextException("mac check in GCM failed");
495            }
496        }
497
498        reset(false);
499
500        return resultLen;
501    }
502
503    public void reset()
504    {
505        reset(true);
506    }
507
508    private void reset(
509        boolean clearMac)
510    {
511        cipher.reset();
512
513        // note: we do not reset the nonce.
514
515        S = new byte[BLOCK_SIZE];
516        S_at = new byte[BLOCK_SIZE];
517        S_atPre = new byte[BLOCK_SIZE];
518        atBlock = new byte[BLOCK_SIZE];
519        atBlockPos = 0;
520        atLength = 0;
521        atLengthPre = 0;
522        counter = Arrays.clone(J0);
523        blocksRemaining = -2;
524        bufOff = 0;
525        totalLength = 0;
526
527        if (bufBlock != null)
528        {
529            Arrays.fill(bufBlock, (byte)0);
530        }
531
532        if (clearMac)
533        {
534            macBlock = null;
535        }
536
537        if (initialAssociatedText != null)
538        {
539            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
540        }
541    }
542
543    private void gCTRBlock(byte[] block, byte[] out, int outOff)
544    {
545        byte[] tmp = getNextCounterBlock();
546
547        GCMUtil.xor(tmp, block);
548        System.arraycopy(tmp, 0, out, outOff, BLOCK_SIZE);
549
550        gHASHBlock(S, forEncryption ? tmp : block);
551
552        totalLength += BLOCK_SIZE;
553    }
554
555    private void gCTRPartial(byte[] buf, int off, int len, byte[] out, int outOff)
556    {
557        byte[] tmp = getNextCounterBlock();
558
559        GCMUtil.xor(tmp, buf, off, len);
560        System.arraycopy(tmp, 0, out, outOff, len);
561
562        gHASHPartial(S, forEncryption ? tmp : buf, 0, len);
563
564        totalLength += len;
565    }
566
567    private void gHASH(byte[] Y, byte[] b, int len)
568    {
569        for (int pos = 0; pos < len; pos += BLOCK_SIZE)
570        {
571            int num = Math.min(len - pos, BLOCK_SIZE);
572            gHASHPartial(Y, b, pos, num);
573        }
574    }
575
576    private void gHASHBlock(byte[] Y, byte[] b)
577    {
578        GCMUtil.xor(Y, b);
579        multiplier.multiplyH(Y);
580    }
581
582    private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
583    {
584        GCMUtil.xor(Y, b, off, len);
585        multiplier.multiplyH(Y);
586    }
587
588    private byte[] getNextCounterBlock()
589    {
590        if (blocksRemaining == 0)
591        {
592            throw new IllegalStateException("Attempt to process too many blocks");
593        }
594        blocksRemaining--;
595
596        int c = 1;
597        c += counter[15] & 0xFF; counter[15] = (byte)c; c >>>= 8;
598        c += counter[14] & 0xFF; counter[14] = (byte)c; c >>>= 8;
599        c += counter[13] & 0xFF; counter[13] = (byte)c; c >>>= 8;
600        c += counter[12] & 0xFF; counter[12] = (byte)c;
601
602        byte[] tmp = new byte[BLOCK_SIZE];
603        // TODO Sure would be nice if ciphers could operate on int[]
604        cipher.processBlock(counter, 0, tmp, 0);
605        return tmp;
606    }
607}
608