From 490eec88dea16c95a1ae843f7ec00c4d86f2d893 Mon Sep 17 00:00:00 2001 From: Jan Hyka Date: Wed, 6 Nov 2024 20:36:05 +0100 Subject: [PATCH 1/2] + sanitize escapable json elements on Stream processor path + test coverage + reduce excessive string allocations on encryption path --- .../Transformation/StreamProcessor.Base.cs | 40 +++++++++++++++++++ .../StreamProcessor.Decryptor.cs | 38 ++++++++---------- .../StreamProcessor.Encryptor.cs | 15 ++++--- .../MdeEncryptionProcessorTests.cs | 26 +++++++++++- .../TestCommon.cs | 16 +++----- 5 files changed, 95 insertions(+), 40 deletions(-) create mode 100644 Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Base.cs diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Base.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Base.cs new file mode 100644 index 0000000000..df0aea73bc --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Base.cs @@ -0,0 +1,40 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation +{ + using System; + using System.Runtime.CompilerServices; + using System.Text.Json; + using Microsoft.Data.Encryption.Cryptography.Serializers; + + internal partial class StreamProcessor + { + private const int ControlledElementsDepth = 1; + private static readonly SqlBitSerializer SqlBoolSerializer = new (); + private static readonly SqlFloatSerializer SqlDoubleSerializer = new (); + private static readonly SqlBigIntSerializer SqlLongSerializer = new (); + private static readonly JsonReaderOptions JsonReaderOptions = new () { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }; + + internal static int InitialBufferSize { get; set; } = 16384; + + internal MdeEncryptor Encryptor { get; set; } = new MdeEncryptor(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan UnescapeValue(ref Utf8JsonReader reader, ArrayPoolManager arrayPoolManager) + { + if (!reader.ValueIsEscaped) + { + return reader.ValueSpan; + } + + Span bytes = arrayPoolManager.Rent(reader.ValueSpan.Length); + int size = reader.CopyString(bytes); + + return bytes[..size]; + } + } +} +#endif \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Decryptor.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Decryptor.cs index d4ca1ccfdf..263604bc4e 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Decryptor.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Decryptor.cs @@ -14,20 +14,10 @@ namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation using System.Text.Json; using System.Threading; using System.Threading.Tasks; - using Microsoft.Data.Encryption.Cryptography.Serializers; internal partial class StreamProcessor { private const string EncryptionPropertiesPath = "/" + Constants.EncryptedInfo; - private static readonly SqlBitSerializer SqlBoolSerializer = new (); - private static readonly SqlFloatSerializer SqlDoubleSerializer = new (); - private static readonly SqlBigIntSerializer SqlLongSerializer = new (); - - private static readonly JsonReaderOptions JsonReaderOptions = new () { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }; - - internal static int InitialBufferSize { get; set; } = 16384; - - internal MdeEncryptor Encryptor { get; set; } = new MdeEncryptor(); internal async Task DecryptStreamAsync( Stream inputStream, @@ -110,7 +100,7 @@ long TransformDecryptBuffer(ReadOnlySpan buffer) { JsonTokenType tokenType = reader.TokenType; - if (isIgnoredBlock && reader.CurrentDepth == 1 && tokenType == JsonTokenType.EndObject) + if (isIgnoredBlock && reader.CurrentDepth == ControlledElementsDepth && tokenType == JsonTokenType.EndObject) { isIgnoredBlock = false; continue; @@ -125,7 +115,7 @@ long TransformDecryptBuffer(ReadOnlySpan buffer) case JsonTokenType.String: if (decryptPropertyName == null) { - writer.WriteStringValue(reader.ValueSpan); + writer.WriteStringValue(UnescapeValue(ref reader, arrayPoolManager)); } else { @@ -160,22 +150,26 @@ long TransformDecryptBuffer(ReadOnlySpan buffer) writer.WriteEndArray(); break; case JsonTokenType.PropertyName: - string propertyName = "/" + reader.GetString(); - if (encryptedPaths.Contains(propertyName)) - { - decryptPropertyName = propertyName; - } - else if (propertyName == StreamProcessor.EncryptionPropertiesPath) + if (reader.CurrentDepth == ControlledElementsDepth) { - if (!reader.TrySkip()) + string propertyName = "/" + reader.GetString(); + if (encryptedPaths.Contains(propertyName)) { - isIgnoredBlock = true; + decryptPropertyName = propertyName; } + else if (propertyName == StreamProcessor.EncryptionPropertiesPath) + { + if (!reader.TrySkip()) + { + isIgnoredBlock = true; + } - break; + break; + } } - writer.WritePropertyName(reader.ValueSpan); + writer.WritePropertyName(UnescapeValue(ref reader, arrayPoolManager)); + break; case JsonTokenType.Comment: break; diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs index a3980ae56a..5c29297ee4 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs @@ -179,16 +179,19 @@ long TransformEncryptBuffer(ReadOnlySpan buffer) break; case JsonTokenType.PropertyName: - string propertyName = "/" + reader.GetString(); - if (pathsToEncrypt.Contains(propertyName)) + if (reader.CurrentDepth == ControlledElementsDepth) { - encryptPropertyName = propertyName; + string propertyName = "/" + reader.GetString(); + if (pathsToEncrypt.Contains(propertyName)) + { + encryptPropertyName = propertyName; + } } - currentWriter.WritePropertyName(reader.ValueSpan); + currentWriter.WritePropertyName(UnescapeValue(ref reader, arrayPoolManager)); break; case JsonTokenType.Comment: - currentWriter.WriteCommentValue(reader.ValueSpan); + currentWriter.WriteCommentValue(UnescapeValue(ref reader, arrayPoolManager)); break; case JsonTokenType.String: if (encryptPropertyName != null && encryptionPayloadWriter == null) @@ -201,7 +204,7 @@ long TransformEncryptBuffer(ReadOnlySpan buffer) } else { - currentWriter.WriteStringValue(reader.ValueSpan); + currentWriter.WriteStringValue(UnescapeValue(ref reader, arrayPoolManager)); } break; diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionProcessorTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionProcessorTests.cs index 46fde1350c..4fcb659fa8 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionProcessorTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionProcessorTests.cs @@ -17,6 +17,7 @@ namespace Microsoft.Azure.Cosmos.Encryption.Tests using Microsoft.Azure.Cosmos.Encryption.Custom; #if NET8_0_OR_GREATER using Microsoft.Azure.Cosmos.Encryption.Custom.Transformation; + using Microsoft.Azure.Cosmos.Linq; #endif using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -324,16 +325,35 @@ private static async Task VerifyEncryptionSucceededNewtonsoft(TestDoc t Assert.IsNull(encryptionProperties.EncryptedData); Assert.IsNotNull(encryptionProperties.EncryptedPaths); + int expectedEncryptedPaths = 5; + if (testDoc.SensitiveStr == null) + { + expectedEncryptedPaths--; + } + if (testDoc.SensitiveUtf8Escapedřňěščřžýáíé == null) + { + expectedEncryptedPaths--; + } + Assert.AreEqual(expectedEncryptedPaths, encryptionProperties.EncryptedPaths.Count()); + if (testDoc.SensitiveStr == null) { Assert.IsNull(encryptedDoc.Property(nameof(TestDoc.SensitiveStr)).Value.Value()); // since null value is not encrypted - Assert.AreEqual(TestDoc.PathsToEncrypt.Count - 1, encryptionProperties.EncryptedPaths.Count()); } else { Assert.IsNotNull(encryptedDoc.Property(nameof(TestDoc.SensitiveStr)).Value.Value()); Assert.AreNotEqual(testDoc.SensitiveStr, encryptedDoc.Property(nameof(TestDoc.SensitiveStr)).Value.Value()); // not equal since value is encrypted - Assert.AreEqual(TestDoc.PathsToEncrypt.Count, encryptionProperties.EncryptedPaths.Count()); + } + + if (testDoc.SensitiveUtf8Escapedřňěščřžýáíé == null) + { + Assert.IsNull(encryptedDoc.Property(nameof(TestDoc.SensitiveUtf8Escapedřňěščřžýáíé)).Value.Value()); // since null value is not encrypted + } + else + { + Assert.IsNotNull(encryptedDoc.Property(nameof(TestDoc.SensitiveUtf8Escapedřňěščřžýáíé)).Value.Value()); + Assert.AreNotEqual(testDoc.SensitiveUtf8Escapedřňěščřžýáíé, encryptedDoc.Property(nameof(TestDoc.SensitiveUtf8Escapedřňěščřžýáíé)).Value.Value()); // not equal since value is encrypted } return encryptedDoc; @@ -347,6 +367,7 @@ private static void VerifyDecryptionSucceeded( bool invalidPathsConfigured = false) { Assert.AreEqual(expectedDoc.SensitiveStr, decryptedDoc.Property(nameof(TestDoc.SensitiveStr)).Value.Value()); + Assert.AreEqual(expectedDoc.SensitiveUtf8Escapedřňěščřžýáíé, decryptedDoc.Property(nameof(TestDoc.SensitiveUtf8Escapedřňěščřžýáíé)).Value.Value()); Assert.AreEqual(expectedDoc.SensitiveInt, decryptedDoc.Property(nameof(TestDoc.SensitiveInt)).Value.Value()); Assert.IsNull(decryptedDoc.Property(Constants.EncryptedInfo)); @@ -383,6 +404,7 @@ private static void VerifyDecryptionSucceeded( bool invalidPathsConfigured = false) { AssertNullableValueKind(expectedDoc.SensitiveStr, decryptedDoc, nameof(TestDoc.SensitiveStr)); + AssertNullableValueKind(expectedDoc.SensitiveUtf8Escapedřňěščřžýáíé, decryptedDoc, nameof(TestDoc.SensitiveUtf8Escapedřňěščřžýáíé)); Assert.AreEqual(expectedDoc.SensitiveInt, decryptedDoc[nameof(TestDoc.SensitiveInt)].GetValue()); Assert.IsNull(decryptedDoc[Constants.EncryptedInfo]); diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestCommon.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestCommon.cs index 4e36dc899a..6edd6cf0c4 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestCommon.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestCommon.cs @@ -64,7 +64,7 @@ internal static T FromStream(Stream stream) internal class TestDoc { - public static List PathsToEncrypt { get; } = new List() { "/SensitiveStr", "/SensitiveInt", "/SensitiveArr", "/SensitiveDict" }; + public static List PathsToEncrypt { get; } = new List() { "/SensitiveStr", "/SensitiveInt", "/SensitiveArr", "/SensitiveDict", "/SensitiveUtf8Escapedřňěščřžýáíé" }; [JsonProperty("id")] public string Id { get; set; } @@ -75,6 +75,8 @@ internal class TestDoc public string SensitiveStr { get; set; } + public string SensitiveUtf8Escapedřňěščřžýáíé { get; set; } + public int SensitiveInt { get; set; } public string[] SensitiveArr { get; set; } @@ -93,21 +95,14 @@ public override bool Equals(object obj) && this.NonSensitive == doc.NonSensitive && this.SensitiveInt == doc.SensitiveInt && this.SensitiveStr == doc.SensitiveStr + && this.SensitiveUtf8Escapedřňěščřžýáíé == doc.SensitiveUtf8Escapedřňěščřžýáíé && this.SensitiveArr?.Equals(doc.SensitiveArr) == true && this.SensitiveDict?.Equals(doc.SensitiveDict) == true; } public override int GetHashCode() { - int hashCode = 1652434776; - hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.Id); - hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.PK); - hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.NonSensitive); - hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.SensitiveStr); - hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.SensitiveInt); - hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.SensitiveArr); - hashCode = (hashCode * -1521134295) + EqualityComparer>.Default.GetHashCode(this.SensitiveDict); - return hashCode; + return HashCode.Combine(this.Id, this.PK, this.NonSensitive, this.SensitiveStr, this.SensitiveUtf8Escapedřňěščřžýáíé, this.SensitiveInt, this.SensitiveArr, this.SensitiveDict); } public static TestDoc Create(string partitionKey = null) @@ -118,6 +113,7 @@ public static TestDoc Create(string partitionKey = null) PK = partitionKey ?? Guid.NewGuid().ToString(), NonSensitive = Guid.NewGuid().ToString(), SensitiveStr = Guid.NewGuid().ToString(), + SensitiveUtf8Escapedřňěščřžýáíé = "řňěščřžýáíéこぼれたお茶", SensitiveInt = new Random().Next(), SensitiveArr = new string[] { From 1095e1c646b4ba58e0a02ccda05dcf3159006bec Mon Sep 17 00:00:00 2001 From: Jan Hyka Date: Wed, 6 Nov 2024 21:08:41 +0100 Subject: [PATCH 2/2] + benchmark --- .../Readme.md | 104 +++++++++--------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Readme.md b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Readme.md index 691dbf0a5d..326ee9b710 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Readme.md +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Readme.md @@ -1,7 +1,7 @@ ``` ini -BenchmarkDotNet=v0.13.3, OS=Windows 11 (10.0.26100.2033) -11th Gen Intel Core i9-11950H 2.60GHz, 1 CPU, 16 logical and 8 physical cores +BenchmarkDotNet=v0.13.3, OS=Windows 11 (10.0.22631.4317) +Intel Core i7-8700 CPU 3.20GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores .NET SDK=8.0.403 [Host] : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 @@ -9,53 +9,53 @@ Job=MediumRun Toolchain=InProcessEmitToolchain IterationCount=15 LaunchCount=2 WarmupCount=10 ``` -| Method | DocumentSizeInKb | CompressionAlgorithm | JsonProcessor | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated | -|------------------------ |----------------- |--------------------- |--------------- |------------:|----------:|----------:|------------:|--------:|--------:|--------:|----------:| -| **Encrypt** | **1** | **None** | **Newtonsoft** | **22.53 μs** | **0.511 μs** | **0.733 μs** | **22.29 μs** | **0.1526** | **0.0305** | **-** | **41784 B** | -| EncryptToProvidedStream | 1 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 1 | None | Newtonsoft | 26.31 μs | 0.224 μs | 0.322 μs | 26.23 μs | 0.1526 | 0.0305 | - | 41440 B | -| DecryptToProvidedStream | 1 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **1** | **None** | **Stream** | **12.85 μs** | **0.095 μs** | **0.143 μs** | **12.84 μs** | **0.0610** | **0.0153** | **-** | **17528 B** | -| EncryptToProvidedStream | 1 | None | Stream | 13.00 μs | 0.096 μs | 0.141 μs | 12.98 μs | 0.0458 | 0.0153 | - | 11392 B | -| Decrypt | 1 | None | Stream | 13.01 μs | 0.152 μs | 0.228 μs | 13.05 μs | 0.0458 | 0.0153 | - | 12672 B | -| DecryptToProvidedStream | 1 | None | Stream | 13.48 μs | 0.132 μs | 0.197 μs | 13.45 μs | 0.0458 | 0.0153 | - | 11504 B | -| **Encrypt** | **1** | **Brotli** | **Newtonsoft** | **27.94 μs** | **0.226 μs** | **0.338 μs** | **27.96 μs** | **0.1526** | **0.0305** | **-** | **38064 B** | -| EncryptToProvidedStream | 1 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 1 | Brotli | Newtonsoft | 33.49 μs | 0.910 μs | 1.335 μs | 33.99 μs | 0.1221 | - | - | 41064 B | -| DecryptToProvidedStream | 1 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **1** | **Brotli** | **Stream** | **21.15 μs** | **1.037 μs** | **1.521 μs** | **20.52 μs** | **0.0610** | **0.0305** | **-** | **16584 B** | -| EncryptToProvidedStream | 1 | Brotli | Stream | 20.57 μs | 0.213 μs | 0.292 μs | 20.57 μs | 0.0305 | - | - | 11672 B | -| Decrypt | 1 | Brotli | Stream | 21.14 μs | 2.212 μs | 3.311 μs | 19.46 μs | 0.0305 | - | - | 13216 B | -| DecryptToProvidedStream | 1 | Brotli | Stream | 19.60 μs | 0.439 μs | 0.600 μs | 19.52 μs | 0.0305 | - | - | 12048 B | -| **Encrypt** | **10** | **None** | **Newtonsoft** | **84.82 μs** | **3.002 μs** | **4.208 μs** | **83.32 μs** | **0.6104** | **0.1221** | **-** | **170993 B** | -| EncryptToProvidedStream | 10 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 10 | None | Newtonsoft | 112.98 μs | 15.294 μs | 21.934 μs | 100.38 μs | 0.6104 | 0.1221 | - | 157425 B | -| DecryptToProvidedStream | 10 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **10** | **None** | **Stream** | **39.63 μs** | **0.658 μs** | **0.923 μs** | **39.41 μs** | **0.3052** | **0.0610** | **-** | **82928 B** | -| EncryptToProvidedStream | 10 | None | Stream | 36.59 μs | 0.272 μs | 0.399 μs | 36.57 μs | 0.1221 | - | - | 37048 B | -| Decrypt | 10 | None | Stream | 28.64 μs | 0.378 μs | 0.517 μs | 28.59 μs | 0.1221 | 0.0305 | - | 29520 B | -| DecryptToProvidedStream | 10 | None | Stream | 27.61 μs | 0.237 μs | 0.332 μs | 27.64 μs | 0.0610 | 0.0305 | - | 18416 B | -| **Encrypt** | **10** | **Brotli** | **Newtonsoft** | **115.28 μs** | **3.336 μs** | **4.677 μs** | **113.71 μs** | **0.6104** | **0.1221** | **-** | **168065 B** | -| EncryptToProvidedStream | 10 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 10 | Brotli | Newtonsoft | 118.98 μs | 1.530 μs | 2.195 μs | 118.76 μs | 0.4883 | - | - | 144849 B | -| DecryptToProvidedStream | 10 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **10** | **Brotli** | **Stream** | **90.10 μs** | **3.136 μs** | **4.693 μs** | **88.92 μs** | **0.2441** | **-** | **-** | **63809 B** | -| EncryptToProvidedStream | 10 | Brotli | Stream | 97.27 μs | 1.885 μs | 2.703 μs | 97.35 μs | 0.1221 | - | - | 32465 B | -| Decrypt | 10 | Brotli | Stream | 58.48 μs | 0.956 μs | 1.372 μs | 58.59 μs | 0.1221 | 0.0610 | - | 30064 B | -| DecryptToProvidedStream | 10 | Brotli | Stream | 59.12 μs | 1.160 μs | 1.664 μs | 59.14 μs | 0.0610 | - | - | 18960 B | -| **Encrypt** | **100** | **None** | **Newtonsoft** | **1,199.74 μs** | **42.805 μs** | **64.069 μs** | **1,206.48 μs** | **23.4375** | **21.4844** | **21.4844** | **1677978 B** | -| EncryptToProvidedStream | 100 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 100 | None | Newtonsoft | 1,177.48 μs | 25.746 μs | 38.535 μs | 1,172.04 μs | 17.5781 | 15.6250 | 15.6250 | 1260228 B | -| DecryptToProvidedStream | 100 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **100** | **None** | **Stream** | **636.72 μs** | **31.468 μs** | **47.099 μs** | **630.15 μs** | **16.6016** | **16.6016** | **16.6016** | **678066 B** | -| EncryptToProvidedStream | 100 | None | Stream | 383.33 μs | 7.441 μs | 10.671 μs | 384.69 μs | 4.3945 | 4.3945 | 4.3945 | 230133 B | -| Decrypt | 100 | None | Stream | 384.93 μs | 12.519 μs | 18.738 μs | 383.59 μs | 5.8594 | 5.8594 | 5.8594 | 230753 B | -| DecryptToProvidedStream | 100 | None | Stream | 295.19 μs | 7.094 μs | 10.618 μs | 296.11 μs | 3.4180 | 3.4180 | 3.4180 | 119116 B | -| **Encrypt** | **100** | **Brotli** | **Newtonsoft** | **1,178.06 μs** | **63.246 μs** | **94.664 μs** | **1,152.03 μs** | **13.6719** | **11.7188** | **9.7656** | **1379183 B** | -| EncryptToProvidedStream | 100 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 100 | Brotli | Newtonsoft | 1,175.01 μs | 41.917 μs | 61.441 μs | 1,156.01 μs | 11.7188 | 9.7656 | 9.7656 | 1124274 B | -| DecryptToProvidedStream | 100 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **100** | **Brotli** | **Stream** | **757.11 μs** | **19.549 μs** | **29.260 μs** | **754.55 μs** | **10.7422** | **10.7422** | **10.7422** | **479493 B** | -| EncryptToProvidedStream | 100 | Brotli | Stream | 563.46 μs | 9.960 μs | 14.284 μs | 561.60 μs | 2.9297 | 2.9297 | 2.9297 | 180637 B | -| Decrypt | 100 | Brotli | Stream | 542.34 μs | 14.514 μs | 21.724 μs | 542.04 μs | 6.8359 | 6.8359 | 6.8359 | 231162 B | -| DecryptToProvidedStream | 100 | Brotli | Stream | 463.69 μs | 9.130 μs | 12.800 μs | 460.71 μs | 3.4180 | 3.4180 | 3.4180 | 119506 B | +| Method | DocumentSizeInKb | CompressionAlgorithm | JsonProcessor | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|------------------------ |----------------- |--------------------- |-------------- |------------:|----------:|----------:|--------:|--------:|--------:|----------:| +| **Encrypt** | **1** | **None** | **Newtonsoft** | **37.85 μs** | **1.110 μs** | **1.627 μs** | **0.4272** | **0.0610** | **-** | **42264 B** | +| EncryptToProvidedStream | 1 | None | Newtonsoft | NA | NA | NA | - | - | - | - | +| Decrypt | 1 | None | Newtonsoft | 44.66 μs | 0.441 μs | 0.619 μs | 0.4272 | 0.0610 | - | 41824 B | +| DecryptToProvidedStream | 1 | None | Newtonsoft | NA | NA | NA | - | - | - | - | +| **Encrypt** | **1** | **None** | **Stream** | **22.58 μs** | **0.125 μs** | **0.175 μs** | **0.1831** | **0.0305** | **-** | **16960 B** | +| EncryptToProvidedStream | 1 | None | Stream | 22.38 μs | 0.184 μs | 0.263 μs | 0.0916 | 0.0305 | - | 10824 B | +| Decrypt | 1 | None | Stream | 22.91 μs | 0.262 μs | 0.384 μs | 0.1221 | 0.0305 | - | 12672 B | +| DecryptToProvidedStream | 1 | None | Stream | 23.72 μs | 0.164 μs | 0.230 μs | 0.1221 | 0.0305 | - | 11504 B | +| **Encrypt** | **1** | **Brotli** | **Newtonsoft** | **43.64 μs** | **0.205 μs** | **0.294 μs** | **0.3662** | **0.0610** | **-** | **38560 B** | +| EncryptToProvidedStream | 1 | Brotli | Newtonsoft | NA | NA | NA | - | - | - | - | +| Decrypt | 1 | Brotli | Newtonsoft | 53.46 μs | 0.820 μs | 1.177 μs | 0.4272 | 0.0610 | - | 41465 B | +| DecryptToProvidedStream | 1 | Brotli | Newtonsoft | NA | NA | NA | - | - | - | - | +| **Encrypt** | **1** | **Brotli** | **Stream** | **32.38 μs** | **1.145 μs** | **1.713 μs** | **0.1221** | **-** | **-** | **16016 B** | +| EncryptToProvidedStream | 1 | Brotli | Stream | 31.38 μs | 0.300 μs | 0.440 μs | 0.0610 | - | - | 11104 B | +| Decrypt | 1 | Brotli | Stream | 29.75 μs | 0.623 μs | 0.933 μs | 0.1221 | 0.0610 | - | 13104 B | +| DecryptToProvidedStream | 1 | Brotli | Stream | 31.54 μs | 0.848 μs | 1.243 μs | 0.1221 | 0.0610 | - | 11936 B | +| **Encrypt** | **10** | **None** | **Newtonsoft** | **150.68 μs** | **1.072 μs** | **1.572 μs** | **1.7090** | **0.2441** | **-** | **172946 B** | +| EncryptToProvidedStream | 10 | None | Newtonsoft | NA | NA | NA | - | - | - | - | +| Decrypt | 10 | None | Newtonsoft | 175.71 μs | 0.510 μs | 0.715 μs | 1.7090 | 0.2441 | - | 159282 B | +| DecryptToProvidedStream | 10 | None | Newtonsoft | NA | NA | NA | - | - | - | - | +| **Encrypt** | **10** | **None** | **Stream** | **84.55 μs** | **0.921 μs** | **1.290 μs** | **0.7324** | **0.1221** | **-** | **76473 B** | +| EncryptToProvidedStream | 10 | None | Stream | 80.35 μs | 0.445 μs | 0.624 μs | 0.2441 | - | - | 30592 B | +| Decrypt | 10 | None | Stream | 73.96 μs | 0.895 μs | 1.340 μs | 0.2441 | - | - | 29521 B | +| DecryptToProvidedStream | 10 | None | Stream | 73.28 μs | 0.410 μs | 0.587 μs | 0.1221 | - | - | 18417 B | +| **Encrypt** | **10** | **Brotli** | **Newtonsoft** | **176.59 μs** | **0.989 μs** | **1.387 μs** | **1.7090** | **0.2441** | **-** | **170034 B** | +| EncryptToProvidedStream | 10 | Brotli | Newtonsoft | NA | NA | NA | - | - | - | - | +| Decrypt | 10 | Brotli | Newtonsoft | 188.99 μs | 1.414 μs | 2.028 μs | 1.4648 | 0.2441 | - | 146722 B | +| DecryptToProvidedStream | 10 | Brotli | Newtonsoft | NA | NA | NA | - | - | - | - | +| **Encrypt** | **10** | **Brotli** | **Stream** | **119.56 μs** | **2.676 μs** | **4.005 μs** | **0.6104** | **0.1221** | **-** | **57353 B** | +| EncryptToProvidedStream | 10 | Brotli | Stream | 135.75 μs | 2.564 μs | 3.758 μs | 0.2441 | - | - | 26009 B | +| Decrypt | 10 | Brotli | Stream | 94.95 μs | 0.886 μs | 1.299 μs | 0.2441 | - | - | 29953 B | +| DecryptToProvidedStream | 10 | Brotli | Stream | 96.03 μs | 0.990 μs | 1.388 μs | 0.1221 | - | - | 18849 B | +| **Encrypt** | **100** | **None** | **Newtonsoft** | **1,840.46 μs** | **32.654 μs** | **48.875 μs** | **37.1094** | **35.1563** | **29.2969** | **1694760 B** | +| EncryptToProvidedStream | 100 | None | Newtonsoft | NA | NA | NA | - | - | - | - | +| Decrypt | 100 | None | Newtonsoft | 1,890.28 μs | 42.505 μs | 62.303 μs | 29.2969 | 21.4844 | 21.4844 | 1276883 B | +| DecryptToProvidedStream | 100 | None | Newtonsoft | NA | NA | NA | - | - | - | - | +| **Encrypt** | **100** | **None** | **Stream** | **1,087.73 μs** | **13.689 μs** | **19.632 μs** | **23.4375** | **23.4375** | **23.4375** | **612646 B** | +| EncryptToProvidedStream | 100 | None | Stream | 740.73 μs | 5.711 μs | 8.190 μs | 5.8594 | 5.8594 | 5.8594 | 164676 B | +| Decrypt | 100 | None | Stream | 788.32 μs | 9.755 μs | 13.990 μs | 9.7656 | 9.7656 | 9.7656 | 230802 B | +| DecryptToProvidedStream | 100 | None | Stream | 708.46 μs | 8.961 μs | 13.135 μs | 4.8828 | 4.8828 | 4.8828 | 119133 B | +| **Encrypt** | **100** | **Brotli** | **Newtonsoft** | **1,651.60 μs** | **24.139 μs** | **34.620 μs** | **23.4375** | **19.5313** | **13.6719** | **1395951 B** | +| EncryptToProvidedStream | 100 | Brotli | Newtonsoft | NA | NA | NA | - | - | - | - | +| Decrypt | 100 | Brotli | Newtonsoft | 1,726.48 μs | 44.020 μs | 63.132 μs | 21.4844 | 19.5313 | 13.6719 | 1140913 B | +| DecryptToProvidedStream | 100 | Brotli | Newtonsoft | NA | NA | NA | - | - | - | - | +| **Encrypt** | **100** | **Brotli** | **Stream** | **1,058.70 μs** | **11.463 μs** | **16.803 μs** | **15.6250** | **15.6250** | **15.6250** | **414066 B** | +| EncryptToProvidedStream | 100 | Brotli | Stream | 915.29 μs | 25.105 μs | 37.576 μs | 3.9063 | 3.9063 | 3.9063 | 115194 B | +| Decrypt | 100 | Brotli | Stream | 913.38 μs | 19.519 μs | 27.993 μs | 8.7891 | 8.7891 | 8.7891 | 231061 B | +| DecryptToProvidedStream | 100 | Brotli | Stream | 808.23 μs | 12.405 μs | 18.568 μs | 4.8828 | 4.8828 | 4.8828 | 119414 B |