View Javadoc
1   /*
2    * GordianKnot: Security Suite
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.gordianknot.impl.ext.modes;
18  
19  import org.bouncycastle.crypto.BlockCipher;
20  import org.bouncycastle.crypto.CipherParameters;
21  import org.bouncycastle.crypto.DataLengthException;
22  import org.bouncycastle.crypto.InvalidCipherTextException;
23  import org.bouncycastle.crypto.OutputLengthException;
24  import org.bouncycastle.crypto.engines.AESEngine;
25  import org.bouncycastle.crypto.modes.AEADBlockCipher;
26  import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
27  import org.bouncycastle.crypto.modes.gcm.Tables4kGCMMultiplier;
28  import org.bouncycastle.crypto.params.AEADParameters;
29  import org.bouncycastle.crypto.params.KeyParameter;
30  import org.bouncycastle.crypto.params.ParametersWithIV;
31  import org.bouncycastle.util.Arrays;
32  import org.bouncycastle.util.Pack;
33  
34  import java.io.ByteArrayOutputStream;
35  
36  /**
37   * GCM-SIV Mode.
38   * Donated to BouncyCastle.
39   * <p>It should be noted that the specified limit of 2<sup>36</sup> bytes is not checked. This is because all bytes are
40   * cached in a <b>ByteArrayOutputStream</b> object (which has a limit of 2<sup>31</sup> bytes), and are output
41   * on the <b>doFinal</b>() call (which can only process a maximum of 2<sup>31</sup> bytes).
42   * <p>The limit of 2<sup>31</sup> bytes is not policed, and attempts to breach the limit will fail on writing to the
43   * <b>ByteArrayOutputStream</b> with <b>OutOfMemoryError</b></p>
44   * <p>In order to properly support the higher limit, the <b>GCMSIVCache</b> would need to be extended to
45   * use multiple arrays to store the data. In addition, a new <b>doOutput</b> method would be required (similar to that in
46   * <b>XOF</b> digests), which would allow the data to be output over multiple calls. Alternatively an extended form
47   * of <b>ByteArrayInputStream</b> could be used to deliver the data</p>
48   */
49  public class GordianGCMSIVBlockCipher
50          implements AEADBlockCipher {
51      /**
52       * The buffer length.
53       */
54      private static final int BUFLEN = 16;
55  
56      /**
57       * The halfBuffer length.
58       */
59      private static final int HALFBUFLEN = BUFLEN >> 1;
60  
61      /**
62       * The nonce length.
63       */
64      private static final int NONCELEN = 12;
65  
66      /**
67       * The maximum data length (AEAD/PlainText). Due to implementation constraints this is restricted to the
68       * maximum array length (<a href="https://programming.guide/java/array-maximum-length.html">Java Maximum Array Length</a>) - the BUFLEN
69       */
70      private static final int MAX_DATALEN = Integer.MAX_VALUE - 8 - BUFLEN;
71  
72      /**
73       * The top bit mask.
74       */
75      private static final byte MASK = (byte) 0b10000000;
76  
77      /**
78       * The addition constant.
79       */
80      private static final byte ADD = (byte) 0b11100001;
81  
82      /**
83       * The initialisation flag.
84       */
85      private static final int INIT = 1;
86  
87      /**
88       * The aeadComplete flag.
89       */
90      private static final int AEAD_COMPLETE = 2;
91  
92      /**
93       * The cipher.
94       */
95      private final BlockCipher theCipher;
96  
97      /**
98       * The multiplier.
99       */
100     private final GCMMultiplier theMultiplier;
101 
102     /**
103      * The gHash buffer.
104      */
105     private final byte[] theGHash = new byte[BUFLEN];
106 
107     /**
108      * The reverse buffer.
109      */
110     private final byte[] theReverse = new byte[BUFLEN];
111 
112     /**
113      * The aeadHasher.
114      */
115     private final GCMSIVHasher theAEADHasher;
116 
117     /**
118      * The dataHasher.
119      */
120     private final GCMSIVHasher theDataHasher;
121 
122     /**
123      * The plainDataStream.
124      */
125     private GCMSIVCache thePlain;
126 
127     /**
128      * The encryptedDataStream (decryption only).
129      */
130     private GCMSIVCache theEncData;
131 
132     /**
133      * Are we encrypting?
134      */
135     private boolean forEncryption;
136 
137     /**
138      * The initialAEAD.
139      */
140     private byte[] theInitialAEAD;
141 
142     /**
143      * The nonce.
144      */
145     private byte[] theNonce;
146 
147     /**
148      * The flags.
149      */
150     private int theFlags;
151 
152     /**
153      * Constructor.
154      */
155     public GordianGCMSIVBlockCipher() {
156         this(AESEngine.newInstance());
157     }
158 
159     /**
160      * Constructor.
161      *
162      * @param pCipher the underlying cipher
163      */
164     public GordianGCMSIVBlockCipher(final BlockCipher pCipher) {
165         this(pCipher, new Tables4kGCMMultiplier());
166     }
167 
168     /**
169      * Constructor.
170      *
171      * @param pCipher     the underlying cipher
172      * @param pMultiplier the multiplier
173      */
174     public GordianGCMSIVBlockCipher(final BlockCipher pCipher,
175                                     final GCMMultiplier pMultiplier) {
176         /* Ensure that the cipher is the correct size */
177         if (pCipher.getBlockSize() != BUFLEN) {
178             throw new IllegalArgumentException("Cipher required with a block size of " + BUFLEN + ".");
179         }
180 
181         /* Store parameters */
182         theCipher = pCipher;
183         theMultiplier = pMultiplier;
184 
185         /* Create the hashers */
186         theAEADHasher = new GCMSIVHasher();
187         theDataHasher = new GCMSIVHasher();
188     }
189 
190     @Override
191     public BlockCipher getUnderlyingCipher() {
192         return theCipher;
193     }
194 
195     @Override
196     public void init(final boolean pEncrypt,
197                      final CipherParameters cipherParameters) throws IllegalArgumentException {
198         /* Set defaults */
199         byte[] myInitialAEAD = null;
200         final byte[] myNonce;
201         final KeyParameter myKey;
202 
203         /* Access parameters */
204         if (cipherParameters instanceof AEADParameters) {
205             final AEADParameters myAEAD = (AEADParameters) cipherParameters;
206             myInitialAEAD = myAEAD.getAssociatedText();
207             myNonce = myAEAD.getNonce();
208             myKey = myAEAD.getKey();
209         } else if (cipherParameters instanceof ParametersWithIV) {
210             final ParametersWithIV myParms = (ParametersWithIV) cipherParameters;
211             myNonce = myParms.getIV();
212             myKey = (KeyParameter) myParms.getParameters();
213         } else {
214             throw new IllegalArgumentException("invalid parameters passed to GCM_SIV");
215         }
216 
217         /* Check nonceSize */
218         if (myNonce == null || myNonce.length != NONCELEN) {
219             throw new IllegalArgumentException("Invalid nonce");
220         }
221 
222         /* Check keysize */
223         if (myKey == null
224                 || (myKey.getKey().length != BUFLEN
225                 && myKey.getKey().length != (BUFLEN << 1))) {
226             throw new IllegalArgumentException("Invalid key");
227         }
228 
229         /* Reset details */
230         forEncryption = pEncrypt;
231         theInitialAEAD = myInitialAEAD;
232         theNonce = myNonce;
233 
234         /* Initialise the keys */
235         deriveKeys(myKey);
236         resetStreams();
237     }
238 
239     @Override
240     public String getAlgorithmName() {
241         return theCipher.getAlgorithmName() + "-GCM-SIV";
242     }
243 
244     /**
245      * check AEAD status.
246      *
247      * @param pLen the dataLength
248      */
249     private void checkAEADStatus(final int pLen) {
250         /* Check we are initialised */
251         if ((theFlags & INIT) == 0) {
252             throw new IllegalStateException("Cipher is not initialised");
253         }
254 
255         /* Check AAD is allowed */
256         if ((theFlags & AEAD_COMPLETE) != 0) {
257             throw new IllegalStateException("AEAD data cannot be processed after ordinary data");
258         }
259 
260         /* Make sure that we haven't breached AEAD data limit */
261         if (theAEADHasher.getBytesProcessed() + Long.MIN_VALUE
262                 > (MAX_DATALEN - pLen) + Long.MIN_VALUE) {
263             throw new IllegalStateException("AEAD byte count exceeded");
264         }
265     }
266 
267     /**
268      * check status.
269      *
270      * @param pLen the dataLength
271      */
272     private void checkStatus(final int pLen) {
273         /* Check we are initialised */
274         if ((theFlags & INIT) == 0) {
275             throw new IllegalStateException("Cipher is not initialised");
276         }
277 
278         /* Complete the AEAD section if this is the first data */
279         if ((theFlags & AEAD_COMPLETE) == 0) {
280             theAEADHasher.completeHash();
281             theFlags |= AEAD_COMPLETE;
282         }
283 
284         /* Make sure that we haven't breached data limit */
285         long dataLimit = MAX_DATALEN;
286         long currBytes = thePlain.size();
287         if (!forEncryption) {
288             dataLimit += BUFLEN;
289             currBytes = theEncData.size();
290         }
291         if (currBytes + Long.MIN_VALUE
292                 > (dataLimit - pLen) + Long.MIN_VALUE) {
293             throw new IllegalStateException("byte count exceeded");
294         }
295     }
296 
297     @Override
298     public void processAADByte(final byte pByte) {
299         /* Check that we can supply AEAD */
300         checkAEADStatus(1);
301 
302         /* Process the aead */
303         theAEADHasher.updateHash(pByte);
304     }
305 
306     @Override
307     public void processAADBytes(final byte[] pData,
308                                 final int pOffset,
309                                 final int pLen) {
310         /* Check that we can supply AEAD */
311         checkAEADStatus(pLen);
312 
313         /* Check input buffer */
314         checkBuffer(pData, pOffset, pLen, false);
315 
316         /* Process the aead */
317         theAEADHasher.updateHash(pData, pOffset, pLen);
318     }
319 
320     @Override
321     public int processByte(final byte pByte,
322                            final byte[] pOutput,
323                            final int pOutOffset) throws DataLengthException {
324         /* Check that we have initialised */
325         checkStatus(1);
326 
327         /* Store the data */
328         if (forEncryption) {
329             thePlain.write(pByte);
330             theDataHasher.updateHash(pByte);
331         } else {
332             theEncData.write(pByte);
333         }
334 
335         /* No data returned */
336         return 0;
337     }
338 
339     @Override
340     public int processBytes(final byte[] pData,
341                             final int pOffset,
342                             final int pLen,
343                             final byte[] pOutput,
344                             final int pOutOffset) throws DataLengthException {
345         /* Check that we have initialised */
346         checkStatus(pLen);
347 
348         /* Check input buffer */
349         checkBuffer(pData, pOffset, pLen, false);
350 
351         /* Store the data */
352         if (forEncryption) {
353             thePlain.write(pData, pOffset, pLen);
354             theDataHasher.updateHash(pData, pOffset, pLen);
355         } else {
356             theEncData.write(pData, pOffset, pLen);
357         }
358 
359         /* No data returned */
360         return 0;
361     }
362 
363     @Override
364     public int doFinal(final byte[] pOutput,
365                        final int pOffset) throws IllegalStateException, InvalidCipherTextException {
366         /* Check that we have initialised */
367         checkStatus(0);
368 
369         /* Check output buffer */
370         checkBuffer(pOutput, pOffset, getOutputSize(0), true);
371 
372         /* If we are encrypting */
373         if (forEncryption) {
374             /* Derive the tag */
375             final byte[] myTag = calculateTag();
376 
377             /* encrypt the plain text */
378             final int myDataLen = BUFLEN + encryptPlain(myTag, pOutput, pOffset);
379 
380             /* Add the tag to the output */
381             System.arraycopy(myTag, 0, pOutput, pOffset + thePlain.size(), BUFLEN);
382 
383             /* Reset the streams */
384             resetStreams();
385             return myDataLen;
386 
387             /* else we are decrypting */
388         } else {
389             /* decrypt to plain text */
390             decryptPlain();
391 
392             /* Release plain text */
393             final int myDataLen = thePlain.size();
394             final byte[] mySrc = thePlain.getBuffer();
395             System.arraycopy(mySrc, 0, pOutput, pOffset, myDataLen);
396 
397             /* Reset the streams */
398             resetStreams();
399             return myDataLen;
400         }
401     }
402 
403     @Override
404     public byte[] getMac() {
405         throw new UnsupportedOperationException();
406     }
407 
408     @Override
409     public int getUpdateOutputSize(final int pLen) {
410         return 0;
411     }
412 
413     @Override
414     public int getOutputSize(final int pLen) {
415         if (forEncryption) {
416             return pLen + thePlain.size() + BUFLEN;
417         }
418         final int myCurr = pLen + theEncData.size();
419         return myCurr > BUFLEN ? myCurr - BUFLEN : 0;
420     }
421 
422     @Override
423     public void reset() {
424         resetStreams();
425     }
426 
427     /**
428      * Reset Streams.
429      */
430     private void resetStreams() {
431         /* Clear the plainText buffer */
432         if (thePlain != null) {
433             thePlain.clearBuffer();
434         }
435 
436         /* Reset hashers */
437         theAEADHasher.reset();
438         theDataHasher.reset();
439 
440         /* Recreate streams (to release memory) */
441         thePlain = new GCMSIVCache();
442         theEncData = forEncryption ? null : new GCMSIVCache();
443 
444         /* Initialise AEAD if required */
445         theFlags &= ~AEAD_COMPLETE;
446         Arrays.fill(theGHash, (byte) 0);
447         if (theInitialAEAD != null) {
448             theAEADHasher.updateHash(theInitialAEAD, 0, theInitialAEAD.length);
449         }
450     }
451 
452     /**
453      * Obtain buffer length (allowing for null).
454      *
455      * @param pBuffer the buffer
456      * @return the length
457      */
458     private static int bufLength(final byte[] pBuffer) {
459         return pBuffer == null ? 0 : pBuffer.length;
460     }
461 
462     /**
463      * Check buffer.
464      *
465      * @param pBuffer the buffer
466      * @param pOffset the offset
467      * @param pLen    the length
468      * @param pOutput is this an output buffer?
469      */
470     private static void checkBuffer(final byte[] pBuffer,
471                                     final int pOffset,
472                                     final int pLen,
473                                     final boolean pOutput) {
474         /* Access lengths */
475         final int myBufLen = bufLength(pBuffer);
476         final int myLast = pOffset + pLen;
477 
478         /* Check for negative values and buffer overflow */
479         final boolean badLen = pLen < 0 || pOffset < 0 || myLast < 0;
480         if (badLen || myLast > myBufLen) {
481             throw pOutput
482                     ? new OutputLengthException("Output buffer too short.")
483                     : new DataLengthException("Input buffer too short.");
484         }
485     }
486 
487     /**
488      * encrypt data stream.
489      *
490      * @param pCounter the counter
491      * @param pTarget  the target buffer
492      * @param pOffset  the target offset
493      * @return the length of data encrypted
494      */
495     private int encryptPlain(final byte[] pCounter,
496                              final byte[] pTarget,
497                              final int pOffset) {
498         /* Access buffer and length */
499         final byte[] mySrc = thePlain.getBuffer();
500         final byte[] myCounter = Arrays.clone(pCounter);
501         myCounter[BUFLEN - 1] |= MASK;
502         final byte[] myMask = new byte[BUFLEN];
503         int myRemaining = thePlain.size();
504         int myOff = 0;
505 
506         /* While we have data to process */
507         while (myRemaining > 0) {
508             /* Generate the next mask */
509             theCipher.processBlock(myCounter, 0, myMask, 0);
510 
511             /* Xor data into mask */
512             final int myLen = Math.min(BUFLEN, myRemaining);
513             xorBlock(myMask, mySrc, myOff, myLen);
514 
515             /* Copy encrypted data to output */
516             System.arraycopy(myMask, 0, pTarget, pOffset + myOff, myLen);
517 
518             /* Adjust counters */
519             myRemaining -= myLen;
520             myOff += myLen;
521             incrementCounter(myCounter);
522         }
523 
524         /* Return the amount of data processed */
525         return thePlain.size();
526     }
527 
528     /**
529      * decrypt data stream.
530      *
531      * @throws InvalidCipherTextException on data too short or mac check failed
532      */
533     private void decryptPlain() throws InvalidCipherTextException {
534         /* Access buffer and length */
535         final byte[] mySrc = theEncData.getBuffer();
536         int myRemaining = theEncData.size() - BUFLEN;
537 
538         /* Check for insufficient data */
539         if (myRemaining < 0) {
540             throw new InvalidCipherTextException("Data too short");
541         }
542 
543         /* Access counter */
544         final byte[] myExpected = Arrays.copyOfRange(mySrc, myRemaining, myRemaining + BUFLEN);
545         final byte[] myCounter = Arrays.clone(myExpected);
546         myCounter[BUFLEN - 1] |= MASK;
547         final byte[] myMask = new byte[BUFLEN];
548         int myOff = 0;
549 
550         /* While we have data to process */
551         while (myRemaining > 0) {
552             /* Generate the next mask */
553             theCipher.processBlock(myCounter, 0, myMask, 0);
554 
555             /* Xor data into mask */
556             final int myLen = Math.min(BUFLEN, myRemaining);
557             xorBlock(myMask, mySrc, myOff, myLen);
558 
559             /* Write data to plain dataStream */
560             thePlain.write(myMask, 0, myLen);
561             theDataHasher.updateHash(myMask, 0, myLen);
562 
563             /* Adjust counters */
564             myRemaining -= myLen;
565             myOff += myLen;
566             incrementCounter(myCounter);
567         }
568 
569         /* Derive and check the tag */
570         final byte[] myTag = calculateTag();
571         if (!Arrays.constantTimeAreEqual(myTag, myExpected)) {
572             reset();
573             throw new InvalidCipherTextException("mac check failed");
574         }
575     }
576 
577     /**
578      * calculate tag.
579      *
580      * @return the calculated tag
581      */
582     private byte[] calculateTag() {
583         /* Complete the hash */
584         theDataHasher.completeHash();
585         final byte[] myPolyVal = completePolyVal();
586 
587         /* calculate polyVal */
588         final byte[] myResult = new byte[BUFLEN];
589 
590         /* Fold in the nonce */
591         for (int i = 0; i < NONCELEN; i++) {
592             myPolyVal[i] ^= theNonce[i];
593         }
594 
595         /* Clear top bit */
596         myPolyVal[BUFLEN - 1] &= (byte) (MASK - 1);
597 
598         /* Calculate tag and return it */
599         theCipher.processBlock(myPolyVal, 0, myResult, 0);
600         return myResult;
601     }
602 
603     /**
604      * complete polyVAL.
605      *
606      * @return the calculated value
607      */
608     private byte[] completePolyVal() {
609         /* Build the polyVal result */
610         final byte[] myResult = new byte[BUFLEN];
611         gHashLengths();
612         fillReverse(theGHash, 0, BUFLEN, myResult);
613         return myResult;
614     }
615 
616     /**
617      * process lengths.
618      */
619     private void gHashLengths() {
620         /* Create reversed bigEndian buffer to keep it simple */
621         final byte[] myIn = new byte[BUFLEN];
622         Pack.longToBigEndian(Byte.SIZE * theDataHasher.getBytesProcessed(), myIn, 0);
623         Pack.longToBigEndian(Byte.SIZE * theAEADHasher.getBytesProcessed(), myIn, Long.BYTES);
624 
625         /* hash value */
626         gHASH(myIn);
627     }
628 
629     /**
630      * perform the next GHASH step.
631      *
632      * @param pNext the next value
633      */
634     private void gHASH(final byte[] pNext) {
635         xorBlock(theGHash, pNext);
636         theMultiplier.multiplyH(theGHash);
637     }
638 
639     /**
640      * Byte reverse a buffer.
641      *
642      * @param pInput  the input buffer
643      * @param pOffset the offset
644      * @param pLength the length of data (<= BUFLEN)
645      * @param pOutput the output buffer
646      */
647     private static void fillReverse(final byte[] pInput,
648                                     final int pOffset,
649                                     final int pLength,
650                                     final byte[] pOutput) {
651         /* Loop through the buffer */
652         for (int i = 0, j = BUFLEN - 1; i < pLength; i++, j--) {
653             /* Copy byte */
654             pOutput[j] = pInput[pOffset + i];
655         }
656     }
657 
658     /**
659      * xor a full block buffer.
660      *
661      * @param pLeft  the left operand and result
662      * @param pRight the right operand
663      */
664     private static void xorBlock(final byte[] pLeft,
665                                  final byte[] pRight) {
666         /* Loop through the bytes */
667         for (int i = 0; i < BUFLEN; i++) {
668             pLeft[i] ^= pRight[i];
669         }
670     }
671 
672     /**
673      * xor a partial block buffer.
674      *
675      * @param pLeft   the left operand and result
676      * @param pRight  the right operand
677      * @param pOffset the offset in the right operand
678      * @param pLength the length of data in the right operand
679      */
680     private static void xorBlock(final byte[] pLeft,
681                                  final byte[] pRight,
682                                  final int pOffset,
683                                  final int pLength) {
684         /* Loop through the bytes */
685         for (int i = 0; i < pLength; i++) {
686             pLeft[i] ^= pRight[i + pOffset];
687         }
688     }
689 
690     /**
691      * increment the counter.
692      *
693      * @param pCounter the counter to increment
694      */
695     private static void incrementCounter(final byte[] pCounter) {
696         /* Loop through the bytes incrementing counter */
697         for (int i = 0; i < Integer.BYTES; i++) {
698             if (++pCounter[i] != 0) {
699                 break;
700             }
701         }
702     }
703 
704     /**
705      * multiply by X.
706      *
707      * @param pValue the value to adjust
708      */
709     private static void mulX(final byte[] pValue) {
710         /* Loop through the bytes */
711         byte myMask = (byte) 0;
712         for (int i = 0; i < BUFLEN; i++) {
713             final byte myValue = pValue[i];
714             pValue[i] = (byte) (((myValue >> 1) & ~MASK) | myMask);
715             myMask = (myValue & 1) == 0 ? 0 : MASK;
716         }
717 
718         /* Xor in addition if last bit was set */
719         if (myMask != 0) {
720             pValue[0] ^= ADD;
721         }
722     }
723 
724     /**
725      * Derive Keys.
726      *
727      * @param pKey the keyGeneration key
728      */
729     private void deriveKeys(final KeyParameter pKey) {
730         /* Create the buffers */
731         final byte[] myIn = new byte[BUFLEN];
732         final byte[] myOut = new byte[BUFLEN];
733         final byte[] myResult = new byte[BUFLEN];
734         final byte[] myEncKey = new byte[pKey.getKey().length];
735 
736         /* Prepare for encryption */
737         System.arraycopy(theNonce, 0, myIn, BUFLEN - NONCELEN, NONCELEN);
738         theCipher.init(true, pKey);
739 
740         /* Derive authentication key */
741         int myOff = 0;
742         theCipher.processBlock(myIn, 0, myOut, 0);
743         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
744         myIn[0]++;
745         myOff += HALFBUFLEN;
746         theCipher.processBlock(myIn, 0, myOut, 0);
747         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
748 
749         /* Derive encryption key */
750         myIn[0]++;
751         myOff = 0;
752         theCipher.processBlock(myIn, 0, myOut, 0);
753         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
754         myIn[0]++;
755         myOff += HALFBUFLEN;
756         theCipher.processBlock(myIn, 0, myOut, 0);
757         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
758 
759         /* If we have a 32byte key */
760         if (myEncKey.length == BUFLEN << 1) {
761             /* Derive remainder of encryption key */
762             myIn[0]++;
763             myOff += HALFBUFLEN;
764             theCipher.processBlock(myIn, 0, myOut, 0);
765             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
766             myIn[0]++;
767             myOff += HALFBUFLEN;
768             theCipher.processBlock(myIn, 0, myOut, 0);
769             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
770         }
771 
772         /* Initialise the Cipher */
773         theCipher.init(true, new KeyParameter(myEncKey));
774 
775         /* Initialise the multiplier */
776         fillReverse(myResult, 0, BUFLEN, myOut);
777         mulX(myOut);
778         theMultiplier.init(myOut);
779         theFlags |= INIT;
780     }
781 
782     /**
783      * GCMSIVCache.
784      */
785     private static class GCMSIVCache
786             extends ByteArrayOutputStream {
787         /**
788          * number of bytes hashed.
789          */
790         private int numHashed;
791 
792         /**
793          * Constructor.
794          */
795         GCMSIVCache() {
796         }
797 
798         /**
799          * Obtain the buffer.
800          *
801          * @return the buffer
802          */
803         byte[] getBuffer() {
804             return this.buf;
805         }
806 
807         /**
808          * Clear the buffer.
809          */
810         void clearBuffer() {
811             Arrays.fill(getBuffer(), (byte) 0);
812         }
813     }
814 
815     /**
816      * Hash Control.
817      */
818     private final class GCMSIVHasher {
819         /**
820          * Cache.
821          */
822         private final byte[] theBuffer = new byte[BUFLEN];
823 
824         /**
825          * Single byte cache.
826          */
827         private final byte[] theByte = new byte[1];
828 
829         /**
830          * Count of active bytes in cache.
831          */
832         private int numActive;
833 
834         /**
835          * Count of hashed bytes.
836          */
837         private long numHashed;
838 
839         /**
840          * Obtain the count of bytes hashed.
841          *
842          * @return the count
843          */
844         long getBytesProcessed() {
845             return numHashed;
846         }
847 
848         /**
849          * Reset the hasher.
850          */
851         void reset() {
852             numActive = 0;
853             numHashed = 0;
854         }
855 
856         /**
857          * update hash.
858          *
859          * @param pByte the byte
860          */
861         void updateHash(final byte pByte) {
862             theByte[0] = pByte;
863             updateHash(theByte, 0, 1);
864         }
865 
866         /**
867          * update hash.
868          *
869          * @param pBuffer the buffer
870          * @param pOffset the offset within the buffer
871          * @param pLen    the length of data
872          */
873         void updateHash(final byte[] pBuffer,
874                         final int pOffset,
875                         final int pLen) {
876             /* If we should process the cache */
877             final int mySpace = BUFLEN - numActive;
878             int numProcessed = 0;
879             int myRemaining = pLen;
880             if (numActive > 0
881                     && pLen >= mySpace) {
882                 /* Copy data into the cache and hash it */
883                 System.arraycopy(pBuffer, pOffset, theBuffer, numActive, mySpace);
884                 fillReverse(theBuffer, 0, BUFLEN, theReverse);
885                 gHASH(theReverse);
886 
887                 /* Adjust counters */
888                 numProcessed += mySpace;
889                 myRemaining -= mySpace;
890                 numActive = 0;
891             }
892 
893             /* While we have full blocks */
894             while (myRemaining >= BUFLEN) {
895                 /* Access the next data */
896                 fillReverse(pBuffer, pOffset + numProcessed, BUFLEN, theReverse);
897                 gHASH(theReverse);
898 
899                 /* Adjust counters */
900                 numProcessed += BUFLEN;
901                 myRemaining -= BUFLEN;
902             }
903 
904             /* If we have remaining data */
905             if (myRemaining > 0) {
906                 /* Copy data into the cache */
907                 System.arraycopy(pBuffer, pOffset + numProcessed, theBuffer, numActive, myRemaining);
908                 numActive += myRemaining;
909             }
910 
911             /* Adjust the number of bytes processed */
912             numHashed += pLen;
913         }
914 
915         /**
916          * complete hash.
917          */
918         void completeHash() {
919             /* If we have remaining data */
920             if (numActive > 0) {
921                 /* Access the next data */
922                 Arrays.fill(theReverse, (byte) 0);
923                 fillReverse(theBuffer, 0, numActive, theReverse);
924 
925                 /* hash value */
926                 gHASH(theReverse);
927             }
928         }
929     }
930 }