packages feed

commsec-keyexchange 0.1.2 → 0.2

raw patch · 5 files changed

+69/−51 lines, 5 filesdep ~DRBGdep ~commsecPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: DRBG, commsec

API changes (from Hackage documentation)

- Network.CommSec.KeyExchange: accept :: PortNumber -> [PublicKey] -> PrivateKey -> IO (PublicKey, Connection)
+ Network.CommSec.KeyExchange: accept :: PortNumber -> [PublicKey] -> PrivateKey -> Maybe HostName -> IO (PublicKey, Connection)

Files

Network/CommSec/KeyExchange.hs view
@@ -35,8 +35,10 @@ import Crypto.Util import Crypto.Modes (zeroIV) import Crypto.Hash.CryptoAPI+import Control.Exception (bracket) import Control.Monad import Control.Monad.CryptoRandom+import Data.Maybe (isNothing, fromMaybe) import qualified Codec.Crypto.RSA as RSA import qualified Data.ByteString as B import Data.ByteString (ByteString)@@ -66,31 +68,47 @@         -> PrivateKey         -> IO (PublicKey,Connection) connect host port thems us = do-    sockaddr <- resolve host port-    socket   <- Net.socket Net.AF_INET Net.Stream Net.defaultProtocol-    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-        ai <- Net.getAddrInfo (Just $ Net.defaultHints {-                                    Net.addrFamily = Net.AF_INET, Net.addrSocketType = Net.Stream } ) (Just h) (Just (show port))-        return (maybe (error $ "Could not resolve host " ++ h) Net.addrAddress (listToMaybe ai))+    ai       <- resolve1 Active (Just host) port+    socket   <- openSocket ai+    res      <- S.connect socket (Net.addrAddress ai) thems us+    case res of+      Nothing -> fail "Could not agree on a key."+      Just x  -> return x +data IsPassive = Passive | Active++-- | Return the first 'AddrInfo' suitable for establishing a+-- stream connection to the given host on the given port.+resolve1 :: IsPassive -> Maybe Net.HostName -> Net.PortNumber -> IO Net.AddrInfo+resolve1 psv h port = do+  let passiveFlag =+         case psv of+           Passive -> [Net.AI_PASSIVE]+           Active  -> []+      flags = Net.defaultHints+                { Net.addrSocketType = Net.Stream+                , Net.addrFlags      = passiveFlag ++ [Net.AI_ADDRCONFIG]+                }+  ais <- Net.getAddrInfo (Just flags) h (Just (show port))+  case ais of+    []   -> fail ("Could not resolve host " ++ fromMaybe "" h)+    ai:_ -> return ai++-- | Open a new socket given the parameters in an 'AddrInfo'+openSocket :: Net.AddrInfo -> IO Net.Socket+openSocket Net.AddrInfo{..} = Net.socket addrFamily addrSocketType addrProtocol+ -- |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 (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-    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)+accept :: Net.PortNumber -> [PublicKey] -> PrivateKey -> Maybe Net.HostName -> IO (PublicKey,Connection)+accept port thems us addr = do+    ai <- resolve1 Passive addr port+    bracket (openSocket ai) Net.close $ \sock -> do+      Net.setSocketOption sock Net.ReuseAddr 1+      Net.bind sock (Net.addrAddress ai)+      Net.listen sock 1+      mconn <- S.accept sock thems us+      case mconn of+          Nothing -> fail "Failed to perform key exchange"+          Just res -> return res
Network/CommSec/KeyExchange/Internal.hs view
@@ -41,11 +41,11 @@ theGenerator :: Integer theGenerator = 5 --- |Sign exponents:  Sign(q_y, Sha256(a | b))+-- |Sign exponents:  Sign(q_y, 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))+-- |Verify exponents and other party was signed as:  Sign(q_y, a | b) verifyExps :: Integer -> Integer -> ByteString -> PublicKey -> Bool verifyExps a b sig k = RSA.verify k (encodeExps a b) (fromStrict sig) @@ -53,11 +53,11 @@ 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@+-- |Get the secret value @x@ and a publicly sharable value+-- @theGenerator ^ x@ getXaX :: IO (Integer, Integer) getXaX = do-    g <- newGenIO :: IO HmacDRBG+    g <- newGenIO :: IO CtrDRBG     let (x,_) = throwLeft $ crandomR (1,thePrime-2) g         ax    = modexp theGenerator x thePrime     return (x,ax)@@ -67,7 +67,7 @@     let publicMe = encode . sha256 . encode . private_pub $ privateMe         mySig    = signExps ax ay privateMe         plaintext = B.append publicMe mySig-    in fst .  ctr aesKey zeroIV $ plaintext+    in fst . ctr aesKey zeroIV $ plaintext  parseSigMessage :: AESKey -> [PublicKey] -> ByteString -> Integer -> Integer -> Maybe PublicKey parseSigMessage aesKey thems enc ax ay =@@ -90,7 +90,7 @@ -- -- 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+-- and the contexts created.  If the initiator does not use one of -- these keys then @Nothing@ is returned. keyExchangeResp :: Net.Socket                 -> [PublicKey]@@ -128,11 +128,12 @@ -- -- 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+-- and the contexts created.  If the responder does not use one of -- these keys then @Nothing@ is returned. -- -- The current design assumes the responder accepts our signature ---- the responder can reject our signature silently.+-- the responder could reject our signature silently and this funcition+-- would complete successfully. keyExchangeInit :: Net.Socket                 -> [PublicKey]                 -> PrivateKey
Network/CommSec/KeyExchange/Socket.hs view
@@ -9,12 +9,14 @@     , CS.close     ) where -import Network.CommSec-import qualified Network.Socket as Net import Control.Concurrent.MVar import Crypto.Types.PubKey.RSA+import Data.Traversable (traverse)+import Network.CommSec import Network.CommSec.KeyExchange.Internal as I import qualified Network.CommSec as CS+import qualified Network.CommSec.Package as CSP+import qualified Network.Socket as Net  connect :: Net.Socket         -> Net.SockAddr@@ -25,12 +27,7 @@     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{..}))+    traverse (wrapContexts socket) res  accept  :: Net.Socket         -> [PublicKey]@@ -40,9 +37,11 @@     (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 {..}))+    traverse (wrapContexts socket) res++-- Helper function for wrapping up the results of a key exchange into a 'Connection'+wrapContexts :: Net.Socket -> (PublicKey, CSP.OutContext, CSP.InContext) -> IO (PublicKey, Connection)+wrapContexts socket (t, oCtx, iCtx) = do+  outCtx <- newMVar oCtx+  inCtx  <- newMVar iCtx+  return (t, Conn {..})
Test/test.hs view
@@ -18,7 +18,7 @@ readSSHKeys :: FilePath -> IO (PublicKey, PrivateKey) readSSHKeys fp = do     OpenSshPublicKeyRsa pub _ <- (either error id . decodePublic) `fmap` B.readFile (fp <.> "pub")-    OpenSshPrivateKeyRsa priv <- (either error id . (\x -> decodePrivate x Nothing)) `fmap` B.readFile fp+    OpenSshPrivateKeyRsa priv <- (either error id . (\x -> decodePrivate x)) `fmap` B.readFile fp     return (pub,priv)  main = do
commsec-keyexchange.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                commsec-keyexchange-version:             0.1.2+version:             0.2 synopsis:            Key agreement for commsec. description:         Use RSA keys to authenticate a key exchange to                      establish a commsec 'Connection'.  This package comes with@@ -30,9 +30,9 @@                        cipher-aes128,                        crypto-api >= 0.12.2,                        bytestring,-                       commsec >= 0.2.5,+                       commsec >= 0.3,                        RSA,                        cryptohash-cryptoapi,-                       DRBG,+                       DRBG >= 0.5,                        monadcryptorandom,                        cereal