peyotls-0.1.6.2: src/Network/PeyoTLS/Run/Crypto.hs
{-# LANGUAGE OverloadedStrings, TupleSections, PackageImports #-}
module Network.PeyoTLS.Run.Crypto (
makeKeys, decrypt, encrypt, sha1, sha256,
Hash, Side(..), finishedHash ) where
import Prelude hiding (splitAt, take)
import Control.Arrow (first)
import Data.Bits (xor)
import Data.Word (Word16, Word64)
import "crypto-random" Crypto.Random (CPRG, cprgGenerate)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import qualified Codec.Bytable.BigEndian as B
import qualified Crypto.Hash.SHA1 as SHA1
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Crypto.Cipher.AES as AES
modNm :: String
modNm = "Network.PeyoTLS.Crypto"
type Hash = BS.ByteString -> BS.ByteString
sha1, sha256 :: (Hash, Int)
sha1 = (SHA1.hash, 20)
sha256 = (SHA256.hash, 32)
makeKeys :: Int -> BS.ByteString -> BS.ByteString -> BS.ByteString ->
(BS.ByteString, BS.ByteString, BS.ByteString, BS.ByteString, BS.ByteString)
makeKeys kl cr sr pms = let
kls = [kl, kl, 16, 16]
ms = take 48 . prf pms $ BS.concat ["master secret", cr, sr]
ems = prf ms $ BS.concat ["key expansion", sr, cr]
[cwmk, swmk, cwk, swk] = sep kls ems in (ms, cwmk, swmk, cwk, swk)
where
sep [] _ = []
sep (n : ns) bs
| BSL.null bs = []
| otherwise = let (x, bs') = splitAt n bs in x : sep ns bs'
prf :: BS.ByteString -> BS.ByteString -> BSL.ByteString
prf sk sd = BSL.fromChunks . ph $ hm sk sd
where
hm = hmac SHA256.hash 64
ph a = hm sk (a `BS.append` sd) : ph (hm sk a)
hmac :: (BS.ByteString -> BS.ByteString) -> Int ->
BS.ByteString -> BS.ByteString -> BS.ByteString
hmac hs bls sk =
hs . BS.append (BS.map (0x5c `xor`) k) .
hs . BS.append (BS.map (0x36 `xor`) k)
where
k = pd $ if BS.length sk > bls then hs sk else sk
pd bs = bs `BS.append` BS.replicate (bls - BS.length bs) 0
decrypt :: (Hash, Int) -> BS.ByteString -> BS.ByteString -> Word64 ->
BS.ByteString -> BS.ByteString -> Either String BS.ByteString
decrypt (hs, ml) k mk sn pre enc =
if rm == em && BS.all (== lst pln) pd
then Right b
else Left $ modNm ++ ".decrypt: bad MAC\n" ++ "\t"++ show b ++ "\n"
where
pln = uncurry (AES.decryptCBC $ AES.initAES k) $ BS.splitAt 16 enc
(up, pd) = BS.splitAt (BS.length pln - fromIntegral (lst pln) - 1) pln
(b, rm) = BS.splitAt (BS.length up - ml) up
em = calcMac hs mk sn $ pre `BS.append` B.addLen w16 b
lst "" = error $ modNm ++ ".decrypt"
lst bs = BS.last bs
encrypt :: CPRG g => (Hash, Int) -> BS.ByteString -> BS.ByteString -> Word64 ->
BS.ByteString -> BS.ByteString -> g -> (BS.ByteString, g)
encrypt (hs, _) k mk sn p m g = (, g') $
iv `BS.append` AES.encryptCBC (AES.initAES k) iv (pln `BS.append` pd)
where
(iv, g') = cprgGenerate 16 g
pln = m `BS.append` calcMac hs mk sn (p `BS.append` B.addLen w16 m)
l = 16 - (BS.length pln + 1) `mod` 16
pd = BS.replicate (l + 1) $ fromIntegral l
calcMac :: Hash -> BS.ByteString -> Word64 -> BS.ByteString -> BS.ByteString
calcMac hs mk sn m = hmac hs 64 mk $ B.encode sn `BS.append` m
data Side = Server | Client deriving (Show, Eq)
finishedHash :: Side -> BS.ByteString -> BS.ByteString -> BS.ByteString
finishedHash s hs ms = take 12 . prf ms . (`BS.append` hs) $ case s of
Client -> "client finished"
Server -> "server finished"
take :: Int -> BSL.ByteString -> BS.ByteString
take = (fst .) . splitAt
splitAt :: Int -> BSL.ByteString -> (BS.ByteString, BSL.ByteString)
splitAt n = first BSL.toStrict . BSL.splitAt (fromIntegral n)
w16 :: Word16; w16 = undefined