package otr

  1. Overview
  2. Docs

Source file otr_smp.ml

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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
open Rresult

open Otr_state

type error =
  | UnexpectedMessage
  | InvalidZeroKnowledgeProof

let error_to_string = function
  | UnexpectedMessage -> "unexpected SMP message"
  | InvalidZeroKnowledgeProof -> "invalid zero knowledge proof"

let fp = Otr_crypto.OtrDsa.fingerprint
let my_fp dsa = fp (Nocrypto.Dsa.pub_of_priv dsa)

let start_smp dsa enc_data smp_state ?question secret =
  ( match smp_state with
    | SMPSTATE_EXPECT1 -> Ok ()
    | _ -> Error UnexpectedMessage ) >>| fun () ->
  let a2, g2a = Otr_crypto.gen_dh_secret ()
  and a3, g3a = Otr_crypto.gen_dh_secret ()
  in
  let c2, d2 = Otr_crypto.proof_knowledge a2 1
  and c3, d3 = Otr_crypto.proof_knowledge a3 2
  in
  let x = Otr_crypto.prepare_secret (my_fp dsa) (fp enc_data.their_dsa) enc_data.ssid secret in
  let data = [ g2a ; c2 ; d2 ; g3a ; c3 ; d3 ]
  and smp_state = SMPSTATE_EXPECT2 (x, a2, a3)
  in
  let out = match question with
    | None -> Otr_builder.tlv ~data Otr_packet.SMP_MESSAGE_1
    | Some x -> Otr_builder.tlv ~data ~predata:(Cstruct.of_string (x ^ "\000")) Otr_packet.SMP_MESSAGE_1Q
  in
  (smp_state, Some out)

let abort_smp smp_state =
  match smp_state with
  | SMPSTATE_EXPECT1 -> Ok (SMPSTATE_EXPECT1, None)
  | _ -> Ok (SMPSTATE_EXPECT1, Some (Otr_builder.tlv Otr_packet.SMP_ABORT))

