Example #1
0
        /// <remarks>
        /// If there isn't any PathsToEncrypt, input stream will be returned without any modification.
        /// Else input stream will be disposed, and a new stream is returned.
        /// In case of an exception, input stream won't be disposed, but position will be end of stream.
        /// </remarks>
        public static async Task <Stream> EncryptAsync(
            Stream input,
            Encryptor encryptor,
            EncryptionOptions encryptionOptions,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            Debug.Assert(diagnosticsContext != null);

            if (input == null)
            {
                throw new ArgumentNullException(nameof(input));
            }

            if (encryptor == null)
            {
                throw new ArgumentNullException(nameof(encryptor));
            }

            if (encryptionOptions == null)
            {
                throw new ArgumentNullException(nameof(encryptionOptions));
            }

            if (string.IsNullOrWhiteSpace(encryptionOptions.DataEncryptionKeyId))
            {
                throw new ArgumentNullException(nameof(encryptionOptions.DataEncryptionKeyId));
            }

            if (string.IsNullOrWhiteSpace(encryptionOptions.EncryptionAlgorithm))
            {
                throw new ArgumentNullException(nameof(encryptionOptions.EncryptionAlgorithm));
            }

            if (encryptionOptions.PathsToEncrypt == null)
            {
                throw new ArgumentNullException(nameof(encryptionOptions.PathsToEncrypt));
            }

            if (!encryptionOptions.PathsToEncrypt.Any())
            {
                return(input);
            }

            foreach (string path in encryptionOptions.PathsToEncrypt)
            {
                if (string.IsNullOrWhiteSpace(path) || path[0] != '/' || path.LastIndexOf('/') != 0)
                {
                    throw new ArgumentException($"Invalid path {path ?? string.Empty}", nameof(encryptionOptions.PathsToEncrypt));
                }
            }

            JObject itemJObj = EncryptionProcessor.BaseSerializer.FromStream <JObject>(input);

            JObject toEncryptJObj = new JObject();

            foreach (string pathToEncrypt in encryptionOptions.PathsToEncrypt)
            {
                string propertyName = pathToEncrypt.Substring(1);
                if (!itemJObj.TryGetValue(propertyName, out JToken propertyValue))
                {
                    throw new ArgumentException($"{nameof(encryptionOptions.PathsToEncrypt)} includes a path: '{pathToEncrypt}' which was not found.");
                }

                toEncryptJObj.Add(propertyName, propertyValue.Value <JToken>());
                itemJObj.Remove(propertyName);
            }

            MemoryStream memoryStream = EncryptionProcessor.BaseSerializer.ToStream <JObject>(toEncryptJObj);

            Debug.Assert(memoryStream != null);
            Debug.Assert(memoryStream.TryGetBuffer(out _));
            byte[] plainText = memoryStream.ToArray();

            byte[] cipherText = await encryptor.EncryptAsync(
                plainText,
                encryptionOptions.DataEncryptionKeyId,
                encryptionOptions.EncryptionAlgorithm,
                cancellationToken);

            if (cipherText == null)
            {
                throw new InvalidOperationException($"{nameof(Encryptor)} returned null cipherText from {nameof(EncryptAsync)}.");
            }

            EncryptionProperties encryptionProperties = new EncryptionProperties(
                encryptionFormatVersion: 2,
                encryptionOptions.EncryptionAlgorithm,
                encryptionOptions.DataEncryptionKeyId,
                encryptedData: cipherText);

            itemJObj.Add(Constants.EncryptedInfo, JObject.FromObject(encryptionProperties));
            input.Dispose();
            return(EncryptionProcessor.BaseSerializer.ToStream(itemJObj));
        }
Example #2
0
 /// <summary>
 /// Populates the DecryptableItem that can be used getting the decryption result.
 /// </summary>
 /// <param name="decryptableContent">The encrypted content which is yet to be decrypted.</param>
 /// <param name="encryptor">Encryptor instance which will be used for decryption.</param>
 /// <param name="cosmosSerializer">Serializer instance which will be used for deserializing the content after decryption.</param>
 protected internal abstract void SetDecryptableItem(
     JToken decryptableContent,
     Encryptor encryptor,
     CosmosSerializer cosmosSerializer);
