-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathExample.hs
163 lines (134 loc) · 4.34 KB
/
Example.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import Prelude
import System.Environment (getArgs)
import Network.Socket hiding (recv)
import Network.Socket.ByteString (recv, sendAll)
import Control.Concurrent
import Control.Concurrent.MVar
import Data.Monoid ((<>))
import Data.Serialize
import Control.Monad.State
import Control.Applicative (liftA)
import Crypto.Error
import Crypto.PubKey.Curve25519
import Crypto.Random
import qualified Data.ByteArray as BA
import qualified Data.ByteArray.Encoding as BAE
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import Ratchet
data Input =
Keyboard BS.ByteString
| Wire BS.ByteString
port :: String
port = "12345"
host :: String
host = "localhost"
main :: IO ()
main = do
let port = "12345"
host = "localhost"
arg <- getArgs
case arg of
["server"] -> server
["client"] -> client
_ -> usage
maxSkipMsgs :: MaxSkipped
maxSkipMsgs = 64
client :: IO ()
client = withSocketsDo $ do
let stateBob = emptyRatchet maxSkipMsgs
(ssk, bobsKeyPair) <- readSharedData
mvar <- newEmptyMVar
addrinfos <- getAddrInfo Nothing (Just host) (Just port)
let serveraddr = head addrinfos
conn <- socket (addrFamily serveraddr) Stream defaultProtocol
connect conn (addrAddress serveraddr)
putStrLn "Connected to server."
forkFinally (wireLoop conn mvar) (\_ -> close conn)
forkIO $ inpLoop conn mvar
evalState' stateBob $ do
ratchetInitBob ssk bobsKeyPair
handle conn mvar
server :: IO ()
server = withSocketsDo $ do
let maxConnQueue = 1
stateAlice = emptyRatchet maxSkipMsgs
(ssk, bobsKeyPair) <- readSharedData
mvar <- newEmptyMVar
addrinfos <- getAddrInfo
(Just $ defaultHints {addrFlags = [AI_PASSIVE]})
Nothing
(Just port)
let serveraddr = head addrinfos
sock <- socket (addrFamily serveraddr) Stream defaultProtocol
setSocketOption sock ReuseAddr 1
bind sock (addrAddress serveraddr)
listen sock maxConnQueue
putStrLn "Waiting for client to connect..."
(conn, _) <- accept sock
putStrLn "Client connected."
forkFinally (wireLoop conn mvar) (\_ -> close conn)
forkIO $ inpLoop conn mvar
evalState' stateAlice $ do
ratchetInitAlice ssk (pubKey bobsKeyPair)
handle conn mvar
handle :: Socket -> MVar Input -> StateT StateRatchet IO ()
handle conn mvar = do
inp <- lift $ takeMVar mvar
case inp of
Keyboard str -> do
let adLen = 42
ad <- lift $ AssocData <$> getRandomBytes adLen
(mh, payload) <- ratchetEncrypt (PlainText $ BA.convert str) ad
case payload of
Left err ->
lift $ print err
Right cipher -> do
let msg = encode (mh, cipher, ad)
lift $ sendAll conn msg
Wire msg -> do
case decode msg of
Left err ->
lift $ putStrLn $ "Could not decode received data: " <> err
Right (mh, cipher, ad) -> do
dec <- ratchetDecrypt mh cipher ad
case dec of
Left err ->
lift $ print err
Right (PlainText plain) -> do
let pre = "\nMessage received: "
lift $ putStrLn $ pre <> show plain
handle conn mvar
inpLoop :: Socket -> MVar Input -> IO ()
inpLoop conn mvar = do
putStr "Type a message: "
str <- getLine
putMVar mvar $ Keyboard $ BSC.pack str
inpLoop conn mvar
wireLoop :: Socket -> MVar Input -> IO ()
wireLoop conn mvar = do
bs <- recv conn 1024
putMVar mvar $ Wire bs
wireLoop conn mvar
usage :: IO ()
usage = putStrLn "stack repl Example.hs \"server\" | \"client\""
-- helper functions
evalState' :: Monad m => s -> StateT s m a -> m a
evalState' = flip evalStateT
readSharedData :: IO (SharedSecretKey, DHKeyPair)
readSharedData = do
let extract pub priv = do
priv' <- secretKey priv
pub' <- publicKey pub
return (priv', pub')
let convert = BAE.convertFromBase BAE.Base64
Right bytes <- liftA convert $ BS.readFile "ssk"
let ssk = SharedSecretKey bytes
Right pub <- liftA convert $ BS.readFile "bob_public"
Right priv <- liftA convert $ BS.readFile "bob_private"
let keys = extract pub priv
case keys of
CryptoFailed err ->
undefined
CryptoPassed (priv', pub') ->
return (ssk, DHKeyPair priv' pub')