// Helper methods

        protected void LoadOuterManifest()
        {
            if (InnerProxy.Manifest.RootDirectory.Files.ContainsKey(
                    DefaultOuterManifestFileName) == false)
            {
                throw new Exception("Encrypted manifest is not present.");
            }

            ManifestFileInfo outerManifestManifestFileInfo =
                InnerProxy.Manifest.RootDirectory.Files[
                    DefaultOuterManifestFileName];

            FileInfo outerManifestFileInfo =
                InnerProxy.GetFile(outerManifestManifestFileInfo);

            Stream outerManifestFileStream =
                outerManifestFileInfo.OpenRead();

            byte[] outerKeyBytes = CryptUtilities.MakeKeyBytesFromString(
                OuterKeyString,
                InnerProxy.Manifest.Guid.ToByteArray());

            Stream outerManifestCryptoStream =
                CryptUtilities.MakeDecryptionReadStreamFrom(
                    outerManifestFileStream,
                    outerKeyBytes);

            OuterManifest =
                Manifest.ReadManifestStream(outerManifestCryptoStream);

            outerManifestCryptoStream.Close();
        }
        public FileInfo CloneFile(
            ManifestFileInfo copyFile,
            DirectoryInfo copyToDirectory)
        {
            ManifestFileInfo innerManifestFileInfo =
                HashToInnerFileMap[copyFile.FileHash];

            FileInfo innerFileInfo =
                InnerProxy.GetFile(innerManifestFileInfo);

            byte[] keyData = CryptUtilities.MakeKeyBytesFromString(
                OuterKeyString,
                copyFile.FileHash.HashData);

            String destFilePath =
                Path.Combine(
                    copyToDirectory.FullName,
                    DefaultDecryptedTempFileName);

            ReadCryptFile(
                innerFileInfo,
                keyData,
                destFilePath);

            FileInfo fileInfo =
                new FileInfo(destFilePath);

            // Make sure that the last-modified date matches that of the
            // expected outer file.  This is necessary because one inner file
            // may correspond to several outer files - each of which might
            // have separate dates.
            fileInfo.LastWriteTimeUtc = copyFile.LastModifiedUtc;

            return(fileInfo);
        }
Exemplo n.º 3
0
 static public FileHash ComputeHash(
     Stream stream,
     string hashType = CryptUtilities.DefaultHashType)
 {
     return(new FileHash(
                CryptUtilities.ComputeHash(stream, hashType),
                hashType));
 }
Exemplo n.º 4
0
 static public FileHash ComputeHash(
     byte[] data,
     string hashType = CryptUtilities.DefaultHashType)
 {
     return(new FileHash(
                CryptUtilities.ComputeHash(data, hashType),
                hashType));
 }