Example #3
0
        /// <remarks>
        /// If there isn't any PathsToEncrypt, input stream will be returned without any modification.
        /// Else input stream will be disposed, and a new stream is returned.
        /// In case of an exception, input stream won't be disposed, but position will be end of stream.
        /// </remarks>
        public static async Task <Stream> EncryptAsync(
            Stream input,
            Encryptor encryptor,
            List <EncryptionOptions> propertyEncryptionOptions,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            Debug.Assert(diagnosticsContext != null);

            if (input == null)
            {
                throw new ArgumentNullException(nameof(input));
            }

            if (encryptor == null)
            {
                throw new ArgumentNullException(nameof(encryptor));
            }

            JObject itemJObj = EncryptionProcessor.BaseSerializer.FromStream <JObject>(input);

            foreach (EncryptionOptions encryptionOptions in propertyEncryptionOptions)
            {
                if (encryptionOptions == null)
                {
                    throw new ArgumentNullException(nameof(encryptionOptions));
                }

                if (string.IsNullOrWhiteSpace(encryptionOptions.DataEncryptionKeyId))
                {
                    throw new ArgumentNullException(nameof(encryptionOptions.DataEncryptionKeyId));
                }

                if (string.IsNullOrWhiteSpace(encryptionOptions.EncryptionAlgorithm))
                {
                    throw new ArgumentNullException(nameof(encryptionOptions.EncryptionAlgorithm));
                }

                if (encryptionOptions.PathsToEncrypt == null)
                {
                    throw new ArgumentNullException(nameof(encryptionOptions.PathsToEncrypt));
                }

                if (!encryptionOptions.PathsToEncrypt.Any())
                {
                    return(input);
                }

                foreach (string path in encryptionOptions.PathsToEncrypt)
                {
                    if (string.IsNullOrWhiteSpace(path) || path[0] != '/' || path.LastIndexOf('/') != 0)
                    {
                        throw new ArgumentException($"Invalid path {path ?? string.Empty}", nameof(encryptionOptions.PathsToEncrypt));
                    }
                }

                if (itemJObj != null)
                {
                    foreach (string pathToEncrypt in encryptionOptions.PathsToEncrypt)
                    {
                        string propertyName = pathToEncrypt.Substring(1);
                        if (!itemJObj.TryGetValue(propertyName, out JToken propertyValue))
                        {
                            throw new ArgumentException($"{nameof(encryptionOptions.PathsToEncrypt)} includes a path: '{pathToEncrypt}' which was not found.");
                        }

                        string value     = propertyValue.Value <string>();
                        byte[] plainText = System.Text.Encoding.UTF8.GetBytes(value);

                        byte[] cipherText = await encryptor.EncryptAsync(
                            plainText,
                            encryptionOptions.DataEncryptionKeyId,
                            encryptionOptions.EncryptionAlgorithm,
                            cancellationToken);

                        if (cipherText == null)
                        {
                            throw new InvalidOperationException($"{nameof(Encryptor)} returned null cipherText from {nameof(EncryptAsync)}.");
                        }

                        itemJObj[propertyName] = cipherText;
                    }
                }
            }

            input.Dispose();
            return(EncryptionProcessor.BaseSerializer.ToStream(itemJObj));
        }
        /// <remarks>
        /// If there isn't any data that needs to be decrypted, input stream will be returned without any modification.
        /// Else input stream will be disposed, and a new stream is returned.
        /// In case of an exception, input stream won't be disposed, but position will be end of stream.
        /// </remarks>
        public static async Task <(Stream, DecryptionContext)> DecryptAsync(
            Stream input,
            Encryptor encryptor,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            if (input == null)
            {
                return(input, null);
            }

            Debug.Assert(input.CanSeek);
            Debug.Assert(encryptor != null);
            Debug.Assert(diagnosticsContext != null);

            JObject itemJObj;

            using (StreamReader sr = new StreamReader(input, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true))
                using (JsonTextReader jsonTextReader = new JsonTextReader(sr))
                {
                    itemJObj = JsonSerializer.Create().Deserialize <JObject>(jsonTextReader);
                }

            JProperty encryptionPropertiesJProp = itemJObj.Property(Constants.EncryptedInfo);
            JObject   encryptionPropertiesJObj  = null;

            if (encryptionPropertiesJProp != null && encryptionPropertiesJProp.Value != null && encryptionPropertiesJProp.Value.Type == JTokenType.Object)
            {
                encryptionPropertiesJObj = (JObject)encryptionPropertiesJProp.Value;
            }

            if (encryptionPropertiesJObj == null)
            {
                input.Position = 0;
                return(input, null);
            }

            EncryptionProperties encryptionProperties = encryptionPropertiesJObj.ToObject <EncryptionProperties>();

            JObject plainTextJObj = await EncryptionProcessor.DecryptContentAsync(
                encryptionProperties,
                encryptor,
                diagnosticsContext,
                cancellationToken);

            List <string> pathsDecrypted = new List <string>();

            foreach (JProperty property in plainTextJObj.Properties())
            {
                itemJObj.Add(property.Name, property.Value);
                pathsDecrypted.Add("/" + property.Name);
            }

            DecryptionInfo decryptionInfo = new DecryptionInfo(
                pathsDecrypted,
                encryptionProperties.DataEncryptionKeyId);

            DecryptionContext decryptionContext = new DecryptionContext(
                new List <DecryptionInfo>()
            {
                decryptionInfo
            });

            itemJObj.Remove(Constants.EncryptedInfo);
            input.Dispose();
            return(EncryptionProcessor.BaseSerializer.ToStream(itemJObj), decryptionContext);
        }
