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