private async Task RoundTrip() { ReaderResult initialHeaderResult = await this.reader.ReadHeaderAsync(await this.thisTestInfo.Database.AsIStorageFile.OpenReadAsync(), CancellationToken.None); Assert.AreEqual(ReaderResult.Success, initialHeaderResult, "Initial header read should be successful"); KdbxDecryptionResult result = await this.reader.DecryptFileAsync(await this.thisTestInfo.Database.AsIStorageFile.OpenReadAsync(), this.thisTestInfo.Password, this.thisTestInfo.Keyfile, CancellationToken.None); Assert.AreEqual(ReaderResult.Success, result.Result, "File should have initially decrypted properly"); KdbxDocument kdbxDoc = result.GetDocument(); IKdbxWriter writer = this.reader.GetWriter(); using (var stream = new InMemoryRandomAccessStream()) { bool writeResult = await writer.WriteAsync(stream, kdbxDoc, CancellationToken.None); Assert.IsTrue(writeResult, "File should have written successfully"); stream.Seek(0); KdbxReader newReader = new KdbxReader(); ReaderResult result2 = await newReader.ReadHeaderAsync(stream, CancellationToken.None); Assert.AreEqual(ReaderResult.Success, result2, "Header should been read back successfully after write"); KdbxDecryptionResult result3 = await newReader.DecryptFileAsync(stream, this.thisTestInfo.Password, this.thisTestInfo.Keyfile, CancellationToken.None); Assert.AreEqual(ReaderResult.Success, result3.Result, "File should have decrypted successfully after write"); KdbxDocument roundTrippedDocument = result3.GetDocument(); Assert.AreEqual(kdbxDoc, roundTrippedDocument, "Round-tripped document should be equal to original document"); } }
/// <summary> /// Parses the header of the CandidateFile and handles updating status on the View. /// </summary> private async Task ValidateHeader() { DebugHelper.Assert(this.kdbxReader != null); if (this.kdbxReader == null) { throw new InvalidOperationException("Cannot validate KDBX header if there is no reader instance"); } try { if (CandidateFile == null) { ParseResult = null; } else { using (IRandomAccessStream fileStream = await CandidateFile.GetRandomReadAccessStreamAsync()) { CancellationTokenSource cts = new CancellationTokenSource(5000); ParseResult = await this.kdbxReader.ReadHeaderAsync(fileStream, cts.Token); } } } catch (COMException) { // In the Windows 8.1 preview, opening a stream to a SkyDrive file can fail with no workaround. ParseResult = new ReaderResult(KdbxParserCode.UnableToReadFile); } finally { RaiseHeaderValidated(); UnlockCommand.RaiseCanExecuteChanged(); UseSavedCredentialsCommand.RaiseCanExecuteChanged(); } }
/// <summary> /// Deserializes this node by using the <see cref="KdbxMetadata"/> binary collection /// to dereference @Ref. /// </summary> /// <param name="xml"></param> /// <param name="metadata">Used to dereference the Ref attribute.</param> /// <param name="parameters"></param> public KdbxBinAttachment(XElement xml, KdbxMetadata metadata, KdbxSerializationParameters parameters) : base(xml) { FileName = GetString("Key", true); XElement valueNode = GetNode("Value"); if (valueNode == null) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Node {rootName} missing required child Value") ); } int refId; string refAttr = valueNode.Attribute("Ref")?.Value; if (refAttr == null || !int.TryParse(refAttr, out refId)) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Child Value node of {rootName} missing required int @Ref") ); } Data = metadata.Binaries.GetById(refId); this.binaryCollection = metadata.Binaries; }
public static Writer <Out, Reader <E, V> > SelectMany <Out, E, T, U, V>( this Writer <Out, T> self, Func <T, Reader <E, U> > bind, Func <T, U, V> project ) { if (bind == null) { throw new ArgumentNullException(nameof(bind)); } if (project == null) { throw new ArgumentNullException(nameof(project)); } return(() => { var resT = self.Valid()(); if (resT.IsBottom) { return WriterResult.Bottom <Out, Reader <E, V> >(resT.Output); } return WriterResult.Return <Out, Reader <E, V> >(env => { var resU = bind(resT.Value).Valid()(env); if (resU.IsBottom) { return ReaderResult.Bottom <V>(); } return ReaderResult.Return(project(resT.Value, resU.Value)); }, resT.Output); }); }
/// <summary> /// Execution action for the UseSavedCredentials - attempts to unlock the document file /// using stored credentials after verifying the user's identity. /// </summary> private async Task DoUnlockWithSavedCredentials() { Task <bool> verificationTask = this.identityService.VerifyIdentityAsync(); this.taskNotificationService.PushOperation(verificationTask, AsyncOperationType.IdentityVerification); if (!await verificationTask) { ParseResult = new ReaderResult(KdbxParserCode.CouldNotVerifyIdentity); return; } Task <IBuffer> credentialTask = this.credentialProvider.GetRawKeyAsync(CandidateFile.File); this.taskNotificationService.PushOperation(credentialTask, AsyncOperationType.CredentialVaultAccess); IBuffer storedCredential = await credentialTask; if (storedCredential == null) { ParseResult = new ReaderResult(KdbxParserCode.CouldNotRetrieveCredentials); return; } Task unlockTask = DoUnlockAsync(storedCredential); this.taskNotificationService.PushOperation(unlockTask, AsyncOperationType.DatabaseDecryption); await unlockTask; }
public KdbxPart(XElement element) { if (element == null) { throw new ArgumentNullException(nameof(element)); } // We should only be parsing expected elements if (element.Name != rootName) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure( $"KdbxPart parse mismatch - expected root node {rootName}, got {element.Name}" ) ); } // Build up a list of child elements to parse later this._rootNode = element; this._pristine = new Dictionary <string, IList <XElement> >(); foreach (XElement node in element.Elements()) { string key = node.Name.ToString(); if (this._pristine.ContainsKey(key)) { this._pristine[key].Add(node); } else { this._pristine[key] = new List <XElement> { node }; } } }
public static State <S, Reader <E, V> > SelectMany <S, E, T, U, V>( this State <S, T> self, Func <T, Reader <E, U> > bind, Func <T, U, V> project ) { if (bind == null) { throw new ArgumentNullException(nameof(bind)); } if (project == null) { throw new ArgumentNullException(nameof(project)); } return((S s) => { var resT = self.Valid()(s); if (resT.IsBottom) { return StateResult.Bottom <S, Reader <E, V> >(s); } return StateResult.Return <S, Reader <E, V> >(resT.State, envInner => { var resU = bind(resT.Value).Valid()(envInner); if (resU.IsBottom) { return new ReaderResult <V>(default(V), true); } return ReaderResult.Return(project(resT.Value, resU.Value)); }); }); }
public string GetString(string name, bool required = false) { DebugHelper.Assert(!string.IsNullOrEmpty(name)); if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name cannot be null or empty", nameof(name)); } XElement child = GetNode(name); if (child == null) { if (!required) { return(null); } else { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Node {rootName} missing required string child {name}") ); } } return(child.Value ?? string.Empty); }
public Color?GetNullableColor(string name) { string cString = GetString(name); if (cString == null || cString.Length != 7 || cString[0] != '#') { return(null); } string rr = cString.Substring(1, 2); string gg = cString.Substring(3, 2); string bb = cString.Substring(5, 2); try { byte red = byte.Parse(rr, NumberStyles.HexNumber); byte green = byte.Parse(gg, NumberStyles.HexNumber); byte blue = byte.Parse(bb, NumberStyles.HexNumber); return(Color.FromArgb(0xFF, red, green, blue)); } catch (FormatException) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Could not parse {rootName}'s color child {name} - value: {cString}") ); } }
/// <summary> /// Initializes the design data. /// </summary> public DesignDatabaseUnlockViewModel() { CandidateFile = new MockDatabaseCandidate { FileName = "My Database.kdbx", LastModified = new DateTimeOffset(DateTime.UtcNow), Size = 12345, }; Password = "******"; UnlockCommand = new AsyncActionCommand( () => HasGoodHeader, () => Task.CompletedTask ); UseSavedCredentialsCommand = new AsyncActionCommand( () => true, () => Task.CompletedTask ); HasGoodHeader = true; ParseResult = new ReaderResult(KdbxParserCode.Success); RememberDatabase = true; }
/// <summary> /// Validates that the specified enum value is defined in the provided enum. /// </summary> /// <typeparam name="T">An enum type to validate against.</typeparam> /// <param name="field">The header field to validated.</param> /// <param name="value">The enum value to validate.</param> private void RequireEnumDefined <T>(InnerHeaderField field, T value) where T : struct { if (!Enum.IsDefined(typeof(T), value)) { throw new KdbxParseException(ReaderResult.FromHeaderDataUnknown(field, value.ToString())); } }
public static Reader <Env, Env> Fold <Env, A>(this Reader <Env, A> self, Func <Env, A, Env> f) => env => { var resA = self(env); return(resA.IsFaulted ? ReaderResult <Env> .New(resA.ErrorInt) : ReaderResult <Env> .New(f(env, resA.Value))); };
/// <summary> /// Initializes the Exception with the specified ReaderResult. /// </summary> /// <param name="error">The ReaderResult causing this Exception.</param> public KdbxParseException(ReaderResult error) { if (!error.IsError) { throw new ArgumentException("The provided ReaderResult is not an error.", "error"); } Error = error; }
/// <summary> /// Validates that the specified header field has a size matching a provided requirement. Throws on failure. /// </summary> /// <param name="field">The header field to validate.</param> /// <param name="size">The size of the data field.</param> /// <param name="requirement">An evaluator function that returns whether the size is valid.</param> /// <param name="explanation">A String explanation of the requirement.</param> private void RequireFieldDataSize(InnerHeaderField field, uint size, Predicate <uint> requirement, string explanation) { bool result = requirement(size); if (result) { return; } throw new KdbxParseException(ReaderResult.FromHeaderDataSize(field, size, explanation)); }
public IActionResult Initialize([FromBody] string payload) { try { var request = JsonDocument.Parse(payload).RootElement; var id = request.GetProperty("Id").GetInt32(); var version = request.GetProperty("Version").GetString(); DbResult result; using (var db = new AccessControlDatabase()) { var sql = @" SELECT r.name, r.timeout, r.enabled, g.name AS groupName, r.settings FROM reader r INNER JOIN `group` g ON r.group_id = g.group_id WHERE r.reader_id = @0 LIMIT 1;" ; result = db.SingleOrDefault <DbResult>(sql, id); } if (id < 1 || result == null) { return(StatusCode(401)); } var clientAddress = HttpContext.Connection.RemoteIpAddress.ToString(); RecordClient(id, clientAddress, version, payload); var output = new ReaderResult { Name = result.name, Timeout = result.timeout, Enabled = result.enabled, Group = result.groupName, Settings = result.settings, }; return(new JsonResult(output)); } catch (Exception ex) { Console.Write(ex.ToString()); return(StatusCode(500)); } }
private static ReaderResult <Unit> bmap <T>(ReaderResult <T> r, Action <T> f) { if (r.IsBottom) { return(Bottom <Unit>()); } else { f(r.Value); return(Return(unit)); } }
public IActionResult Lookup(string id) { try { DbResult result; using (var db = new AccessControlDatabase()) { var sql = @" SELECT r.name, r.timeout, r.enabled, g.name AS groupName, r.address FROM reader r INNER JOIN `group` g ON r.group_id = g.group_id WHERE r.reader_id = @0 LIMIT 1;" ; result = db.SingleOrDefault <DbResult>(sql, id); } if (result == null) { return(StatusCode(403)); } var clientAddress = HttpContext.Connection.RemoteIpAddress.ToString(); if (result.address != clientAddress) { RecordClientAddress(id, clientAddress); } var output = new ReaderResult { Name = result.name, Timeout = result.timeout, Enabled = result.enabled, Group = result.groupName, }; return(new JsonResult(output)); } catch { return(StatusCode(500)); } }
public bool GetBool(string name) { bool?nullableB = GetNullableBool(name, true); if (nullableB.HasValue) { return(nullableB.Value); } else { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Node {rootName} missing required bool child {name}") ); } }
/// <summary> /// Parses a collection of binaries from the given XML. /// </summary> /// <param name="xml"></param> /// <param name="parameters"></param> public KdbxBinaries(XElement xml, KdbxSerializationParameters parameters) : base(xml) { this.binaries = new SortedDictionary <int, KdbxBinary>(); foreach (XElement ele in GetNodes(KdbxBinary.RootName)) { KdbxBinary bin = new KdbxBinary(ele, parameters); if (this.binaries.ContainsKey(bin.Id)) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Duplicate binary key {bin.Id}") ); } this.binaries.Add(bin.Id, bin); } }
/// <summary> /// Runs the Reader monad and memoizes the result in a TryOption monad. Use /// Match, IfSucc, IfNone, etc to extract. /// </summary> public static ReaderResult <A> Run <Env, A>(this Reader <Env, A> self, Env env) { try { if (self == null) { throw new ArgumentNullException(nameof(self)); } if (env == null) { throw new ArgumentNullException(nameof(env)); } return(self(env)); } catch (Exception e) { return(ReaderResult <A> .New(Error.New(e))); } }
public int GetInt(string name, int?def = null) { string iString = GetString(name, !def.HasValue); if (iString == null) { iString = def.Value.ToString(); } int i; if (int.TryParse(iString, out i)) { return(i); } else { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Could not parse {rootName}'s int child {name} - value: {iString}") ); } }
/// <summary> /// Parses a KeePass document from the specified XML. /// </summary> /// <param name="xml">XML to deserialize.</param> /// <param name="headerBinaries">Any binaries that were parsed from a header.</param> /// <param name="rng">RNG used to encrypt protected strings.</param> /// <param name="parameters">Parameters controlling serialization.</param> public KdbxDocument(XElement xml, IEnumerable <ProtectedBinary> headerBinaries, IRandomNumberGenerator rng, KdbxSerializationParameters parameters) : base(xml) { XElement metadata = GetNode(KdbxMetadata.RootName); if (metadata == null) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Document has no {KdbxMetadata.RootName} node") ); } Metadata = new KdbxMetadata(metadata, headerBinaries, parameters); XElement root = GetNode(KdbxRoot.RootName); if (root == null) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Document has no {KdbxRoot.RootName} node") ); } Root = new KdbxRoot(root, rng, Metadata, parameters); }
private void backgroundReader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { ReaderResult result = (ReaderResult)e.Result; if (result.Status == ReaderResult.ReaderStatus.ReaderError) { MessageBox.Show(result.Message, Properties.Resources.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error); } btConnect.Tag = true; btConnect.Text = currentTranslation.btConnectText; btConnect.BackColor = Color.LimeGreen; if (calibRequest) { calibRequest = false; btCalib.PerformClick(); } else { indicatorWorker.CancelAsync(); } }
internal static Reader <Env, T> Valid <Env, T>(this Reader <Env, T> self) => self ?? (_ => ReaderResult.Bottom <T>());
private static ReaderResult <T> Bottom <T>() => ReaderResult.Bottom <T>();
private static ReaderResult <T> Return <T>(T value) => ReaderResult.Return(value);
private static ReaderResult <R> bmap <T, R>(ReaderResult <T> r, Func <T, R> f) => r.IsBottom ? Bottom <R>() : Return(f(r.Value));
private static async Task <Dictionary <string /*Table0..N*/, ReaderResult> > ProcessExecQueryAsync(CancellationToken cancellationToken, SqlCommand cmd, Dictionary <string, string> inputParameters) { var reader = await cmd.ExecuteReaderAsync(cancellationToken); int resultIx = 0; var allResultSets = new Dictionary <string, ReaderResult>(); var limitToFields = ProcssSelectInstruction(inputParameters); do { var readerResult = new ReaderResult(); allResultSets.Add($"Table{resultIx}", readerResult); var fieldTypes = Enumerable.Range(0, reader.FieldCount).Select(reader.GetFieldType).ToArray(); //?var hasGeoType = fieldTypes.Contains(typeof(SqlGeography)) || fieldTypes.Contains(typeof(SqlGeometry)); var allFieldNamesUpper = Enumerable.Range(0, reader.FieldCount).Select(reader.GetName).Select(n => n.ToUpper()).ToList(); int fieldCount = reader.FieldCount; var fieldMappingIx = new List <int>(); // if there exists a select list if (limitToFields?.ContainsKey(resultIx) ?? false) { foreach (var limit in limitToFields[resultIx]) { var ix = allFieldNamesUpper.IndexOf(limit); if (ix >= 0) { fieldMappingIx.Add(ix); } } } else { // default to ALL available fields fieldMappingIx = Enumerable.Range(0, reader.FieldCount).ToList(); } fieldCount = fieldMappingIx.Count; readerResult.Fields = Enumerable.Range(0, fieldCount) .Select(i => new DataField(reader.GetName(fieldMappingIx[i]), ToJqxType(reader.GetFieldType(fieldMappingIx[i])))) .ToArray(); readerResult.Data = new List <object[]>(); while (await reader.ReadAsync()) { // TODO: Cannot use GetValues as SqlGeography types currently throw as .NET Core cannot deserialize it properly. If SqlClient adds support review in future for possible perf benefit - fetch all rows at once instead of one by one. Also just consider the a $select instruction comes through (thus only asking for a subset of the columns) // TODO: We can also possible split it like the below snippet - only take perf hit if we know a geo type is coming up // if (!hasGeoType) // { // var fullDataRow = new object[reader.FieldCount];// has to be all fields // // TODO: Handle $select // // TODO: What about Bytes? // reader.GetValues(fullDataRow); // readerResult.Data.Add(fullDataRow); // continue; // } var data = new object[fieldCount]; for (var dstIx = 0; dstIx < fieldCount; dstIx++) { var srcIx = fieldMappingIx[dstIx]; var isDBNull = await reader.IsDBNullAsync(srcIx, cancellationToken); if (isDBNull) { continue; } if (fieldTypes[srcIx] == typeof(byte[])) { var bytes = reader.GetSqlBytes(srcIx).Value; data[dstIx] = Convert.ToBase64String(bytes); } else if (fieldTypes[srcIx] == typeof(SqlGeography)) { var geometryReader = new NetTopologySuite.IO.SqlServerBytesReader { IsGeography = true }; var bytes = reader.GetSqlBytes(srcIx).Value; var geometry = geometryReader.Read(bytes); var point = geometry as NetTopologySuite.Geometries.Point; if (point != null) { data[dstIx] = new { lat = point.Y, lng = point.X }; } else { data[dstIx] = geometry.ToString(); } } else { //var x = reader.GetProviderSpecificValue(colIx); data[dstIx] = reader.GetValue(srcIx); } } // for each column readerResult.Data.Add(data); } resultIx++; }while (await reader.NextResultAsync()); return(allResultSets); }
/// <summary> /// Attempts to unlock the document file. /// </summary> /// <param name="storedCredential">The key to use for decryption - if null, the ViewModel's /// credentials are used instead.</param> private async Task DoUnlockAsync(IBuffer storedCredential) { DebugHelper.Assert(CanUnlock()); if (!CanUnlock()) { throw new InvalidOperationException("The ViewModel is not in a state that can unlock the database!"); } CancellationTokenSource cts = new CancellationTokenSource(); try { using (IRandomAccessStream stream = await CandidateFile.GetRandomReadAccessStreamAsync()) { Task <KdbxDecryptionResult> decryptionTask; if (storedCredential != null) { decryptionTask = this.kdbxReader.DecryptFile(stream, storedCredential, cts.Token); } else { decryptionTask = this.kdbxReader.DecryptFileAsync(stream, Password, KeyFile, cts.Token); } if (this.taskNotificationService.CurrentTask == null || this.taskNotificationService.CurrentTask.IsCompleted) { this.taskNotificationService.PushOperation(decryptionTask, cts, AsyncOperationType.DatabaseDecryption); } KdbxDecryptionResult result = await decryptionTask; ParseResult = result.Result; DebugHelper.Trace($"Got ParseResult from database unlock attempt: {ParseResult}"); if (!ParseResult.IsError) { // The database candidate to proceed into the next stage with IDatabaseCandidate candidateToUse = CandidateFile; if (CacheDatabase) { // We do not use UseAppControlledDatabaseAsync here because it has extra baggage. // We don't need to refresh the view at this stage, just fire an event using // the cached file. candidateToUse = await GetCachedCandidateAsync(); } if (RememberDatabase) { string accessToken = this.futureAccessList.Add(candidateToUse.File, candidateToUse.FileName); DebugHelper.Trace($"Unlock was successful and database was remembered with token: {accessToken}"); } else { DebugHelper.Trace("Unlock was successful but user opted not to remember the database."); } if (SaveCredentials) { bool storeCredential = false; // If we were not already using a stored credential, we need user // consent to continue. if (storedCredential == null) { Task <bool> identityTask = this.identityService.VerifyIdentityAsync(); if (this.taskNotificationService.CurrentTask == null || this.taskNotificationService.CurrentTask.IsCompleted) { this.taskNotificationService.PushOperation(identityTask, AsyncOperationType.IdentityVerification); } storeCredential = await identityTask; storedCredential = result.GetRawKey(); } else { // If we have a stored credential, we already got consent. storeCredential = true; } if (storeCredential) { if (!await this.credentialProvider.TryStoreRawKeyAsync(candidateToUse.File, storedCredential)) { EventHandler <CredentialStorageFailureEventArgs> handler = CredentialStorageFailed; if (handler != null) { // If we could not store a credential, give the View a chance to try again. CredentialStorageFailureEventArgs eventArgs = new CredentialStorageFailureEventArgs( this.credentialProvider, this.credentialViewModelFactory, candidateToUse, storedCredential ); handler(this, eventArgs); await eventArgs.DeferAsync(); } } } } await RaiseDocumentReady(result.GetDocument(), candidateToUse); } } } catch (COMException) { // In the Windows 8.1 preview, opening a stream to a SkyDrive file can fail with no workaround. ParseResult = new ReaderResult(KdbxParserCode.UnableToReadFile); } }
/// <summary> /// Decodes an instance from serialized XML. /// </summary> /// <param name="xml">The XML to deserialize.</param> /// <param name="parameters">Parameters controlling serialization.</param> public KdbxBinary(XElement xml, KdbxSerializationParameters parameters) { // Parse out int ID attribute string idAttr = xml?.Attribute("ID")?.Value; if (idAttr == null) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"KdbxBinary was missing required ID attribute") ); } if (!Int32.TryParse(idAttr, out int id)) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"KdbxBinary ID attribute could not be parsed into an int") ); } Id = id; // Parse out bool Compressed attribute string compressAttr = xml?.Attribute("Compressed")?.Value ?? "false"; if (!Boolean.TryParse(compressAttr, out bool compressed)) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"KdbxBinary Compressed attribute could not be parsed into a bool") ); } // Parse base64-encoded content byte[] content; try { content = CryptographicBuffer.DecodeFromBase64String(xml?.Value)?.ToArray(); if (content == null) { content = new byte[0]; } } catch (Exception) { throw new KdbxParseException( ReaderResult.FromXmlParseFailure($"Could not decode KdbxBinary content as base64 data") ); } // Decompress content if needed if (compressed && content.Length > 0) { byte[] decompressed; using (Stream memStream = new MemoryStream(content)) { using (Stream gzipStream = new GZipStream(memStream, CompressionMode.Decompress)) { byte[] buffer = new byte[1024]; int read = gzipStream.Read(buffer, 0, buffer.Length); List <byte> bytes = new List <byte>(); while (read > 0) { bytes.AddRange(buffer.Take(read)); read = gzipStream.Read(buffer, 0, buffer.Length); } decompressed = bytes.ToArray(); } } content = decompressed; } this.binaryData = new ProtectedBinary(content, false); }