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.macs;
18  
19  import io.github.tonywasher.joceanus.gordianknot.impl.ext.engines.GordianZuc128Engine;
20  import org.bouncycastle.crypto.CipherParameters;
21  import org.bouncycastle.crypto.Mac;
22  
23  /**
24   * Zuc128Mac implementation.
25   * Based on <a href="http://www.qtc.jp/3GPP/Specs/eea3eia3specificationv16.pdf">Zuc Specification</a>
26   * Donated to BouncyCastle
27   */
28  public class GordianZuc128Mac
29          implements Mac {
30      /**
31       * The Maximum Bit Mask.
32       */
33      private static final int TOPBIT = 0x80;
34  
35      /**
36       * The Zuc128 Engine.
37       */
38      private final GordianZuc128Engine theEngine;
39  
40      /**
41       * The calculated Mac in words.
42       */
43      private int theMac;
44  
45      /**
46       * The active keyStream.
47       */
48      private final int[] theKeyStream;
49  
50      /**
51       * The initialised state.
52       */
53      private GordianZuc128Engine theState;
54  
55      /**
56       * The current word index.
57       */
58      private int theWordIndex;
59  
60      /**
61       * The current byte index.
62       */
63      private int theByteIndex;
64  
65      /**
66       * Constructor.
67       */
68      public GordianZuc128Mac() {
69          theEngine = new GordianZuc128Engine();
70          theKeyStream = new int[2];
71      }
72  
73      /**
74       * Obtain Algorithm Name.
75       *
76       * @return the name
77       */
78      public String getAlgorithmName() {
79          return "Zuc128Mac";
80      }
81  
82      /**
83       * Obtain Mac Size.
84       *
85       * @return the size in Bytes
86       */
87      public int getMacSize() {
88          return Integer.BYTES;
89      }
90  
91      /**
92       * Initialise the Mac.
93       *
94       * @param pParams the parameters
95       */
96      public void init(final CipherParameters pParams) {
97          /* Initialise the engine */
98          theEngine.init(true, pParams);
99          theState = theEngine.copy();
100         initKeyStream();
101     }
102 
103     /**
104      * Initialise the keyStream.
105      */
106     private void initKeyStream() {
107         /* Initialise the Mac */
108         theMac = 0;
109 
110         /* Initialise the KeyStream */
111         for (int i = 0; i < theKeyStream.length - 1; i++) {
112             theKeyStream[i] = theEngine.makeKeyStreamWord();
113         }
114         theWordIndex = theKeyStream.length - 1;
115         theByteIndex = Integer.BYTES - 1;
116     }
117 
118     /**
119      * Update the mac with a single byte.
120      *
121      * @param in the byte to update with
122      */
123     public void update(final byte in) {
124         /* shift for next byte */
125         shift4NextByte();
126 
127         /* Loop through the bits */
128         final int bitBase = theByteIndex * Byte.SIZE;
129         for (int bitMask = TOPBIT, bitNo = 0; bitMask > 0; bitMask >>= 1, bitNo++) {
130             /* If the bit is set */
131             if ((in & bitMask) != 0) {
132                 /* update theMac */
133                 updateMac(bitBase + bitNo);
134             }
135         }
136     }
137 
138     /**
139      * Shift for next byte.
140      */
141     private void shift4NextByte() {
142         /* Adjust the byte index */
143         theByteIndex = (theByteIndex + 1) % Integer.BYTES;
144 
145         /* Adjust keyStream if required */
146         if (theByteIndex == 0) {
147             theKeyStream[theWordIndex] = theEngine.makeKeyStreamWord();
148             theWordIndex = (theWordIndex + 1) % theKeyStream.length;
149         }
150     }
151 
152     /**
153      * Update the Mac.
154      *
155      * @param bitNo the bit number
156      */
157     private void updateMac(final int bitNo) {
158         /* Update the Mac */
159         theMac ^= getKeyStreamWord(bitNo);
160     }
161 
162     /**
163      * Obtain the keyStreamWord.
164      *
165      * @param bitNo the bitNumber
166      * @return the word
167      */
168     private int getKeyStreamWord(final int bitNo) {
169         /* Access the first word and return it if this is bit 0 */
170         final int myFirst = theKeyStream[theWordIndex];
171         if (bitNo == 0) {
172             return myFirst;
173         }
174 
175         /* Access the second word */
176         final int mySecond = theKeyStream[(theWordIndex + 1) % theKeyStream.length];
177         return (myFirst << bitNo) | (mySecond >>> (Integer.SIZE - bitNo));
178     }
179 
180     /**
181      * Update the mac.
182      *
183      * @param in    the input buffer
184      * @param inOff the starting offset in the input buffer
185      * @param len   the length of data to process
186      */
187     public void update(final byte[] in, final int inOff, final int len) {
188         for (int byteNo = 0; byteNo < len; byteNo++) {
189             update(in[inOff + byteNo]);
190         }
191     }
192 
193     /**
194      * Obtain the final word.
195      *
196      * @return the final word
197      */
198     private int getFinalWord() {
199         if (theByteIndex != 0) {
200             return theEngine.makeKeyStreamWord();
201         }
202         theWordIndex = (theWordIndex + 1) % theKeyStream.length;
203         return theKeyStream[theWordIndex];
204     }
205 
206     /**
207      * Finalize the mac.
208      *
209      * @param out    the output buffer
210      * @param outOff the starting offset in the input buffer
211      * @return the size of the mac
212      */
213     public int doFinal(final byte[] out, final int outOff) {
214         /* Finish the Mac and output it */
215         shift4NextByte();
216         theMac ^= getKeyStreamWord(theByteIndex * Byte.SIZE);
217         theMac ^= getFinalWord();
218         GordianZuc128Engine.encode32be(theMac, out, outOff);
219 
220         /* Reset the Mac */
221         reset();
222         return getMacSize();
223     }
224 
225     @Override
226     public void reset() {
227         theEngine.reset(theState);
228         initKeyStream();
229     }
230 }