let handle_smp_1 data =
  match Otr_parser.parse_datas data 6 with
  | Error _ -> Error UnexpectedMessage
  | Ok xs ->
    let g2a = List.nth xs 0
    and c2 = List.nth xs 1
    and d2 = List.nth xs 2
    and g3a = List.nth xs 3
    and c3 = List.nth xs 4
    and d3 = List.nth xs 5
    in
    if Otr_crypto.check_proof g2a c2 d2 1 && Otr_crypto.check_proof g3a c3 d3 2 then
      Ok (SMPSTATE_WAIT_FOR_Y (g2a, g3a), None, [ `SMP_awaiting_secret ])
    else
      Error InvalidZeroKnowledgeProof

let handle_secret dsa enc_data smp_state secret =
  match smp_state with
  | SMPSTATE_WAIT_FOR_Y (g2a, g3a) ->
    let b2, g2b = Otr_crypto.gen_dh_secret ()
    and b3, g3b = Otr_crypto.gen_dh_secret ()
    in
    let c2, d2 = Otr_crypto.proof_knowledge b2 3
    and c3, d3 = Otr_crypto.proof_knowledge b3 4
    in
    ( match Otr_crypto.dh_shared b2 g2a, Otr_crypto.dh_shared b3 g3a with
      | Some g2, Some g3 ->
        let r, gr = Otr_crypto.gen_dh_secret ()
        and y = Otr_crypto.prepare_secret (fp enc_data.their_dsa) (my_fp dsa) enc_data.ssid secret
        in
        let pb = Otr_crypto.pow_s g3 r
        and qb = Otr_crypto.mult_pow gr g2 y
        in
        let cp, d5, d6 = Otr_crypto.proof_equal_coords g2 g3 r y 5 in
        let out = Otr_builder.tlv ~data:[ g2b ; c2 ; d2 ; g3b ; c3 ; d3 ; pb ; qb ; cp ; d5 ; d6 ] Otr_packet.SMP_MESSAGE_2
        and smp_state = SMPSTATE_EXPECT3 (g3a, g2, g3, b3, pb, qb)
        in
        Ok (smp_state, Some out)
      | _ -> Error UnexpectedMessage )
  | _ -> Error UnexpectedMessage

let handle_smp_2 x a2 a3 data =
  match Otr_parser.parse_datas data 11 with
  | Error _ -> Error UnexpectedMessage
  | Ok xs ->
    let g2b = List.nth xs 0
    and c2 = List.nth xs 1
    and d2 = List.nth xs 2
    and g3b = List.nth xs 3
    and c3 = List.nth xs 4
    and d3 = List.nth xs 5
    and pb = List.nth xs 6
    and qb = List.nth xs 7
    and cp = List.nth xs 8
    and d5 = List.nth xs 9
    and d6 = List.nth xs 10
    in
    if Otr_crypto.check_proof g2b c2 d2 3 && Otr_crypto.check_proof g3b c3 d3 4 then
      match Otr_crypto.dh_shared a2 g2b, Otr_crypto.dh_shared a3 g3b with
      | Some g2, Some g3 ->
        if Otr_crypto.check_equal_coords g2 g3 pb qb cp d5 d6 5 then
          let r, gr = Otr_crypto.gen_dh_secret () in
          let pa = Otr_crypto.pow_s g3 r
          and qa = Otr_crypto.mult_pow gr g2 x
          in
          let cp, d5, d6 = Otr_crypto.proof_equal_coords g2 g3 r x 6 in
          let pab = Otr_crypto.compute_p pa pb
          and qab = Otr_crypto.compute_p qa qb
          in
          let ra = Otr_crypto.pow_s qab a3
          and cr, d7 = Otr_crypto.proof_eq_logs qab a3 7
          in
          let out = Otr_builder.tlv ~data:[ pa ; qa ; cp ; d5 ; d6 ; ra ; cr ; d7 ] Otr_packet.SMP_MESSAGE_3
          and smp_state = SMPSTATE_EXPECT4 (g3b, pab, qab, a3)
          in
          Ok (smp_state, out)
        else
          Error UnexpectedMessage
      | _ -> Error UnexpectedMessage
    else
      Error UnexpectedMessage

let handle_smp_3 g3a g2 g3 b3 pb qb data =
  match Otr_parser.parse_datas data 8 with
  | Error _ -> Error UnexpectedMessage
  | Ok xs ->
    let pa = List.nth xs 0
    and qa = List.nth xs 1
    and cp = List.nth xs 2
    and d5 = List.nth xs 3
    and d6 = List.nth xs 4
    and ra = List.nth xs 5
    and cr = List.nth xs 6
    and d7 = List.nth xs 7
    in
    if Otr_crypto.check_equal_coords g2 g3 pa qa cp d5 d6 6 then
      let pab = Otr_crypto.compute_p pa pb
      and qab = Otr_crypto.compute_p qa qb
      in
      if Otr_crypto.check_eq_logs cr g3a qab d7 ra 7 then
        let rb = Otr_crypto.pow_s qab b3
        and cr, d7 = Otr_crypto.proof_eq_logs qab b3 8
        in
        let out = Otr_builder.tlv ~data:[ rb ; cr ; d7 ] Otr_packet.SMP_MESSAGE_4 in
        let rab = Otr_crypto.pow_s ra b3 in
        let ret =
          if Cstruct.equal rab pab then
            `SMP_success
          else
            `SMP_failure
        in
        let smp_state = SMPSTATE_EXPECT1 in
        Ok (smp_state, out, ret)
      else
        Error UnexpectedMessage
    else
      Error UnexpectedMessage

let handle_smp_4 g3b pab qab a3 data =
  match Otr_parser.parse_datas data 3 with
  | Error _ -> Error UnexpectedMessage
  | Ok xs ->
    let rb = List.nth xs 0
    and cr = List.nth xs 1
    and d7 = List.nth xs 2
    in
    if Otr_crypto.check_eq_logs cr g3b qab d7 rb 8 then
      let rab = Otr_crypto.pow_s rb a3 in
      let ret =
        if Cstruct.equal rab pab then
          `SMP_success
        else
          `SMP_failure
      in
      Ok (SMPSTATE_EXPECT1, ret)
    else
      Error UnexpectedMessage

let handle_smp smp_state typ data =
  let open Otr_packet in
  match smp_state, typ with
  | SMPSTATE_EXPECT1, SMP_MESSAGE_1 ->
    handle_smp_1 data >>| fun (s, o, r) ->
    (s, o, r)
  | SMPSTATE_EXPECT1, SMP_MESSAGE_1Q ->
    let str = Cstruct.to_string data in
    ( try
        let stop = String.index str '\000' in
        let stop' = succ stop in
        Ok (String.sub str 0 stop, Cstruct.shift data stop')
      with
        Not_found -> Error UnexpectedMessage ) >>= fun (question, data) ->
    handle_smp_1 data >>| fun (s, o, r) ->
    (s, o, [ `SMP_received_question question ] @ r)
  | SMPSTATE_EXPECT2 (x, a2, a3), SMP_MESSAGE_2 ->
    handle_smp_2 x a2 a3 data >>| fun (s, o) ->
    (s, Some o, [])
  | SMPSTATE_EXPECT3 (g3a, g2, g3, b3, pb, qb), SMP_MESSAGE_3 ->
    handle_smp_3 g3a g2 g3 b3 pb qb data >>| fun (s, o, r) ->
    (s, Some o, [r])
  | SMPSTATE_EXPECT4 (g3b, pab, qab, ra), SMP_MESSAGE_4 ->
    handle_smp_4 g3b pab qab ra data >>| fun (s, r) ->
    (s, None, [r])
  | _, SMP_ABORT ->
    Ok (SMPSTATE_EXPECT1, None, [])
  | _, _ ->
    let abort = Otr_builder.tlv SMP_ABORT in
    Ok (SMPSTATE_EXPECT1, Some abort, [])
OCaml

Innovation. Community. Security.