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 +42/−24
- Network/CommSec/KeyExchange/Internal.hs +10/−9
- Network/CommSec/KeyExchange/Socket.hs +13/−14
- Test/test.hs +1/−1
- commsec-keyexchange.cabal +3/−3
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