diff --git a/SimpleAES.cabal b/SimpleAES.cabal
--- a/SimpleAES.cabal
+++ b/SimpleAES.cabal
@@ -1,7 +1,7 @@
 Name: SimpleAES
 Synopsis: Fast AES encryption/decryption for bytestrings
 Description: A simplified binding to Brian Gladman's AES implementation, including a copy of that implementation
-Version: 0.2
+Version: 0.3
 License: BSD3
 License-file: COPYING
 Copyright: Copyright (c) 2009 University of Tromsø, 2009 David Himmelstrup
@@ -18,7 +18,7 @@
 
 Library
   Build-Depends:
-        base >= 4 && < 5 , bytestring, mwc-random >= 0.4.1.1
+        base >= 4 && < 5 , bytestring, mwc-random >= 0.4.1.1, binary
   Extensions:
         ForeignFunctionInterface,
         ViewPatterns,
diff --git a/src/Codec/Crypto/AES/IO.hsc b/src/Codec/Crypto/AES/IO.hsc
--- a/src/Codec/Crypto/AES/IO.hsc
+++ b/src/Codec/Crypto/AES/IO.hsc
@@ -30,11 +30,6 @@
 
 data Direction = Encrypt | Decrypt
 
--- | Modes ECB and CBC can only handle full 16-byte frames. This means
--- the length of every strict bytestring passed in must be a multiple
--- of 16; when using lazy bytestrings, its /component/ strict
--- bytestrings must all satisfy this.
---
 data Mode = ECB | CBC --  CFB  | OFB  | CTR
 
 -- Other modes can handle bytestrings of any length. However,
diff --git a/src/Codec/Crypto/SimpleAES.hs b/src/Codec/Crypto/SimpleAES.hs
--- a/src/Codec/Crypto/SimpleAES.hs
+++ b/src/Codec/Crypto/SimpleAES.hs
@@ -1,7 +1,11 @@
 -- | A pure interface to AES
-module Codec.Crypto.SimpleAES(
-  Mode(..), Direction(..), Key, IV, newIV, randomKey, crypt
-  ) where
+module Codec.Crypto.SimpleAES
+    ( Mode(..)
+    , Direction(..)
+    , Key, IV
+    , newIV, randomKey, crypt
+    , encryptMsg, encryptMsg', decryptMsg
+    ) where
 
 import qualified Codec.Crypto.AES.IO as AES
 import Codec.Crypto.AES.IO(Mode(..), Direction(..))
@@ -10,6 +14,7 @@
 import Control.Monad
 import System.IO.Unsafe
 import Control.Monad.ST
+import Data.Binary
 
 import System.Random.MWC
 
@@ -19,9 +24,12 @@
 type Key = B.ByteString
 type IV = B.ByteString
 
+ivLength :: Num a => a
+ivLength = 16
+
 newIV :: IO IV
 newIV = do seed <- newSeed
-           let (iv, seed') = randomByteString seed 16
+           let (iv, seed') = randomByteString seed ivLength
            return iv
 
 randomKey :: IO Key
@@ -29,19 +37,48 @@
                let (key, seed') = randomByteString seed 32
                return key
 
-{-
+
 -- Properties:
 --   decryptMsg mode key . encryptMsg mode key == id
 --   x == y => encryptMsg mode key x == encryptMsg mode key y
+-- | Encrypt a bytestring using a random seed (IV). Since the
+--   seed is random, this function is tainted by IO.
 encryptMsg :: Mode -> Key -> BL.ByteString -> IO BL.ByteString
 encryptMsg mode key bs
     = do iv <- newIV
-         cxt <- AES.newCtx mode key iv Encrypt
+         return $ encryptMsg' mode key iv bs
+
+-- | Encrypt a ByteString using a given seed (IV).
+--   The resulting ByteString contains both the seed and the original
+--   length of the input (before padding).
+encryptMsg' :: Mode -> Key -> IV -> BL.ByteString -> BL.ByteString
+encryptMsg' mode key iv bs
+    = unsafePerformIO $
+      do ctx <- AES.newCtx mode key iv Encrypt
          chunks <- unsafeInterleaveIO $ lazyCrypt ctx (repack bs)
--}
+         let encrypted = BL.fromChunks chunks
+         return $ BL.concat [ BL.fromChunks [iv]
+                            , encode (BL.length bs)
+                            , encrypted ]
 
+decryptMsg :: Mode -> Key -> BL.ByteString -> BL.ByteString
+decryptMsg mode key bs
+    = unsafePerformIO $
+      do when (BL.length iv /= ivLength)  $ error "Codec.Crypto.SimpleAES.decryptMsg: Invalid IV length. Message garbled."
+         when (BL.length lenStr /= 8)     $ error "Codec.Crypto.SimpleAES.decryptMsg: Invalid encoding. Message garbled."
+         when (BL.length encrypted < len) $ error "Codec.Crypto.SimpleAES.decryptMsg: Invalid size. Message garbled."
+         when (BL.length encrypted `mod` 16 /= 0) $ error "Codec.Crypto.SimpleAES.decryptMsg: Invalid padding. Message garbled."
+         ctx <- AES.newCtx mode key (B.concat $ BL.toChunks iv) Decrypt
+         chunks <- unsafeInterleaveIO $ lazyCrypt ctx (repack encrypted)
+         return $ BL.take len (BL.fromChunks chunks)
+    where (iv, bs') = BL.splitAt ivLength bs
+          (lenStr,encrypted) = BL.splitAt 8 bs'
+          len = decode lenStr
 
--- | Encryption/decryption for lazy bytestrings
+
+-- | Encryption/decryption for lazy bytestrings. The input string is zero-padded to
+--   a multiple of 16. It is your obligation to separate encode the length of the string.
+--
 --   Properties:
 --     x == y => crypt mode key iv dir x == crypt mode key iv dir y
 --     take (length x) (crypt mode key iv Decrypt (crypt mode key iv Encrypt x)) == x
