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
open Util
let len_off = 0
let get_pkt_len_str buf =
String.get_int32_be buf len_off |> Int32.to_int
let get_pkt_len buf =
Cstruct.BE.get_uint32 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_str buf = String.get_uint8 buf pad_len_off
let get_pad_len buf = Cstruct.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 (Cstruct.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 (Cstruct.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 = Cstruct.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 <= Cstruct.length buf);
let buf = Cstruct.to_string buf ~off:0 ~len:block_len in
let* hdr, _ = Cipher.decrypt ~len:true seq cipher buf in
Ok (get_pkt_len_str hdr)
let partial buf =
if Cstruct.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 Cstruct.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 Cstruct.length buf < pkt_len + 4 + digest_len + mac_len then
partial buf
else
let pkt_enc, digest1 =
Cstruct.to_string buf ~off:0 ~len:(pkt_len + 4 + mac_len),
Cstruct.to_string buf ~off:(pkt_len + 4 + mac_len) ~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_str pkt_dec in
let* () =
guard (pad_len >= 4 && pad_len <= 255 && pad_len < pkt_len)
"decrypt: Bogus pad len"
in
let buf = Cstruct.shift buf (4 + pkt_len + mac_len + digest_len) in
let keys = Kex.{ cipher; mac; seq = Int32.succ keys.Kex.seq; tx_rx } in
Ok (Some (Cstruct.of_string 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 = Dbuf.reserve sizeof_pkt_hdr (Dbuf.create ()) |> Wire.put_message msg in
let len = Dbuf.used buf in
let len = if Cipher.aead cipher.Cipher.cipher then len - 4 else len 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);
let pkt = Wire.put_random padlen buf |> Dbuf.to_cstruct |> Cstruct.to_bytes in
set_pkt_len pkt (Bytes.length pkt - 4);
set_pad_len pkt padlen;
let pkt = Bytes.unsafe_to_string pkt 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
Cstruct.of_string packet, keys