| | 1 | | // Copyright (c) Microsoft Corporation. All rights reserved. |
| | 2 | | // Licensed under the MIT License. |
| | 3 | |
|
| | 4 | | using System; |
| | 5 | | using System.Collections.Generic; |
| | 6 | | using System.Text; |
| | 7 | | using System.Text.Json; |
| | 8 | |
|
| | 9 | | namespace Azure.Storage.Cryptography.Models |
| | 10 | | { |
| | 11 | | internal static class EncryptionDataSerializer |
| | 12 | | { |
| | 13 | | private const string EncryptionAgent_EncryptionVersionName = "Protocol"; |
| | 14 | |
|
| | 15 | | #region Serialize |
| | 16 | |
|
| | 17 | | /// <summary> |
| | 18 | | /// Serializes an EncryptionData instance into JSON. |
| | 19 | | /// </summary> |
| | 20 | | /// <param name="data">Data to serialize.</param> |
| | 21 | | /// <returns>The JSON string.</returns> |
| | 22 | | public static string Serialize(EncryptionData data) |
| | 23 | | { |
| 0 | 24 | | return Encoding.UTF8.GetString(SerializeEncryptionData(data).ToArray()); |
| | 25 | | } |
| | 26 | |
|
| | 27 | | /// <summary> |
| | 28 | | /// Serializes an EncryptionData instance into JSON. |
| | 29 | | /// </summary> |
| | 30 | | /// <param name="data">Data to serialize.</param> |
| | 31 | | /// <returns>The JSON UTF8 bytes.</returns> |
| | 32 | | private static ReadOnlyMemory<byte> SerializeEncryptionData(EncryptionData data) |
| | 33 | | { |
| 0 | 34 | | var writer = new Core.ArrayBufferWriter<byte>(); |
| 0 | 35 | | using var json = new Utf8JsonWriter(writer); |
| | 36 | |
|
| 0 | 37 | | json.WriteStartObject(); |
| 0 | 38 | | WriteEncryptionData(json, data); |
| 0 | 39 | | json.WriteEndObject(); |
| | 40 | |
|
| 0 | 41 | | json.Flush(); |
| 0 | 42 | | return writer.WrittenMemory; |
| 0 | 43 | | } |
| | 44 | |
|
| | 45 | | /// <summary> |
| | 46 | | /// Serializes an EncryptionData instance into JSON and writes it to the given JSON writer. |
| | 47 | | /// </summary> |
| | 48 | | /// <param name="json">The writer to write the serialization to.</param> |
| | 49 | | /// <param name="data">Data to serialize.</param> |
| | 50 | | public static void WriteEncryptionData(Utf8JsonWriter json, EncryptionData data) |
| | 51 | | { |
| 6 | 52 | | json.WriteString(nameof(data.EncryptionMode), data.EncryptionMode); |
| | 53 | |
|
| 6 | 54 | | json.WriteStartObject(nameof(data.WrappedContentKey)); |
| 6 | 55 | | WriteWrappedKey(json, data.WrappedContentKey); |
| 6 | 56 | | json.WriteEndObject(); |
| | 57 | |
|
| 6 | 58 | | json.WriteStartObject(nameof(data.EncryptionAgent)); |
| 6 | 59 | | WriteEncryptionAgent(json, data.EncryptionAgent); |
| 6 | 60 | | json.WriteEndObject(); |
| | 61 | |
|
| 6 | 62 | | json.WriteString(nameof(data.ContentEncryptionIV), Convert.ToBase64String(data.ContentEncryptionIV)); |
| | 63 | |
|
| 6 | 64 | | json.WriteStartObject(nameof(data.KeyWrappingMetadata)); |
| 6 | 65 | | WriteDictionary(json, data.KeyWrappingMetadata); |
| 6 | 66 | | json.WriteEndObject(); |
| 6 | 67 | | } |
| | 68 | |
|
| | 69 | | private static void WriteWrappedKey(Utf8JsonWriter json, KeyEnvelope key) |
| | 70 | | { |
| 6 | 71 | | json.WriteString(nameof(key.KeyId), key.KeyId); |
| 6 | 72 | | json.WriteString(nameof(key.EncryptedKey), Convert.ToBase64String(key.EncryptedKey)); |
| 6 | 73 | | json.WriteString(nameof(key.Algorithm), key.Algorithm); |
| 6 | 74 | | } |
| | 75 | |
|
| | 76 | | private static void WriteEncryptionAgent(Utf8JsonWriter json, EncryptionAgent encryptionAgent) |
| | 77 | | { |
| 6 | 78 | | json.WriteString(EncryptionAgent_EncryptionVersionName, encryptionAgent.EncryptionVersion.Serialize()); |
| 6 | 79 | | json.WriteString(nameof(encryptionAgent.EncryptionAlgorithm), encryptionAgent.EncryptionAlgorithm.ToString() |
| 6 | 80 | | } |
| | 81 | |
|
| | 82 | | private static void WriteDictionary(Utf8JsonWriter json, IDictionary<string, string> dictionary) |
| | 83 | | { |
| 24 | 84 | | foreach (var entry in dictionary) |
| | 85 | | { |
| 6 | 86 | | json.WriteString(entry.Key, entry.Value); |
| | 87 | | } |
| 6 | 88 | | } |
| | 89 | | #endregion |
| | 90 | |
|
| | 91 | | #region Deserialize |
| | 92 | | /// <summary> |
| | 93 | | /// Deserializes an EncryptionData instance from JSON. |
| | 94 | | /// </summary> |
| | 95 | | /// <param name="serializedData">The serialized data string.</param> |
| | 96 | | /// <returns>The instance.</returns> |
| | 97 | | public static EncryptionData Deserialize(string serializedData) |
| | 98 | | { |
| 0 | 99 | | var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(serializedData)); |
| 0 | 100 | | return DeserializeEncryptionData(ref reader); |
| | 101 | | } |
| | 102 | |
|
| | 103 | | /// <summary> |
| | 104 | | /// Reads an EncryptionData instance from a JSON reader. |
| | 105 | | /// </summary> |
| | 106 | | /// <param name="reader">The reader to parse an EncryptionData isntance from.</param> |
| | 107 | | /// <returns>The instance.</returns> |
| | 108 | | public static EncryptionData DeserializeEncryptionData(ref Utf8JsonReader reader) |
| | 109 | | { |
| 0 | 110 | | using JsonDocument json = JsonDocument.ParseValue(ref reader); |
| 0 | 111 | | JsonElement root = json.RootElement; |
| 0 | 112 | | return ReadEncryptionData(root); |
| 0 | 113 | | } |
| | 114 | |
|
| | 115 | | /// <summary> |
| | 116 | | /// Reads an EncryptionData instance from a parsed JSON object. |
| | 117 | | /// </summary> |
| | 118 | | /// <param name="root">The JSON object model.</param> |
| | 119 | | /// <returns>The instance.</returns> |
| | 120 | | public static EncryptionData ReadEncryptionData(JsonElement root) |
| | 121 | | { |
| 6 | 122 | | var data = new EncryptionData(); |
| 52 | 123 | | foreach (var property in root.EnumerateObject()) |
| | 124 | | { |
| 20 | 125 | | ReadPropertyValue(data, property); |
| | 126 | | } |
| 6 | 127 | | return data; |
| | 128 | | } |
| | 129 | |
|
| | 130 | | private static void ReadPropertyValue(EncryptionData data, JsonProperty property) |
| | 131 | | { |
| 20 | 132 | | if (property.NameEquals(nameof(data.EncryptionMode))) |
| | 133 | | { |
| 4 | 134 | | data.EncryptionMode = property.Value.GetString(); |
| | 135 | | } |
| 16 | 136 | | else if (property.NameEquals(nameof(data.WrappedContentKey))) |
| | 137 | | { |
| 4 | 138 | | var key = new KeyEnvelope(); |
| 32 | 139 | | foreach (var subProperty in property.Value.EnumerateObject()) |
| | 140 | | { |
| 12 | 141 | | ReadPropertyValue(key, subProperty); |
| | 142 | | } |
| 4 | 143 | | data.WrappedContentKey = key; |
| | 144 | | } |
| 12 | 145 | | else if (property.NameEquals(nameof(data.EncryptionAgent))) |
| | 146 | | { |
| 4 | 147 | | var agent = new EncryptionAgent(); |
| 24 | 148 | | foreach (var subProperty in property.Value.EnumerateObject()) |
| | 149 | | { |
| 8 | 150 | | ReadPropertyValue(agent, subProperty); |
| | 151 | | } |
| 4 | 152 | | data.EncryptionAgent = agent; |
| | 153 | | } |
| 8 | 154 | | else if (property.NameEquals(nameof(data.ContentEncryptionIV))) |
| | 155 | | { |
| 4 | 156 | | data.ContentEncryptionIV = Convert.FromBase64String(property.Value.GetString()); |
| | 157 | | } |
| 4 | 158 | | else if (property.NameEquals(nameof(data.KeyWrappingMetadata))) |
| | 159 | | { |
| 4 | 160 | | var metadata = new Dictionary<string, string>(); |
| 16 | 161 | | foreach (var entry in property.Value.EnumerateObject()) |
| | 162 | | { |
| 4 | 163 | | metadata.Add(entry.Name, entry.Value.GetString()); |
| | 164 | | } |
| 4 | 165 | | data.KeyWrappingMetadata = metadata; |
| | 166 | | } |
| 4 | 167 | | } |
| | 168 | |
|
| | 169 | | private static void ReadPropertyValue(KeyEnvelope key, JsonProperty property) |
| | 170 | | { |
| 12 | 171 | | if (property.NameEquals(nameof(key.Algorithm))) |
| | 172 | | { |
| 4 | 173 | | key.Algorithm = property.Value.GetString(); |
| | 174 | | } |
| 8 | 175 | | else if (property.NameEquals(nameof(key.EncryptedKey))) |
| | 176 | | { |
| 4 | 177 | | key.EncryptedKey = Convert.FromBase64String(property.Value.GetString()); |
| | 178 | | } |
| 4 | 179 | | else if (property.NameEquals(nameof(key.KeyId))) |
| | 180 | | { |
| 4 | 181 | | key.KeyId = property.Value.GetString(); |
| | 182 | | } |
| 4 | 183 | | } |
| | 184 | |
|
| | 185 | | private static void ReadPropertyValue(EncryptionAgent agent, JsonProperty property) |
| | 186 | | { |
| 8 | 187 | | if (property.NameEquals(nameof(agent.EncryptionAlgorithm))) |
| | 188 | | { |
| 4 | 189 | | agent.EncryptionAlgorithm = new ClientSideEncryptionAlgorithm(property.Value.GetString()); |
| | 190 | | } |
| 4 | 191 | | else if (property.NameEquals(EncryptionAgent_EncryptionVersionName)) |
| | 192 | | { |
| 4 | 193 | | agent.EncryptionVersion = property.Value.GetString().ToClientSideEncryptionVersion(); |
| | 194 | | } |
| 4 | 195 | | } |
| | 196 | | #endregion |
| | 197 | | } |
| | 198 | | } |