diff --git a/EtM_Transforms.cs b/EtM_Transforms.cs index d2f1fe5..80a184c 100644 --- a/EtM_Transforms.cs +++ b/EtM_Transforms.cs @@ -127,6 +127,7 @@ public class EtM_DecryptTransform : ICryptoTransform byte[] key; uint currentChunkNumber; ArraySegment? salt; + ArraySegment? saltWithPrepend; public uint CurrentChunkNumber { get { return this.currentChunkNumber; } } @@ -140,6 +141,14 @@ public EtM_DecryptTransform(byte[] key, ArraySegment? salt = null, bool au this.IsAuthenticateOnly = authenticateOnly; } + public void SetCurrentChunkNumber(uint chunk) + { + if (chunk > EtM_Transform_Constants.INITIAL_CHUNK_NUMBER && !saltWithPrepend.HasValue) + throw new Exception("First chunk must be read first."); + + this.currentChunkNumber = chunk; + } + public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { int partialBlockSize = inputCount % EtM_Transform_Constants.OUTPUT_BLOCK_SIZE; @@ -154,6 +163,8 @@ public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, b var authenticateOnly = this.IsAuthenticateOnly; for (; i < fullBlockSize; i += EtM_Transform_Constants.OUTPUT_BLOCK_SIZE, j += EtM_Transform_Constants.INPUT_BLOCK_SIZE) { + var isFirstChunk = this.currentChunkNumber == EtM_Transform_Constants.INITIAL_CHUNK_NUMBER; + var outputSegment = new ArraySegment?(new ArraySegment(outputBuffer, outputOffset + j, EtM_Transform_Constants.INPUT_BLOCK_SIZE)); var cipherText = new ArraySegment(inputBuffer, inputOffset + i, EtM_Transform_Constants.OUTPUT_BLOCK_SIZE); @@ -162,7 +173,7 @@ public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, b if (!EtM_CTR.Authenticate( masterKey: this.key, ciphertext: cipherText, - salt: this.salt, + salt: isFirstChunk ? this.salt : this.saltWithPrepend, counter: this.currentChunkNumber)) outputSegment = null; } @@ -172,8 +183,9 @@ public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, b masterKey: this.key, ciphertext: cipherText, outputSegment: ref outputSegment, - salt: this.salt, + salt: isFirstChunk ? this.salt : this.saltWithPrepend, counter: this.currentChunkNumber); + } if (outputSegment == null) @@ -182,9 +194,11 @@ public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, b throw new CryptographicException("Decryption failed for block " + this.currentChunkNumber.ToString() + "."); } - if (this.currentChunkNumber == EtM_Transform_Constants.INITIAL_CHUNK_NUMBER) + if (isFirstChunk && !saltWithPrepend.HasValue) { - EtM_EncryptTransform.PrependSaltWith1stBlockContext(ref this.salt, inputBuffer, inputOffset); + if (salt.HasValue) + saltWithPrepend = new ArraySegment(salt.Value.Array); + EtM_EncryptTransform.PrependSaltWith1stBlockContext(ref this.saltWithPrepend, inputBuffer, inputOffset); } checked { ++this.currentChunkNumber; } @@ -204,6 +218,7 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input byte[] outputBuffer = null; var cipherText = new ArraySegment(inputBuffer, inputOffset, inputCount); + var isFirstChunk = this.currentChunkNumber == EtM_Transform_Constants.INITIAL_CHUNK_NUMBER; if (this.IsAuthenticateOnly) { @@ -219,10 +234,10 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input outputBuffer = EtM_CTR.Decrypt( masterKey: this.key, ciphertext: cipherText, - salt: this.salt, + salt: isFirstChunk ? this.salt : this.saltWithPrepend, counter: this.currentChunkNumber); } - this.Dispose(); + if (outputBuffer == null) throw new CryptographicException("Decryption failed for block " + this.currentChunkNumber.ToString() + ".");