ROT 128 Stream Upgrade Sample, Part 1

The mission for the next five days is to build and demonstrate an implementation of a stream upgrade. For review, a stream upgrade is a component that plugs into the transport and rewrites the byte stream as it goes on and off of the network. Stream upgrades are stackable and composable. You add stream upgrades by including stream upgrade binding elements in the desired order in the binding. I went over the basics of stream upgrades a few weeks ago. Here are the articles in that series:

  1. Stream Upgrades, Part 1
  2. Stream Upgrades, Part 2
  3. Stream Upgrades, Part 3

The example I've picked out is a stream upgrade that applies ROT 128 (ROTate by 128). ROT 13 is a famous example of a Caesar cipher that occludes messages by replacing letters in the English alphabet with the letter that is 13 places higher. When you go past 'Z', you wrap back around to 'A'. Since there are 26 letters, applying ROT 13 twice returns a letter back to where it started. ROT 128 is the equivalent for a single byte value.

Here is the stream class that I want to have my messages run through. It includes some debugging so that we can see the messages as they go in and out.

 using System;
using System.IO;

namespace Microsoft.ServiceModel.Samples
{
   class ROT128Stream : Stream
   {
      Stream stream;

      public ROT128Stream(Stream stream)
      {
         this.stream = stream;
      }

      public override bool CanRead
      {
         get { return this.stream.CanRead; }
      }

      public override bool CanSeek
      {
         get { return this.stream.CanSeek; }
      }

      public override bool CanWrite
      {
         get { return this.stream.CanWrite; }
      }

      protected override void Dispose(bool disposing)
      {
         if (disposing)
         {
            this.stream.Dispose();
         }
         else
         {
            this.stream.Close();
         }
         base.Dispose(disposing);
      }

      public override void Flush()
      {
         this.stream.Flush();
      }

      public override long Length
      {
         get { return this.stream.Length; }
      }

      public override long Position
      {
         get
         {
            return this.stream.Position;
         }
         set
         {
            this.stream.Position = value;
         }
      }

      static void DumpBuffer(byte[] buffer, int offset, int count)
      {
         int pos = 0;
         while (pos < count)
         {
            int lineCount = count - pos;
            if (lineCount > 15)
            {
               lineCount = 15;
            }
            for (int linePos = 0; linePos < lineCount; linePos++)
            {
               Console.Write(" {0:X2}", buffer[offset + pos + linePos]);
            }
            for (int linePos = 15 - lineCount; linePos >= 0; linePos--)
            {
               Console.Write("   ");
            }
            for (int linePos = 0; linePos < lineCount; linePos++)
            {
               byte item = buffer[offset + pos + linePos];
               if (item < 0x20 || item >= 0x80)
               {
                  Console.Write(".");
               }
               else
               {
                  Console.Write((char)item);
               }
            }
            Console.WriteLine();
            pos += lineCount;
         }
      }

      public override int Read(byte[] buffer, int offset, int count)
      {
         int result = this.stream.Read(buffer, offset, count);
         Console.WriteLine("[READ] {0} bytes", result);
         DumpBuffer(buffer, offset, result);
         for (int pos = 0; pos < result; pos++)
         {
            buffer[pos + offset] ^= 0x80;
         }
         return result;
      }

      public override long Seek(long offset, SeekOrigin origin)
      {
         return this.stream.Seek(offset, origin);
      }

      public override void SetLength(long value)
      {
         this.stream.SetLength(value);
      }

      public override void Write(byte[] buffer, int offset, int count)
      {
         Console.WriteLine("[WRITE] {0} bytes", count);
         DumpBuffer(buffer, offset, count);
         byte[] encodedBuffer = new byte[count];
         for (int pos = 0; pos < count; pos++)
         {
            encodedBuffer[pos] = (byte)(buffer[pos + offset] ^ 0x80);
         }
         this.stream.Write(encodedBuffer, 0, count);
      }
   }
}

Next time: ROT 128 Stream Upgrade Sample, Part 2