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 }