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