Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Client encryption]: Fixes handling of escaped utf8 strings in StreamProcessor #4871

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -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<byte> UnescapeValue(ref Utf8JsonReader reader, ArrayPoolManager arrayPoolManager)
{
if (!reader.ValueIsEscaped)
{
return reader.ValueSpan;
}

Span<byte> bytes = arrayPoolManager.Rent(reader.ValueSpan.Length);
int size = reader.CopyString(bytes);

return bytes[..size];
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -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<DecryptionContext> DecryptStreamAsync(
Stream inputStream,
Expand Down Expand Up @@ -110,7 +100,7 @@ long TransformDecryptBuffer(ReadOnlySpan<byte> buffer)
{
JsonTokenType tokenType = reader.TokenType;

if (isIgnoredBlock && reader.CurrentDepth == 1 && tokenType == JsonTokenType.EndObject)
if (isIgnoredBlock && reader.CurrentDepth == ControlledElementsDepth && tokenType == JsonTokenType.EndObject)
{
isIgnoredBlock = false;
continue;
Expand All @@ -125,7 +115,7 @@ long TransformDecryptBuffer(ReadOnlySpan<byte> buffer)
case JsonTokenType.String:
if (decryptPropertyName == null)
{
writer.WriteStringValue(reader.ValueSpan);
writer.WriteStringValue(UnescapeValue(ref reader, arrayPoolManager));
}
else
{
Expand Down Expand Up @@ -160,22 +150,26 @@ long TransformDecryptBuffer(ReadOnlySpan<byte> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,19 @@ long TransformEncryptBuffer(ReadOnlySpan<byte> 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)
Expand All @@ -201,7 +204,7 @@ long TransformEncryptBuffer(ReadOnlySpan<byte> buffer)
}
else
{
currentWriter.WriteStringValue(reader.ValueSpan);
currentWriter.WriteStringValue(UnescapeValue(ref reader, arrayPoolManager));
}

break;
Expand Down
Loading