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