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 }