/// <summary> /// Hashes and writes a null terminator block to the specified writer. /// </summary> /// <param name="writer">Writer used to output data.</param> /// <param name="blockIndex">Block index used to compute HMAC key.</param> /// <returns>A task that resolves when output is finished.</returns> public async Task WriteTerminatorAsync(DataWriter writer, ulong blockIndex) { if (writer == null) { throw new ArgumentNullException(nameof(writer)); } IBuffer nullData = WindowsRuntimeBuffer.Create(0); IBuffer hmacValue = GetMacForBlock(blockIndex, nullData, 0, 0); writer.WriteBuffer(hmacValue); await writer.StoreAsync(); writer.WriteUInt32(0); await writer.StoreAsync(); DebugHelper.Trace($"Wrote HMAC terminator block #{blockIndex}."); DebugHelper.Trace($"MAC[0]: {hmacValue.GetByte(0)}, MAC[31]: {hmacValue.GetByte(31)}"); }
private static void Verify(IBuffer buffer, byte[] source, int offset, int length, int capacity) { Assert.Equal(length, (int)buffer.Length); Assert.Equal(capacity, (int)buffer.Capacity); for (uint i = 0; i < length; i++) { Assert.Equal(source[i + offset], buffer.GetByte(i)); } }
public void Create_Buffer_ReturnsExpected(byte[] source, int offset, int length, int capacity) { IBuffer buffer = WindowsRuntimeBuffer.Create(source, offset, length, capacity); Assert.Equal(capacity, (int)buffer.Capacity); Assert.Equal(length, (int)buffer.Length); for (uint i = 0; i < length; i++) { Assert.Equal(source[i + offset], buffer.GetByte(i)); } // The source byte array should be copied. if (source.Length > 0) { source[0] = 45; Assert.NotEqual(45, buffer.GetByte(0)); } }
/// <summary> /// Generates the HMAC-SHA-256 value for a given block. /// The actual hashed value is i || n (block size) || C. /// </summary> /// <param name="i">The block index.</param> /// <param name="cipherText">Encrypted block value.</param> /// <param name="offset">Offset into <paramref name="cipherText"/>.</param> /// <param name="length">Number of bytes of <paramref name="cipherText"/> to use.</param> /// <returns>The HMAC value of the block.</returns> public IBuffer GetMacForBlock(ulong i, IBuffer cipherText, uint offset, uint length) { if (cipherText == null) { throw new ArgumentNullException(nameof(cipherText)); } if (offset > cipherText.Length) { throw new ArgumentOutOfRangeException(nameof(offset)); } length = (offset + length > cipherText.Length ? cipherText.Length - offset : length); DebugHelper.Assert(length <= int.MaxValue); // Construct the HMAC buffer: i || n || C byte[] buffer = new byte[8 + 4 + length]; ByteHelper.GetLittleEndianBytes(i, buffer); ByteHelper.GetLittleEndianBytes(length, buffer, 8); if (length > 0) { // Necessary because buffers don't play nice if they're empty... cipherText.CopyTo(offset, buffer, 12, (int)length); } CryptographicHash hash = this.hmacAlgo.CreateHash(GetKeyForBlock(i)); hash.Append(buffer.AsBuffer()); IBuffer macValue = hash.GetValueAndReset(); DebugHelper.Trace($"Generating MAC value for block #{i}, data length {length}"); if (length > 0) { DebugHelper.Trace($"data[0]: { buffer[12]}, data[n]: { buffer[buffer.Length - 1]}"); } DebugHelper.Trace($"MAC[0]: {macValue.GetByte(0)}, MAC[31]: {macValue.GetByte(31)}"); return(macValue); }
/// <summary> /// Hashes and writes the provided data to the provided stream. /// </summary> /// <param name="writer">Writer used to output data.</param> /// <param name="cipherText">The data block to write.</param> /// <param name="offset">Offset into <paramref name="cipherText"/>.</param> /// <param name="length">Number of bytes of <paramref name="cipherText"/> to use.</param> /// <param name="blockIndex">Block index used to compute HMAC key.</param> /// <returns>A task that resolves when output is finished.</returns> public async Task WriteCipherBlockAsync(DataWriter writer, IBuffer cipherText, uint offset, uint length, ulong blockIndex) { if (writer == null) { throw new ArgumentNullException(nameof(writer)); } if (cipherText == null) { throw new ArgumentNullException(nameof(cipherText)); } if (cipherText.Length > int.MaxValue) { throw new ArgumentException("Block size is limited to int.MaxValue", nameof(cipherText)); } if (offset >= cipherText.Length) { throw new ArgumentOutOfRangeException(nameof(offset)); } length = (offset + length > cipherText.Length ? cipherText.Length - offset : length); IBuffer hmacValue = GetMacForBlock(blockIndex, cipherText, offset, length); writer.WriteBuffer(hmacValue); await writer.StoreAsync(); writer.WriteUInt32(length); await writer.StoreAsync(); writer.WriteBuffer(cipherText, offset, length); await writer.StoreAsync(); DebugHelper.Trace($"Wrote HMAC block #{blockIndex}; block is {length} bytes."); DebugHelper.Trace($"MAC[0]: {hmacValue.GetByte(0)}, MAC[31]: {hmacValue.GetByte(31)}, data[0]: {cipherText.GetByte(offset)}, data[n]: {cipherText.GetByte(offset + length - 1)}"); }
/// <summary> /// Calculate histogram from CanvasBitmap. Currently, only B8G8R8A8UIntNormalized pixel format is supported. /// </summary> /// <param name="bitmap"></param> private void CalculateHistogramFromPixelBuffer() { for (uint i = 0; i + 3 < ByteBuffer.Length; i += (PixelSkipRate << 2)) { SortPixel(ByteBuffer.GetByte(i), PixelColor.Blue); SortPixel(ByteBuffer.GetByte(i + 1), PixelColor.Green); SortPixel(ByteBuffer.GetByte(i + 2), PixelColor.Red); // pixels[i+3] is Alpha channel in B8G8R8A8UIntNormalized format. } for (int i = 0; i < Resolution; i++) { red[i] = red[i] >> 4; green[i] = green[i] >> 4; blue[i] = blue[i] >> 4; } if (OnHistogramCreated != null) { OnHistogramCreated(red, green, blue); } IsRunning = false; }
public static string ConvertToStringHex(IBuffer buffer) { var str = ""; for (uint i = 0; i < buffer.Length; i++) { if (str != "") { str += " "; } str += $"{buffer.GetByte(i):X2}"; } return(str); }
private static string AsErrorString(IBuffer buffer, string prefix) { string str = ""; for (uint i = 0; i < buffer.Length; i++) { if (str != "") { str += " "; } str += $"{buffer.GetByte(i):X2}"; } return(prefix + str); }
public static string ValueAsString(DisplayType displayType, IBuffer buffer, string prefix = "") { string str = prefix; switch (displayType) { default: case DisplayType.Hex: for (uint i = 0; i < buffer.Length; i++) { if (str != "") { str += " "; } str += $"{buffer.GetByte(i):X2}"; } break; case DisplayType.Percent: if (buffer.Length != 1) { return(AsErrorString(buffer, "Incorrect percent length:")); } var b = buffer.GetByte(0); str += $"{b}%"; break; case DisplayType.String: var dr = Windows.Storage.Streams.DataReader.FromBuffer(buffer); var rawstr = dr.ReadString(dr.UnconsumedBufferLength); rawstr = rawstr.Replace("\\", "\\\\"); // escape all back-slashes rawstr = rawstr.Replace("\0", "\\0"); // replace a real NUL with an escaped null. str += rawstr; break; } return(str); }
public static string GetBits(IBuffer buffer) { byte[] vals = new byte[buffer.Length]; for (uint i = 0; i < buffer.Length; i++) { vals[i] = buffer.GetByte(i); } string hexStr = BitConverter.ToString(vals); string[] hexSplit = hexStr.Split('-'); string bits = string.Empty; foreach (var hex in hexSplit) { ushort longValue = Convert.ToUInt16("0x" + hex, 16); bits = bits + Convert.ToString(longValue, 2).PadLeft(8, '0'); } return(bits); }
public Rc4RandomGenerator(IBuffer key) { if (key == null) { throw new ArgumentNullException("key"); } _state = new byte[256]; var keyLength = key.Length; for (uint w = 0; w < 256; ++w) { _state[w] = (byte)w; } unchecked { byte j = 0; uint keyIndex = 0; for (uint w = 0; w < 256; ++w) // Key setup { j += (byte)(_state[w] + key.GetByte(keyIndex)); var temp = _state[0]; _state[0] = _state[j]; _state[j] = temp; ++keyIndex; if (keyIndex >= keyLength) { keyIndex = 0; } } } GetRandomBytes(512); }
public IBuffer Parse() { // Skip the first 32 bytes. // These are random bytes generated by the writer. this.reader.ReadBuffer(32); // Create a reusable hash object. var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); var hash = sha256.CreateHash(); int currentIndex = 0; List <IBuffer> buffers = new List <IBuffer>(); uint requiredSize = 0; // Loop until we're done with the stream. while (this.reader.UnconsumedBufferLength > 0) { // We require at least 40 bytes per block. if (this.reader.UnconsumedBufferLength < 40) { throw new FormatException("The data is incomplete."); } // Read the current index, current hash, and expected data size. UInt32 bufferIndex = this.reader.ReadUInt32(); if (bufferIndex != currentIndex++) { throw new FormatException(string.Format("The hash check failed. Unexpected buffer index: {0} (should be {1})", bufferIndex, currentIndex - 1)); } IBuffer expectedHash = this.reader.ReadBuffer(32); UInt32 bufferSize = this.reader.ReadUInt32(); // If this an empty buffer? We're supposedly done. if (bufferSize == 0) { for (uint i = 0; i < expectedHash.Length; i++) { if (expectedHash.GetByte(i) != 0) { throw new FormatException("The hash check failed. The final buffer had a nonzero hash."); } } break; } // Make sure we have enough data to keep reading. if (this.reader.UnconsumedBufferLength < bufferSize) { throw new FormatException("The data is incomplete."); } // Read the next chunk and check its hash. IBuffer data = this.reader.ReadBuffer(bufferSize); hash.Append(data); IBuffer computedHash = hash.GetValueAndReset(); for (uint i = 0; i < expectedHash.Length; i++) { if (expectedHash.GetByte(i) != computedHash.GetByte(i)) { throw new FormatException("The hash check failed. One of the buffers has a bad hash."); } } buffers.Add(data); requiredSize += data.Length; } // Build a giant buffer from the rest of them. uint offset = 0; IBuffer finalBuffer = (new byte[requiredSize]).AsBuffer(); foreach (IBuffer buffer in buffers) { buffer.CopyTo(0, finalBuffer, offset, buffer.Length); offset += buffer.Length; } return(finalBuffer); }
/// <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; } } }
public static YFirmwareFile imm_Parse(string path, byte[] data) { int ofs; if (data[0] != 'B' || data[1] != 'Y' || data[2] != 'N' || data[3] != 0) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Not a firmware file"); } ofs = 4; int rev = data[ofs] + (data[ofs + 1] << 8); ofs += 2; string serial = imm_getString(data, ofs, YAPI.YOCTO_SERIAL_LEN); ofs += YAPI.YOCTO_SERIAL_LEN; string pictype = imm_getString(data, ofs, 20); ofs += 20; string product = imm_getString(data, ofs, YAPI.YOCTO_PRODUCTNAME_LEN); ofs += YAPI.YOCTO_PRODUCTNAME_LEN; string firmware = imm_getString(data, ofs, YAPI.YOCTO_FIRMWARE_LEN); ofs += YAPI.YOCTO_FIRMWARE_LEN; if (serial.Length >= YAPI.YOCTO_SERIAL_LEN) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Bad serial_buf"); } if (product.Length >= YAPI.YOCTO_PRODUCTNAME_LEN) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Bad product name"); } if (firmware.Length >= YAPI.YOCTO_FIRMWARE_LEN) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Bad firmware revision"); } int ROM_nb_zone = 0; int FLA_nb_zone = 0; int ROM_total_size = 0; int FLA_total_size = 0; string prog_version = ""; int zone_ofs; switch (rev) { case BYN_REV_V4: zone_ofs = BYN_HEAD_SIZE_V4; ROM_nb_zone = imm_getInt(data, ofs); ofs += 4; int datasize = imm_getInt(data, ofs); ofs += 4; if (ROM_nb_zone > MAX_ROM_ZONES_PER_FILES) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Too many zones"); } if (datasize != data.Length - BYN_HEAD_SIZE_V4) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Incorrect file size"); } break; case BYN_REV_V5: zone_ofs = BYN_HEAD_SIZE_V5; prog_version = imm_checkProgField(data, ofs, YAPI.YOCTO_FIRMWARE_LEN); ofs += YAPI.YOCTO_FIRMWARE_LEN; ofs += 2; //skip pad ROM_nb_zone = imm_getInt(data, ofs); ofs += 4; datasize = imm_getInt(data, ofs); ofs += 4; if (ROM_nb_zone > MAX_ROM_ZONES_PER_FILES) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Too many zones"); } if (datasize != data.Length - BYN_HEAD_SIZE_V5) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Incorrect file size"); } break; case BYN_REV_V6: zone_ofs = BYN_HEAD_SIZE_V6; int size = data.Length - BYN_MD5_OFS_V6; var alg = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5); IBuffer buff = data.AsBuffer(BYN_MD5_OFS_V6, size); IBuffer messageDigest = alg.HashData(buff); for (uint i = 0; i < 16; i++) { if (data[ofs + i] != messageDigest.GetByte(i)) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Invalid checksum"); } } ofs += 16; prog_version = imm_checkProgField(data, ofs, YAPI.YOCTO_FIRMWARE_LEN); ofs += YAPI.YOCTO_FIRMWARE_LEN; ROM_nb_zone = data[ofs++]; FLA_nb_zone = data[ofs++]; ROM_total_size = imm_getInt(data, ofs); ofs += 4; FLA_total_size = imm_getInt(data, ofs); ofs += 4; if (ROM_nb_zone > MAX_ROM_ZONES_PER_FILES) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Too many ROM zones"); } if (FLA_nb_zone > MAX_FLASH_ZONES_PER_FILES) { throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Too many FLASH zones"); } break; default: throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "unknown BYN file revision"); } return(new YFirmwareFile(path, serial, pictype, product, firmware, prog_version, ROM_nb_zone, FLA_nb_zone, ROM_total_size, FLA_total_size, data, zone_ofs)); }
/// <summary> /// Attempts to load KDBX header data from the provided data stream. /// </summary> /// <remarks> /// On success, the HeaderData property of this KdbxReader will be populated. /// On failure, it will be null. /// </remarks> /// <param name="stream">A stream representing a KDBX document (or header).</param> /// <param name="token">A token allowing the operation to be cancelled.</param> /// <returns>A Task representing the result of the read operation.</returns> public async Task <ReaderResult> ReadHeaderAsync(IRandomAccessStream stream, CancellationToken token) { HeaderData = null; using (DataReader reader = GetReaderForStream(stream)) { ReaderResult result = await ValidateSignature(reader); if (result != ReaderResult.Success) { reader.DetachStream(); return(result); } result = await ValidateVersion(reader); if (result != ReaderResult.Success) { reader.DetachStream(); return(result); } // If we get this far, we've passed basic sanity checks. // Construct a HeaderData entity and start reading fields. KdbxHeaderData headerData = new KdbxHeaderData(KdbxHeaderData.Mode.Read); bool gotEndOfHeader = false; while (!gotEndOfHeader) { if (token.IsCancellationRequested) { return(new ReaderResult(KdbxParserCode.OperationCancelled)); } try { OuterHeaderField field = await ReadOuterHeaderField(reader, headerData); if (field == OuterHeaderField.EndOfHeader) { gotEndOfHeader = true; } this.headerInitializationMap[field] = true; } catch (KdbxParseException e) { reader.DetachStream(); return(e.Error); } } // Ensure all headers are initialized bool gotAllHeaders = this.headerInitializationMap.All( kvp => this.headerInitializationMap[kvp.Key] ); if (!gotAllHeaders) { reader.DetachStream(); return(new ReaderResult(KdbxParserCode.HeaderMissing)); } // Hash entire header var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); CryptographicHash hash = sha256.CreateHash(); ulong streamPos = stream.Position; stream.Seek(0); await reader.LoadAsync((uint)streamPos); headerData.FullHeader = reader.ReadBuffer((uint)streamPos); hash.Append(headerData.FullHeader); IBuffer plainHeaderHash = hash.GetValueAndReset(); if (this.parameters.UseXmlHeaderAuthentication) { // The header was parsed successfully - finish creating the object and return success headerData.HeaderHash = CryptographicBuffer.EncodeToBase64String(plainHeaderHash); headerData.Size = streamPos; } if (this.parameters.UseInlineHeaderAuthentication) { // In KDBX4, the header hash is written directly after the header fields. // After the unencrypted hash, an HMAC-SHA-256 hash of the header is written. await reader.LoadAsync(32); IBuffer existingPlainHash = reader.ReadBuffer(32); // Validate plaintext hash if (plainHeaderHash.Length != existingPlainHash.Length) { return(new ReaderResult(KdbxParserCode.BadHeaderHash)); } for (uint i = 0; i < plainHeaderHash.Length; i++) { if (plainHeaderHash.GetByte(i) != existingPlainHash.GetByte(i)) { return(new ReaderResult(KdbxParserCode.BadHeaderHash)); } } DebugHelper.Trace("Validated plaintext KDBX4 header hash"); } if (this.parameters.Version <= KdbxVersion.Three && headerData.KdfParameters == null) { // Need to manually populate KDF parameters for older versions headerData.KdfParameters = new AesParameters( headerData.TransformRounds, headerData.TransformSeed ); } HeaderData = headerData; reader.DetachStream(); return(ReaderResult.Success); } }
/// <summary> /// Asynchronously attempts to unlock the document file. /// </summary> /// <remarks> /// Algorithm is as of this writing (11/5/2012): /// 0. Use UTF8 encoding with no BOM. /// 1. Read header. /// 2. Compute SHA256 hash of header. /// 3. Decrypt the rest of the viewModel using header parameters. /// Relies on: /// a. MasterSeed.Length == 32 /// Write masterseed to stream /// b. GenerateKey32(_transformSeed, KeyEncryptionRounds) /// Create raw32 (CreateRawCompositeKey32) /// Concatenate all data and Sha256 /// TransformKey(raw32, _transformSeed, numRounds) /// Init Rijndael: /// 128 bit (16 byte) blocks /// ECB mode /// k = _transformSeed /// For numRounds: /// Transform in place raw32[0:15] /// Transform in place raw32[16:31] /// c. Write 32 bytes of Key32 to stream /// d. aesKey = Sha256 the stream /// e. DecryptStream with aesKey and _encryptionIV /// 4. Verify the first 32 bytes of the decrypted viewModel match up with /// "StreamStartBytes" from the header. /// 5. Read from the decrypted viewModel as a "HashedBlockStream" /// /// File format at the time of this writing (11/5/2012): /// /// 4 bytes: SIG1 /// 4 bytes: SIG2 /// Failure to match these constants results in a parse Result. /// /// 4 bytes: File version /// /// Header fields: /// 1 byte: Field ID /// 2 bytes: Field size (n) /// n bytes: Data /// </remarks> /// <param name="stream">An IRandomAccessStream containing the document to unlock (including the header).</param> /// <param name="rawKey">The aggregate raw key to use for decrypting the database.</param> /// <param name="token">A token allowing the parse to be cancelled.</param> /// <returns>A Task representing the result of the descryiption operation.</returns> public async Task <KdbxDecryptionResult> DecryptFile(IRandomAccessStream stream, IBuffer rawKey, CancellationToken token) { if (HeaderData == null) { throw new InvalidOperationException("Cannot decrypt database before ReadHeader has been called."); } // Init a SHA256 hash buffer and append the master seed to it HashAlgorithmProvider sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); CryptographicHash hash = sha256.CreateHash(); hash.Append(HeaderData.MasterSeed); this.rawKey = rawKey; this.logger.LogEvent("KdbxReader.GotRawKey", EventVerbosity.Verbose); // Transform the key (this can take a while) IBuffer transformedKey; try { IKdfEngine kdf = HeaderData.KdfParameters.CreateEngine(); LoggingFields fields = new LoggingFields(); fields.AddString("KdfEngine", kdf.GetType().Name); this.logger.LogEvent("KdbxReader.StartingKeyTransform", fields, EventVerbosity.Info); transformedKey = await HeaderData.KdfParameters.CreateEngine().TransformKeyAsync(rawKey, token) .ConfigureAwait(false); if (transformedKey == null) { throw new OperationCanceledException(); } this.logger.LogEvent("KdbxReader.KeyTransformSucceeded", EventVerbosity.Info); } catch (OperationCanceledException) { return(new KdbxDecryptionResult(new ReaderResult(KdbxParserCode.OperationCancelled))); } // In KDBX4, after the header is an HMAC-SHA-256 value computed over the header // allowing validation of header integrity. IBuffer hmacKey = HmacBlockHandler.DeriveHmacKey(transformedKey, HeaderData.MasterSeed); HmacBlockHandler hmacHandler = new HmacBlockHandler(hmacKey); IBuffer expectedMac = null; if (this.parameters.UseInlineHeaderAuthentication) { var algorithm = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256); CryptographicHash hmacHash = algorithm.CreateHash(hmacHandler.GetKeyForBlock(UInt64.MaxValue)); DebugHelper.Assert(HeaderData.FullHeader != null); hmacHash.Append(HeaderData.FullHeader); expectedMac = hmacHash.GetValueAndReset(); } // Hash transformed k (with the master seed) to get final cipher k hash.Append(transformedKey); IBuffer cipherKey = hash.GetValueAndReset(); this.logger.LogEvent("KdbxReader.GotFinalCipherKey", EventVerbosity.Info); // Decrypt the document starting from the end of the header ulong headerLength = HeaderData.FullHeader.Length; if (this.parameters.UseInlineHeaderAuthentication) { // KDBX4 has a hash at the end of the header headerLength += 32; } stream.Seek(headerLength); if (expectedMac != null) { using (DataReader macReader = GetReaderForStream(stream)) { await macReader.LoadAsync(expectedMac.Length); IBuffer actualMac = macReader.ReadBuffer(expectedMac.Length); for (uint i = 0; i < expectedMac.Length; i++) { if (expectedMac.GetByte(i) != actualMac.GetByte(i)) { this.logger.LogEvent("KdbxReader.HmacFailure", EventVerbosity.Critical); return(new KdbxDecryptionResult(new ReaderResult(KdbxParserCode.CouldNotDecrypt))); } } macReader.DetachStream(); } } IBuffer cipherText; try { cipherText = await GetCipherText(stream, hmacHandler); } catch (FormatException ex) { this.logger.LogEvent("KdbxReader.DataIntegrityFailure", ex.ToLoggingFields(), EventVerbosity.Critical); return(new KdbxDecryptionResult(new ReaderResult(KdbxParserCode.DataIntegrityProblem, ex))); } IBuffer decryptedFile = DecryptDatabaseData(cipherText, cipherKey); if (decryptedFile == null) { this.logger.LogEvent("KdbxReader.DecryptionFailure", EventVerbosity.Critical); return(new KdbxDecryptionResult(new ReaderResult(KdbxParserCode.CouldNotDecrypt))); } this.logger.LogEvent("KdbxReader.DecryptionSucceeded", EventVerbosity.Info); // Verify first 32 bytes of the clear data; if StreamStartBytes wasn't set // (e.g. due to KDBX4), nothing happens here. for (uint i = 0; i < (HeaderData.StreamStartBytes?.Length ?? 0); i++) { byte actualByte = decryptedFile.GetByte(i); byte expectedByte = HeaderData.StreamStartBytes.GetByte(i); if (actualByte != expectedByte) { this.logger.LogEvent("KdbxReader.PlaintextValidationFailure", EventVerbosity.Critical); return(new KdbxDecryptionResult(new ReaderResult(KdbxParserCode.FirstBytesMismatch))); } } this.logger.LogEvent("KdbxReader.PlaintextValidationSucceeded", EventVerbosity.Verbose); IBuffer plainText = await UnhashAndInflate(decryptedFile); if (plainText == null) { return(new KdbxDecryptionResult(new ReaderResult(KdbxParserCode.CouldNotInflate))); } // Update HeaderData with info from the inner header, if relevant if (this.parameters.UseInnerHeader) { using (IRandomAccessStream plainTextStream = plainText.AsStream().AsRandomAccessStream()) { using (DataReader reader = GetReaderForStream(plainTextStream)) { ReaderResult innerHeaderResult = await ReadInnerHeader(reader, HeaderData); if (innerHeaderResult != ReaderResult.Success) { LoggingFields fields = new LoggingFields(); fields.AddInt32("Code", (int)innerHeaderResult.Code); this.logger.LogEvent("KdbxReader.InnerHeaderReadFailure", fields, EventVerbosity.Critical); return(new KdbxDecryptionResult(innerHeaderResult)); } // Update plainText to point to the remainder of the buffer uint bytesRemaining = plainText.Length - (uint)plainTextStream.Position; await reader.LoadAsync(bytesRemaining); plainText = reader.ReadBuffer(bytesRemaining); } } } XDocument finalTree = null; try { finalTree = XDocument.Load(plainText.AsStream()); } catch (XmlException) { return(null); } if (finalTree == null) { return(new KdbxDecryptionResult(new ReaderResult(KdbxParserCode.MalformedXml))); } try { KdbxDocument parsedDocument = await Task.Run(() => new KdbxDocument(finalTree.Root, HeaderData.ProtectedBinaries, HeaderData.GenerateRng(), this.parameters)); // Validate the final parsed header hash before returning if (this.parameters.UseXmlHeaderAuthentication && !String.IsNullOrEmpty(parsedDocument.Metadata.HeaderHash) && parsedDocument.Metadata.HeaderHash != HeaderData.HeaderHash) { return(new KdbxDecryptionResult(new ReaderResult(KdbxParserCode.BadHeaderHash))); } return(new KdbxDecryptionResult(this.parameters, parsedDocument, this.rawKey)); } catch (KdbxParseException e) { return(new KdbxDecryptionResult(e.Error)); } }
private async Task <HTTPRequest> ProcessStream(IInputStream stream) { Dictionary <string, string> _httpHeaders = null; Dictionary <string, string> _urlParameters = null; byte[] data = new byte[BufferSize]; StringBuilder requestString = new StringBuilder(); uint dataRead = BufferSize; IBuffer buffer = data.AsBuffer(); string hValue = ""; string hKey = ""; try { // binary data buffer index uint bfndx = 0; // Incoming message may be larger than the buffer size. while (dataRead == BufferSize) { await stream.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial); requestString.Append(Encoding.UTF8.GetString(data, 0, data.Length)); dataRead = buffer.Length; // read buffer index uint ndx = 0; do { switch (ParserState) { case HttpParserState.METHOD: if (data[ndx] != ' ') { HTTPRequest.Method += (char)buffer.GetByte(ndx++); } else { ndx++; ParserState = HttpParserState.URL; } break; case HttpParserState.URL: if (data[ndx] == '?') { ndx++; hKey = ""; HTTPRequest.Execute = true; _urlParameters = new Dictionary <string, string>(); ParserState = HttpParserState.URLPARM; } else if (data[ndx] != ' ') { HTTPRequest.URL += (char)buffer.GetByte(ndx++); } else { ndx++; HTTPRequest.URL = WebUtility.UrlDecode(HTTPRequest.URL); ParserState = HttpParserState.VERSION; } break; case HttpParserState.URLPARM: if (data[ndx] == '=') { ndx++; hValue = ""; ParserState = HttpParserState.URLVALUE; } else if (data[ndx] == ' ') { ndx++; HTTPRequest.URL = WebUtility.UrlDecode(HTTPRequest.URL); ParserState = HttpParserState.VERSION; } else { hKey += (char)buffer.GetByte(ndx++); } break; case HttpParserState.URLVALUE: if (data[ndx] == '&') { ndx++; hKey = WebUtility.UrlDecode(hKey); hValue = WebUtility.UrlDecode(hValue); _urlParameters[hKey] = _urlParameters.ContainsKey(hKey) ? _urlParameters[hKey] + ", " + hValue : hValue; hKey = ""; ParserState = HttpParserState.URLPARM; } else if (data[ndx] == ' ') { ndx++; hKey = WebUtility.UrlDecode(hKey); hValue = WebUtility.UrlDecode(hValue); _urlParameters[hKey] = _urlParameters.ContainsKey(hKey) ? _urlParameters[hKey] + ", " + hValue : hValue; HTTPRequest.URL = WebUtility.UrlDecode(HTTPRequest.URL); ParserState = HttpParserState.VERSION; } else { hValue += (char)buffer.GetByte(ndx++); } break; case HttpParserState.VERSION: if (data[ndx] == '\r') { ndx++; } else if (data[ndx] != '\n') { HTTPRequest.Version += (char)buffer.GetByte(ndx++); } else { ndx++; hKey = ""; _httpHeaders = new Dictionary <string, string>(); ParserState = HttpParserState.HEADERKEY; } break; case HttpParserState.HEADERKEY: if (data[ndx] == '\r') { ndx++; } else if (data[ndx] == '\n') { ndx++; if (_httpHeaders.ContainsKey("Content-Length")) { HTTPRequest.BodySize = Convert.ToInt32(_httpHeaders["Content-Length"]); ParserState = HttpParserState.BODY; } else { ParserState = HttpParserState.OK; } } else if (data[ndx] == ':') { ndx++; } else if (data[ndx] != ' ') { hKey += (char)buffer.GetByte(ndx++); } else { ndx++; hValue = ""; ParserState = HttpParserState.HEADERVALUE; } break; case HttpParserState.HEADERVALUE: if (data[ndx] == '\r') { ndx++; } else if (data[ndx] != '\n') { hValue += (char)buffer.GetByte(ndx++); } else { ndx++; _httpHeaders.Add(hKey, hValue); hKey = ""; ParserState = HttpParserState.HEADERKEY; } break; case HttpParserState.BODY: // Append to request BodyData this.HTTPRequest.BodyContent = Encoding.UTF8.GetString(data, 0, this.HTTPRequest.BodySize); bfndx += dataRead - ndx; ndx = dataRead; if (this.HTTPRequest.BodySize <= bfndx) { ParserState = HttpParserState.OK; } break; //default: // ndx++; // break; } }while (ndx < dataRead); } ; // Print out the received message to the console. Debug.WriteLine("You received the following message : \n" + requestString); if (_httpHeaders != null) { HTTPRequest.Headers = _httpHeaders.AsEnumerable(); } if (_urlParameters != null) { HTTPRequest.URLParametes = _urlParameters.AsEnumerable(); } return(HTTPRequest); } catch (Exception e) { Debug.WriteLine(e.ToString()); } return(null); }
/// <summary> /// Attempts to read an HMAC-authenticated cipher block from a given data source. /// Returns null on EOS. /// </summary> /// <param name="reader">Reader used to access data.</param> /// <param name="blockIndex">The index of this block.</param> /// <returns>A buffer of read data.</returns> public async Task <IBuffer> ReadCipherBlockAsync(DataReader reader, ulong blockIndex) { if (reader == null) { throw new ArgumentNullException(nameof(reader)); } uint loadedBytes = await reader.LoadAsync(32); if (loadedBytes == 0) { return(null); } if (loadedBytes < 32) { throw new FormatException("Couldn't load 32 bytes for HMAC value"); } IBuffer actualHmacValue = reader.ReadBuffer(32); loadedBytes = await reader.LoadAsync(4); if (loadedBytes < 4) { throw new FormatException("Couldn't load 4 bytes for block size"); } int blockSize = reader.ReadInt32(); if (blockSize == 0) { return(null); } else if (blockSize < 0) { throw new FormatException($"Block size must be a positive int32, got {blockSize}"); } loadedBytes = await reader.LoadAsync((uint)blockSize); if (loadedBytes < blockSize) { throw new FormatException($"Expected to load {blockSize} bytes for block, only got {loadedBytes}"); } IBuffer cipherText = reader.ReadBuffer(loadedBytes); IBuffer expectedHmacValue = GetMacForBlock(blockIndex, cipherText); // Validate HMAC if (expectedHmacValue.Length != actualHmacValue.Length) { throw new FormatException("Expected and actual HMAC values had different lengths"); } DebugHelper.Trace($"Read HMAC block #{blockIndex}; block is {loadedBytes} bytes."); DebugHelper.Trace($"MAC[0]: {actualHmacValue.GetByte(0)}, MAC[31]: {actualHmacValue.GetByte(31)}, data[0]: {cipherText.GetByte(0)}, data[n]: {cipherText.GetByte(loadedBytes - 1)}"); for (uint i = 0; i < expectedHmacValue.Length; i++) { if (expectedHmacValue.GetByte(i) != actualHmacValue.GetByte(i)) { throw new FormatException($"HMAC values differed at index {i}"); } } // Ciphertext was validated return(cipherText); }
public static async Task <ExportResult> ExportSvgAsync( ExportStyle style, InstalledFont selectedFont, FontVariant selectedVariant, Character selectedChar, CanvasTextLayoutAnalysis analysis, CanvasTypography typography) { try { string name = GetFileName(selectedFont, selectedVariant, selectedChar, "svg"); if (await PickFileAsync(name, "SVG", new[] { ".svg" }) is StorageFile file) { CachedFileManager.DeferUpdates(file); CanvasDevice device = Utils.CanvasDevice; Color textColor = style == ExportStyle.Black ? Colors.Black : Colors.White; // If COLR format (e.g. Segoe UI Emoji), we have special export path. if (style == ExportStyle.ColorGlyph && analysis.HasColorGlyphs && !analysis.GlyphFormats.Contains(GlyphImageFormat.Svg)) { NativeInterop interop = Utils.GetInterop(); List <string> paths = new List <string>(); Rect bounds = Rect.Empty; foreach (var thing in analysis.Indicies) { var path = interop.GetPathDatas(selectedVariant.FontFace, thing.ToArray()).First(); paths.Add(path.Path); if (!path.Bounds.IsEmpty) { var left = Math.Min(bounds.Left, path.Bounds.Left); var top = Math.Min(bounds.Top, path.Bounds.Top); var right = Math.Max(bounds.Right, path.Bounds.Right); var bottom = Math.Max(bounds.Bottom, path.Bounds.Bottom); bounds = new Rect( left, top, right - left, bottom - top); } } using (CanvasSvgDocument document = Utils.GenerateSvgDocument(device, bounds, paths, analysis.Colors, invertBounds: false)) { await Utils.WriteSvgAsync(document, file); } return(new ExportResult(true, file)); } var data = GetGeometry(1024, selectedVariant, selectedChar, analysis, typography); async Task SaveMonochromeAsync() { using CanvasSvgDocument document = Utils.GenerateSvgDocument(device, data.Bounds, data.Path, textColor); await Utils.WriteSvgAsync(document, file); } // If the font uses SVG glyphs, we can extract the raw SVG from the font file if (analysis.GlyphFormats.Contains(GlyphImageFormat.Svg)) { string str = null; IBuffer b = GetGlyphBuffer(selectedVariant.FontFace, selectedChar.UnicodeIndex, GlyphImageFormat.Svg); if (b.Length > 2 && b.GetByte(0) == 31 && b.GetByte(1) == 139) { using var stream = b.AsStream(); using var gzip = new GZipStream(stream, CompressionMode.Decompress); using var reader = new StreamReader(gzip); str = reader.ReadToEnd(); } else { using var dataReader = DataReader.FromBuffer(b); dataReader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8; str = dataReader.ReadString(b.Length); } if (str.StartsWith("<?xml")) { str = str.Remove(0, str.IndexOf(">") + 1); } str = str.TrimStart(); try { using (CanvasSvgDocument document = CanvasSvgDocument.LoadFromXml(Utils.CanvasDevice, str)) { // We need to transform the SVG to fit within the default document bounds, as characters // are based *above* the base origin of (0,0) as (0,0) is the Baseline (bottom left) position for a character, // so by default a will appear out of bounds of the default SVG viewport (towards top left). //if (!document.Root.IsAttributeSpecified("viewBox")) // Specified viewbox requires baseline transform? { // We'll regroup all the elements inside a "g" / group tag, // and apply a transform to the "g" tag to try and put in // in the correct place. There's probably a more accurate way // to do this by directly setting the root viewBox, if anyone // can find the correct calculation... List <ICanvasSvgElement> elements = new List <ICanvasSvgElement>(); double minTop = 0; double minLeft = double.MaxValue; double maxWidth = double.MinValue; double maxHeight = double.MinValue; void ProcessChildren(CanvasSvgNamedElement root) { CanvasSvgNamedElement ele = root.FirstChild as CanvasSvgNamedElement; while (true) { CanvasSvgNamedElement next = root.GetNextSibling(ele) as CanvasSvgNamedElement; if (ele.Tag == "g") { ProcessChildren(ele); } else if (ele.Tag == "path") { // Create a XAML geometry to try and find the bounds of each character // Probably more efficient to do in Win2D, but far less code to do with XAML. Geometry gm = XamlBindingHelper.ConvertValue(typeof(Geometry), ele.GetStringAttribute("d")) as Geometry; minTop = Math.Min(minTop, gm.Bounds.Top); minLeft = Math.Min(minLeft, gm.Bounds.Left); maxWidth = Math.Max(maxWidth, gm.Bounds.Width); maxHeight = Math.Max(maxHeight, gm.Bounds.Height); } ele = next; if (ele == null) { break; } } } ProcessChildren(document.Root); double top = minTop < 0 ? minTop : 0; double left = minLeft; document.Root.SetRectangleAttribute("viewBox", new Rect(left, top, data.Bounds.Width, data.Bounds.Height)); } await Utils.WriteSvgAsync(document, file); } } catch { // Certain fonts seem to have their SVG glyphs encoded with... I don't even know what encoding. // for example: https://github.com/adobe-fonts/emojione-color // In these cases, fallback to monochrome black await SaveMonochromeAsync(); } } else { await SaveMonochromeAsync(); } await CachedFileManager.CompleteUpdatesAsync(file); return(new ExportResult(true, file)); } } catch (Exception ex) { await SimpleIoc.Default.GetInstance <IDialogService>() .ShowMessageBox(ex.Message, Localization.Get("SaveImageError")); } return(new ExportResult(false, null)); }
private static ValueParserResult ConvertHelper(DataReader dr, string decodeCommands) { var str = ""; var vps = ValueParserSplit.ParseLine(decodeCommands); var valueList = new BCBasic.BCValueList(); bool isOptional = false; for (int i = 0; i < vps.Count; i++) { var stritem = ""; var command = vps[i]; var readcmd = command.ByteFormatPrimary; var readindicator = readcmd[0]; var displayFormat = command.DisplayFormatPrimary; var displayFormatSecondary = command.Get(1, 1); var name = command.NamePrimary; if (string.IsNullOrEmpty(name)) { name = $"param{i}"; } var units = command.UnitsPrimary; var resultState = ResultState.IsDouble; // the most common result double dvalue = double.NaN; try { switch (readindicator) { case 'F': // FLOAT if (dr.UnconsumedBufferLength == 0) { if (isOptional) { dvalue = double.NaN; stritem = ""; break; } else { return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i+1}")); } } switch (readcmd) { case "F32": { dvalue = dr.ReadSingle(); switch (displayFormat) { case "": case "FIXED": displayFormat = (displayFormatSecondary == "") ? "N3" : displayFormatSecondary; break; case "DEC": displayFormat = (displayFormatSecondary == "") ? "N0" : displayFormatSecondary; break; case "HEX": return(ValueParserResult.CreateError(decodeCommands, $"Float displayFormat unrecognized; should be FIXED {displayFormat}")); } stritem = dvalue.ToString(displayFormat); // e.g. N3 for 3 digits } break; case "F64": { dvalue = dr.ReadDouble(); switch (displayFormat) { case "": case "FIXED": displayFormat = (displayFormatSecondary == "") ? "N3" : displayFormatSecondary; break; case "DEC": displayFormat = (displayFormatSecondary == "") ? "N0" : displayFormatSecondary; break; case "HEX": return(ValueParserResult.CreateError(decodeCommands, $"Float displayFormat unrecognized; should be FIXED {displayFormat}")); } stritem = dvalue.ToString(displayFormat); // e.g. N3 for 3 digits } break; default: return(ValueParserResult.CreateError(decodeCommands, $"Float command unrecognized; should be F32 or F64 not {readcmd}")); } break; case 'I': if (dr.UnconsumedBufferLength == 0) { if (isOptional) { dvalue = double.NaN; stritem = ""; break; } else { return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i + 1}")); } } switch (readcmd) { case "I8": case "I16": case "I24": case "I32": { string floatFormat = "F2"; string intFormat = "X6"; switch (readcmd) { case "I8": { var value = (sbyte)dr.ReadByte(); dvalue = (double)value; } break; case "I16": { var value = dr.ReadInt16(); dvalue = (double)value; } break; case "I24": { var b0 = dr.ReadByte(); var b1 = dr.ReadByte(); var b2 = dr.ReadByte(); var msb = (sbyte)(dr.ByteOrder == ByteOrder.BigEndian ? b0 : b2); var lsb = dr.ByteOrder == ByteOrder.BigEndian ? b2 : b0; int value = (int)(msb << 16) + (b1 << 8) + (lsb); dvalue = (double)value; } break; case "I32": { var value = dr.ReadInt32(); dvalue = (double)value; intFormat = "X8"; } break; } string calculateCommand = command.Get(0, 1); // e.g. for I24^100_/ for TI 1350 barometer values if (!string.IsNullOrEmpty(calculateCommand)) { dvalue = ValueCalculate.Calculate(calculateCommand, dvalue).D; if (double.IsNaN(dvalue)) { return(ValueParserResult.CreateError(decodeCommands, $"Calculation failed for {calculateCommand} in {readcmd}")); } else { // Everything worked and got a value stritem = DoubleToString(dvalue, displayFormat, displayFormatSecondary); if (stritem == null) { return(ValueParserResult.CreateError(decodeCommands, $"Integer display format command unrecognized; should be FIXED or HEX or DEC not {displayFormat} in {readcmd}")); } } } else { if (displayFormat == "") { displayFormat = "HEX"; } stritem = DoubleToString(dvalue, displayFormat, displayFormatSecondary, floatFormat, intFormat); if (stritem == null) { return(ValueParserResult.CreateError(decodeCommands, $"Integer display format command unrecognized; should be FIXED or HEX or DEC not {displayFormat} in {readcmd}")); } } } break; default: return(ValueParserResult.CreateError(decodeCommands, $"Integer command unrecognized; should be I8/16/24/32 not {readcmd}")); } break; case 'O': switch (readcmd) { case "OEB": resultState = ResultState.NoResult; dr.ByteOrder = ByteOrder.LittleEndian; break; case "OEL": resultState = ResultState.NoResult; dr.ByteOrder = ByteOrder.LittleEndian; break; case "OOPT": isOptional = true; break; default: return(ValueParserResult.CreateError(decodeCommands, $"Optional command unrecognized; should be OEL or OEB not {readcmd}")); } break; case 'Q': if (dr.UnconsumedBufferLength == 0) { if (isOptional) { dvalue = double.NaN; stritem = ""; break; } else { return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i + 1}")); } } // e.g. Q12Q4.Fixed { var subtypes = readcmd.Split(new char[] { 'Q' }); if (subtypes.Length != 3) // Actually 2, but first is blank { return(ValueParserResult.CreateError(decodeCommands, $"Q command unrecognized; wrong number of Qs {readcmd}")); } stritem = FixedQuotientHelper(dr, subtypes[1], subtypes[2], displayFormat, out dvalue); //NOTE: fail on failure } break; case 'U': if (dr.UnconsumedBufferLength == 0) { if (isOptional) { dvalue = double.NaN; stritem = ""; break; } else { return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i + 1}")); } } switch (readcmd) { case "U8": case "U16": case "U24": case "U32": string xfmt = "X2"; switch (readcmd) { case "U8": { var value = dr.ReadByte(); dvalue = (double)value; xfmt = "X2"; } break; case "U16": { var value = dr.ReadUInt16(); dvalue = (double)value; xfmt = "X4"; } break; case "U24": { var b0 = dr.ReadByte(); var b1 = dr.ReadByte(); var b2 = dr.ReadByte(); var msb = (byte)(dr.ByteOrder == ByteOrder.BigEndian ? b0 : b2); var lsb = dr.ByteOrder == ByteOrder.BigEndian ? b2 : b0; int value = (int)(msb << 16) + (b1 << 8) + (lsb); dvalue = (double)value; } break; case "U32": { var value = dr.ReadUInt32(); dvalue = (double)value; xfmt = "X8"; } break; } string calculateCommand = command.Get(0, 1); // e.g. for I24^100_/ for TI 1350 barometer values if (!string.IsNullOrEmpty(calculateCommand)) { dvalue = ValueCalculate.Calculate(calculateCommand, dvalue).D; if (double.IsNaN(dvalue)) { return(ValueParserResult.CreateError(decodeCommands, $"Calculation failed for {calculateCommand} in {readcmd}")); } else { stritem = DoubleToString(dvalue, displayFormat, displayFormatSecondary); if (stritem == null) { return(ValueParserResult.CreateError(decodeCommands, $"Integer display format command unrecognized; should be FIXED or HEX or DEC not {displayFormat} in {readcmd}")); } } } else { if (displayFormat == "") { displayFormat = "HEX"; } stritem = DoubleToString(dvalue, displayFormat, displayFormatSecondary, "F2", xfmt); if (stritem == null) { return(ValueParserResult.CreateError(decodeCommands, $"Integer display format command unrecognized;\nshould be FIXED or HEX or DEC not {displayFormat} in {readcmd}")); } } break; default: return(ValueParserResult.CreateError(decodeCommands, $"UInteger command unrecognized;\nshould be U8/U16/U24/U32 not {readcmd}")); } break; case '/': // e.g. /U8/I16|Fixed if (dr.UnconsumedBufferLength == 0) { if (isOptional) { dvalue = double.NaN; stritem = ""; break; } else { return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i + 1}")); } } { var subtypes = readcmd.Split(new char[] { '/' }); if (subtypes.Length != 3) // Actually 2, but first is blank { return(ValueParserResult.CreateError(decodeCommands, $"/ command unrecognized; wrong number of slashes {readcmd}")); } stritem = FixedFractionHelper(dr, subtypes[1], subtypes[2], displayFormat, out dvalue); // NOTE: fail on failure } break; default: if (readcmd != readcmd.ToUpper()) { System.Diagnostics.Debug.WriteLine("ERROR: readcmd {readcmd} but should be uppercase"); } switch (readcmd.ToUpper()) //NOTE: should be all-uppercase; any lowercase is wrong { case "STRING": { try { stritem = dr.ReadString(dr.UnconsumedBufferLength); switch (displayFormat) { case "": case "ASCII": stritem = EscapeString(stritem, displayFormatSecondary); break; case "Eddystone": stritem = BluetoothDefinitionLanguage.Eddystone.EddystoneUrlToString(stritem); break; default: return(ValueParserResult.CreateError(decodeCommands, $"Unknown string format type {displayFormat}")); } } catch (Exception) { // The string can't be read. Let's try reading as a buffer instead. IBuffer buffer = dr.ReadBuffer(dr.UnconsumedBufferLength); for (uint ii = 0; ii < buffer.Length; ii++) { if (ii != 0) { stritem += " "; } stritem += buffer.GetByte(ii).ToString("X2"); } } resultState = ResultState.IsString; } break; case "BYTES": { IBuffer buffer = dr.ReadBuffer(dr.UnconsumedBufferLength); for (uint ii = 0; ii < buffer.Length; ii++) { if (ii != 0) { stritem += " "; } stritem += buffer.GetByte(ii).ToString("X2"); } resultState = ResultState.IsString; } break; default: return(ValueParserResult.CreateError(decodeCommands, $"Other command unrecognized; should be String or Bytes {readcmd}")); } break; } } catch (Exception e) { stritem = $"EXCEPTION reading data {e} index {i} command {command} len {dr.UnconsumedBufferLength}"; return(ValueParserResult.CreateError(str + stritem, stritem)); } switch (resultState) { case ResultState.IsDouble: valueList.SetProperty(name, new BCBasic.BCValue(dvalue)); break; case ResultState.IsString: valueList.SetProperty(name, new BCBasic.BCValue(stritem)); break; } if (str != "") { str += " "; } str += stritem; if (dr.UnconsumedBufferLength <= 0) { break; } } return(ValueParserResult.CreateOk(str, valueList)); }