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
open Util
let len_off = 0
let get_pkt_len buf =
String.get_int32_be buf len_off |> Int32.to_int
let set_pkt_len buf v =
Bytes.set_int32_be buf len_off (Int32.of_int v)
let pad_len_off = 4
let get_pad_len buf = String.get_uint8 buf pad_len_off
let set_pad_len buf v = Bytes.set_uint8 buf pad_len_off v
let sizeof_pkt_hdr = 5
let get_payload buf =
let* () = guard (String.length buf >= 5) "Buf too short" in
let pkt_len = get_pkt_len buf in
let pad_len = get_pad_len buf in
let* () = guard (pkt_len > 0 && pkt_len < Ssh.max_pkt_len) "Bogus pkt len" in
let* () = guard (pad_len < pkt_len) "Bogus pad len" in
let* () = guard (String.length buf = pkt_len + 4) "Bogus buf len" in
let payload_len = pkt_len - pad_len - 1 in
let* () = guard (payload_len > 0) "Bogus payload_len" in
let payload = String.sub buf 5 payload_len in
Ok payload
let hmac mac seq buf =
let hmac = mac.Hmac.hmac in
let key = mac.Hmac.key in
let seqbuf = Bytes.create 4 in
Bytes.set_int32_be seqbuf 0 seq;
Hmac.hmacv hmac ~key [ Bytes.unsafe_to_string seqbuf; buf ]
let peek_len cipher seq block_len buf =
assert (block_len <= String.length buf);
let buf =
if block_len = String.length buf then
buf
else
String.sub buf 0 block_len
in
let* hdr, _ = Cipher.decrypt ~len:true seq cipher buf in
Ok (get_pkt_len hdr)
let partial buf =
if String.length buf < Ssh.max_pkt_len then
Ok None
else
Error "Buffer is too big"
let to_msg pkt =
Result.bind (get_payload pkt) Wire.get_message
let decrypt keys buf =
let open Ssh in
let cipher = keys.Kex.cipher in
let mac = keys.Kex.mac in
let seq = keys.Kex.seq in
let block_len = max 8 (Cipher.block_len cipher.Cipher.cipher) in
let digest_len = Hmac.(digest_len mac.hmac)
and mac_len = Cipher.(mac_len cipher.Cipher.cipher)
in
if String.length buf < max sizeof_pkt_hdr (digest_len + mac_len + block_len) then
partial buf
else
let* pkt_len = peek_len cipher seq block_len buf in
let* () =
guard (pkt_len > 0 && pkt_len < max_pkt_len) "decrypt: Bogus pkt len"
in
if String.length buf < pkt_len + 4 + digest_len + mac_len then
partial buf
else
let pkt_enc, digest1 =
String.sub buf 0 (pkt_len + 4 + mac_len),
String.sub buf (pkt_len + 4 + mac_len) digest_len
in
let tx_rx = Int64.(add keys.Kex.tx_rx (String.length pkt_enc - mac_len |> of_int)) in
let* pkt_dec, cipher = Cipher.decrypt ~len:false seq cipher pkt_enc in
let digest2 = hmac mac seq pkt_dec in
let* () =
guard (String.equal digest1 digest2) "decrypt: Bad digest"
in
let pad_len = get_pad_len pkt_dec in
let* () =
guard (pad_len >= 4 && pad_len <= 255 && pad_len < pkt_len)
"decrypt: Bogus pad len"
in
let buf =
let off = 4 + pkt_len + mac_len + digest_len in
String.sub buf off (String.length buf - off)
in
let keys = Kex.{ cipher; mac; seq = Int32.succ keys.Kex.seq; tx_rx } in
Ok (Some (pkt_dec, buf, keys))
let encrypt keys msg =
let cipher = keys.Kex.cipher in
let mac = keys.Kex.mac in
let seq = keys.Kex.seq in
let block_len = max 8 (Cipher.block_len cipher.Cipher.cipher) in
let buf = Buffer.create 14 in
Wire.put_message buf msg;
let off = Buffer.length buf + sizeof_pkt_hdr in
let len = if Cipher.aead cipher.Cipher.cipher then off - 4 else off in
let padlen =
let x = block_len - (len mod block_len) in
if x < 4 then x + block_len else x
in
assert (padlen >= 4 && padlen <= 255);
Wire.put_random buf padlen;
let bytes = Bytes.create (Buffer.length buf + sizeof_pkt_hdr) in
Buffer.blit buf 0 bytes sizeof_pkt_hdr (Buffer.length buf);
set_pkt_len bytes (Buffer.length buf + 1);
set_pad_len bytes padlen;
let pkt = Bytes.unsafe_to_string bytes in
let digest = hmac mac seq pkt in
let enc, cipher = Cipher.encrypt ~len:false seq cipher pkt in
let packet = enc ^ digest in
let tx_rx = Int64.add keys.Kex.tx_rx (String.length packet |> Int64.of_int) in
let keys = Kex.{ cipher; mac; seq = Int32.succ keys.Kex.seq; tx_rx } in
packet, keys