Skip to content

Commit f8d638b

Browse files
committed
Perf. opts. in GCMBlockCipher
- avoid double-copying for long encryption inputs
1 parent bb1ac12 commit f8d638b

File tree

2 files changed

+127
-41
lines changed

2 files changed

+127
-41
lines changed

crypto/src/crypto/modes/GCMBlockCipher.cs

Lines changed: 93 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,16 @@ public virtual int ProcessByte(
306306
bufBlock[bufOff] = input;
307307
if (++bufOff == bufBlock.Length)
308308
{
309-
OutputBlock(output, outOff);
309+
ProcessBlock(bufBlock, 0, output, outOff);
310+
if (forEncryption)
311+
{
312+
bufOff = 0;
313+
}
314+
else
315+
{
316+
Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
317+
bufOff = macSize;
318+
}
310319
return BlockSize;
311320
}
312321
return 0;
@@ -321,41 +330,58 @@ public virtual int ProcessBytes(
321330
{
322331
CheckStatus();
323332

324-
if (input.Length < (inOff + len))
325-
throw new DataLengthException("Input buffer too short");
333+
Check.DataLength(input, inOff, len, "input buffer too short");
326334

327335
int resultLen = 0;
328336

329-
for (int i = 0; i < len; ++i)
337+
if (forEncryption)
330338
{
331-
bufBlock[bufOff] = input[inOff + i];
332-
if (++bufOff == bufBlock.Length)
339+
if (bufOff != 0)
333340
{
334-
OutputBlock(output, outOff + resultLen);
335-
resultLen += BlockSize;
341+
while (len > 0)
342+
{
343+
--len;
344+
bufBlock[bufOff] = input[inOff++];
345+
if (++bufOff == BlockSize)
346+
{
347+
ProcessBlock(bufBlock, 0, output, outOff);
348+
bufOff = 0;
349+
resultLen += BlockSize;
350+
break;
351+
}
352+
}
336353
}
337-
}
338354

339-
return resultLen;
340-
}
355+
while (len >= BlockSize)
356+
{
357+
ProcessBlock(input, inOff, output, outOff + resultLen);
358+
inOff += BlockSize;
359+
len -= BlockSize;
360+
resultLen += BlockSize;
361+
}
341362

342-
private void OutputBlock(byte[] output, int offset)
343-
{
344-
Check.OutputLength(output, offset, BlockSize, "Output buffer too short");
345-
if (totalLength == 0)
346-
{
347-
InitCipher();
348-
}
349-
gCTRBlock(bufBlock, output, offset);
350-
if (forEncryption)
351-
{
352-
bufOff = 0;
363+
if (len > 0)
364+
{
365+
Array.Copy(input, inOff, bufBlock, 0, len);
366+
bufOff = len;
367+
}
353368
}
354369
else
355370
{
356-
Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
357-
bufOff = macSize;
371+
for (int i = 0; i < len; ++i)
372+
{
373+
bufBlock[bufOff] = input[inOff + i];
374+
if (++bufOff == bufBlock.Length)
375+
{
376+
ProcessBlock(bufBlock, 0, output, outOff + resultLen);
377+
Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
378+
bufOff = macSize;
379+
resultLen += BlockSize;
380+
}
381+
}
358382
}
383+
384+
return resultLen;
359385
}
360386

361387
public int DoFinal(byte[] output, int outOff)
@@ -385,7 +411,7 @@ public int DoFinal(byte[] output, int outOff)
385411

386412
if (extra > 0)
387413
{
388-
gCTRPartial(bufBlock, 0, extra, output, outOff);
414+
ProcessPartial(bufBlock, 0, extra, output, outOff);
389415
}
390416

391417
atLength += (uint)atBlockPos;
@@ -515,27 +541,50 @@ private void Reset(
515541
}
516542
}
517543

518-
private void gCTRBlock(byte[] block, byte[] output, int outOff)
544+
private void ProcessBlock(byte[] buf, int bufOff, byte[] output, int outOff)
519545
{
520-
byte[] tmp = GetNextCounterBlock();
546+
Check.OutputLength(output, outOff, BlockSize, "Output buffer too short");
521547

522-
GcmUtilities.Xor(tmp, block);
523-
Array.Copy(tmp, 0, output, outOff, BlockSize);
548+
if (totalLength == 0)
549+
{
550+
InitCipher();
551+
}
552+
553+
byte[] ctrBlock = new byte[BlockSize];
554+
GetNextCtrBlock(ctrBlock);
524555

525-
gHASHBlock(S, forEncryption ? tmp : block);
556+
if (forEncryption)
557+
{
558+
GcmUtilities.Xor(ctrBlock, buf, bufOff);
559+
gHASHBlock(S, ctrBlock);
560+
Array.Copy(ctrBlock, 0, output, outOff, BlockSize);
561+
}
562+
else
563+
{
564+
gHASHBlock(S, buf, bufOff);
565+
GcmUtilities.Xor(ctrBlock, 0, buf, bufOff, output, outOff);
566+
}
526567

527568
totalLength += BlockSize;
528569
}
529570

530-
private void gCTRPartial(byte[] buf, int off, int len, byte[] output, int outOff)
571+
private void ProcessPartial(byte[] buf, int off, int len, byte[] output, int outOff)
531572
{
532-
byte[] tmp = GetNextCounterBlock();
573+
byte[] ctrBlock = new byte[BlockSize];
574+
GetNextCtrBlock(ctrBlock);
533575

534-
GcmUtilities.Xor(tmp, buf, off, len);
535-
Array.Copy(tmp, 0, output, outOff, len);
536-
537-
gHASHPartial(S, forEncryption ? tmp : buf, 0, len);
576+
if (forEncryption)
577+
{
578+
GcmUtilities.Xor(buf, off, ctrBlock, 0, len);
579+
gHASHPartial(S, buf, off, len);
580+
}
581+
else
582+
{
583+
gHASHPartial(S, buf, off, len);
584+
GcmUtilities.Xor(buf, off, ctrBlock, 0, len);
585+
}
538586

587+
Array.Copy(buf, off, output, outOff, len);
539588
totalLength += (uint)len;
540589
}
541590

@@ -554,13 +603,19 @@ private void gHASHBlock(byte[] Y, byte[] b)
554603
multiplier.MultiplyH(Y);
555604
}
556605

