public ChunkEncryptorStreamReader(
            Stream previousStream,
            SymmetricAlgorithm algorithm,
            long from,
            long to,
            long totalFileLength,
            IEnumerable <GeneralDigest> digests = null,
            byte[] iv      = null,
            ILogger logger = null
            )
        {
            this.PreviousStream = previousStream;

            this.TotalClearLength  = totalFileLength;
            this.ClearStreamLength = to - from + 1;

            this.BlockSizeBytes = algorithm.BlockSize / 8;

            this.CipherBuffer = new byte[BlockSizeBytes];
            this.BlockBuffer  = new byte[BlockSizeBytes];

            this.Digests = digests;

            if (from == 0)
            {
                Header = new StreamEncryptionHeader(BlockSizeBytes)
                {
                    From = 0,
                    To   = this.TotalClearLength
                };

                algorithm.GenerateIV();
                algorithm.IV.CopyTo(Header.IV, 0);
                // rant: this seems a poor API design because of the race condition.
                // but seeing as it's random data, if it collides with other
                // data, well, that might not be so bad.... but still.

                HeaderStream = new MemoryStream(StreamEncryptionHeader.HeaderLength(BlockSizeBytes));
                Header.WriteTo(HeaderStream);
                HeaderStream.Seek(0, SeekOrigin.Begin);
                if (iv != null)
                {
                    throw new Exception("Encountered non-null IV on first chunk");
                }

                iv           = Header.IV;
                InjectHeader = true;
            }
            else
            if (iv == null)
            {
                throw new Exception("Secondary chunk with null IV encountered");
            }

            long clearLength  = to - from + 1;
            long headerLength = (from == 0)
                ? StreamEncryptionHeader.HeaderLength(BlockSizeBytes)
                : 0;

            // note, we are not adding room for padding. EVEN if it's the last block.
            // This class does not use any traditional padding modes, it's length prefixed.
            long cipherLength =
                (clearLength / BlockSizeBytes) * BlockSizeBytes
                + ((clearLength % BlockSizeBytes == 0) ? 0 : BlockSizeBytes); // if it was not a multiple, add another block

            this.StreamLength = headerLength + cipherLength;

            this.Encryptor = algorithm.CreateEncryptor(algorithm.Key, iv);
        }
Ejemplo n.º 2
0
        public DecryptingStreamWriter(
            Stream previousStream,
            SymmetricAlgorithm algorithm,
            long from,
            long to,
            long totalLength,
            ILogger logger = null
            )
        {
            this.PreviousStream = previousStream;
            this.Algorithm      = algorithm;

            Algorithm.Padding = PaddingMode.None;

            this.From        = from;
            this.To          = to;
            this.TotalLength = totalLength;

            this.BlockSizeBytes = algorithm.BlockSize / 8;

            this.CipherBuffer = new byte[BlockSizeBytes];
            this.BlockBuffer  = new byte[BlockSizeBytes];

            this.IsSeeking = this.From != 0;
            int fullHeaderLength = StreamEncryptionHeader.HeaderLength(BlockSizeBytes);

            this.ExpectedHeaderBytes = this.IsSeeking
                ? BlockSizeBytes
                : fullHeaderLength;

            this.HeaderStream = new MemoryStream();

            this.StreamLength = to - from + 1;

            from += fullHeaderLength;
            to   += fullHeaderLength;

            var blockNumber = from / BlockSizeBytes;
            var blockStart  = blockNumber * BlockSizeBytes;

            SkipBytes = (int)(from - blockStart);

            // backup to either the full header or a prior block
            from = IsSeeking
                ? from - BlockSizeBytes - SkipBytes
                : 0;

            if (from < 0)
            {
                throw new Exception("nonsense output from encryption stream seeking algorithm");
            }

            // if "to" isn't on the edge of a complete block,
            // round it up to the nearest end of block
            if ((to + 1) % BlockSizeBytes != 0)
            {
                to = (((to + 1) / BlockSizeBytes) + 1) * BlockSizeBytes - 1;
            }

            this.CipherFrom = from;

            this.CipherTo = to;

            // note, we are not adding room for padding. EVEN if it's the last block.
            // This class does not use any traditional padding modes, it's length prefixed.
            this.CipherTotalLength = this.ExpectedHeaderBytes
                                     + (this.TotalLength / BlockSizeBytes) * BlockSizeBytes
                                     + ((this.TotalLength % BlockSizeBytes == 0) ? 0 : BlockSizeBytes); // if it was not a multiple, add another block
        }