/// <summary>ctor</summary> public AesCtrCryptoTransform(byte[] key, ArraySegment<byte> counterBufferSegment, Func<Aes> aesFactory = null) { if (counterBufferSegment.Count != AesConstants.AES_BLOCK_SIZE) throw new ArgumentException("counterBufferSegment.Count must be " + AesConstants.STR_AES_BLOCK_SIZE + "."); var aes = aesFactory == null ? AesFactories.Aes() : aesFactory(); aes.Mode = CipherMode.ECB; aes.Padding = PaddingMode.None; var counterBufferSegmentArray = counterBufferSegment.Array; var counterBufferSegmentOffset = counterBufferSegment.Offset; byte[] counterBuffer_KeyStreamBuffer = this.counterBuffer_KeyStreamBuffer; // looks dumb, but local-access is faster than field-access System.Diagnostics.Debug.Assert(AesConstants.AES_BLOCK_SIZE == 16); //Utils.BlockCopy(counterBufferSegment.Array, counterBufferSegment.Offset, counterBuffer_KeyStreamBuffer, 0, AesConstants.AES_BLOCK_SIZE); counterBuffer_KeyStreamBuffer[00] = counterBufferSegmentArray[counterBufferSegmentOffset + 00]; counterBuffer_KeyStreamBuffer[01] = counterBufferSegmentArray[counterBufferSegmentOffset + 01]; counterBuffer_KeyStreamBuffer[02] = counterBufferSegmentArray[counterBufferSegmentOffset + 02]; counterBuffer_KeyStreamBuffer[03] = counterBufferSegmentArray[counterBufferSegmentOffset + 03]; counterBuffer_KeyStreamBuffer[04] = counterBufferSegmentArray[counterBufferSegmentOffset + 04]; counterBuffer_KeyStreamBuffer[05] = counterBufferSegmentArray[counterBufferSegmentOffset + 05]; counterBuffer_KeyStreamBuffer[06] = counterBufferSegmentArray[counterBufferSegmentOffset + 06]; counterBuffer_KeyStreamBuffer[07] = counterBufferSegmentArray[counterBufferSegmentOffset + 07]; this.counterStruct = new Utils.LongStruct { B8 = counterBufferSegmentArray[counterBufferSegmentOffset + 08], B7 = counterBufferSegmentArray[counterBufferSegmentOffset + 09], B6 = counterBufferSegmentArray[counterBufferSegmentOffset + 10], B5 = counterBufferSegmentArray[counterBufferSegmentOffset + 11], B4 = counterBufferSegmentArray[counterBufferSegmentOffset + 12], B3 = counterBufferSegmentArray[counterBufferSegmentOffset + 13], B2 = counterBufferSegmentArray[counterBufferSegmentOffset + 14], B1 = counterBufferSegmentArray[counterBufferSegmentOffset + 15] }; this.cryptoTransform = aes.CreateEncryptor(rgbKey: key, rgbIV: null); this.aes = aes; }// ctor
}// ctor #region public public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (inputCount == 0) return 0; int i, j, remainingInputCount = inputCount; byte[] counterBuffer_KeyStreamBuffer = this.counterBuffer_KeyStreamBuffer; // looks dumb, but local-access is faster than field-access // process any available key stream first if (this.keyStreamBytesRemaining > 0) { j = inputCount > this.keyStreamBytesRemaining ? this.keyStreamBytesRemaining : inputCount; for (i = 0; i < j; ++i) outputBuffer[outputOffset + i] = (byte)(counterBuffer_KeyStreamBuffer[AesConstants.AES_BLOCK_SIZE * 2 - this.keyStreamBytesRemaining + i] ^ inputBuffer[inputOffset + i]); this.keyStreamBytesRemaining -= j; remainingInputCount -= j; if (remainingInputCount == 0) return inputCount; inputOffset += j; outputOffset += j; } int fullBlockSize = (remainingInputCount >> 4) << 4; int partialBlockSize = remainingInputCount - fullBlockSize; var _counterStruct = this.counterStruct; // process full blocks, if any if (fullBlockSize > 0) { byte counterBuffer_00 = counterBuffer_KeyStreamBuffer[00]; byte counterBuffer_01 = counterBuffer_KeyStreamBuffer[01]; byte counterBuffer_02 = counterBuffer_KeyStreamBuffer[02]; byte counterBuffer_03 = counterBuffer_KeyStreamBuffer[03]; byte counterBuffer_04 = counterBuffer_KeyStreamBuffer[04]; byte counterBuffer_05 = counterBuffer_KeyStreamBuffer[05]; byte counterBuffer_06 = counterBuffer_KeyStreamBuffer[06]; byte counterBuffer_07 = counterBuffer_KeyStreamBuffer[07]; unchecked { for (i = outputOffset, /* reusing j as iMax */ j = outputOffset + fullBlockSize; i < j; i += AesConstants.AES_BLOCK_SIZE) { outputBuffer[i + 00] = counterBuffer_00; outputBuffer[i + 01] = counterBuffer_01; outputBuffer[i + 02] = counterBuffer_02; outputBuffer[i + 03] = counterBuffer_03; outputBuffer[i + 04] = counterBuffer_04; outputBuffer[i + 05] = counterBuffer_05; outputBuffer[i + 06] = counterBuffer_06; outputBuffer[i + 07] = counterBuffer_07; outputBuffer[i + 08] = _counterStruct.B8; outputBuffer[i + 09] = _counterStruct.B7; outputBuffer[i + 10] = _counterStruct.B6; outputBuffer[i + 11] = _counterStruct.B5; outputBuffer[i + 12] = _counterStruct.B4; outputBuffer[i + 13] = _counterStruct.B3; outputBuffer[i + 14] = _counterStruct.B2; outputBuffer[i + 15] = _counterStruct.B1; { ++_counterStruct.UlongValue; }; }//for } fullBlockSize = this.cryptoTransform.TransformBlock(outputBuffer, outputOffset, fullBlockSize, outputBuffer, outputOffset); i = 0; const bool VECTORIZE = true; if (VECTORIZE) { // vectorized xor int vectorLength = Vector<byte>.Count, vectorLimit = (fullBlockSize / vectorLength) * vectorLength; for (; i < vectorLimit; i += vectorLength) { var destVector = new Vector<byte>(outputBuffer, outputOffset + i); var leftVector = new Vector<byte>(inputBuffer, inputOffset + i); (destVector ^ leftVector).CopyTo(outputBuffer, outputOffset + i); } } for (; i < fullBlockSize; ++i) outputBuffer[outputOffset + i] ^= inputBuffer[inputOffset + i]; }// if fullBlockSize > 0 // process the remaining partial block, if any if (partialBlockSize > 0) { inputOffset += fullBlockSize; outputOffset += fullBlockSize; counterBuffer_KeyStreamBuffer[08] = _counterStruct.B8; counterBuffer_KeyStreamBuffer[09] = _counterStruct.B7; counterBuffer_KeyStreamBuffer[10] = _counterStruct.B6; counterBuffer_KeyStreamBuffer[11] = _counterStruct.B5; counterBuffer_KeyStreamBuffer[12] = _counterStruct.B4; counterBuffer_KeyStreamBuffer[13] = _counterStruct.B3; counterBuffer_KeyStreamBuffer[14] = _counterStruct.B2; counterBuffer_KeyStreamBuffer[15] = _counterStruct.B1; { ++_counterStruct.UlongValue; }; this.cryptoTransform.TransformBlock(counterBuffer_KeyStreamBuffer, 0, AesConstants.AES_BLOCK_SIZE, counterBuffer_KeyStreamBuffer, AesConstants.AES_BLOCK_SIZE); for (i = 0; i < partialBlockSize; ++i) outputBuffer[outputOffset + i] = (byte)(counterBuffer_KeyStreamBuffer[AesConstants.AES_BLOCK_SIZE + i] ^ inputBuffer[inputOffset + i]); this.keyStreamBytesRemaining = AesConstants.AES_BLOCK_SIZE - partialBlockSize; }//if partialBlockSize > 0 this.counterStruct = _counterStruct; return inputCount; }// TransformBlock()