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 +46/−6
- Test/test.hs +4/−3
- commsec-keyexchange.cabal +1/−1
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