packages feed

commsec-keyexchange 0.1 → 0.1.1

raw patch · 3 files changed

+51/−10 lines, 3 filesPVP ok

version bump matches the API change (PVP)

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)

Files

Network/CommSec/KeyExchange.hs view
@@ -1,7 +1,29 @@ {-# LANGUAGE RecordWildCards, BangPatterns #-}+-- |This module provides an authenticated key exchange using the station to+-- station protocol and RSA signatures for authentication.+--+-- For example, after presharing ssh keys generated using ssh-keygen:+--+-- @+--  import Crypto.PubKey.OpenSsh+--  import qualified Data.ByteString as B+--  import Network.CommSec.KeyExchange+--+--  main = do+--      -- Step 1: (not shown) get file paths, host, and port somehow.+--      -- Step 2: Read in the keys+--      OpenSshPrivateKeyRsa priv <- (either error id . (\x -> decodePrivate x)) `fmap` B.readFile myPrivateKeyFile+--      OpenSshPublicKeyRsa them _ <- (either error id . decodePublic) `fmap` B.readFile theirPublicKeyFile+--+--      -- Step 3: Listen for and accept a connection (or connect to the listener)+--      if listener+--          then accept host port them priv+--          else connect host port them priv+-- @ module Network.CommSec.KeyExchange     ( connect     , accept+    , keyExchangeInit, keyExchangeResp     , CS.send, CS.recv, CS.Connection, Net.HostName, Net.PortNumber     ) where @@ -33,7 +55,7 @@ import Network.CommSec hiding (accept, connect) import Network.CommSec.Package (InContext(..), OutContext(..)) --- RFC 5114 section 2.3+-- |This prime is from RFC 5114 section 2.3 thePrime :: Integer thePrime = 0x87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597 @@ -41,17 +63,20 @@ theGenerator :: Integer theGenerator = 5 --- Sign exponents and atest to our ID:  Sign(q_y, Sha256(a | b | pk_x))+-- |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 | pk_x))+-- |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@@ -59,11 +84,16 @@         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 = ax * ay `mod` thePrime+    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) ]@@ -86,6 +116,11 @@            (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.@@ -96,7 +131,7 @@         decodePkg = runGet (do i <- get -- Integer                                e <- get -- Encrypted signature                                return (i,e))-        axy = ax * ay `mod` thePrime :: Integer+        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) ]@@ -118,10 +153,12 @@     sendMsg sock enc     return (outCtx, inCtx) +-- |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     sockaddr <- resolve host port-    socket <- Net.socket Net.AF_INET Net.Stream Net.defaultProtocol+    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@@ -136,6 +173,9 @@                                     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)) +-- |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     let sockaddr = Net.SockAddrInet port Net.iNADDR_ANY
Test/test.hs view
@@ -6,6 +6,7 @@ import qualified Data.ByteString as B import Data.ByteString.Char8 () import Crypto.PubKey.OpenSsh+import Crypto.Types.PubKey.RSA import Control.Concurrent import System.FilePath @@ -14,10 +15,10 @@ port = 1874 host = "127.0.0.1" -readSSHKeys :: FilePath -> IO (OpenSshPublicKey, OpenSshPrivateKey)+readSSHKeys :: FilePath -> IO (PublicKey, PrivateKey) readSSHKeys fp = do-    pub  <- (either error id . decodePublic) `fmap` B.readFile (fp <.> "pub")-    priv <- (either error id . (\x -> decodePrivate x Nothing)) `fmap` B.readFile fp+    OpenSshPublicKeyRsa pub _ <- (either error id . decodePublic) `fmap` B.readFile (fp <.> "pub")+    OpenSshPrivateKeyRsa priv <- (either error id . (\x -> decodePrivate x Nothing)) `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+version:             0.1.1 synopsis:            Key agreement for commsec. description:         Use RSA keys to authenticate a key exchange to                      establish a commsec 'Connection'.  This package comes with