Recipe17.6.Wrapping a String Hash for Ease of Use


Recipe 17.6. Wrapping a String Hash for Ease of Use

Problem

You need to create a class to isolate other developers on your team from the details of adding a hash to a string, as well as the details of using the hash to verify if the string has been modified or corrupted.

Solution

The following classes decorate the StringWriter and StringReader classes to handle a hash added to its contained string. The WriterDecorator and StringWriterHash classes allow the StringWriter class to be decorated with extra functionality to add a hash value to the StringWriter's internal string. Note that the CreateStringHash method that creates the hash value is defined in Recipe 17.5.

The code for the WriterDecorator abstract base class is shown in Example 17-9.

Example 17-9. WriterDecorator class

 using System; using System.Text; using System.IO; [Serializable]  public abstract class WriterDecorator : TextWriter {     public WriterDecorator( ) {}     public WriterDecorator(StringWriter stringWriter)     {         internalStringWriter = stringWriter;     }     protected bool isHashed = false;     protected StringWriter internalStringWriter = null;     public void SetWriter(StringWriter stringWriter)     {         internalStringWriter = stringWriter;     } } 

The StringWriterHash class shown in Example 17-10 is a concrete implementation of the WriterDecorator class.

Example 17-10. StringWriterHash class

 [Serializable] public class StringWriterHash : WriterDecorator {     public StringWriterHash( ) : base( ) {}     public StringWriterHash(StringWriter stringWriter) : base(stringWriter)     {     }     public override Encoding Encoding     {         get {return (internalStringWriter.Encoding);}     }     public override void Close( )     {         internalStringWriter.Close( );         base.Dispose(true); // Completes the cleanup.     }     public override void Flush( )     {         internalStringWriter.Flush( );         base.Flush( );     }     public virtual StringBuilder GetStringBuilder( )     {         return (internalStringWriter.GetStringBuilder( ));     }     public override string ToString( )     {         return (internalStringWriter.ToString( ));     }     public void WriteHash( )     {         int originalStrLen = internalStringWriter.GetStringBuilder( ).Length;         // Call hash generator here for whole string.         string hashedString = HashOps.CreateStringHash(this.ToString( ));         internalStringWriter.Write(hashedString.Substring(originalStrLen));         isHashed = true;     }     public override void Write(char value)     {         if (isHashed)         {             throw (new Exception("A hash has already been added to this string"+                                   ", it cannot be modified."));         }         else         {             internalStringWriter.Write(value);         }     }     public override void Write(string value)     {         if (isHashed)         {             throw (new Exception("A hash has already been added to this string"+                                   ", it cannot be modified."));         }         else         {             internalStringWriter.Write(value);         }     }     public override void Write(char[] buffer, int index, int count)     {         if (isHashed)         {             throw (new Exception("A hash has already been added to this string"+                                  ", it cannot be modified."));         }         else         {             internalStringWriter.Write(buffer, index, count);         }     }     protected override void Dispose(bool disposing)     {         base.Dispose(disposing);     } } 

The ReaderDecorator and StringReaderHash classes shown in Examples 17-11 and 17-12 allow the StringReader class to be decorated with extra functionality to handle the verification of a string's hash value. Note that the TestReceivedStringHash method that verifies the hash value is defined in Recipe 17.5.

Example 17-11. ReaderDecorator class

 [Serializable] public abstract class ReaderDecorator : TextReader {     public ReaderDecorator( ) {}     public ReaderDecorator(StringReader stringReader)     {         internalStringReader = stringReader;     }     protected StringReader internalStringReader = null;     public void SetReader(StringReader stringReader)     {         internalStringReader = stringReader;     } } 

StringReaderHash, shown in Example 17-12, is the concrete implementation of the ReaderDecorator class.

Example 17-12. StringReaderHash class

 [Serializable] public class StringReaderHash : ReaderDecorator {     public StringReaderHash( ) : base( ) {}     public StringReaderHash(StringReader stringReader) : base(stringReader)     {     }     public override void Close( )     {         internalStringReader.Close( );         base.Dispose(true); // Completes the cleanup.     }     public string ReadToEndHash( )     {         string hashStr = internalStringReader.ReadToEnd( );         string originalStr = "";         // Call hash reader here.         bool isInvalid = HashOps.TestReceivedStringHash(hashStr,                                                         out originalStr);         if (isInvalid)         {             throw (new Exception("This string has failed its hash check."));         }         return (originalStr);     }     public override int Read( )     {         return (internalStringReader.Read( ));     }     public override int Read(char[] buffer, int index, int count)     {         return (internalStringReader.Read(buffer, index, count));     }     public override string ReadLine( )     {         return (internalStringReader.ReadLine( ));     }     public override string ReadToEnd( )     {         return (internalStringReader.ReadToEnd( ));     }     protected override void Dispose(bool disposing)     {         base.Dispose(disposing);     } } 

The following code creates a StringWriter object (stringWriter) and decorates it with a StringWriterHash object:

 StringWriter stringWriter = new StringWriter(new StringBuilder("Initial Text")); StringWriterHash stringWriterHash = new StringWriterHash( ); stringWriterHash.SetWriter(stringWriter); stringWriterHash.Write("-Extra Text-"); stringWriterHash.WriteHash( ); Console.WriteLine("stringWriterHash.ToString( ): " + stringWriterHash.ToString( )); 

The string "Initial Text" is added to the StringWriter on initialization, and later the string "-Extra Text-" is added. Next, the WriteHash method is called to add a hash value to the end of the complete string. Notice that if the code attempts to write more text to the StringWriterHash object after the WriteHash method has been called, an exception will be thrown. The string cannot be modified once the hash has been calculated and added.

The following code takes a StringReader object (stringReader) that was initialized with the string and hash produced by the previous code and decorates it with a StringReaderHash object:

 StringReader stringReader = new StringReader(stringWriterHash.ToString( )); StringReaderHash stringReaderHash = new StringReaderHash( ); stringReaderHash.SetReader(stringReader); Console.WriteLine("stringReaderHash.ReadToEndHash( ): " +                       stringReaderHash.ReadToEndHash( )); 

If the original string is modified after the hash is added, the ReadToEndHash method throws an exception.

Discussion

The decorator design pattern provides the ability to modify individual objects without having to modify or subclass the object's class. This allows for the creation of both decorated and undecorated objects. The implementation of a decorator pattern is sometimes hard to understand at first. An abstract decorator class is created that inherits from the same base class as the class you will decorate. In the case of this recipe, you will decorate the StringReader/StringWriter classes to allow a hash to be calculated and used. The StringReader class inherits from Textreader, and the StringWriter class inherits from TextWriter. Knowing this, you create two abstract decorator classes: ReaderDecorator, which inherits from Textreader, and WriterDecorator, which inherits from TextWriter.

The abstract decorator classes contain two constructors, a private field named internalStreamReader\internalStreamWriter and a method named SetReader\SetWriter. Basically, the field stores a reference to the contained StringReader or StringWriter object that is being decorated. This field can be set through either a constructor or the SetReader\SetWriter method. The interesting thing about this pattern is that each of the decorator objects must also contain an instance of the class that they decorate. The StringReaderHash class contains a StringReader object in its internalStreamReader field, and the StringWriterHash class contains a StringWriter object in its internalStreamWriter field.

A concrete decorator class that inherits from the abstract decorator classes is created. The StringReaderHash class inherits from ReaderDecorator, while the StringWriterHash inherits from WriterDecorator. This pattern allows you the flexibility to add concrete decorator classes without having to touch the existing code.

Most of the methods in the StringReaderHash and StringWriterHash classes simply act as wrappers to the internalStreamReader or internalStreamWriter objects, respectively. The method that actually decorates the StringReader object with a hash is the StringReaderHash.ReadToEndHash method, and the method that actually decorates the StringWriter object is StringWriterHash.WriteHash. These two methods allow the hash to be attached to a string and later used to determine whether the string contents have changed.

The attractiveness of the decorator pattern is that you can add any number of concrete decorator classes that derive from either ReaderDecorator or WriterDecorator. If you need to use a different hashing algorithm, or even a quick and dirty hash algorithm, you can subclass the ReaderDecorator or WriterDecorator classes and add functionality to use these new algorithms. Now you have more choices of how to decorate these classes.

See Also

See the "StringWriter Class" and "StringReader Class" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net