// 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); }
static public FileHash ComputeHash( Stream stream, string hashType = CryptUtilities.DefaultHashType) { return(new FileHash( CryptUtilities.ComputeHash(stream, hashType), hashType)); }
static public FileHash ComputeHash( byte[] data, string hashType = CryptUtilities.DefaultHashType) { return(new FileHash( CryptUtilities.ComputeHash(data, hashType), hashType)); }
// 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; }