Exemplo n.º 5
0
        // Static

        static public FileHash ComputeHash(
            FileInfo file,
            string hashType = CryptUtilities.DefaultHashType)
        {
            return(new FileHash(
                       CryptUtilities.ComputeHash(file, hashType),
                       hashType));
        }
        protected void SaveOuterManifest()
        {
            // Serialize the manifest to memory
            MemoryStream serializedManifestStream =
                new MemoryStream();

            OuterManifest.WriteManifestStream(serializedManifestStream);
            serializedManifestStream.Position = 0;

            String tempFilePath =
                Path.Combine(
                    InnerProxy.TempDirectory.FullName,
                    DefaultOuterManifestFileName);

            // We use the inner GUID as salt for the outer manifest, so update
            // it each time we write the outer manifest.  The inner GUID is
            // really useless anyways.
            InnerProxy.Manifest.ChangeGUID();

            byte[] outerKeyData = CryptUtilities.MakeKeyBytesFromString(
                OuterKeyString,
                InnerProxy.Manifest.Guid.ToByteArray());

            byte[] cryptHash = WriteCryptFileAndHash(
                serializedManifestStream,
                outerKeyData,
                tempFilePath);

            // The new ManifestFileInfo is actually rooted in the inner
            // Manifest object, but that is ok - although it is kind of a
            // hack.  The fact is that we don't maintain an actual Manifest
            // object to mirror the inner manifest - and we know that the
            // implementation of PutFile won't be affected by doing this.
            ManifestDirectoryInfo parentDirectory =
                InnerProxy.Manifest.RootDirectory;

            ManifestFileInfo destManifestFile =
                new ManifestFileInfo(
                    DefaultOuterManifestFileName,
                    parentDirectory);

            destManifestFile.RegisteredUtc = DateTime.Now;

            FileInfo outerManifestFileInfo = new FileInfo(tempFilePath);

            destManifestFile.LastModifiedUtc =
                outerManifestFileInfo.LastWriteTimeUtc;

            destManifestFile.FileLength =
                outerManifestFileInfo.Length;

            destManifestFile.FileHash =
                new FileHash(cryptHash, CryptUtilities.DefaultHashType);

            InnerProxy.PutFile(ProxyToInner, destManifestFile);
        }
        protected void ValidateFile(
            ManifestFileInfo outerManFileInfo,
            Utilities.Console console)
        {
            ManifestFileInfo innerManifestFileInfo = null;

            try
            {
                innerManifestFileInfo =
                    HashToInnerFileMap[outerManFileInfo.FileHash];

                FileInfo innerFileInfo =
                    InnerProxy.GetFile(innerManifestFileInfo);

                byte[] keyData = CryptUtilities.MakeKeyBytesFromString(
                    OuterKeyString,
                    outerManFileInfo.FileHash.HashData);

                Stream sourceFileStream =
                    innerFileInfo.OpenRead();

                Stream cryptoStream =
                    CryptUtilities.MakeDecryptionReadStreamFrom(
                        sourceFileStream,
                        keyData);

                FileHash computedHash = FileHash.ComputeHash(
                    cryptoStream,
                    outerManFileInfo.FileHash.HashType);

                if (computedHash.Equals(outerManFileInfo.FileHash) == false)
                {
                    throw new Exception("FAILED VALIDATION");
                }
            }
            catch (Exception e)
            {
                console.WriteLine(
                    Manifest.MakeStandardPathString(outerManFileInfo));

                console.WriteLine(
                    Manifest.MakeNativePathString(innerManifestFileInfo));

                console.WriteLine(e.Message);

                console.WriteLine();
            }
        }
        protected void ReadCryptFile(
            FileInfo sourceFileInfo,
            byte[] keyData,
            String destFilePath)
        {
            Stream sourceFileStream =
                sourceFileInfo.OpenRead();

            Stream cryptoStream =
                CryptUtilities.MakeDecryptionReadStreamFrom(
                    sourceFileStream,
                    keyData);

            FileStream destFileStream =
                File.OpenWrite(destFilePath);

            StreamUtilities.CopyStream(
                cryptoStream, destFileStream);

            destFileStream.Close();
        }
        protected byte[] WriteCryptFileAndHash(
            Stream sourceFileStream,
            byte[] keyData,
            String destFilePath,
            String hashType = CryptUtilities.DefaultHashType)
        {
            long bytesToSkip       = 0;
            long bytesReadUntilNow = 0;

            FileStream destFileStream = null;

            System.Security.Cryptography.CryptoStream cryptoStream = null;

            try
            {
                if (File.Exists(destFilePath))
                {
                    bytesToSkip = new FileInfo(destFilePath).Length;

                    destFileStream =
                        File.Open(destFilePath, FileMode.Append);

                    // TODO: Use delegate for console like in other classes
                    System.Console.WriteLine(
                        "Appending after " +
                        bytesToSkip.ToString() +
                        " bytes...");
                }
                else
                {
                    // Set up the dest file streams
                    destFileStream =
                        File.OpenWrite(destFilePath);
                }

                // Set up a temporary stream to hold encrypted data chunks so
                // that we can send the encrypted data to the file and to the
                // hash algorithm.
                MemoryStream encryptedMemoryStream =
                    new MemoryStream();

                // Set up a CryptoStream and attach it to the temporary stream
                cryptoStream =
                    CryptUtilities.MakeEncryptionWriteStreamFrom(
                        encryptedMemoryStream,
                        keyData);

                // Set up the hash algorithm
                System.Security.Cryptography.HashAlgorithm hashAlgorithm =
                    CryptUtilities.GetHashAlgorithm(hashType);

                // Buffers and chunks
                int    chunkSize       = 1024;
                byte[] fileReadBuffer  = new byte[chunkSize];
                byte[] encryptedBuffer = null;

                // Read the first chunk to set up the loop
                int newBytesRead = sourceFileStream.Read(
                    fileReadBuffer,
                    0,
                    chunkSize);

                // Read until the end
                while (newBytesRead > 0)
                {
                    // Encrypt to the MemoryStream
                    cryptoStream.Write(fileReadBuffer, 0, newBytesRead);
                    encryptedBuffer = encryptedMemoryStream.GetBuffer();
                    long newBytesEncrypted = encryptedMemoryStream.Length;

                    // Figure out the number of bytes to write
                    long bytesToWrite =
                        bytesReadUntilNow + newBytesEncrypted - bytesToSkip;

                    if (bytesToWrite < 0)
                    {
                        bytesToWrite = 0;
                    }
                    else if (bytesToWrite > newBytesEncrypted)
                    {
                        bytesToWrite = newBytesEncrypted;
                    }

                    if (bytesToWrite > 0)
                    {
                        long bufferStartPosition =
                            newBytesEncrypted - bytesToWrite;

                        // Write encrypted data to file
                        destFileStream.Write(
                            encryptedBuffer,
                            (int)bufferStartPosition,
                            (int)bytesToWrite);
                    }

                    bytesReadUntilNow += newBytesEncrypted;

                    // Hash encrypted data
                    hashAlgorithm.TransformBlock(
                        encryptedBuffer,
                        0,
                        (int)newBytesEncrypted,
                        encryptedBuffer,
                        0);

                    // Read next chunk
                    newBytesRead = sourceFileStream.Read(
                        fileReadBuffer,
                        0,
                        chunkSize);

                    // Reset position so we don't use a lot of memory
                    encryptedMemoryStream.SetLength(0);
                }

                // Need to do this - I think it writes special padding
                // information to the end of the data.
                cryptoStream.FlushFinalBlock();
                encryptedBuffer = encryptedMemoryStream.GetBuffer();

                // Write final data to file
                destFileStream.Write(
                    encryptedBuffer,
                    0,
                    (int)encryptedMemoryStream.Length);

                // Hash final data
                hashAlgorithm.TransformFinalBlock(
                    encryptedBuffer,
                    0,
                    (int)encryptedMemoryStream.Length);

                sourceFileStream.Close();
                cryptoStream.Close();
                destFileStream.Close();

                return(hashAlgorithm.Hash);
            }
            catch (Exception e)
            {
                sourceFileStream.Close();

                if (cryptoStream != null)
                {
                    cryptoStream.Close();
                }

                if (destFileStream != null)
                {
                    destFileStream.Close();
                    File.Delete(destFilePath);
                }

                throw e;
            }
        }
        public void PutFile(
            IRepositoryProxy sourceRepository,
            ManifestFileInfo sourceManifestFile)
        {
            // Name the inner file with the hash of the hash.  We protect
            // the hash in this way because it is used as the salt to
            // encrypt the data in the file, and it might provide some
            // benefit to a cryptographic attack.
            FileHash hashedHash = FileHash.ComputeHash(
                sourceManifestFile.FileHash.HashData);

            String hashedHashString = hashedHash.ToString();

            // Only add the file data if we don't have it already.
            if (myHashedStringMap.ContainsKey(hashedHashString) == false)
            {
                FileInfo sourceFileInfo =
                    sourceRepository.GetFile(sourceManifestFile);

                byte[] keyData = CryptUtilities.MakeKeyBytesFromString(
                    OuterKeyString,
                    sourceManifestFile.FileHash.HashData);

                // Use the inner proxy temp directory because that is likely
                // the ultimate destination of the file and we don't want to
                // copy the data if we can avoid it.  This is a minor break in
                // encapsulation but has a significant impact on performance.
                String destFilePath =
                    Path.Combine(
                        InnerProxy.TempDirectory.FullName,
                        hashedHashString);

                Stream sourceFileStream = sourceFileInfo.OpenRead();

                byte[] cryptHash = WriteCryptFileAndHash(
                    sourceFileStream,
                    keyData,
                    destFilePath);

                FileInfo cryptFileInfo =
                    new FileInfo(destFilePath);

                // Make a dummy parent manifest directory to give to the inner
                // proxy.  This is actually rooted in the inner manifest, but
                // that is ok - although it is kind of a hack.  The fact is
                // that we don't maintain an actual manifest to mirror the
                // inner manifest - and we know that the implementation of
                // PutFile won't be affected by doing this.
                ManifestDirectoryInfo parentDirectory =
                    MakeInnerParentDirectory(
                        hashedHashString,
                        InnerProxy.Manifest.RootDirectory);

                ManifestFileInfo destManifestFile =
                    new ManifestFileInfo(
                        hashedHashString,
                        parentDirectory);

                destManifestFile.RegisteredUtc =
                    DateTime.UtcNow;

                destManifestFile.LastModifiedUtc =
                    cryptFileInfo.LastWriteTimeUtc;

                destManifestFile.FileLength =
                    cryptFileInfo.Length;

                destManifestFile.FileHash =
                    new FileHash(cryptHash, CryptUtilities.DefaultHashType);

                InnerProxy.PutFile(ProxyToInner, destManifestFile);

                myHashedStringMap.Add(hashedHashString, destManifestFile);

                myNeedToRegenerateFileMap = true;
            }

            ManifestFileInfo outerManifestFileInfo =
                Manifest.PutFileFromOtherManifest(sourceManifestFile);

            myManifestChanged = true;
        }