packages feed

commsec-keyexchange 0.1.1 → 0.1.2

raw patch · 5 files changed

+286/−157 lines, 5 filesdep ~commsecdep ~crypto-apiPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: commsec, crypto-api

API changes (from Hackage documentation)

- Network.CommSec.KeyExchange: keyExchangeInit :: Socket -> PublicKey -> PrivateKey -> IO (OutContext, InContext)
- Network.CommSec.KeyExchange: keyExchangeResp :: Socket -> PublicKey -> PrivateKey -> IO (OutContext, InContext)
+ Network.CommSec.KeyExchange: close :: Connection -> IO ()
+ Network.CommSec.KeyExchange.Internal: instance Serialize PublicKey
+ Network.CommSec.KeyExchange.Internal: keyExchangeInit :: Socket -> [PublicKey] -> PrivateKey -> IO (Maybe (PublicKey, OutContext, InContext))
+ Network.CommSec.KeyExchange.Internal: keyExchangeResp :: Socket -> [PublicKey] -> PrivateKey -> IO (Maybe (PublicKey, OutContext, InContext))
+ Network.CommSec.KeyExchange.Socket: accept :: Socket -> [PublicKey] -> PrivateKey -> IO (Maybe (PublicKey, Connection))
+ Network.CommSec.KeyExchange.Socket: close :: Connection -> IO ()
+ Network.CommSec.KeyExchange.Socket: connect :: Socket -> SockAddr -> [PublicKey] -> PrivateKey -> IO (Maybe (PublicKey, Connection))
+ Network.CommSec.KeyExchange.Socket: data Connection :: *
+ Network.CommSec.KeyExchange.Socket: listen :: Socket -> Int -> IO ()
+ Network.CommSec.KeyExchange.Socket: recv :: Connection -> IO ByteString
+ Network.CommSec.KeyExchange.Socket: send :: Connection -> ByteString -> IO ()
+ Network.CommSec.KeyExchange.Socket: socket :: Family -> SocketType -> ProtocolNumber -> IO Socket
- Network.CommSec.KeyExchange: accept :: PortNumber -> PublicKey -> PrivateKey -> IO Connection
+ Network.CommSec.KeyExchange: accept :: PortNumber -> [PublicKey] -> PrivateKey -> IO (PublicKey, Connection)
- Network.CommSec.KeyExchange: connect :: HostName -> PortNumber -> PublicKey -> PrivateKey -> IO Connection
+ Network.CommSec.KeyExchange: connect :: HostName -> PortNumber -> [PublicKey] -> PrivateKey -> IO (PublicKey, Connection)

Files

