1 package org.bouncycastle.crypto.modes; 2 3 import org.bouncycastle.crypto.BlockCipher; 4 import org.bouncycastle.crypto.CipherParameters; 5 import org.bouncycastle.crypto.DataLengthException; 6 import org.bouncycastle.crypto.InvalidCipherTextException; 7 import org.bouncycastle.crypto.modes.gcm.GCMMultiplier; 8 import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier; 9 import org.bouncycastle.crypto.params.AEADParameters; 10 import org.bouncycastle.crypto.params.KeyParameter; 11 import org.bouncycastle.crypto.params.ParametersWithIV; 12 import org.bouncycastle.crypto.util.Pack; 13 import org.bouncycastle.util.Arrays; 14 15 /** 16 * Implements the Galois/Counter mode (GCM) detailed in 17 * NIST Special Publication 800-38D. 18 */ 19 public class GCMBlockCipher 20 implements AEADBlockCipher 21 { 22 private static final int BLOCK_SIZE = 16; 23 private static final byte[] ZEROES = new byte[BLOCK_SIZE]; 24 25 // not final due to a compiler bug 26 private BlockCipher cipher; 27 private GCMMultiplier multiplier; 28 29 // These fields are set by init and not modified by processing 30 private boolean forEncryption; 31 private int macSize; 32 private byte[] nonce; 33 private byte[] A; 34 private KeyParameter keyParam; 35 private byte[] H; 36 private byte[] initS; 37 private byte[] J0; 38 39 // These fields are modified during processing 40 private byte[] bufBlock; 41 private byte[] macBlock; 42 private byte[] S; 43 private byte[] counter; 44 private int bufOff; 45 private long totalLength; 46 GCMBlockCipher(BlockCipher c)47 public GCMBlockCipher(BlockCipher c) 48 { 49 this(c, null); 50 } 51 GCMBlockCipher(BlockCipher c, GCMMultiplier m)52 public GCMBlockCipher(BlockCipher c, GCMMultiplier m) 53 { 54 if (c.getBlockSize() != BLOCK_SIZE) 55 { 56 throw new IllegalArgumentException( 57 "cipher required with a block size of " + BLOCK_SIZE + "."); 58 } 59 60 if (m == null) 61 { 62 // TODO Consider a static property specifying default multiplier 63 m = new Tables8kGCMMultiplier(); 64 } 65 66 this.cipher = c; 67 this.multiplier = m; 68 } 69 getUnderlyingCipher()70 public BlockCipher getUnderlyingCipher() 71 { 72 return cipher; 73 } 74 getAlgorithmName()75 public String getAlgorithmName() 76 { 77 return cipher.getAlgorithmName() + "/GCM"; 78 } 79 init(boolean forEncryption, CipherParameters params)80 public void init(boolean forEncryption, CipherParameters params) 81 throws IllegalArgumentException 82 { 83 this.forEncryption = forEncryption; 84 this.macBlock = null; 85 86 if (params instanceof AEADParameters) 87 { 88 AEADParameters param = (AEADParameters)params; 89 90 nonce = param.getNonce(); 91 A = param.getAssociatedText(); 92 93 int macSizeBits = param.getMacSize(); 94 if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0) 95 { 96 throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits); 97 } 98 99 macSize = macSizeBits / 8; 100 keyParam = param.getKey(); 101 } 102 else if (params instanceof ParametersWithIV) 103 { 104 ParametersWithIV param = (ParametersWithIV)params; 105 106 nonce = param.getIV(); 107 A = null; 108 macSize = 16; 109 keyParam = (KeyParameter)param.getParameters(); 110 } 111 else 112 { 113 throw new IllegalArgumentException("invalid parameters passed to GCM"); 114 } 115 116 int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize); 117 this.bufBlock = new byte[bufLength]; 118 119 if (nonce == null || nonce.length < 1) 120 { 121 throw new IllegalArgumentException("IV must be at least 1 byte"); 122 } 123 124 if (A == null) 125 { 126 // Avoid lots of null checks 127 A = new byte[0]; 128 } 129 130 // Cipher always used in forward mode 131 cipher.init(true, keyParam); 132 133 // TODO This should be configurable by init parameters 134 // (but must be 16 if nonce length not 12) (BLOCK_SIZE?) 135 // this.tagLength = 16; 136 137 this.H = new byte[BLOCK_SIZE]; 138 cipher.processBlock(ZEROES, 0, H, 0); 139 multiplier.init(H); 140 141 this.initS = gHASH(A); 142 143 if (nonce.length == 12) 144 { 145 this.J0 = new byte[16]; 146 System.arraycopy(nonce, 0, J0, 0, nonce.length); 147 this.J0[15] = 0x01; 148 } 149 else 150 { 151 this.J0 = gHASH(nonce); 152 byte[] X = new byte[16]; 153 packLength((long)nonce.length * 8, X, 8); 154 xor(this.J0, X); 155 multiplier.multiplyH(this.J0); 156 } 157 158 this.S = Arrays.clone(initS); 159 this.counter = Arrays.clone(J0); 160 this.bufOff = 0; 161 this.totalLength = 0; 162 } 163 getMac()164 public byte[] getMac() 165 { 166 return Arrays.clone(macBlock); 167 } 168 getOutputSize(int len)169 public int getOutputSize(int len) 170 { 171 if (forEncryption) 172 { 173 return len + bufOff + macSize; 174 } 175 176 return len + bufOff - macSize; 177 } 178 getUpdateOutputSize(int len)179 public int getUpdateOutputSize(int len) 180 { 181 return ((len + bufOff) / BLOCK_SIZE) * BLOCK_SIZE; 182 } 183 processByte(byte in, byte[] out, int outOff)184 public int processByte(byte in, byte[] out, int outOff) 185 throws DataLengthException 186 { 187 return process(in, out, outOff); 188 } 189 processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)190 public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) 191 throws DataLengthException 192 { 193 int resultLen = 0; 194 195 for (int i = 0; i != len; i++) 196 { 197 // resultLen += process(in[inOff + i], out, outOff + resultLen); 198 bufBlock[bufOff++] = in[inOff + i]; 199 200 if (bufOff == bufBlock.length) 201 { 202 gCTRBlock(bufBlock, BLOCK_SIZE, out, outOff + resultLen); 203 if (!forEncryption) 204 { 205 System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize); 206 } 207 // bufOff = 0; 208 bufOff = bufBlock.length - BLOCK_SIZE; 209 // return bufBlock.Length; 210 resultLen += BLOCK_SIZE; 211 } 212 } 213 214 return resultLen; 215 } 216 process(byte in, byte[] out, int outOff)217 private int process(byte in, byte[] out, int outOff) 218 throws DataLengthException 219 { 220 bufBlock[bufOff++] = in; 221 222 if (bufOff == bufBlock.length) 223 { 224 gCTRBlock(bufBlock, BLOCK_SIZE, out, outOff); 225 if (!forEncryption) 226 { 227 System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize); 228 } 229 // bufOff = 0; 230 bufOff = bufBlock.length - BLOCK_SIZE; 231 // return bufBlock.length; 232 return BLOCK_SIZE; 233 } 234 235 return 0; 236 } 237 doFinal(byte[] out, int outOff)238 public int doFinal(byte[] out, int outOff) 239 throws IllegalStateException, InvalidCipherTextException 240 { 241 int extra = bufOff; 242 if (!forEncryption) 243 { 244 if (extra < macSize) 245 { 246 throw new InvalidCipherTextException("data too short"); 247 } 248 extra -= macSize; 249 } 250 251 if (extra > 0) 252 { 253 byte[] tmp = new byte[BLOCK_SIZE]; 254 System.arraycopy(bufBlock, 0, tmp, 0, extra); 255 gCTRBlock(tmp, extra, out, outOff); 256 } 257 258 // Final gHASH 259 byte[] X = new byte[16]; 260 packLength((long)A.length * 8, X, 0); 261 packLength(totalLength * 8, X, 8); 262 263 xor(S, X); 264 multiplier.multiplyH(S); 265 266 // TODO Fix this if tagLength becomes configurable 267 // T = MSBt(GCTRk(J0,S)) 268 byte[] tag = new byte[BLOCK_SIZE]; 269 cipher.processBlock(J0, 0, tag, 0); 270 xor(tag, S); 271 272 int resultLen = extra; 273 274 // We place into macBlock our calculated value for T 275 this.macBlock = new byte[macSize]; 276 System.arraycopy(tag, 0, macBlock, 0, macSize); 277 278 if (forEncryption) 279 { 280 // Append T to the message 281 System.arraycopy(macBlock, 0, out, outOff + bufOff, macSize); 282 resultLen += macSize; 283 } 284 else 285 { 286 // Retrieve the T value from the message and compare to calculated one 287 byte[] msgMac = new byte[macSize]; 288 System.arraycopy(bufBlock, extra, msgMac, 0, macSize); 289 if (!Arrays.constantTimeAreEqual(this.macBlock, msgMac)) 290 { 291 throw new InvalidCipherTextException("mac check in GCM failed"); 292 } 293 } 294 295 reset(false); 296 297 return resultLen; 298 } 299 reset()300 public void reset() 301 { 302 reset(true); 303 } 304 reset( boolean clearMac)305 private void reset( 306 boolean clearMac) 307 { 308 S = Arrays.clone(initS); 309 counter = Arrays.clone(J0); 310 bufOff = 0; 311 totalLength = 0; 312 313 if (bufBlock != null) 314 { 315 Arrays.fill(bufBlock, (byte)0); 316 } 317 318 if (clearMac) 319 { 320 macBlock = null; 321 } 322 323 cipher.reset(); 324 } 325 gCTRBlock(byte[] buf, int bufCount, byte[] out, int outOff)326 private void gCTRBlock(byte[] buf, int bufCount, byte[] out, int outOff) 327 { 328 // inc(counter); 329 for (int i = 15; i >= 12; --i) 330 { 331 byte b = (byte)((counter[i] + 1) & 0xff); 332 counter[i] = b; 333 334 if (b != 0) 335 { 336 break; 337 } 338 } 339 340 byte[] tmp = new byte[BLOCK_SIZE]; 341 cipher.processBlock(counter, 0, tmp, 0); 342 343 byte[] hashBytes; 344 if (forEncryption) 345 { 346 System.arraycopy(ZEROES, bufCount, tmp, bufCount, BLOCK_SIZE - bufCount); 347 hashBytes = tmp; 348 } 349 else 350 { 351 hashBytes = buf; 352 } 353 354 for (int i = bufCount - 1; i >= 0; --i) 355 { 356 tmp[i] ^= buf[i]; 357 out[outOff + i] = tmp[i]; 358 } 359 360 // gHASHBlock(hashBytes); 361 xor(S, hashBytes); 362 multiplier.multiplyH(S); 363 364 totalLength += bufCount; 365 } 366 gHASH(byte[] b)367 private byte[] gHASH(byte[] b) 368 { 369 byte[] Y = new byte[16]; 370 371 for (int pos = 0; pos < b.length; pos += 16) 372 { 373 byte[] X = new byte[16]; 374 int num = Math.min(b.length - pos, 16); 375 System.arraycopy(b, pos, X, 0, num); 376 xor(Y, X); 377 multiplier.multiplyH(Y); 378 } 379 380 return Y; 381 } 382 383 // private void gHASHBlock(byte[] block) 384 // { 385 // xor(S, block); 386 // multiplier.multiplyH(S); 387 // } 388 389 // private static void inc(byte[] block) 390 // { 391 // for (int i = 15; i >= 12; --i) 392 // { 393 // byte b = (byte)((block[i] + 1) & 0xff); 394 // block[i] = b; 395 // 396 // if (b != 0) 397 // { 398 // break; 399 // } 400 // } 401 // } 402 xor(byte[] block, byte[] val)403 private static void xor(byte[] block, byte[] val) 404 { 405 for (int i = 15; i >= 0; --i) 406 { 407 block[i] ^= val[i]; 408 } 409 } 410 packLength(long count, byte[] bs, int off)411 private static void packLength(long count, byte[] bs, int off) 412 { 413 Pack.intToBigEndian((int)(count >>> 32), bs, off); 414 Pack.intToBigEndian((int)count, bs, off + 4); 415 } 416 } 417