7fd2884af449e5afb751eaa14d443e397ae686a3
[platform/upstream/dotnet/runtime.git] /
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 using System.Runtime.Serialization.Formatters.Binary;
5 using System.Runtime.Serialization;
6 using System.Collections;
7 using System.IO;
8 using System.Diagnostics;
9 using System.Diagnostics.CodeAnalysis;
10
11 namespace System.ComponentModel.Design
12 {
13     /// <summary>
14     /// Provides support for design-time license context serialization.
15     /// </summary>
16     public class DesigntimeLicenseContextSerializer
17     {
18         internal const byte BinaryWriterMagic = 255;
19
20         private static bool EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization { get; } = AppContext.TryGetSwitch("System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization", out bool isEnabled) ? isEnabled : false;
21
22         // Not creatable.
23         private DesigntimeLicenseContextSerializer()
24         {
25         }
26
27         /// <summary>
28         /// Serializes the licenses within the specified design-time license context
29         /// using the specified key and output stream.
30         /// </summary>
31         public static void Serialize(Stream o, string cryptoKey, DesigntimeLicenseContext context)
32         {
33             if (EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization)
34             {
35                 SerializeWithBinaryFormatter(o, cryptoKey, context);
36             }
37             else
38             {
39                 using (BinaryWriter writer = new BinaryWriter(o, encoding: Text.Encoding.UTF8, leaveOpen: true))
40                 {
41                     writer.Write(BinaryWriterMagic); // flag to identify BinaryWriter
42                     writer.Write(cryptoKey);
43                     writer.Write(context._savedLicenseKeys.Count);
44                     foreach (DictionaryEntry keyAndValue in context._savedLicenseKeys)
45                     {
46                         writer.Write(keyAndValue.Key.ToString()!);
47                         writer.Write(keyAndValue.Value!.ToString()!);
48                     }
49                 }
50             }
51         }
52
53         private static void SerializeWithBinaryFormatter(Stream o, string cryptoKey, DesigntimeLicenseContext context)
54         {
55             IFormatter formatter = new BinaryFormatter();
56 #pragma warning disable SYSLIB0011
57 #pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
58             formatter.Serialize(o, new object[] { cryptoKey, context._savedLicenseKeys });
59 #pragma warning restore IL2026
60 #pragma warning restore SYSLIB0011
61         }
62
63         private sealed class StreamWrapper : Stream
64         {
65             private Stream _stream;
66             private bool _readFirstByte;
67             internal byte _firstByte;
68             public StreamWrapper(Stream stream)
69             {
70                 _stream = stream;
71                 _readFirstByte = false;
72                 _firstByte = 0;
73             }
74
75             public override bool CanRead => _stream.CanRead;
76
77             public override bool CanSeek => _stream.CanSeek;
78
79             public override bool CanWrite => _stream.CanWrite;
80
81             public override long Length => _stream.Length;
82
83             public override long Position { get => _stream.Position; set => _stream.Position = value; }
84
85             public override void Flush() => _stream.Flush();
86
87             public override int Read(byte[] buffer, int offset, int count) =>
88                 Read(new Span<byte>(buffer, offset, count));
89
90             public override int Read(Span<byte> buffer)
91             {
92                 Debug.Assert(_stream.Position != 0, "Expected the first byte to be read first");
93                 if (_stream.Position == 1)
94                 {
95                     Debug.Assert(_readFirstByte);
96                     // Add the first byte read by ReadByte into buffer here
97                     buffer[0] = _firstByte;
98                     return _stream.Read(buffer.Slice(1)) + 1;
99                 }
100                 return _stream.Read(buffer);
101             }
102
103             public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin);
104
105             public override void SetLength(long value) => _stream.SetLength(value);
106
107             public override void Write(byte[] buffer, int offset, int count) => _stream.Write(buffer, offset, count);
108
109             public override int ReadByte()
110             {
111                 byte read = (byte)_stream.ReadByte();
112                 _firstByte = read;
113                 _readFirstByte = true;
114                 return read;
115             }
116         }
117
118         /// <summary>
119         /// During deserialization, the stream passed in may be binary formatted or may have used binary writer. This is a quick test to discern between them.
120         /// </summary>
121         private static bool StreamIsBinaryFormatted(StreamWrapper stream)
122         {
123             // For binary formatter, the first byte is the SerializationHeaderRecord and has a value 0
124             int firstByte = stream.ReadByte();
125             if (firstByte != 0)
126             {
127                 return false;
128             }
129
130             return true;
131         }
132
133         private static void DeserializeUsingBinaryFormatter(StreamWrapper wrappedStream, string cryptoKey, RuntimeLicenseContext context)
134         {
135             if (EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization)
136             {
137 #pragma warning disable SYSLIB0011
138                 IFormatter formatter = new BinaryFormatter();
139
140 #pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
141                 object obj = formatter.Deserialize(wrappedStream);
142 #pragma warning restore IL2026
143 #pragma warning restore SYSLIB0011
144
145                 if (obj is object[] value)
146                 {
147                     if (value[0] is string && (string)value[0] == cryptoKey)
148                     {
149                         context._savedLicenseKeys = (Hashtable)value[1];
150                     }
151                 }
152             }
153             else
154             {
155                 throw new NotSupportedException(SR.BinaryFormatterMessage);
156             }
157         }
158
159         internal static void Deserialize(Stream o, string cryptoKey, RuntimeLicenseContext context)
160         {
161             StreamWrapper wrappedStream = new StreamWrapper(o);
162             if (StreamIsBinaryFormatted(wrappedStream))
163             {
164                 DeserializeUsingBinaryFormatter(wrappedStream, cryptoKey, context);
165             }
166             else
167             {
168                 using (BinaryReader reader = new BinaryReader(wrappedStream, encoding: Text.Encoding.UTF8, leaveOpen: true))
169                 {
170                     byte binaryWriterIdentifier = wrappedStream._firstByte;
171                     Debug.Assert(binaryWriterIdentifier == BinaryWriterMagic, $"Expected the first byte to be {BinaryWriterMagic}");
172                     string streamCryptoKey = reader.ReadString();
173                     int numEntries = reader.ReadInt32();
174                     if (streamCryptoKey == cryptoKey)
175                     {
176                         context._savedLicenseKeys!.Clear();
177                         for (int i = 0; i < numEntries; i++)
178                         {
179                             string key = reader.ReadString();
180                             string value = reader.ReadString();
181                             context._savedLicenseKeys.Add(key, value);
182                         }
183                     }
184                 }
185             }
186         }
187     }
188 }