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 myAEAD) {
205             myInitialAEAD = myAEAD.getAssociatedText();
206             myNonce = myAEAD.getNonce();
207             myKey = myAEAD.getKey();
208         } else if (cipherParameters instanceof ParametersWithIV myParms) {
209             myNonce = myParms.getIV();
210             myKey = (KeyParameter) myParms.getParameters();
211         } else {
212             throw new IllegalArgumentException("invalid parameters passed to GCM_SIV");
213         }
214 
215         /* Check nonceSize */
216         if (myNonce == null || myNonce.length != NONCELEN) {
217             throw new IllegalArgumentException("Invalid nonce");
218         }
219 
220         /* Check keysize */
221         if (myKey == null
222                 || (myKey.getKey().length != BUFLEN
223                 && myKey.getKey().length != (BUFLEN << 1))) {
224             throw new IllegalArgumentException("Invalid key");
225         }
226 
227         /* Reset details */
228         forEncryption = pEncrypt;
229         theInitialAEAD = myInitialAEAD;
230         theNonce = myNonce;
231 
232         /* Initialise the keys */
233         deriveKeys(myKey);
234         resetStreams();
235     }
236 
237     @Override
238     public String getAlgorithmName() {
239         return theCipher.getAlgorithmName() + "-GCM-SIV";
240     }
241 
242     /**
243      * check AEAD status.
244      *
245      * @param pLen the dataLength
246      */
247     private void checkAEADStatus(final int pLen) {
248         /* Check we are initialised */
249         if ((theFlags & INIT) == 0) {
250             throw new IllegalStateException("Cipher is not initialised");
251         }
252 
253         /* Check AAD is allowed */
254         if ((theFlags & AEAD_COMPLETE) != 0) {
255             throw new IllegalStateException("AEAD data cannot be processed after ordinary data");
256         }
257 
258         /* Make sure that we haven't breached AEAD data limit */
259         if (theAEADHasher.getBytesProcessed() + Long.MIN_VALUE
260                 > (MAX_DATALEN - pLen) + Long.MIN_VALUE) {
261             throw new IllegalStateException("AEAD byte count exceeded");
262         }
263     }
264 
265     /**
266      * check status.
267      *
268      * @param pLen the dataLength
269      */
270     private void checkStatus(final int pLen) {
271         /* Check we are initialised */
272         if ((theFlags & INIT) == 0) {
273             throw new IllegalStateException("Cipher is not initialised");
274         }
275 
276         /* Complete the AEAD section if this is the first data */
277         if ((theFlags & AEAD_COMPLETE) == 0) {
278             theAEADHasher.completeHash();
279             theFlags |= AEAD_COMPLETE;
280         }
281 
282         /* Make sure that we haven't breached data limit */
283         long dataLimit = MAX_DATALEN;
284         long currBytes = thePlain.size();
285         if (!forEncryption) {
286             dataLimit += BUFLEN;
287             currBytes = theEncData.size();
288         }
289         if (currBytes + Long.MIN_VALUE
290                 > (dataLimit - pLen) + Long.MIN_VALUE) {
291             throw new IllegalStateException("byte count exceeded");
292         }
293     }
294 
295     @Override
296     public void processAADByte(final byte pByte) {
297         /* Check that we can supply AEAD */
298         checkAEADStatus(1);
299 
300         /* Process the aead */
301         theAEADHasher.updateHash(pByte);
302     }
303 
304     @Override
305     public void processAADBytes(final byte[] pData,
306                                 final int pOffset,
307                                 final int pLen) {
308         /* Check that we can supply AEAD */
309         checkAEADStatus(pLen);
310 
311         /* Check input buffer */
312         checkBuffer(pData, pOffset, pLen, false);
313 
314         /* Process the aead */
315         theAEADHasher.updateHash(pData, pOffset, pLen);
316     }
317 
318     @Override
319     public int processByte(final byte pByte,
320                            final byte[] pOutput,
321                            final int pOutOffset) throws DataLengthException {
322         /* Check that we have initialised */
323         checkStatus(1);
324 
325         /* Store the data */
326         if (forEncryption) {
327             thePlain.write(pByte);
328             theDataHasher.updateHash(pByte);
329         } else {
330             theEncData.write(pByte);
331         }
332 
333         /* No data returned */
334         return 0;
335     }
336 
337     @Override
338     public int processBytes(final byte[] pData,
339                             final int pOffset,
340                             final int pLen,
341                             final byte[] pOutput,
342                             final int pOutOffset) throws DataLengthException {
343         /* Check that we have initialised */
344         checkStatus(pLen);
345 
346         /* Check input buffer */
347         checkBuffer(pData, pOffset, pLen, false);
348 
349         /* Store the data */
350         if (forEncryption) {
351             thePlain.write(pData, pOffset, pLen);
352             theDataHasher.updateHash(pData, pOffset, pLen);
353         } else {
354             theEncData.write(pData, pOffset, pLen);
355         }
356 
357         /* No data returned */
358         return 0;
359     }
360 
361     @Override
362     public int doFinal(final byte[] pOutput,
363                        final int pOffset) throws IllegalStateException, InvalidCipherTextException {
364         /* Check that we have initialised */
365         checkStatus(0);
366 
367         /* Check output buffer */
368         checkBuffer(pOutput, pOffset, getOutputSize(0), true);
369 
370         /* If we are encrypting */
371         if (forEncryption) {
372             /* Derive the tag */
373             final byte[] myTag = calculateTag();
374 
375             /* encrypt the plain text */
376             final int myDataLen = BUFLEN + encryptPlain(myTag, pOutput, pOffset);
377 
378             /* Add the tag to the output */
379             System.arraycopy(myTag, 0, pOutput, pOffset + thePlain.size(), BUFLEN);
380 
381             /* Reset the streams */
382             resetStreams();
383             return myDataLen;
384 
385             /* else we are decrypting */
386         } else {
387             /* decrypt to plain text */
388             decryptPlain();
389 
390             /* Release plain text */
391             final int myDataLen = thePlain.size();
392             final byte[] mySrc = thePlain.getBuffer();
393             System.arraycopy(mySrc, 0, pOutput, pOffset, myDataLen);
394 
395             /* Reset the streams */
396             resetStreams();
397             return myDataLen;
398         }
399     }
400 
401     @Override
402     public byte[] getMac() {
403         throw new UnsupportedOperationException();
404     }
405 
406     @Override
407     public int getUpdateOutputSize(final int pLen) {
408         return 0;
409     }
410 
411     @Override
412     public int getOutputSize(final int pLen) {
413         if (forEncryption) {
414             return pLen + thePlain.size() + BUFLEN;
415         }
416         final int myCurr = pLen + theEncData.size();
417         return myCurr > BUFLEN ? myCurr - BUFLEN : 0;
418     }
419 
420     @Override
421     public void reset() {
422         resetStreams();
423     }
424 
425     /**
426      * Reset Streams.
427      */
428     private void resetStreams() {
429         /* Clear the plainText buffer */
430         if (thePlain != null) {
431             thePlain.clearBuffer();
432         }
433 
434         /* Reset hashers */
435         theAEADHasher.reset();
436         theDataHasher.reset();
437 
438         /* Recreate streams (to release memory) */
439         thePlain = new GCMSIVCache();
440         theEncData = forEncryption ? null : new GCMSIVCache();
441 
442         /* Initialise AEAD if required */
443         theFlags &= ~AEAD_COMPLETE;
444         Arrays.fill(theGHash, (byte) 0);
445         if (theInitialAEAD != null) {
446             theAEADHasher.updateHash(theInitialAEAD, 0, theInitialAEAD.length);
447         }
448     }
449 
450     /**
451      * Obtain buffer length (allowing for null).
452      *
453      * @param pBuffer the buffer
454      * @return the length
455      */
456     private static int bufLength(final byte[] pBuffer) {
457         return pBuffer == null ? 0 : pBuffer.length;
458     }
459 
460     /**
461      * Check buffer.
462      *
463      * @param pBuffer the buffer
464      * @param pOffset the offset
465      * @param pLen    the length
466      * @param pOutput is this an output buffer?
467      */
468     private static void checkBuffer(final byte[] pBuffer,
469                                     final int pOffset,
470                                     final int pLen,
471                                     final boolean pOutput) {
472         /* Access lengths */
473         final int myBufLen = bufLength(pBuffer);
474         final int myLast = pOffset + pLen;
475 
476         /* Check for negative values and buffer overflow */
477         final boolean badLen = pLen < 0 || pOffset < 0 || myLast < 0;
478         if (badLen || myLast > myBufLen) {
479             throw pOutput
480                     ? new OutputLengthException("Output buffer too short.")
481                     : new DataLengthException("Input buffer too short.");
482         }
483     }
484 
485     /**
486      * encrypt data stream.
487      *
488      * @param pCounter the counter
489      * @param pTarget  the target buffer
490      * @param pOffset  the target offset
491      * @return the length of data encrypted
492      */
493     private int encryptPlain(final byte[] pCounter,
494                              final byte[] pTarget,
495                              final int pOffset) {
496         /* Access buffer and length */
497         final byte[] mySrc = thePlain.getBuffer();
498         final byte[] myCounter = Arrays.clone(pCounter);
499         myCounter[BUFLEN - 1] |= MASK;
500         final byte[] myMask = new byte[BUFLEN];
501         int myRemaining = thePlain.size();
502         int myOff = 0;
503 
504         /* While we have data to process */
505         while (myRemaining > 0) {
506             /* Generate the next mask */
507             theCipher.processBlock(myCounter, 0, myMask, 0);
508 
509             /* Xor data into mask */
510             final int myLen = Math.min(BUFLEN, myRemaining);
511             xorBlock(myMask, mySrc, myOff, myLen);
512 
513             /* Copy encrypted data to output */
514             System.arraycopy(myMask, 0, pTarget, pOffset + myOff, myLen);
515 
516             /* Adjust counters */
517             myRemaining -= myLen;
518             myOff += myLen;
519             incrementCounter(myCounter);
520         }
521 
522         /* Return the amount of data processed */
523         return thePlain.size();
524     }
525 
526     /**
527      * decrypt data stream.
528      *
529      * @throws InvalidCipherTextException on data too short or mac check failed
530      */
531     private void decryptPlain() throws InvalidCipherTextException {
532         /* Access buffer and length */
533         final byte[] mySrc = theEncData.getBuffer();
534         int myRemaining = theEncData.size() - BUFLEN;
535 
536         /* Check for insufficient data */
537         if (myRemaining < 0) {
538             throw new InvalidCipherTextException("Data too short");
539         }
540 
541         /* Access counter */
542         final byte[] myExpected = Arrays.copyOfRange(mySrc, myRemaining, myRemaining + BUFLEN);
543         final byte[] myCounter = Arrays.clone(myExpected);
544         myCounter[BUFLEN - 1] |= MASK;
545         final byte[] myMask = new byte[BUFLEN];
546         int myOff = 0;
547 
548         /* While we have data to process */
549         while (myRemaining > 0) {
550             /* Generate the next mask */
551             theCipher.processBlock(myCounter, 0, myMask, 0);
552 
553             /* Xor data into mask */
554             final int myLen = Math.min(BUFLEN, myRemaining);
555             xorBlock(myMask, mySrc, myOff, myLen);
556 
557             /* Write data to plain dataStream */
558             thePlain.write(myMask, 0, myLen);
559             theDataHasher.updateHash(myMask, 0, myLen);
560 
561             /* Adjust counters */
562             myRemaining -= myLen;
563             myOff += myLen;
564             incrementCounter(myCounter);
565         }
566 
567         /* Derive and check the tag */
568         final byte[] myTag = calculateTag();
569         if (!Arrays.constantTimeAreEqual(myTag, myExpected)) {
570             reset();
571             throw new InvalidCipherTextException("mac check failed");
572         }
573     }
574 
575     /**
576      * calculate tag.
577      *
578      * @return the calculated tag
579      */
580     private byte[] calculateTag() {
581         /* Complete the hash */
582         theDataHasher.completeHash();
583         final byte[] myPolyVal = completePolyVal();
584 
585         /* calculate polyVal */
586         final byte[] myResult = new byte[BUFLEN];
587 
588         /* Fold in the nonce */
589         for (int i = 0; i < NONCELEN; i++) {
590             myPolyVal[i] ^= theNonce[i];
591         }
592 
593         /* Clear top bit */
594         myPolyVal[BUFLEN - 1] &= (byte) (MASK - 1);
595 
596         /* Calculate tag and return it */
597         theCipher.processBlock(myPolyVal, 0, myResult, 0);
598         return myResult;
599     }
600 
601     /**
602      * complete polyVAL.
603      *
604      * @return the calculated value
605      */
606     private byte[] completePolyVal() {
607         /* Build the polyVal result */
608         final byte[] myResult = new byte[BUFLEN];
609         gHashLengths();
610         fillReverse(theGHash, 0, BUFLEN, myResult);
611         return myResult;
612     }
613 
614     /**
615      * process lengths.
616      */
617     private void gHashLengths() {
618         /* Create reversed bigEndian buffer to keep it simple */
619         final byte[] myIn = new byte[BUFLEN];
620         Pack.longToBigEndian(Byte.SIZE * theDataHasher.getBytesProcessed(), myIn, 0);
621         Pack.longToBigEndian(Byte.SIZE * theAEADHasher.getBytesProcessed(), myIn, Long.BYTES);
622 
623         /* hash value */
624         gHASH(myIn);
625     }
626 
627     /**
628      * perform the next GHASH step.
629      *
630      * @param pNext the next value
631      */
632     private void gHASH(final byte[] pNext) {
633         xorBlock(theGHash, pNext);
634         theMultiplier.multiplyH(theGHash);
635     }
636 
637     /**
638      * Byte reverse a buffer.
639      *
640      * @param pInput  the input buffer
641      * @param pOffset the offset
642      * @param pLength the length of data (<= BUFLEN)
643      * @param pOutput the output buffer
644      */
645     private static void fillReverse(final byte[] pInput,
646                                     final int pOffset,
647                                     final int pLength,
648                                     final byte[] pOutput) {
649         /* Loop through the buffer */
650         for (int i = 0, j = BUFLEN - 1; i < pLength; i++, j--) {
651             /* Copy byte */
652             pOutput[j] = pInput[pOffset + i];
653         }
654     }
655 
656     /**
657      * xor a full block buffer.
658      *
659      * @param pLeft  the left operand and result
660      * @param pRight the right operand
661      */
662     private static void xorBlock(final byte[] pLeft,
663                                  final byte[] pRight) {
664         /* Loop through the bytes */
665         for (int i = 0; i < BUFLEN; i++) {
666             pLeft[i] ^= pRight[i];
667         }
668     }
669 
670     /**
671      * xor a partial block buffer.
672      *
673      * @param pLeft   the left operand and result
674      * @param pRight  the right operand
675      * @param pOffset the offset in the right operand
676      * @param pLength the length of data in the right operand
677      */
678     private static void xorBlock(final byte[] pLeft,
679                                  final byte[] pRight,
680                                  final int pOffset,
681                                  final int pLength) {
682         /* Loop through the bytes */
683         for (int i = 0; i < pLength; i++) {
684             pLeft[i] ^= pRight[i + pOffset];
685         }
686     }
687 
688     /**
689      * increment the counter.
690      *
691      * @param pCounter the counter to increment
692      */
693     private static void incrementCounter(final byte[] pCounter) {
694         /* Loop through the bytes incrementing counter */
695         for (int i = 0; i < Integer.BYTES; i++) {
696             if (++pCounter[i] != 0) {
697                 break;
698             }
699         }
700     }
701 
702     /**
703      * multiply by X.
704      *
705      * @param pValue the value to adjust
706      */
707     private static void mulX(final byte[] pValue) {
708         /* Loop through the bytes */
709         byte myMask = (byte) 0;
710         for (int i = 0; i < BUFLEN; i++) {
711             final byte myValue = pValue[i];
712             pValue[i] = (byte) (((myValue >> 1) & ~MASK) | myMask);
713             myMask = (myValue & 1) == 0 ? 0 : MASK;
714         }
715 
716         /* Xor in addition if last bit was set */
717         if (myMask != 0) {
718             pValue[0] ^= ADD;
719         }
720     }
721 
722     /**
723      * Derive Keys.
724      *
725      * @param pKey the keyGeneration key
726      */
727     private void deriveKeys(final KeyParameter pKey) {
728         /* Create the buffers */
729         final byte[] myIn = new byte[BUFLEN];
730         final byte[] myOut = new byte[BUFLEN];
731         final byte[] myResult = new byte[BUFLEN];
732         final byte[] myEncKey = new byte[pKey.getKey().length];
733 
734         /* Prepare for encryption */
735         System.arraycopy(theNonce, 0, myIn, BUFLEN - NONCELEN, NONCELEN);
736         theCipher.init(true, pKey);
737 
738         /* Derive authentication key */
739         int myOff = 0;
740         theCipher.processBlock(myIn, 0, myOut, 0);
741         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
742         myIn[0]++;
743         myOff += HALFBUFLEN;
744         theCipher.processBlock(myIn, 0, myOut, 0);
745         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
746 
747         /* Derive encryption key */
748         myIn[0]++;
749         myOff = 0;
750         theCipher.processBlock(myIn, 0, myOut, 0);
751         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
752         myIn[0]++;
753         myOff += HALFBUFLEN;
754         theCipher.processBlock(myIn, 0, myOut, 0);
755         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
756 
757         /* If we have a 32byte key */
758         if (myEncKey.length == BUFLEN << 1) {
759             /* Derive remainder of encryption key */
760             myIn[0]++;
761             myOff += HALFBUFLEN;
762             theCipher.processBlock(myIn, 0, myOut, 0);
763             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
764             myIn[0]++;
765             myOff += HALFBUFLEN;
766             theCipher.processBlock(myIn, 0, myOut, 0);
767             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
768         }
769 
770         /* Initialise the Cipher */
771         theCipher.init(true, new KeyParameter(myEncKey));
772 
773         /* Initialise the multiplier */
774         fillReverse(myResult, 0, BUFLEN, myOut);
775         mulX(myOut);
776         theMultiplier.init(myOut);
777         theFlags |= INIT;
778     }
779 
780     /**
781      * GCMSIVCache.
782      */
783     private static class GCMSIVCache
784             extends ByteArrayOutputStream {
785         /**
786          * number of bytes hashed.
787          */
788         private int numHashed;
789 
790         /**
791          * Constructor.
792          */
793         GCMSIVCache() {
794         }
795 
796         /**
797          * Obtain the buffer.
798          *
799          * @return the buffer
800          */
801         byte[] getBuffer() {
802             return this.buf;
803         }
804 
805         /**
806          * Clear the buffer.
807          */
808         void clearBuffer() {
809             Arrays.fill(getBuffer(), (byte) 0);
810         }
811     }
812 
813     /**
814      * Hash Control.
815      */
816     private final class GCMSIVHasher {
817         /**
818          * Cache.
819          */
820         private final byte[] theBuffer = new byte[BUFLEN];
821 
822         /**
823          * Single byte cache.
824          */
825         private final byte[] theByte = new byte[1];
826 
827         /**
828          * Count of active bytes in cache.
829          */
830         private int numActive;
831 
832         /**
833          * Count of hashed bytes.
834          */
835         private long numHashed;
836 
837         /**
838          * Obtain the count of bytes hashed.
839          *
840          * @return the count
841          */
842         long getBytesProcessed() {
843             return numHashed;
844         }
845 
846         /**
847          * Reset the hasher.
848          */
849         void reset() {
850             numActive = 0;
851             numHashed = 0;
852         }
853 
854         /**
855          * update hash.
856          *
857          * @param pByte the byte
858          */
859         void updateHash(final byte pByte) {
860             theByte[0] = pByte;
861             updateHash(theByte, 0, 1);
862         }
863 
864         /**
865          * update hash.
866          *
867          * @param pBuffer the buffer
868          * @param pOffset the offset within the buffer
869          * @param pLen    the length of data
870          */
871         void updateHash(final byte[] pBuffer,
872                         final int pOffset,
873                         final int pLen) {
874             /* If we should process the cache */
875             final int mySpace = BUFLEN - numActive;
876             int numProcessed = 0;
877             int myRemaining = pLen;
878             if (numActive > 0
879                     && pLen >= mySpace) {
880                 /* Copy data into the cache and hash it */
881                 System.arraycopy(pBuffer, pOffset, theBuffer, numActive, mySpace);
882                 fillReverse(theBuffer, 0, BUFLEN, theReverse);
883                 gHASH(theReverse);
884 
885                 /* Adjust counters */
886                 numProcessed += mySpace;
887                 myRemaining -= mySpace;
888                 numActive = 0;
889             }
890 
891             /* While we have full blocks */
892             while (myRemaining >= BUFLEN) {
893                 /* Access the next data */
894                 fillReverse(pBuffer, pOffset + numProcessed, BUFLEN, theReverse);
895                 gHASH(theReverse);
896 
897                 /* Adjust counters */
898                 numProcessed += BUFLEN;
899                 myRemaining -= BUFLEN;
900             }
901 
902             /* If we have remaining data */
903             if (myRemaining > 0) {
904                 /* Copy data into the cache */
905                 System.arraycopy(pBuffer, pOffset + numProcessed, theBuffer, numActive, myRemaining);
906                 numActive += myRemaining;
907             }
908 
909             /* Adjust the number of bytes processed */
910             numHashed += pLen;
911         }
912 
913         /**
914          * complete hash.
915          */
916         void completeHash() {
917             /* If we have remaining data */
918             if (numActive > 0) {
919                 /* Access the next data */
920                 Arrays.fill(theReverse, (byte) 0);
921                 fillReverse(theBuffer, 0, numActive, theReverse);
922 
923                 /* hash value */
924                 gHASH(theReverse);
925             }
926         }
927     }
928 }