Network/CommSec/KeyExchange.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE RecordWildCards, BangPatterns #-}+{-# LANGUAGE RecordWildCards, BangPatterns, OverloadedStrings #-} -- |This module provides an authenticated key exchange using the station to -- station protocol and RSA signatures for authentication. --@@ -23,8 +23,8 @@ module Network.CommSec.KeyExchange     ( connect     , accept-    , keyExchangeInit, keyExchangeResp-    , CS.send, CS.recv, CS.Connection, Net.HostName, Net.PortNumber+    , CS.send, CS.recv, CS.Connection, CS.close+    , Net.HostName, Net.PortNumber     ) where  import qualified Network.Socket as Net@@ -55,117 +55,21 @@ import Network.CommSec hiding (accept, connect) import Network.CommSec.Package (InContext(..), OutContext(..)) --- |This prime is from RFC 5114 section 2.3-thePrime :: Integer-thePrime = 0x87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597---- A common generator, refered to as "a" in literature.-theGenerator :: Integer-theGenerator = 5---- |Sign exponents:  Sign(q_y, Sha256(a | b))-signExps :: Integer -> Integer -> PrivateKey -> ByteString-signExps a b k = L.toStrict . RSA.sign k $ encodeExps a b---- |Verify exponents and other party was signed as:  Sign(q_y, Sha256(a | b))-verifyExps :: Integer -> Integer -> ByteString -> PublicKey -> Bool-verifyExps a b sig k = RSA.verify k (encodeExps a b) (fromStrict sig)---- |Serialize exponents in an agreed upon format-encodeExps :: Integer -> Integer -> L.ByteString-encodeExps a b = fromStrict . runPut $ put a >> put b---- |Get the secret value @x@ and a publicly sharable value @theGenerator--- ^ x@-getXaX :: IO (Integer, Integer)-getXaX = do-    g <- newGenIO :: IO HmacDRBG-    let (x,_) = throwLeft $ crandomR (1,thePrime-2) g-        ax    = modexp theGenerator x thePrime-    return (x,ax)---- |@keyExchangeResp sock them me@------ Act as the responder in an authenticated key exchange using the socket--- @sock@ as the communications channel, the public key @them@ to verify--- the end point and the private key @me@ to prove ourself.-keyExchangeResp :: Net.Socket -> PublicKey -> PrivateKey -> IO (OutContext, InContext)-keyExchangeResp sock publicThem privateMe = do-    (y,ay) <- getXaX-    ax     <- (either error id . decode) `fmap` recvMsg sock-    let axy = modexp ax y thePrime-        sharedSecret = encode . sha256 $ i2bs (2048 `div` 8) axy-        shared512    = expandSecret sharedSecret (16 + 16 + 4 + 4)-        -- Split the 512 bit secret into [ Key 1 (128b) | Key 2 (128b) | salt 1 (32b) | salt 2 (32 b) ]-        (aesKey1, aesKey2, salt1, salt2) =-            let (key1tmp, rest1)  = B.splitAt (keyLengthBytes `for` aesKey1) shared512-                (key2tmp, rest2)  = B.splitAt (keyLengthBytes `for` aesKey2) rest1-                (salt1tmp, rest3) = B.splitAt (sizeOf salt1) rest2-                salt2tmp          = B.take    (sizeOf salt2) rest3-                op = fromIntegral . bs2i-                bk = maybe (error "failed to build key") id . buildKey-            in (bk key1tmp, bk key2tmp, op salt1tmp, op salt2tmp)-        mySig    = signExps ay ax privateMe-        (enc, _) = ctr aesKey1 zeroIV mySig-        outCtx = Out 2 salt1 aesKey1-        inCtx  = InStrict 1 salt2 aesKey2-    sendMsg sock (runPut (put ay >> put enc))-    encSaAxAy <- recvMsg sock-    let theirSig = fst $ unCtr aesKey2 zeroIV encSaAxAy-    when (not $ verifyExps ax ay theirSig publicThem)-           (error "RESP: Verification failed when exchanging key.  Man in the middle?")-    return (outCtx, inCtx)---- |@keyExchangeInit sock them me@------ Act as the initiator in an authenticated key exchange using the socket--- @sock@ as the communications channel, the public key @them@ to verify--- the end point and the private key @me@ to prove ourself.-keyExchangeInit :: Net.Socket -> PublicKey -> PrivateKey -> IO (OutContext, InContext)-keyExchangeInit sock publicThem privateMe = do-    -- our secret big number, x, and a^x for exchange.-    (x,ax) <- getXaX-    sendMsg sock (encode ax)-    pkg <- recvMsg sock-    let (ay, encSbAyAx) = either error id (decodePkg pkg)-        decodePkg = runGet (do i <- get -- Integer-                               e <- get -- Encrypted signature-                               return (i,e))-        axy = modexp ay x thePrime :: Integer-        sharedSecret = encode . sha256 $ i2bs (2048 `div` 8) axy-        shared512    = expandSecret sharedSecret 64-        -- Split the 512 bit secret into [ Key 1 (128b) | Key 2 (128b) | salt 1 (32b) | salt 2 (32 b) ]-        (aesKey1, aesKey2, salt1, salt2) =-            let (key1tmp, rest1)  = B.splitAt (keyLengthBytes `for` aesKey1) shared512-                (key2tmp, rest2)  = B.splitAt (keyLengthBytes `for` aesKey2) rest1-                (salt1tmp, rest3) = B.splitAt (sizeOf salt1) rest2-                salt2tmp          = B.take    (sizeOf salt2) rest3-                op = fromIntegral . bs2i-                bk = maybe (error "failed to build key") id . buildKey-            in (bk key1tmp, bk key2tmp, op salt1tmp, op salt2tmp)-        mySig = signExps ax ay privateMe-        (enc, _) = ctr aesKey2 zeroIV mySig-        outCtx = Out 2 salt2 aesKey2-        inCtx  = InStrict 1 salt1 aesKey1-        theirSig = fst $ unCtr aesKey1 zeroIV encSbAyAx-    when (not $ verifyExps ay ax theirSig publicThem)-           (error "INIT: Verification failed when exchanging key.  Man in the middle?")-    sendMsg sock enc-    return (outCtx, inCtx)+import qualified Network.CommSec.KeyExchange.Internal as I+import qualified Network.CommSec.KeyExchange.Socket as S  -- |Connect to the specified host and port, establishing a secure, -- authenticated connection with a party holding the public key.-connect :: Net.HostName -> Net.PortNumber -> PublicKey -> PrivateKey -> IO Connection-connect host port them us = do+connect :: Net.HostName+        -> Net.PortNumber+        -> [PublicKey]+        -> PrivateKey+        -> IO (PublicKey,Connection)+connect host port thems us = do     sockaddr <- resolve host port     socket   <- Net.socket Net.AF_INET Net.Stream Net.defaultProtocol-    Net.connect socket sockaddr-    Net.setSocketOption socket Net.NoDelay 1-    Net.setSocketOption socket Net.ReuseAddr 1-    (oCtx, iCtx) <- keyExchangeInit socket them us-    inCtx  <- newMVar iCtx-    outCtx <- newMVar oCtx-    return (Conn {..})+    maybe (error "Could not agree on a key.") id+          `fmap` (S.connect socket sockaddr thems us)   where       resolve :: Net.HostName -> Net.PortNumber -> IO Net.SockAddr       resolve h port = do@@ -176,51 +80,17 @@ -- |Listen for and accept a connection on the host and port, establishing -- a secure, authenticated connection with a party holding the specified -- public key.-accept :: Net.PortNumber -> PublicKey -> PrivateKey -> IO Connection-accept port them us = do+accept :: Net.PortNumber -> [PublicKey] -> PrivateKey -> IO (PublicKey,Connection)+accept port thems us = do     let sockaddr = Net.SockAddrInet port Net.iNADDR_ANY     sock <- Net.socket Net.AF_INET Net.Stream Net.defaultProtocol     Net.setSocketOption sock Net.ReuseAddr 1     Net.bind sock sockaddr     Net.listen sock 1-    socket <- fst `fmap` Net.accept sock-    Net.setSocketOption socket Net.NoDelay 1-    Net.close sock-    (oCtx, iCtx) <- keyExchangeResp socket them us-    outCtx <- newMVar oCtx-    inCtx  <- newMVar iCtx-    return (Conn {..})--recvMsg :: Net.Socket -> IO ByteString-recvMsg s = do-    lenBS <- recvAll s 4-    let len = fromIntegral . either error id . runGet getWord32be $ lenBS-    recvAll s len--recvAll :: Net.Socket -> Int -> IO ByteString-recvAll s nr = go nr []-  where-    go 0 x = return $ B.concat (reverse x)-    go n x = do-        bs <- NetBS.recv s n-        go (n - B.length bs) (bs:x)--sendMsg :: Net.Socket -> ByteString -> IO ()-sendMsg s msg = do-    let pkt = B.append (runPut . putWord32be . fromIntegral . B.length $ msg) msg-    NetBS.sendAll s pkt--keyLengthBytes = fmap ((`div` 8) . (+7)) keyLength--sha256 :: ByteString -> SHA256-sha256 bs = hash' bs--modexp :: Integer -> Integer -> Integer -> Integer-modexp b e n = go 1 b e-  where-    go !p _ 0 = p-    go !p !x !e =-        if even e-          then go p (mod (x*x) n) (div e 2)-          else go (mod (p*x) n) x (pred e)-+    -- socket <- fst `fmap` Net.accept sock+    mconn  <- S.accept sock thems us+    case mconn of+        Nothing -> error "Failed to perform key exchange"+        Just (t,c) -> do+            Net.close sock+            return (t,c)
+ Network/CommSec/KeyExchange/Internal.hs view
@@ -0,0 +1,209 @@+{-# LANGUAGE RecordWildCards, BangPatterns, OverloadedStrings #-}+-- |This module provides an authenticated key exchange using the station to+-- station protocol and RSA signatures for authentication.+--+module Network.CommSec.KeyExchange.Internal+    ( keyExchangeInit, keyExchangeResp+    ) where++import qualified Network.Socket as Net+import qualified Network.Socket.ByteString as NetBS+import Crypto.Types.PubKey.RSA+import Crypto.Cipher.AES128+import Crypto.Classes+import Crypto.Util+import Crypto.Modes (zeroIV)+import Crypto.Hash.CryptoAPI+import Control.Monad+import Control.Monad.CryptoRandom+import qualified Codec.Crypto.RSA as RSA+import qualified Data.ByteString as B+import Data.ByteString (ByteString)+import Data.ByteString.Lazy (fromStrict, toChunks)+import qualified Data.ByteString.Lazy as L+import Data.Serialize+import Data.Serialize.Get+import Data.Serialize.Put+import Crypto.Random.DRBG+import Data.Maybe (listToMaybe)+import Control.Concurrent+import Foreign.Storable++-- For types+import Network.CommSec hiding (accept, connect)+import Network.CommSec.Package (InContext(..), OutContext(..))++-- |This prime is from RFC 5114 section 2.3+thePrime :: Integer+thePrime = 0x87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597++-- A common generator, refered to as "a" in literature.+theGenerator :: Integer+theGenerator = 5++-- |Sign exponents:  Sign(q_y, Sha256(a | b))+signExps :: Integer -> Integer -> PrivateKey -> ByteString+signExps a b k = L.toStrict . RSA.sign k $ encodeExps a b++-- |Verify exponents and other party was signed as:  Sign(q_y, Sha256(a | b))+verifyExps :: Integer -> Integer -> ByteString -> PublicKey -> Bool+verifyExps a b sig k = RSA.verify k (encodeExps a b) (fromStrict sig)++-- |Serialize exponents in an agreed upon format+encodeExps :: Integer -> Integer -> L.ByteString+encodeExps a b = fromStrict . runPut $ put a >> put b++-- |Get the secret value @x@ and a publicly sharable value @theGenerator+-- ^ x@+getXaX :: IO (Integer, Integer)+getXaX = do+    g <- newGenIO :: IO HmacDRBG+    let (x,_) = throwLeft $ crandomR (1,thePrime-2) g+        ax    = modexp theGenerator x thePrime+    return (x,ax)++buildSigMessage :: AESKey -> PrivateKey -> Integer -> Integer -> ByteString+buildSigMessage aesKey privateMe ax ay =+    let publicMe = encode . sha256 . encode . private_pub $ privateMe+        mySig    = signExps ax ay privateMe+        plaintext = B.append publicMe mySig+    in fst .  ctr aesKey zeroIV $ plaintext++parseSigMessage :: AESKey -> [PublicKey] -> ByteString -> Integer -> Integer -> Maybe PublicKey+parseSigMessage aesKey thems enc ax ay =+    let (pubHash, theirSig) = B.splitAt (256 `div` 8)+                            . fst . unCtr aesKey zeroIV+                            $ enc+        pubHashes = map (\k -> (encode (sha256 $ encode k),k)) thems+    in case lookup pubHash pubHashes of+            Just publicThem ->+                if (not $ verifyExps ax ay theirSig publicThem)+                    then Nothing+                    else Just publicThem+            Nothing -> Nothing++-- |@keyExchangeResp sock pubKeys me@+--+-- Act as the responder in an authenticated key exchange using the socket+-- @sock@ as the communications channel, the public keys @pubKeys@ to+-- verify the end point and the private key @me@ to prove ourself.+--+-- If the initiator uses one of the assocated public keys for+-- authentication, it will return the tuple of the public key used+-- and the contexts created.  If the initiator does not use on of+-- these keys then @Nothing@ is returned.+keyExchangeResp :: Net.Socket+                -> [PublicKey]+                -> PrivateKey+                -> IO (Maybe (PublicKey , OutContext, InContext))+keyExchangeResp sock thems privateMe = do+    ax     <- (either error id . decode) `fmap` recvMsg sock+    (y,ay) <- getXaX+    let axy = modexp ax y thePrime+        sharedSecret = encode . sha256 $ i2bs (2048 `div` 8) axy+        shared512    = expandSecret sharedSecret (16 + 16 + 4 + 4)+        -- Split the 512 bit secret into [ Key 1 (128b) | Key 2 (128b) | salt 1 (32b) | salt 2 (32 b) ]+        (aesKey1, aesKey2, salt1, salt2) =+            let (key1tmp, rest1)  = B.splitAt (keyLengthBytes `for` aesKey1) shared512+                (key2tmp, rest2)  = B.splitAt (keyLengthBytes `for` aesKey2) rest1+                (salt1tmp, rest3) = B.splitAt (sizeOf salt1) rest2+                salt2tmp          = B.take    (sizeOf salt2) rest3+                op = fromIntegral . bs2i+                bk = maybe (error "failed to build key") id . buildKey+            in (bk key1tmp, bk key2tmp, op salt1tmp, op salt2tmp)+        msg2     = buildSigMessage aesKey1 privateMe ay ax+        outCtx   = Out 2 salt1 aesKey1+        inCtx    = InStrict 1 salt2 aesKey2+    sendMsg sock (runPut $ put ay >> put msg2)+    encSaAxAy <- recvMsg sock+    case parseSigMessage aesKey2 thems encSaAxAy ax ay of+        Just t  -> return (Just (t, outCtx, inCtx))+        Nothing -> return Nothing++-- |@keyExchangeInit sock pubKeys me@+--+-- Act as the initiator in an authenticated key exchange using the socket+-- @sock@ as the communications channel, the public keys @pubKeys@ to+-- verify the end point and the private key @me@ to prove ourself.+--+-- If the responder uses one of the assocated public keys for+-- authentication, it will return the tuple of the public key used+-- and the contexts created.  If the responder does not use ond of+-- these keys then @Nothing@ is returned.+--+-- The current design assumes the responder accepts our signature -+-- the responder can reject our signature silently.+keyExchangeInit :: Net.Socket+                -> [PublicKey]+                -> PrivateKey+                -> IO (Maybe (PublicKey, OutContext, InContext))+keyExchangeInit sock thems privateMe = do+    -- our secret big number, x, and a^x for exchange.+    (x,ax) <- getXaX+    sendMsg sock (encode ax)+    msg2 <- recvMsg sock+    let (ay, encSbAyAx) = either error id (decodePkg msg2)+        decodePkg = runGet (do i <- get -- Integer+                               e <- get -- Encrypted signature+                               return (i,e))+        axy = modexp ay x thePrime :: Integer+        sharedSecret = encode . sha256 $ i2bs (2048 `div` 8) axy+        shared512    = expandSecret sharedSecret 64+        -- Split the 512 bit secret into [ Key 1 (128b) | Key 2 (128b) | salt 1 (32b) | salt 2 (32 b) ]+        (aesKey1, aesKey2, salt1, salt2) =+            let (key1tmp, rest1)  = B.splitAt (keyLengthBytes `for` aesKey1) shared512+                (key2tmp, rest2)  = B.splitAt (keyLengthBytes `for` aesKey2) rest1+                (salt1tmp, rest3) = B.splitAt (sizeOf salt1) rest2+                salt2tmp          = B.take    (sizeOf salt2) rest3+                op = fromIntegral . bs2i+                bk = maybe (error "failed to build key") id . buildKey+            in (bk key1tmp, bk key2tmp, op salt1tmp, op salt2tmp)+        msg3     = buildSigMessage aesKey2 privateMe ax ay+        outCtx   = Out 2 salt2 aesKey2+        inCtx    = InStrict 1 salt1 aesKey1+    case parseSigMessage aesKey1 thems encSbAyAx ay ax of+        Just t -> do+            sendMsg sock msg3+            return (Just (t,outCtx,inCtx))+        Nothing -> do+            sendMsg sock "FAIL"+            return Nothing++recvMsg :: Net.Socket -> IO ByteString+recvMsg s = do+    lenBS <- recvAll s 4+    let len = fromIntegral . either error id . runGet getWord32be $ lenBS+    recvAll s len++recvAll :: Net.Socket -> Int -> IO ByteString+recvAll s nr = go nr []+  where+    go 0 x = return $ B.concat (reverse x)+    go n x = do+        bs <- NetBS.recv s n+        go (n - B.length bs) (bs:x)++sendMsg :: Net.Socket -> ByteString -> IO ()+sendMsg s msg = do+    let pkt = B.append (runPut . putWord32be . fromIntegral . B.length $ msg) msg+    NetBS.sendAll s pkt++sha256 :: ByteString -> SHA256+sha256 bs = hash' bs++modexp :: Integer -> Integer -> Integer -> Integer+modexp b e n = go 1 b e+  where+    go !p _ 0 = p+    go !p !x !e =+        if even e+          then go p (mod (x*x) n) (div e 2)+          else go (mod (p*x) n) x (pred e)++instance Serialize PublicKey where+    put (PublicKey {..}) = put public_size >> put public_n >> put public_e+    get = do+        public_size <- get+        public_n <- get+        public_e <- get+        return (PublicKey {..})
+ Network/CommSec/KeyExchange/Socket.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE RecordWildCards #-}+module Network.CommSec.KeyExchange.Socket+    ( Network.CommSec.KeyExchange.Socket.connect+    , Network.CommSec.KeyExchange.Socket.accept+    , Net.listen, Net.socket+    , CS.send+    , CS.recv+    , CS.Connection+    , CS.close+    ) where++import Network.CommSec+import qualified Network.Socket as Net+import Control.Concurrent.MVar+import Crypto.Types.PubKey.RSA+import Network.CommSec.KeyExchange.Internal as I+import qualified Network.CommSec as CS++connect :: Net.Socket+        -> Net.SockAddr+        -> [PublicKey]+        -> PrivateKey+        -> IO (Maybe (PublicKey, Connection))+connect socket addr pubKeys privateMe = do+    Net.connect socket addr+    Net.setSocketOption socket Net.NoDelay 1+    res <- I.keyExchangeInit socket pubKeys privateMe+    case res of+      Nothing -> return Nothing+      Just (t,oCtx,iCtx) -> do+          inCtx  <- newMVar iCtx+          outCtx <- newMVar oCtx+          return (Just (t,Conn{..}))++accept  :: Net.Socket+        -> [PublicKey]+        -> PrivateKey+        -> IO (Maybe (PublicKey, Connection))+accept sock pubKeys privateMe = do+    (socket,_) <- Net.accept sock+    Net.setSocketOption socket Net.NoDelay 1+    res <- keyExchangeResp socket pubKeys privateMe+    case res of+      Nothing -> return Nothing+      Just (t, oCtx, iCtx) -> do+          outCtx <- newMVar oCtx+          inCtx  <- newMVar iCtx+          return (Just (t, Conn {..}))
Test/test.hs view
@@ -34,13 +34,13 @@     connecter privB pubA  listener priv pub = do-    conn <- accept port pub priv+    conn <- snd `fmap` accept port [pub] priv     recv conn >>= print     send conn "Hello to you too!"     return ()  connecter priv pub = do-    conn <- connect host port pub priv+    conn <- snd `fmap` connect host port [pub] priv     send conn "Hello!"     recv conn >>= print     return ()
commsec-keyexchange.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                commsec-keyexchange-version:             0.1.1+version:             0.1.2 synopsis:            Key agreement for commsec. description:         Use RSA keys to authenticate a key exchange to                      establish a commsec 'Connection'.  This package comes with@@ -21,14 +21,16 @@  library   exposed-modules:     Network.CommSec.KeyExchange+                     , Network.CommSec.KeyExchange.Socket+                     , Network.CommSec.KeyExchange.Internal   -- other-modules:          build-depends:       base <5,                        network,                        crypto-pubkey-types,                        cipher-aes128,-                       crypto-api >= 0.12,+                       crypto-api >= 0.12.2,                        bytestring,-                       commsec >= 0.2.4,+                       commsec >= 0.2.5,                        RSA,                        cryptohash-cryptoapi,                        DRBG,