606+
private void gHASHBlock(byte[] Y, byte[] b, int off)
607+
{
608+
GcmUtilities.Xor(Y, b, off);
609+
multiplier.MultiplyH(Y);
610+
}
611+
557612
private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
558613
{
559614
GcmUtilities.Xor(Y, b, off, len);
560615
multiplier.MultiplyH(Y);
561616
}
562617

563-
private byte[] GetNextCounterBlock()
618+
private void GetNextCtrBlock(byte[] block)
564619
{
565620
if (blocksRemaining == 0)
566621
throw new InvalidOperationException("Attempt to process too many blocks");
@@ -573,10 +628,7 @@ private byte[] GetNextCounterBlock()
573628
c += counter[13]; counter[13] = (byte)c; c >>= 8;
574629
c += counter[12]; counter[12] = (byte)c;
575630

576-
byte[] tmp = new byte[BlockSize];
577-
// TODO Sure would be nice if ciphers could operate on int[]
578-
cipher.ProcessBlock(counter, 0, tmp, 0);
579-
return tmp;
631+
cipher.ProcessBlock(counter, 0, block, 0);
580632
}
581633

582634
private void CheckStatus()

crypto/src/crypto/modes/gcm/GcmUtilities.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,32 @@ internal static void Xor(byte[] x, byte[] y)
267267
while (i < 16);
268268
}
269269

270+
internal static void Xor(byte[] x, byte[] y, int yOff)
271+
{
272+
int i = 0;
273+
do
274+
{
275+
x[i] ^= y[yOff + i]; ++i;
276+
x[i] ^= y[yOff + i]; ++i;
277+
x[i] ^= y[yOff + i]; ++i;
278+
x[i] ^= y[yOff + i]; ++i;
279+
}
280+
while (i < 16);
281+
}
282+
283+
internal static void Xor(byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff)
284+
{
285+
int i = 0;
286+
do
287+
{
288+
z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
289+
z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
290+
z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
291+
z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
292+
}
293+
while (i < 16);
294+
}
295+
270296
internal static void Xor(byte[] x, byte[] y, int yOff, int yLen)
271297
{
272298
while (--yLen >= 0)
@@ -275,6 +301,14 @@ internal static void Xor(byte[] x, byte[] y, int yOff, int yLen)
275301
}
276302
}
277303

304+
internal static void Xor(byte[] x, int xOff, byte[] y, int yOff, int len)
305+
{
306+
while (--len >= 0)
307+
{
308+
x[xOff + len] ^= y[yOff + len];
309+
}
310+
}
311+
278312
internal static void Xor(byte[] x, byte[] y, byte[] z)
279313
{
280314
int i = 0;

0 commit comments

Comments
 (0)