Produits  
  Services et Conseils  
  Fiches Technique  
  SipXTapi avec .Net  
  transformNode en .Net  
  Blowfish et le CF  
  MD5 et le CF  
  Compression HTTP et le CF  
 
 

  Fiches Technique  


Encrypting with the Compact Framework
on a PocketPC using a "BlowFish" Stream

lire la version francophone

Implementation
Limits
Some useful links...
Revision history...

While developing applications for PocketPC we were faced with the need to encrypt the data to be persisted onto the disk. In fact, because of its "mobile" nature, the PocketPC might be easily stolen.

We searched the web and found that Markus Hahn ported in c# the Blowfish algorithm, from Bruce Schneier. In order to use it easily in our applications, we implemented auround it a Stream derived class.

Implementation

Because we do not know what kind of stream we will require, our Blowfish stream will simply "adapt" a stream object parameter.

namespace FlowGroup.Crypto
{
    ...
    public class BlowfishStream : Stream
    {
        ...
        private System.IO.Stream mStream;

        /// <summary>
        /// Construct a BlowfishStream that adapt the 
        /// "stream" parameter in order to encrypt/decrypt
        /// the data
        /// </summary>
        /// <param name="stream">inner stream</param>
        /// <param name="key">key for the encryption</param>
        /// <param name="mode">Read or Write</param>
        public BlowfishStream(Stream stream,
                              byte[] key,
                              BlowfishStreamMode mode)
        {
            ...
            mStream = stream;
            ...
        }

        ...
    }
}

