Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / mactripledes.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 // 
8
9 //
10 // MACTripleDES.cs -- Implementation of the MAC-CBC keyed hash w/ 3DES
11 //
12
13 // See: http://www.itl.nist.gov/fipspubs/fip81.htm for a spec
14
15 namespace System.Security.Cryptography {
16     using System.IO;
17     using System.Diagnostics.Contracts;
18
19     [System.Runtime.InteropServices.ComVisible(true)]
20     public class MACTripleDES : KeyedHashAlgorithm 
21     {
22         // Output goes to HashMemorySink since we don't care about the actual data
23         private ICryptoTransform m_encryptor;
24         private CryptoStream _cs;
25         private TailStream _ts;
26         private const int m_bitsPerByte = 8;
27         private int m_bytesPerBlock;
28         private TripleDES des;
29
30         //
31         // public constructors
32         //
33
34         public MACTripleDES() {
35             KeyValue = new byte[24];
36             Utils.StaticRandomNumberGenerator.GetBytes(KeyValue);
37
38             // Create a TripleDES encryptor
39             des = TripleDES.Create();
40             HashSizeValue = des.BlockSize;
41
42             m_bytesPerBlock = des.BlockSize/m_bitsPerByte;
43             // By definition, MAC-CBC-3DES takes an IV=0.  C# zero-inits arrays,
44             // so all we have to do here is define it.
45             des.IV = new byte[m_bytesPerBlock];
46             des.Padding = PaddingMode.Zeros;
47
48             m_encryptor = null;
49         }
50
51         public MACTripleDES(byte[] rgbKey) 
52             : this("System.Security.Cryptography.TripleDES",rgbKey) {}
53
54         public MACTripleDES(String strTripleDES, byte[] rgbKey) {
55             // Make sure we know which algorithm to use
56             if (rgbKey == null)
57                 throw new ArgumentNullException("rgbKey");
58             Contract.EndContractBlock();
59             // Create a TripleDES encryptor
60             if (strTripleDES == null) {
61                 des = TripleDES.Create();
62             } else {
63                 des = TripleDES.Create(strTripleDES);
64             }
65
66             HashSizeValue = des.BlockSize;
67             // Stash the key away
68             KeyValue = (byte[]) rgbKey.Clone();
69
70             m_bytesPerBlock = des.BlockSize/m_bitsPerByte;
71             // By definition, MAC-CBC-3DES takes an IV=0.  C# zero-inits arrays,
72             // so all we have to do here is define it.
73             des.IV = new byte[m_bytesPerBlock];
74             des.Padding = PaddingMode.Zeros;
75
76             m_encryptor = null;
77         }
78
79         public override void Initialize() {
80             m_encryptor = null;
81         }
82
83         [System.Runtime.InteropServices.ComVisible(false)]
84         public PaddingMode Padding {
85             get { return des.Padding; }
86             set { 
87                 if ((value < PaddingMode.None) || (PaddingMode.ISO10126 < value))
88                     throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidPaddingMode"));
89                 des.Padding = value;
90             }
91         }
92
93         //  
94         // protected methods
95         //
96
97         protected override void HashCore(byte[] rgbData, int ibStart, int cbSize) {
98             // regenerate the TripleDES object before each call to ComputeHash
99             if (m_encryptor == null) {
100                 des.Key = this.Key;
101                 m_encryptor = des.CreateEncryptor();
102                 _ts = new TailStream(des.BlockSize / 8); // 8 bytes
103                 _cs = new CryptoStream(_ts, m_encryptor, CryptoStreamMode.Write);
104             }
105
106             // Encrypt using 3DES
107             _cs.Write(rgbData, ibStart, cbSize);
108         }
109
110         protected override byte[] HashFinal() {
111             // If Hash has been called on a zero buffer
112             if (m_encryptor == null) {
113                 des.Key = this.Key;
114                 m_encryptor = des.CreateEncryptor();
115                 _ts = new TailStream(des.BlockSize / 8); // 8 bytes 
116                 _cs = new CryptoStream(_ts, m_encryptor, CryptoStreamMode.Write);
117             }
118
119             // Finalize the hashing and return the result
120             _cs.FlushFinalBlock();
121             return _ts.Buffer;
122         }
123
124         // IDisposable methods
125         protected override void Dispose(bool disposing) {
126             if (disposing) {
127                 // dispose of our internal state
128                 if (des != null)
129                     des.Clear();
130                 if (m_encryptor != null)
131                     m_encryptor.Dispose();
132                 if (_cs != null)
133                     _cs.Clear();
134                 if (_ts != null)
135                     _ts.Clear();
136             }
137             base.Dispose(disposing);
138         }
139     }
140
141     //
142     // TailStream is another utility class -- it remembers the last n bytes written to it
143     // This is useful for MAC-3DES since we need to capture only the result of the last block
144
145     internal sealed class TailStream : Stream {
146         private byte[] _Buffer;
147         private int _BufferSize;
148         private int _BufferIndex = 0;
149         private bool _BufferFull = false;
150
151         public TailStream(int bufferSize) {
152             _Buffer = new byte[bufferSize];
153             _BufferSize = bufferSize;
154         }
155
156         public void Clear() {
157             Close();
158         }
159
160         protected override void Dispose(bool disposing) {
161             try {
162                 if (disposing) {
163                     if (_Buffer != null) {
164                         Array.Clear(_Buffer, 0, _Buffer.Length);
165                     }
166                     _Buffer = null;
167                 }
168             }
169             finally {
170                 base.Dispose(disposing);
171             }
172         }
173
174         public byte[] Buffer {
175             get { return (byte[]) _Buffer.Clone(); }
176         }
177
178         public override bool CanRead {
179             [Pure]
180             get { return false; }
181         }
182
183         public override bool CanSeek {
184             [Pure]
185             get { return false; }
186         }
187
188         public override bool CanWrite {
189             [Pure]
190             get { return _Buffer != null; }
191         }
192
193         public override long Length {
194             get { throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream")); }
195         }
196
197         public override long Position {
198             get { throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream")); }
199             set { throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream")); }
200         }
201
202         public override void Flush() {
203             return;
204         }
205
206         public override long Seek(long offset, SeekOrigin origin) {
207             throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream"));
208         }
209
210         public override void SetLength(long value) {
211             throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream"));
212         }
213
214         public override int Read(byte[] buffer, int offset, int count) {
215             throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream"));
216         }
217
218         public override void Write(byte[] buffer, int offset, int count) {
219             if (_Buffer == null)
220                 throw new ObjectDisposedException("TailStream");
221
222             // If no bytes to write, then return
223             if (count == 0) return;
224             // The most common case will be when we have a full buffer
225             if (_BufferFull) {
226                 // if more bytes are written in this call than the size of the buffer,
227                 // just remember the last _BufferSize bytes
228                 if (count > _BufferSize) {
229                     System.Buffer.InternalBlockCopy(buffer, offset+count-_BufferSize, _Buffer, 0, _BufferSize);
230                     return;
231                 } else {
232                     // move _BufferSize - count bytes left, then copy the new bytes
233                     System.Buffer.InternalBlockCopy(_Buffer, _BufferSize - count, _Buffer, 0, _BufferSize - count);
234                     System.Buffer.InternalBlockCopy(buffer, offset, _Buffer, _BufferSize - count, count);
235                     return;
236                 }
237             } else {
238                 // buffer isn't full yet, so more cases
239                 if (count > _BufferSize) {
240                     System.Buffer.InternalBlockCopy(buffer, offset+count-_BufferSize, _Buffer, 0, _BufferSize);
241                     _BufferFull = true;
242                     return;
243                 } else if (count + _BufferIndex >= _BufferSize) {
244                     System.Buffer.InternalBlockCopy(_Buffer, _BufferIndex+count-_BufferSize, _Buffer, 0, _BufferSize - count);
245                     System.Buffer.InternalBlockCopy(buffer, offset, _Buffer, _BufferIndex, count);
246                     _BufferFull = true;
247                     return;
248                 } else {
249                     System.Buffer.InternalBlockCopy(buffer, offset, _Buffer, _BufferIndex, count);
250                     _BufferIndex += count;
251                     return;
252                 }
253             }
254         }
255     }
256 }