SSH server library, fork of Hackage one but hoping to get patches upstream
Clone
HTTPS:
darcs clone https://vervis.peers.community/repos/6r4Ao
SSH:
darcs clone USERNAME@vervis.peers.community:6r4Ao
Tags
TODO
Sender.hs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | module Network.SSH.Internal.Sender
( SenderState (..)
, SenderMessage (..)
, Sender (..)
, sender
, encrypt
)
where
import Control.Concurrent.Chan (Chan, readChan)
import Control.Monad (replicateM)
import Data.Binary.Put (Put, runPut)
import Data.Word (Word8, Word32)
import System.IO (Handle, hFlush)
import System.Random (randomRIO)
import qualified Codec.Crypto.SimpleAES as A
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Network.SSH.Internal.Crypto
import Network.SSH.Internal.Debug (dump)
import Network.SSH.Internal.NetPut
data SenderState
= NoKeys
{ senderThem :: Handle
, senderOutSeq :: Word32
}
| GotKeys
{ senderThem :: Handle
, senderOutSeq :: Word32
, senderEncrypting :: Bool
, senderCipher :: Cipher
, senderKey :: BS.ByteString
, senderVector :: BS.ByteString
, senderHMAC :: HMAC
}
data SenderMessage
= Prepare Cipher BS.ByteString BS.ByteString HMAC
| StartEncrypting
| Send LBS.ByteString
| Stop
class Sender a where
send :: SenderMessage -> a ()
sendPacket :: Put -> a ()
sendPacket = send . Send . runPut
sender :: Chan SenderMessage -> SenderState -> IO ()
sender ms ss = do
m <- readChan ms
case m of
Stop -> return ()
Prepare cipher key iv hmac -> do
dump ("initiating encryption", key, iv)
sender ms (GotKeys (senderThem ss) (senderOutSeq ss) False cipher key iv hmac)
StartEncrypting -> do
dump ("starting encryption")
sender ms (ss { senderEncrypting = True })
Send msg -> do
pad <- fmap (LBS.pack . map fromIntegral) $
replicateM (fromIntegral $ paddingLen msg) (randomRIO (0, 255 :: Int))
let f = full msg pad
case ss of
GotKeys h os True cipher key iv (HMAC _ mac) -> do
dump ("sending encrypted", os, f)
let (encrypted, newVector) = encrypt cipher key iv f
LBS.hPut h . LBS.concat $
[ encrypted
, mac . runPut $ long os >> raw f
]
hFlush h
sender ms $ ss
{ senderOutSeq = senderOutSeq ss + 1
, senderVector = newVector
}
_ -> do
dump ("sending unencrypted", senderOutSeq ss, f)
LBS.hPut (senderThem ss) f
hFlush (senderThem ss)
sender ms (ss { senderOutSeq = senderOutSeq ss + 1 })
where
blockSize =
case ss of
GotKeys { senderCipher = Cipher _ _ bs _ }
| bs > 8 -> bs
_ -> 8
full msg pad = runPut $ do
long (len msg)
byte (paddingLen msg)
raw msg
raw pad
len :: LBS.ByteString -> Word32
len msg = 1 + fromIntegral (LBS.length msg) + fromIntegral (paddingLen msg)
paddingNeeded :: LBS.ByteString -> Word8
paddingNeeded msg = fromIntegral blockSize - (fromIntegral $ (5 + LBS.length msg) `mod` fromIntegral blockSize)
paddingLen :: LBS.ByteString -> Word8
paddingLen msg =
if paddingNeeded msg < 4
then paddingNeeded msg + fromIntegral blockSize
else paddingNeeded msg
encrypt :: Cipher -> BS.ByteString -> BS.ByteString -> LBS.ByteString -> (LBS.ByteString, BS.ByteString)
encrypt (Cipher AES CBC bs _) key vector m =
( fromBlocks encrypted
, case encrypted of
(_:_) -> LBS.toStrict (last encrypted)
[] -> error ("encrypted data empty for `" ++ show m ++ "' in encrypt") vector
)
where
encrypted = toBlocks bs $ A.crypt A.CBC key vector A.Encrypt m
|