The same stream object might be used for streams that are encypted or not, we decided to sign the stream. If the stream is not encrypted, all the calls are delegated to the parameter stream object.

   public class BlowfishStream : Stream
    {
        static private readonly byte[] signature = 
          { 5, 11, 14, 22, 6, 17, 15, 192};
    
        private System.IO.Stream mStream;
        ...
        private bool mIsEncrypted = false;
        private byte[] mBuffer;
        private int mCount = 0;
        private byte mOffset = 0;

        ...
        public BlowfishStream(Stream stream,
                              byte[] key,
                              BlowfishStreamMode mode)
        {
            ...

            mStream = stream;

            mBuffer = new byte[256];
            mMode = mode;
            switch(mode)
            {
                case BlowfishStreamMode.Read:
                    mCount = (byte)mStream.Read(mBuffer, 0, 8);
                    mOffset = 0;
                    if(mCount == 8)
                    {
                        mIsEncrypted = true;
                        while(mCount > 0)
                        {
                            mCount--;
                            if(mBuffer[mCount] != signature[mCount])
                            {
                                mCount = 8;
                                mIsEncrypted = false;
                                break;
                            } // if
                        }
                    }
                    break;
                case BlowfishStreamMode.Write:
                    mIsEncrypted = true;
                    mStream.Write(signature, 0, 8);
                    break;
            }
        }
        
        ...

        public override long Seek(long offset, System.IO.SeekOrigin origin)
        {
            if(mIsEncrypted)
                throw new System.NotSupportedException();
            return mStream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            if(mIsEncrypted)
                throw new NotSupportedException();
            mStream.SetLength(value);
        }

        public override bool CanRead
        {
            get
            {
                return mIsEncrypted ? (mMode == BlowfishStreamMode.Read)
                  :  mStream.CanRead;
            }
        }

        public override bool CanSeek
        {
            get
            {
                return mIsEncrypted ? false : mStream.CanSeek;
            }
        }

        public override bool CanWrite
        {
            get
            {
                return mIsEncrypted ? (mMode == BlowfishStreamMode.Write)
                  :  mStream.CanWrite;
            }
        }

        public override long Length
        {
            get
            {
                if(mIsEncrypted)
                    throw new System.NotSupportedException();
                return mStream.Length;
            }
        }

        public override long Position
        {
            get
            {
                if(mIsEncrypted)
                    throw new System.NotSupportedException();
                return mStream.Position;
            }
            set
            {
                if(mIsEncrypted)
                    throw new System.NotSupportedException();
                mStream.Position = value;
            }
        }

The algorithm used 8 bytes blocks, so we have to use a buffer to read/write to and from the stream. So let's implement the read, write and flush functions...

       public override void Flush()
        {
            if(!mIsEncrypted)
            {
            }
            else if(mMode == BlowfishStreamMode.Write)
            {
                if(mCount > 0)
                {
                    int bound = ((mCount % 8) == 0) ? mCount
                      : (((mCount / 8) + 1) * 8);
                    mBlowfish.Encrypt(mBuffer, mBuffer, 0, 0, bound);
                    mStream.WriteByte((byte)(mCount - 1));
                    mStream.Write(mBuffer, 0, bound);
                    mCount = 0;
                    mOffset = 0;
                }
            }
            mStream.Flush();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if(mIsEncrypted)
            {
                int totalRead = 0;
                int read = 0;
                int bound;

                // while data are required, fill the internal buffer
                while(count > 0)
                {
                    if(mCount == 0)
                    {
                        int nRead = 0;
                        read = mStream.ReadByte();
                        if(read == -1)
                            break; // the stream is empty
                        mCount = read + 1;
                        mOffset = 0;
                        bound = ((mCount % 8) == 0) ? mCount 
                          : (((mCount / 8) + 1) * 8);
                        read = mStream.Read(mBuffer, mOffset, bound);
                        nRead += read;
                        if(bound != read)
                        {
                            int nMaxCount = 256;
                            while(nRead < bound)
                            {
                                read = mStream.Read(mBuffer, 
                                                    mOffset+nRead, 
                                                    bound-nRead);
                                nRead += read;

                                nMaxCount--;
                                if(nMaxCount <= 0)
                                    throw new System.IO.IOException();
                            }
                        }
                        mBlowfish.Decrypt(mBuffer, 
                                          mBuffer, 
                                          mOffset, 
                                          mOffset, 
                                          bound);
                    }

                    buffer[offset++] = mBuffer[mOffset++];
                    count--;
                    mCount--;
                    totalRead++;
                }
                return totalRead;
            }
            else
            {
                int cbRead = 0;
                if(mCount > 0)
                {
                    while((mOffset < 8) && (count > 0))
                    {
                        buffer[offset++] = mBuffer[mOffset++];
                        count--;
                        cbRead++;
                    }
                    if(mOffset == 8)
                    { // the "wrong" signature has been transfered back 
                      // to the reader, so empty the buffer
                        mCount = 0;
                        mOffset = 0;
                    }
                }
                cbRead += mStream.Read(buffer, offset, count);
                return cbRead;
            }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            if(mIsEncrypted)
            {
                string str = 
                  System.Text.ASCIIEncoding.ASCII.GetString(buffer,
                                                            0,
                                                            count);

                // while data are required, fill the internal buffer
                while(count > 0)
                {
                    if(mCount == 256)
                    {
                        mStream.WriteByte(255);
                        mBlowfish.Encrypt(mBuffer, mBuffer, 0, 0, 256);
                        mStream.Write(mBuffer, 0, 256);
                        mOffset = 0;
                        mCount = 0;
                    }

                    mBuffer[mOffset++] = buffer[offset++];
                    count--;
                    mCount++;
                }
            }
            else
                mStream.Write(buffer, offset, count);
        }

Limits

In order to simplify the code, the stream can be opened for reading or writing, but not both, and cannot be seeked.

Some useful links...

The Blowfish Encryption Algorithm...
Where to find information on the Blowfish algorithm
Where to download the c# implementation of the Blowfish
Homepage of Markus Hahn, who implented the Blowfish algorithm in c#.
Download the source code
size : 12 Ko, last modification date : 03/19/2003
Contacts us
Any comments, questions? Just contact us.

Revision history...

date révision
01/26/2004 The link to Markus Hahn website has been updated.

all the informations here are provided as is, without any warranty of any kind.
Copyright © FlowGroup SAS, 2002-2005