diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright David Anderson 2010
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of David Anderson nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Network/Secure.hs b/Network/Secure.hs
new file mode 100644
--- /dev/null
+++ b/Network/Secure.hs
@@ -0,0 +1,131 @@
+-- |This library simplifies the task of securely connecting two
+-- servers to each other. It closely mimicks the regular socket API,
+-- and adds the concept of identity: each communicating server has an
+-- identity, and connections can only be established between two
+-- servers who know each other and expect to be communicating.
+--
+-- Under the hood, the library takes care of strongly authenticating
+-- the connection, and of encrypting all traffic. If you successfully
+-- establish a connection using this library, you have the guarantee
+-- that the connection is secure.
+
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Network.Secure
+    (
+      -- * Tutorial
+      -- $tutorial
+
+      -- * Internals and caveats
+      -- $internals
+
+      -- * Managing identities
+
+      -- ** Authenticating peers
+      PeerIdentity
+    , writePeerIdentity
+    , readPeerIdentity
+
+      -- ** Authenticating to peers
+    , LocalIdentity
+    , writeLocalIdentity
+    , readLocalIdentity
+    , toPeerIdentity
+    , newLocalIdentity
+
+      -- * Communicating
+
+      -- ** Connecting to peers
+    , connect
+
+      -- ** Accepting connections from peers
+    , Socket
+    , newServer
+    , accept
+
+      -- ** Talking to connected peers
+    , Connection
+    , peer  
+    , read
+    , write
+    , close
+
+      -- ** Misc reexports from 'Network.Socket'
+    , HostName
+    , ServiceName
+    ) where
+
+import Network.Secure.Connection
+import Network.Secure.Identity
+
+-- $tutorial
+--
+-- First, each host needs to generate a local identity for itself. A
+-- local identity allows a server to authenticate itself to remote
+-- peers.
+--
+-- > do
+-- >   id <- newLocalIdentity "server1.domain.com" 365
+-- >   writeLocalIdentity id >>= writeFile "server.key"
+--
+-- The name is not used at all by the library, it just allows you to
+-- identify the key later on if you need to.
+--
+-- This identity contains secret key material that only the generating
+-- host should have. From this, we need to generate a public identity
+-- that can be given to other hosts.
+--
+-- > do
+-- >   id <- readFile "server.key" >>= readLocalIdentity
+-- >   writePeerIdentity (toPeerIdentity id) >>= writeFile "server.pub"
+--
+-- This public file should be distributed to the servers with whom you
+-- want to communicate. Once everyone has the public identities of
+-- their peers, we can start connecting. First, one host needs to
+-- start listening for connections.
+--
+-- > do
+-- >   me     <- readFile "a.key" >>= readLocalIdentity
+-- >   you    <- readFile "b.pub" >>= readPeerIdentity
+-- >   server <- newServer (Nothing, "4242")
+-- >   conn   <- accept me [you] server
+--
+-- Then, another host needs to connect.
+--
+-- > do
+-- >   me   <- readFile "b.key" >>= readLocalIdentity
+-- >   you  <- readFile "a.pub" >>= readPeerIdentity
+-- >   conn <- connect me you ("a.com", "4242")
+--
+-- Et voila! From there on, you can communicate using the usual
+-- socket-ish API:
+--
+-- > do
+-- >   write conn "hello?"
+-- >   read conn >>= putStrLn
+-- >   close conn
+
+-- $internals
+--
+-- Note that this section gives out internal implementation details
+-- which are subject to change! Compatibility breakages will be
+-- indicated by appropriate version number bumps for the package, and
+-- the internal details of new versions may bear no resemblance
+-- whatsoever to the old version.
+--
+-- The current implementation uses OpenSSL (via HsOpenSSL) for
+-- transport security, with the @AES256-SHA@ ciphersuite and 4096 bit
+-- RSA keys.
+--
+-- Due to a current limitation of the HsOpenSSL API, we do not use a
+-- ciphersuite that makes use of ephemeral keys for encryption. The
+-- consequence is that connections established with this library do
+-- not provide perfect forward secrecy.
+--
+-- That is, if an attacker can compromise the private keys of the
+-- communicating servers, she can decrypt all past communications that
+-- she has recorded.
+--
+-- This shortcoming will be fixed at some point, either by adding
+-- Diffie-Hellman keying support to HsOpenSSL, or by switching to a
+-- different underlying implementation.
diff --git a/Network/Secure/Connection.hs b/Network/Secure/Connection.hs
new file mode 100644
--- /dev/null
+++ b/Network/Secure/Connection.hs
@@ -0,0 +1,151 @@
+module Network.Secure.Connection
+    (
+      HostName
+    , ServiceName
+
+    , Connection
+    , peer
+    , Network.Secure.Connection.connect
+    , Network.Secure.Connection.read
+    , Network.Secure.Connection.write
+    , close
+    
+    , Network.Secure.Connection.Socket
+    , newServer
+    , Network.Secure.Connection.accept
+    ) where
+
+import Prelude hiding (read)
+
+import Control.Applicative ((<$>))
+import Control.Exception (IOException, bracketOnError, onException, try)
+import Control.Monad (liftM, unless)
+import Data.ByteString (ByteString)
+import Data.Maybe (fromJust)
+import OpenSSL.Session (ShutdownType(Unidirectional), SSLContext, SSL,
+                        VerificationMode(VerifyPeer), accept, connect,
+                        connection, context, contextSetPrivateKey,
+                        contextSetCertificate, contextSetCiphers,
+                        contextSetVerificationMode, contextGetCAStore,
+                        getPeerCertificate, getVerifyResult, read, shutdown,
+                        write)
+import OpenSSL.X509 (compareX509)
+import OpenSSL.X509.Store (addCertToStore)
+import Network.Socket hiding (shutdown)
+
+import Network.Secure.Identity
+
+-- |An established authenticated connection to a peer. It is
+-- guaranteed that all Connection objects are with a known peer, and
+-- that the connection is strongly encrypted.
+data Connection = C 
+    {
+      ssl  :: SSL
+      -- |Return the 'PeerIdentity' of the remote end of the connection.
+    , peer :: PeerIdentity
+    }
+
+-- |A server socket that accepts only secure connections.
+newtype Socket = S { 
+    unSocket :: Network.Socket.Socket
+    }
+
+-- |Connect securely to the given host/port. The 'Connection' is
+-- returned only if the peer accepts the given 'LocalIdentity', and if
+-- the remote endpoint successfully authenticates as the given
+-- 'PeerIdentity'.
+connect :: LocalIdentity -> PeerIdentity -> (HostName, ServiceName)
+        -> IO Connection
+connect myId peerId (host, port) = bracketOnError newSock sClose tryConnect
+  where
+    tryConnect sock = do
+        addr <- getSockAddr (Just host) port
+        Network.Socket.connect sock addr
+        connectSSL myId [peerId] False sock
+
+-- |Read at most 'n' bytes from the given connection.
+read :: Connection -> Int -> IO ByteString
+read = OpenSSL.Session.read . ssl
+
+-- |Send data to the connected peer.
+write :: Connection -> ByteString -> IO ()
+write = OpenSSL.Session.write . ssl
+
+-- |Close the connection. No other operations on 'Connection's should
+-- be used after closing it.
+close :: Connection -> IO ()
+close conn = shutdown (ssl conn) Unidirectional
+
+-- |Create a new secure socket server, listening on the given
+-- address/port. The host may be 'Nothing' to signify that the socket
+-- should listen on all available addresses.
+newServer :: (Maybe HostName, ServiceName)
+          -> IO Network.Secure.Connection.Socket
+newServer (host, port) = do
+    addr <- getSockAddr host port
+    sock <- newSock
+    bindSocket sock addr
+    listen sock 10
+    return $ S sock
+
+-- |Accept one secure connection from a remote peer. The peer may
+-- authenticate as any of the given peer identities. A 'Connection' is
+-- returned iff the autentication completes successfully.
+accept :: LocalIdentity -> [PeerIdentity] -> Network.Secure.Connection.Socket 
+       -> IO Connection
+accept myId peerIds listenSock = do
+    result <- try setup :: IO (Either IOException Connection)
+    case result of
+        Left _     -> Network.Secure.Connection.accept myId peerIds listenSock
+        Right conn -> return conn
+  where
+    setup = do
+        sock <- fst <$> Network.Socket.accept (unSocket listenSock)
+        connectSSL myId peerIds True sock
+
+getSockAddr :: Maybe HostName -> ServiceName -> IO SockAddr
+getSockAddr hn sn = do
+    let hints = defaultHints { addrFlags = [AI_PASSIVE, AI_ADDRCONFIG] }
+    info <- getAddrInfo (Just hints) hn (Just sn)
+    return . addrAddress . head $ info
+
+connectSSL :: LocalIdentity -> [PeerIdentity] -> Bool -> Network.Socket.Socket
+           -> IO Connection
+connectSSL myId peerIds isServer sock = do
+    sslCtx <- newSSLContext myId peerIds
+    conn <- connection sslCtx sock
+    flip onException (shutdown conn Unidirectional) $ do
+        initiate conn
+        verifyConnection conn >>= flip unless (fail "Peer verification error")
+        peerId <- fromX509 . fromJust <$> getPeerCertificate conn
+        return $ C conn peerId
+  where
+    verifyConnection conn = do
+        verified <- getVerifyResult conn
+        if not verified then return False else
+            getPeerCertificate conn >>= \c -> case c of
+                Nothing   -> return False
+                Just cert -> do
+                    let match = liftM (EQ ==) . compareX509 cert . piX509
+                    anyM match peerIds
+    initiate = if isServer
+               then OpenSSL.Session.accept
+               else OpenSSL.Session.connect
+
+newSock :: IO Network.Socket.Socket
+newSock = socket AF_INET Stream defaultProtocol
+
+newSSLContext :: LocalIdentity -> [PeerIdentity] -> IO SSLContext
+newSSLContext localId validCerts = do
+    ctx <- context
+    contextSetPrivateKey ctx (liKey localId)
+    contextSetCertificate ctx (liX509 localId)
+    contextSetCiphers ctx "AES256-SHA"
+    contextSetVerificationMode ctx $ VerifyPeer True False
+    store <- contextGetCAStore ctx
+    mapM_ (addCertToStore store . piX509) validCerts
+    return ctx
+
+anyM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool
+anyM _ []        = return False
+anyM test (x:xs) = test x >>= \r -> if r then return True else anyM test xs
diff --git a/Network/Secure/Identity.hs b/Network/Secure/Identity.hs
new file mode 100644
--- /dev/null
+++ b/Network/Secure/Identity.hs
@@ -0,0 +1,132 @@
+module Network.Secure.Identity
+    (
+      PeerIdentity
+    , readPeerIdentity
+    , writePeerIdentity
+      
+    , LocalIdentity
+    , readLocalIdentity
+    , writeLocalIdentity
+    , toPeerIdentity
+    , newLocalIdentity
+      
+    , piX509
+    , liX509
+    , liKey
+    , fromX509
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Exception (bracket)
+import Control.Monad (when)
+import Data.ByteString (ByteString, append, hPut)
+import Data.ByteString.Char8 (pack, unpack)
+import Data.Maybe (fromJust, isNothing)
+import HSH
+import OpenSSL.EVP.PKey (toKeyPair)
+import OpenSSL.PEM (PemPasswordSupply(PwNone), readPrivateKey,
+                    writePKCS8PrivateKey, readX509, writeX509)
+import OpenSSL.RSA (RSAKeyPair)
+import OpenSSL.Session (context, contextSetPrivateKey,
+                        contextSetCertificate, contextCheckPrivateKey)
+import OpenSSL.X509 (X509, compareX509)
+import System.Directory (getTemporaryDirectory, removeFile)
+import System.IO (openBinaryTempFile, hFlush)
+import System.IO.Unsafe (unsafePerformIO)
+
+-- |The public identity of a peer. This kind of identity can be used
+-- to authenticate the remote ends of connections.
+newtype PeerIdentity = PI {
+      piX509 :: X509
+    }
+
+-- |Serialize a 'PeerIdentity' to a 'ByteString' for storage or
+-- transmission.
+writePeerIdentity :: PeerIdentity -> IO ByteString
+writePeerIdentity (PI cert) = pack <$> writeX509 cert
+
+-- |Read back a 'PeerIdentity' previously serialized with
+-- 'writePeerIdentity'.
+readPeerIdentity :: ByteString -> IO PeerIdentity
+readPeerIdentity = fmap PI . readX509 . unpack
+
+instance Eq PeerIdentity where
+    a == b = compare a b == EQ
+
+instance Ord PeerIdentity where
+    compare (PI a) (PI b) = unsafePerformIO $ compareX509 a b
+
+fromX509 :: X509 -> PeerIdentity
+fromX509 = PI
+
+-- |A local identity. This kind of identity can be used to
+-- authenticate /to/ remote ends of connections.
+data LocalIdentity = LI
+    {
+      liX509 :: X509 
+    , liKey  :: RSAKeyPair
+    }
+
+instance Eq LocalIdentity where
+    a == b = compare a b == EQ
+
+instance Ord LocalIdentity where
+    compare (LI c1 k1) (LI c2 k2) =
+        case compare (PI c1) (PI c2) of
+            EQ -> compare k1 k2
+            GT -> GT
+            LT -> LT
+
+-- |Serialize a 'LocalIdentity' to a 'ByteString' for storage.
+writeLocalIdentity :: LocalIdentity -> IO ByteString
+writeLocalIdentity (LI cert key) = do
+    c <- writeX509 cert
+    k <- writePKCS8PrivateKey key Nothing
+    return $ pack (c ++ k)
+
+-- |Read back a 'LocalIdentity' previously serialized with
+-- 'writeLocalIdentity'.
+readLocalIdentity :: ByteString -> IO LocalIdentity
+readLocalIdentity b = do
+    let s = unpack b
+    cert <- readX509 s
+    key <- toKeyPair <$> readPrivateKey s PwNone
+    when (isNothing key) $ fail "Bad private key"
+    certMatchesKey cert (fromJust key) >>= \r ->
+        if r
+        then return $ LI cert (fromJust key)
+        else fail "Cert and key don't match"
+
+-- |Extract the public parts of a 'LocalIdentity' into a
+-- 'PeerIdentity' suitable for sharing with peers. The resulting
+-- 'PeerIdentity' will allow them to verify your identity when you
+-- authenticate using the corresponding 'LocalIdentity'.
+toPeerIdentity :: LocalIdentity -> PeerIdentity
+toPeerIdentity (LI cert _) = PI cert
+
+-- |Generate a new 'LocalIdentity', giving it an identifying name and
+-- a validity period in days.
+--
+-- Note that this function may take quite a while to execute, as it is
+-- generating key material for the identity.
+newLocalIdentity :: String -> Int -> IO LocalIdentity
+newLocalIdentity commonName days = bracket mkKeyFile rmKeyFile $ \(p,h) -> do
+    key <- run genKey
+    hPut h key >> hFlush h
+    cert <- run $ genCert p
+    readLocalIdentity $ append key cert
+  where
+    mkKeyFile = getTemporaryDirectory >>= flip openBinaryTempFile "key.pem"
+    rmKeyFile = removeFile . fst
+    genKey = "openssl genrsa 4096 2>/dev/null"
+    genCert p = ("openssl", ["req", "-batch", "-new", "-x509",
+                             "-key", p, "-nodes",
+                             "-subj", "/CN=" ++ commonName,
+                             "-days", show days])
+
+certMatchesKey :: X509 -> RSAKeyPair -> IO Bool
+certMatchesKey cert key = do
+    ctx <- context
+    contextSetPrivateKey ctx key
+    contextSetCertificate ctx cert
+    contextCheckPrivateKey ctx
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMain
diff --git a/secure-sockets.cabal b/secure-sockets.cabal
new file mode 100644
--- /dev/null
+++ b/secure-sockets.cabal
@@ -0,0 +1,34 @@
+Name:                secure-sockets
+Version:             1.0
+Synopsis:            Secure point-to-point connectivity library
+Description:
+    This library simplifies the task of securely connecting two
+    servers to each other, with strong authentication and
+    encryption on the wire.
+Homepage:            http://code.google.com/p/secure-hs/
+License:             BSD3
+License-file:        LICENSE
+Author:              David Anderson
+Maintainer:          dave@natulte.net
+Copyright:           Google Inc. 2010
+Stability:           Experimental
+Category:            Network
+Build-type:          Simple
+Cabal-version:       >=1.6
+
+Library
+  Exposed-modules:     Network.Secure
+  Other-modules:       Network.Secure.Identity
+                       Network.Secure.Connection
+  Build-depends:       base ==4.*,
+                       bytestring ==0.9.*,
+                       directory ==1.0.*,
+                       HSH ==2.0.*,
+                       HsOpenSSL ==0.8.*,
+                       network ==2.*
+  ghc-prof-options:    -auto-all
+  ghc-options:         -Wall -funbox-strict-fields -fwarn-tabs
+
+Source-repository      head
+  Type:                hg
+  Location:            http://secure-hs.googlecode.com/hg