Example #5
0
        /// <remarks>
        /// If there isn't any data that needs to be decrypted, input stream will be returned without any modification.
        /// Else input stream will be disposed, and a new stream is returned.
        /// In case of an exception, input stream won't be disposed, but position will be end of stream.
        /// </remarks>
        public static async Task <Stream> DecryptAsync(
            Stream input,
            Encryptor encryptor,
            CosmosDiagnosticsContext diagnosticsContext,
            IReadOnlyDictionary <List <string>, string> pathsToEncrypt,
            CancellationToken cancellationToken)
        {
            Debug.Assert(input != null);
            Debug.Assert(input.CanSeek);
            Debug.Assert(encryptor != null);
            Debug.Assert(diagnosticsContext != null);

            JObject itemJObj;

            using (StreamReader sr = new StreamReader(input, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true))
            {
                using JsonTextReader jsonTextReader = new JsonTextReader(sr);
                itemJObj = JsonSerializer.Create().Deserialize <JObject>(jsonTextReader);
            }

            if (pathsToEncrypt != null)
            {
                foreach (List <string> paths in pathsToEncrypt.Keys)
                {
                    foreach (string path in paths)
                    {
                        if (itemJObj.TryGetValue(path.Substring(1), out JToken propertyValue))
                        {
                            EncryptionProperties encryptionProperties = new EncryptionProperties(
                                encryptionFormatVersion: 2,
                                CosmosEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256,
                                pathsToEncrypt[paths],
                                propertyValue.ToObject <byte[]>(),
                                path);

                            JObject propPlainTextJObj = await PropertyEncryptionProcessor.DecryptContentAsync(
                                encryptionProperties,
                                encryptor,
                                diagnosticsContext,
                                cancellationToken);

                            foreach (JProperty property in propPlainTextJObj.Properties())
                            {
                                itemJObj[property.Name] = property.Value;
                            }
                        }
                    }

                    input.Dispose();
                }
            }

            JToken token = itemJObj[Constants.EncryptedInfo];

            if (token != null)
            {
                JProperty encryptionPropertiesJProp = itemJObj.Property(Constants.EncryptedInfo);
                JObject   encryptionPropertiesJObj  = null;
                if (encryptionPropertiesJProp != null && encryptionPropertiesJProp.Value != null && encryptionPropertiesJProp.Value.Type == JTokenType.Object)
                {
                    encryptionPropertiesJObj = (JObject)encryptionPropertiesJProp.Value;
                }

                if (encryptionPropertiesJObj == null)
                {
                    input.Position = 0;
                    return(input);
                }

                EncryptionProperties encryptionProperties = encryptionPropertiesJObj.ToObject <EncryptionProperties>();

                JObject plainTextJObj = await PropertyEncryptionProcessor.DecryptContentAsync(
                    encryptionProperties,
                    encryptor,
                    diagnosticsContext,
                    cancellationToken);

                foreach (JProperty property in plainTextJObj.Properties())
                {
                    itemJObj.Add(property.Name, property.Value);
                }

                itemJObj.Remove(Constants.EncryptedInfo);
                input.Dispose();
            }

            return(EncryptionProcessor.BaseSerializer.ToStream(itemJObj));
        }