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