2010-09-09 21:47:19 +00:00
|
|
|
-- |
|
|
|
|
-- Module : Network.TLS.Sending
|
|
|
|
-- License : BSD-style
|
|
|
|
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
|
|
|
|
-- Stability : experimental
|
|
|
|
-- Portability : unknown
|
|
|
|
--
|
|
|
|
-- the Sending module contains calls related to marshalling packets according
|
|
|
|
-- to the TLS state
|
|
|
|
--
|
|
|
|
module Network.TLS.Sending (
|
|
|
|
writePacket
|
|
|
|
) where
|
|
|
|
|
2011-08-12 17:41:49 +00:00
|
|
|
import Control.Applicative ((<$>))
|
2010-09-09 21:47:19 +00:00
|
|
|
import Control.Monad.State
|
|
|
|
|
2010-09-26 09:34:47 +00:00
|
|
|
import Data.ByteString (ByteString)
|
2010-09-09 21:47:19 +00:00
|
|
|
import qualified Data.ByteString as B
|
|
|
|
|
2010-09-26 17:51:23 +00:00
|
|
|
import Network.TLS.Util
|
2010-09-26 13:57:35 +00:00
|
|
|
import Network.TLS.Cap
|
2010-09-26 09:34:47 +00:00
|
|
|
import Network.TLS.Wire
|
2010-09-09 21:47:19 +00:00
|
|
|
import Network.TLS.Struct
|
2011-08-12 17:41:49 +00:00
|
|
|
import Network.TLS.Record
|
2010-09-09 21:47:19 +00:00
|
|
|
import Network.TLS.Packet
|
|
|
|
import Network.TLS.State
|
|
|
|
import Network.TLS.Cipher
|
2011-08-12 17:41:49 +00:00
|
|
|
import Network.TLS.Compression
|
2010-09-09 21:47:19 +00:00
|
|
|
import Network.TLS.Crypto
|
|
|
|
|
|
|
|
{-
|
|
|
|
- 'makePacketData' create a Header and a content bytestring related to a packet
|
|
|
|
- this doesn't change any state
|
|
|
|
-}
|
2011-08-12 17:41:49 +00:00
|
|
|
makeRecord :: Packet -> TLSSt (Record Plaintext)
|
|
|
|
makeRecord pkt = do
|
|
|
|
ver <- stVersion <$> get
|
2010-09-09 21:47:19 +00:00
|
|
|
content <- writePacketContent pkt
|
2011-08-12 17:41:49 +00:00
|
|
|
return $ Record (packetType pkt) ver (fragmentPlaintext content)
|
2010-09-09 21:47:19 +00:00
|
|
|
|
|
|
|
{-
|
|
|
|
- Handshake data need to update a digest
|
|
|
|
-}
|
2011-08-12 17:41:49 +00:00
|
|
|
processRecord :: Record Plaintext -> TLSSt (Record Plaintext)
|
|
|
|
processRecord record@(Record ty _ fragment) = do
|
|
|
|
when (ty == ProtocolType_Handshake) (updateHandshakeDigest $ fragmentGetBytes fragment)
|
|
|
|
return record
|
|
|
|
|
|
|
|
compressRecord :: Record Plaintext -> TLSSt (Record Compressed)
|
|
|
|
compressRecord record =
|
|
|
|
onRecordFragment record $ fragmentCompress $ \bytes -> do
|
|
|
|
withCompression $ compressionDeflate bytes
|
2010-09-09 21:47:19 +00:00
|
|
|
|
|
|
|
{-
|
|
|
|
- when Tx Encrypted is set, we pass the data through encryptContent, otherwise
|
|
|
|
- we just return the packet
|
|
|
|
-}
|
2011-08-12 17:41:49 +00:00
|
|
|
encryptRecord :: Record Compressed -> TLSSt (Record Ciphertext)
|
|
|
|
encryptRecord record = onRecordFragment record $ fragmentCipher $ \bytes -> do
|
2011-03-01 20:01:40 +00:00
|
|
|
st <- get
|
2010-09-09 21:47:19 +00:00
|
|
|
if stTxEncrypted st
|
2011-08-12 17:41:49 +00:00
|
|
|
then encryptContent record bytes
|
|
|
|
else return bytes
|
2010-09-09 21:47:19 +00:00
|
|
|
|
|
|
|
{-
|
|
|
|
- ChangeCipherSpec state change need to be handled after encryption otherwise
|
|
|
|
- its own packet would be encrypted with the new context, instead of beeing sent
|
|
|
|
- under the current context
|
|
|
|
-}
|
2011-08-12 17:41:49 +00:00
|
|
|
postprocessRecord :: Record Ciphertext -> TLSSt (Record Ciphertext)
|
|
|
|
postprocessRecord record@(Record ProtocolType_ChangeCipherSpec _ _) =
|
|
|
|
switchTxEncryption >> isClientContext >>= \cc -> when cc setKeyBlock >> return record
|
|
|
|
postprocessRecord record = return record
|
2010-09-09 21:47:19 +00:00
|
|
|
|
|
|
|
{-
|
|
|
|
- marshall packet data
|
|
|
|
-}
|
2011-08-12 17:41:49 +00:00
|
|
|
encodeRecord :: Record Ciphertext -> TLSSt ByteString
|
|
|
|
encodeRecord record = return $ B.concat [ encodeHeader hdr, content ]
|
|
|
|
where (hdr, content) = recordToRaw record
|
2010-09-09 21:47:19 +00:00
|
|
|
|
2010-10-02 21:02:37 +00:00
|
|
|
{-
|
|
|
|
- just update TLS state machine
|
|
|
|
-}
|
2011-06-10 20:24:46 +00:00
|
|
|
preProcessPacket :: Packet -> TLSSt ()
|
|
|
|
preProcessPacket (Alert _) = return ()
|
|
|
|
preProcessPacket (AppData _) = return ()
|
|
|
|
preProcessPacket (ChangeCipherSpec) = updateStatusCC True >> return () -- FIXME don't ignore this error just in case
|
|
|
|
preProcessPacket (Handshake hss) = forM_ hss $ \hs -> do
|
|
|
|
-- FIXME don't ignore this error
|
|
|
|
_ <- updateStatusHs (typeOfHandshake hs)
|
|
|
|
case hs of
|
|
|
|
Finished fdata -> updateVerifiedData True fdata
|
|
|
|
_ -> return ()
|
2010-09-09 21:47:19 +00:00
|
|
|
|
|
|
|
{-
|
|
|
|
- writePacket transform a packet into marshalled data related to current state
|
|
|
|
- and updating state on the go
|
|
|
|
-}
|
2011-03-01 20:01:40 +00:00
|
|
|
writePacket :: Packet -> TLSSt ByteString
|
2011-08-12 17:41:49 +00:00
|
|
|
writePacket pkt = do
|
|
|
|
preProcessPacket pkt
|
|
|
|
makeRecord pkt >>= processRecord >>= compressRecord >>= encryptRecord >>= postprocessRecord >>= encodeRecord
|
2010-09-09 21:47:19 +00:00
|
|
|
|
|
|
|
{------------------------------------------------------------------------------}
|
|
|
|
{- SENDING Helpers -}
|
|
|
|
{------------------------------------------------------------------------------}
|
|
|
|
|
|
|
|
{- if the RSA encryption fails we just return an empty bytestring, and let the protocol
|
|
|
|
- fail by itself; however it would be probably better to just report it since it's an internal problem.
|
|
|
|
-}
|
2011-03-01 20:01:40 +00:00
|
|
|
encryptRSA :: ByteString -> TLSSt ByteString
|
2010-09-09 21:47:19 +00:00
|
|
|
encryptRSA content = do
|
2011-03-01 20:01:40 +00:00
|
|
|
st <- get
|
2011-02-20 08:37:19 +00:00
|
|
|
let rsakey = fromJust "rsa public key" $ hstRSAPublicKey $ fromJust "handshake" $ stHandshake st
|
2011-04-11 18:54:21 +00:00
|
|
|
case withTLSRNG (stRandomGen st) (\g -> kxEncrypt g rsakey content) of
|
|
|
|
Left err -> fail ("rsa encrypt failed: " ++ show err)
|
|
|
|
Right (econtent, rng') -> put (st { stRandomGen = rng' }) >> return econtent
|
2010-09-09 21:47:19 +00:00
|
|
|
|
2011-08-12 17:41:49 +00:00
|
|
|
encryptContent :: Record Compressed -> ByteString -> TLSSt ByteString
|
|
|
|
encryptContent record content = do
|
|
|
|
digest <- makeDigest True (recordToHeader record) content
|
|
|
|
encryptData $ B.concat [content, digest]
|
2010-09-09 21:47:19 +00:00
|
|
|
|
2011-03-01 20:01:40 +00:00
|
|
|
encryptData :: ByteString -> TLSSt ByteString
|
2010-09-09 21:47:19 +00:00
|
|
|
encryptData content = do
|
2011-03-01 20:01:40 +00:00
|
|
|
st <- get
|
2010-09-09 21:47:19 +00:00
|
|
|
|
2011-02-20 08:37:19 +00:00
|
|
|
let cipher = fromJust "cipher" $ stCipher st
|
2011-08-13 10:06:23 +00:00
|
|
|
let bulk = cipherBulk cipher
|
2011-02-20 08:37:19 +00:00
|
|
|
let cst = fromJust "tx crypt state" $ stTxCryptState st
|
2010-09-09 21:47:19 +00:00
|
|
|
let padding_size = fromIntegral $ cipherPaddingSize cipher
|
|
|
|
|
2010-09-26 09:34:47 +00:00
|
|
|
let msg_len = B.length content
|
2010-09-09 21:47:19 +00:00
|
|
|
let padding = if padding_size > 0
|
|
|
|
then
|
|
|
|
let padbyte = padding_size - (msg_len `mod` padding_size) in
|
|
|
|
let padbyte' = if padbyte == 0 then padding_size else padbyte in
|
2010-09-26 09:34:47 +00:00
|
|
|
B.replicate padbyte' (fromIntegral (padbyte' - 1))
|
2010-09-09 21:47:19 +00:00
|
|
|
else
|
2010-09-26 09:34:47 +00:00
|
|
|
B.empty
|
|
|
|
let writekey = cstKey cst
|
2010-09-09 21:47:19 +00:00
|
|
|
|
2011-08-13 10:06:23 +00:00
|
|
|
case cipherF bulk of
|
2011-01-05 09:20:33 +00:00
|
|
|
CipherNoneF -> return content
|
2010-09-09 21:47:19 +00:00
|
|
|
CipherBlockF encrypt _ -> do
|
2011-05-13 07:10:13 +00:00
|
|
|
-- before TLS 1.1, the block cipher IV is made of the residual of the previous block.
|
|
|
|
iv <- if hasExplicitBlockIV $ stVersion st
|
2011-08-13 10:08:29 +00:00
|
|
|
then genTLSRandom (cipherIVSize bulk)
|
2011-05-13 07:10:13 +00:00
|
|
|
else return $ cstIV cst
|
2010-09-26 09:34:47 +00:00
|
|
|
let e = encrypt writekey iv (B.concat [ content, padding ])
|
2011-05-13 07:10:13 +00:00
|
|
|
if hasExplicitBlockIV $ stVersion st
|
|
|
|
then return $ B.concat [iv,e]
|
|
|
|
else do
|
2011-08-13 10:08:29 +00:00
|
|
|
let newiv = fromJust "new iv" $ takelast (cipherIVSize bulk) e
|
2011-05-13 07:10:13 +00:00
|
|
|
put $ st { stTxCryptState = Just $ cst { cstIV = newiv } }
|
|
|
|
return e
|
2010-09-09 21:47:19 +00:00
|
|
|
CipherStreamF initF encryptF _ -> do
|
2010-09-26 13:57:35 +00:00
|
|
|
let iv = cstIV cst
|
2010-09-09 21:47:19 +00:00
|
|
|
let (e, newiv) = encryptF (if iv /= B.empty then iv else initF writekey) content
|
2011-03-01 20:01:40 +00:00
|
|
|
put $ st { stTxCryptState = Just $ cst { cstIV = newiv } }
|
2010-09-09 21:47:19 +00:00
|
|
|
return e
|
|
|
|
|
2011-03-01 20:01:40 +00:00
|
|
|
writePacketContent :: Packet -> TLSSt ByteString
|
2011-06-10 20:24:46 +00:00
|
|
|
writePacketContent (Handshake hss) = return . B.concat =<< mapM makeContent hss where
|
|
|
|
makeContent hs@(ClientKeyXchg _ _) = do
|
|
|
|
ver <- get >>= return . stVersion
|
|
|
|
let premastersecret = runPut $ encodeHandshakeContent hs
|
|
|
|
setMasterSecret premastersecret
|
|
|
|
econtent <- encryptRSA premastersecret
|
|
|
|
|
|
|
|
let extralength =
|
|
|
|
if ver < TLS10
|
|
|
|
then B.empty
|
|
|
|
else runPut $ putWord16 $ fromIntegral $ B.length econtent
|
|
|
|
let hdr = runPut $ encodeHandshakeHeader (typeOfHandshake hs)
|
|
|
|
(fromIntegral (B.length econtent + B.length extralength))
|
|
|
|
return $ B.concat [hdr, extralength, econtent]
|
|
|
|
makeContent hs = return $ encodeHandshakes [hs]
|
|
|
|
|
|
|
|
writePacketContent (Alert a) = return $ encodeAlerts a
|
|
|
|
writePacketContent (ChangeCipherSpec) = return $ encodeChangeCipherSpec
|
|
|
|
writePacketContent (AppData x) = return x
|