3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <OWNER>Microsoft</OWNER>
10 // MACTripleDES.cs -- Implementation of the MAC-CBC keyed hash w/ 3DES
13 // See: http://www.itl.nist.gov/fipspubs/fip81.htm for a spec
15 namespace System.Security.Cryptography {
17 using System.Diagnostics.Contracts;
19 [System.Runtime.InteropServices.ComVisible(true)]
20 public class MACTripleDES : KeyedHashAlgorithm
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;
31 // public constructors
34 public MACTripleDES() {
35 KeyValue = new byte[24];
36 Utils.StaticRandomNumberGenerator.GetBytes(KeyValue);
38 // Create a TripleDES encryptor
39 des = TripleDES.Create();
40 HashSizeValue = des.BlockSize;
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;
51 public MACTripleDES(byte[] rgbKey)
52 : this("System.Security.Cryptography.TripleDES",rgbKey) {}
54 public MACTripleDES(String strTripleDES, byte[] rgbKey) {
55 // Make sure we know which algorithm to use
57 throw new ArgumentNullException("rgbKey");
58 Contract.EndContractBlock();
59 // Create a TripleDES encryptor
60 if (strTripleDES == null) {
61 des = TripleDES.Create();
63 des = TripleDES.Create(strTripleDES);
66 HashSizeValue = des.BlockSize;
68 KeyValue = (byte[]) rgbKey.Clone();
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;
79 public override void Initialize() {
83 [System.Runtime.InteropServices.ComVisible(false)]
84 public PaddingMode Padding {
85 get { return des.Padding; }
87 if ((value < PaddingMode.None) || (PaddingMode.ISO10126 < value))
88 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidPaddingMode"));
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) {
101 m_encryptor = des.CreateEncryptor();
102 _ts = new TailStream(des.BlockSize / 8); // 8 bytes
103 _cs = new CryptoStream(_ts, m_encryptor, CryptoStreamMode.Write);
106 // Encrypt using 3DES
107 _cs.Write(rgbData, ibStart, cbSize);
110 protected override byte[] HashFinal() {
111 // If Hash has been called on a zero buffer
112 if (m_encryptor == null) {
114 m_encryptor = des.CreateEncryptor();
115 _ts = new TailStream(des.BlockSize / 8); // 8 bytes
116 _cs = new CryptoStream(_ts, m_encryptor, CryptoStreamMode.Write);
119 // Finalize the hashing and return the result
120 _cs.FlushFinalBlock();
124 // IDisposable methods
125 protected override void Dispose(bool disposing) {
127 // dispose of our internal state
130 if (m_encryptor != null)
131 m_encryptor.Dispose();
137 base.Dispose(disposing);
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
145 internal sealed class TailStream : Stream {
146 private byte[] _Buffer;
147 private int _BufferSize;
148 private int _BufferIndex = 0;
149 private bool _BufferFull = false;
151 public TailStream(int bufferSize) {
152 _Buffer = new byte[bufferSize];
153 _BufferSize = bufferSize;
156 public void Clear() {
160 protected override void Dispose(bool disposing) {
163 if (_Buffer != null) {
164 Array.Clear(_Buffer, 0, _Buffer.Length);
170 base.Dispose(disposing);
174 public byte[] Buffer {
175 get { return (byte[]) _Buffer.Clone(); }
178 public override bool CanRead {
180 get { return false; }
183 public override bool CanSeek {
185 get { return false; }
188 public override bool CanWrite {
190 get { return _Buffer != null; }
193 public override long Length {
194 get { throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream")); }
197 public override long Position {
198 get { throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream")); }
199 set { throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream")); }
202 public override void Flush() {
206 public override long Seek(long offset, SeekOrigin origin) {
207 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream"));
210 public override void SetLength(long value) {
211 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnseekableStream"));
214 public override int Read(byte[] buffer, int offset, int count) {
215 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream"));
218 public override void Write(byte[] buffer, int offset, int count) {
220 throw new ObjectDisposedException("TailStream");
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
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);
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);
238 // buffer isn't full yet, so more cases
239 if (count > _BufferSize) {
240 System.Buffer.InternalBlockCopy(buffer, offset+count-_BufferSize, _Buffer, 0, _BufferSize);
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);
249 System.Buffer.InternalBlockCopy(buffer, offset, _Buffer, _BufferIndex, count);
250 _BufferIndex += count;