/// <summary> /// Decrypts the specified input stream. /// </summary> /// <param name="input">The input stream.</param> /// <param name="masterKey">The master key.</param> /// <param name="headers">The database file headers.</param> /// <returns>The decrypted buffer.</returns> /// <exception cref="ArgumentNullException"> /// The <paramref name="input"/>, <paramref name="masterKey"/>, /// <paramref name="headers"/> cannot be <c>null</c>. /// </exception> public static Task<IInputStream> Decrypt(IRandomAccessStream input, IBuffer masterKey, FileHeaders headers) { if (headers == null) throw new ArgumentNullException("headers"); return Decrypt(input, masterKey, headers.MasterSeed, headers.EncryptionIV); }
public async Task ParseContent_should_decrypt_passwords() { using (var decrypted = TestFiles.Read("IO.Demo7Pass.Decrypted.bin")) { var headers = new FileHeaders { RandomAlgorithm = CrsAlgorithm.Salsa20, ProtectedStreamKey = CryptographicBuffer.DecodeFromBase64String( "FDNbUwE9jt6Y9+syU+btBIOGRxYt2tiUqnb6FXWIF1E="), }; var doc = await FileFormat.ParseContent( decrypted, true, headers); Assert.NotNull(doc); var entry = doc.Descendants("Entry") .Where(x => (string)x.Element("UUID") == "H/DqrrE1mEKScz8VpPrafg==") .SelectMany(x => x.Elements("String")) .Where(x => (string)x.Element("Key") == "Password") .Select(x => (string)x.Element("Value")) .Single(); Assert.Equal("Password", entry); } }
public async Task ParseContent_should_decompress_content() { using (var decrypted = TestFiles.Read("IO.Demo7Pass.Decrypted.bin")) { var headers = new FileHeaders { RandomAlgorithm = CrsAlgorithm.Salsa20, ProtectedStreamKey = CryptographicBuffer.DecodeFromBase64String( "FDNbUwE9jt6Y9+syU+btBIOGRxYt2tiUqnb6FXWIF1E="), }; var doc = await FileFormat.ParseContent( decrypted, true, headers); Assert.NotNull(doc); var root = doc.Root; Assert.NotNull(root); Assert.Equal("KeePassFile", root.Name.LocalName); } }
GetMasterKey(FileHeaders headers) { if (headers == null) throw new ArgumentNullException("headers"); return GetMasterKey(headers.TransformSeed, headers.TransformRounds); }
/// <summary> /// Parse the headers fields. /// </summary> /// <param name="input">The input stream.</param> /// <param name="buffer">The header bytes reader.</param> /// <returns>The file headers.</returns> private static async Task<FileHeaders> GetHeaders( IInputStream input, IBuffer buffer) { var result = new FileHeaders(); while (true) { buffer = await input.ReadAsync(buffer, 3); var field = (HeaderFields)buffer.GetByte(0); var size = BitConverter.ToUInt16(buffer.ToArray(1, 2), 0); if (size > 0) buffer = await input.ReadAsync(buffer, size); switch (field) { case HeaderFields.EndOfHeader: return result; case HeaderFields.CompressionFlags: result.UseGZip = buffer.GetByte(0) == 1; break; case HeaderFields.EncryptionIV: result.EncryptionIV = buffer .ToArray().AsBuffer(); break; case HeaderFields.MasterSeed: result.MasterSeed = buffer .ToArray().AsBuffer(); break; case HeaderFields.StreamStartBytes: result.StartBytes = buffer .ToArray().AsBuffer(); break; case HeaderFields.TransformSeed: result.TransformSeed = buffer .ToArray().AsBuffer(); break; case HeaderFields.TransformRounds: result.TransformRounds = BitConverter.ToUInt64( buffer.ToArray(), 0); break; case HeaderFields.ProtectedStreamKey: result.ProtectedStreamKey = buffer .ToArray().AsBuffer(); break; case HeaderFields.InnerRandomStreamID: result.RandomAlgorithm = (CrsAlgorithm) BitConverter.ToUInt32(buffer.ToArray(), 0); break; } } }
private static void Decrypt(FileHeaders headers, XDocument doc) { var protectedStrings = doc.Descendants("Entry") .SelectMany(x => x.Elements("String")) .Select(x => x.Element("Value")) .Where(x => { var protect = x.Attribute("Protected"); return protect != null && (bool)protect; }); IRandomGenerator generator; switch (headers.RandomAlgorithm) { case CrsAlgorithm.ArcFourVariant: generator = new Rc4RandomGenerator( headers.ProtectedStreamKey); break; default: generator = new Salsa20RandomGenerator( headers.ProtectedStreamKey); break; } foreach (var protectedString in protectedStrings) { var encrypted = Convert.FromBase64String( protectedString.Value); var length = encrypted.Length; var padding = generator.GetRandomBytes(length); for (var i = 0U; i < length; i++) encrypted[i] ^= padding.GetByte(i); protectedString.Value = Encoding.UTF8 .GetString(encrypted, 0, length); } }
/// <summary> /// Verifies the start bytes of the decrypted content stream. /// </summary> /// <param name="input">The decrypted content stream.</param> /// <param name="headers">The database file headers.</param> /// <returns><c>true</c> if the bytes match; otherwise, <c>false</c>.</returns> /// <exception cref="System.ArgumentNullException"> /// <paramref name="input"/> and <paramref name="headers"/> cannot be <c>null</c>. /// </exception> public static Task<bool> VerifyStartBytes(IInputStream input, FileHeaders headers) { if (headers == null) throw new ArgumentNullException("headers"); return VerifyStartBytes(input, headers.StartBytes); }
/// <summary> /// Verifies the database file headers integrity. /// </summary> /// <param name="headers">The database file headers.</param> /// <param name="doc">The database content.</param> /// <returns><c>true</c> if the header is valid; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentNullException"> /// The <paramref name="headers"/> and <paramref name="doc"/> cannot be <c>null</c>. /// </exception> public static bool VerifyHeaders(FileHeaders headers, XDocument doc) { return VerifyHeaders(headers.Hash, doc); }
/// <summary> /// Parses the decrypted content. /// </summary> /// <param name="decrypted">The input stream.</param> /// <param name="useGZip">Set to <c>true</c> to decompress the input stream before parsing.</param> /// <returns>The decrypted content.</returns> /// <param name="headers">The database file headers.</param> /// <exception cref="System.ArgumentNullException"> /// The <paramref name="decrypted"/> or <paramref name="headers"/> parameter cannot be <c>null</c>. /// </exception> public static async Task<XDocument> ParseContent( IInputStream decrypted, bool useGZip, FileHeaders headers) { if (decrypted == null) throw new ArgumentNullException("decrypted"); if (headers == null) throw new ArgumentNullException("headers"); var deHashed = await HashedBlockFileFormat.Read(decrypted); var input = deHashed; try { if (useGZip) { input = new GZipStream(input, CompressionMode.Decompress); } var doc = XDocument.Load(input); Decrypt(headers, doc); return doc; } finally { deHashed.Dispose(); input.Dispose(); } }