-
Notifications
You must be signed in to change notification settings - Fork 0
/
parsePGP.ml
295 lines (253 loc) · 9.49 KB
/
parsePGP.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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
(***********************************************************************)
(* parsePGP.ml *)
(* *)
(* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, *)
(* 2011, 2012 Yaron Minsky and Contributors *)
(* *)
(* This file is part of SKS. SKS is free software; you can *)
(* redistribute it and/or modify it under the terms of the GNU General *)
(* Public License as published by the Free Software Foundation; either *)
(* version 2 of the License, or (at your option) any later version. *)
(* *)
(* This program is distributed in the hope that it will be useful, but *)
(* WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *)
(* General Public License for more details. *)
(* *)
(* You should have received a copy of the GNU General Public License *)
(* along with this program; if not, write to the Free Software *)
(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *)
(* USA or see <http://www.gnu.org/licenses/>. *)
(***********************************************************************)
open StdLabels
open MoreLabels
open Common
open Packet
open Printf
exception Overlong_mpi
exception Partial_body_length of int
(********************************************************)
(** parse new-style packet length *)
let parse_new_packet_length cin =
let byte1 = cin#read_byte in
if byte1 <= 191 then byte1 (* one-octet length *)
else if byte1 <= 223 then (* two-octet length *)
let byte2 = cin#read_byte in
(byte1 - 192) lsl 8 + byte2 + 192
else if byte1 = 255 then (* five-octet length *)
let byte2 = cin#read_byte in
let byte3 = cin#read_byte in
let byte4 = cin#read_byte in
let byte5 = cin#read_byte in
(byte2 lsl 24) lor (byte3 lsl 16) lor (byte4 lsl 8) lor byte5
else (* partial body length *)
raise (Partial_body_length (1 lsl (byte1 land 0x1f)))
(********************************************************)
let read_packet cin =
let packet_tag = cin#read_byte in
if ((packet_tag lsr 7) land 1 <> 1)
then failwith (sprintf "Bit 7 of packet tag was not 1 as expected: %x"
packet_tag);
match (packet_tag lsr 6) land 1 with
0 -> (* old format *)
let content_tag = (packet_tag land 0b111100) lsr 2
and length_type = packet_tag land 0b11
in
(match length_type with
0 | 1 | 2 ->
let length_length = 1 lsl length_type in
let length_str = cin#read_string length_length in
let length = Utils.int_from_bstring length_str
~pos:0 ~len:length_length in
{ content_tag = content_tag;
packet_type = content_tag_to_ptype content_tag;
packet_length = length;
packet_body = cin#read_string length;
}
| 3 -> (* indeterminate length header --- extends to end of file *)
failwith "Unexpected indeterminate length packet"
| _ ->
failwith "Unexpected length type"
)
| 1 -> (* new_format *)
let content_tag = packet_tag land 0b111111 in
let length = parse_new_packet_length cin in
{ (* packet_tag = packet_tag; *)
content_tag = content_tag;
packet_type = content_tag_to_ptype content_tag;
packet_length = length;
packet_body = cin#read_string length;
}
| _ -> raise (Bug "ParsePGP.read_packet: expected 0/1 value")
(********************************************************)
let offset_read_packet cin =
let offset = LargeFile.pos_in cin#inchan in
let packet = read_packet cin in
(offset,packet)
(********************************************************)
let offset_length_read_packet cin =
let offset = pos_in cin#inchan in
let packet = read_packet cin in
let final_offset = pos_in cin#inchan in
(packet,offset,final_offset - offset)
(********************************************************)
let read_mpi cin =
let byte1 = cin#read_byte in
try
let byte2 = cin#read_byte in
let length = (byte1 lsl 8) + byte2 in
let data = cin#read_string
((length + 7)/8)
in
{ mpi_bits = length; mpi_data = data }
with
End_of_file -> raise Overlong_mpi
(********************************************************)
let read_mpis cin =
let rec loop list =
match (try (Some (read_mpi cin))
with End_of_file -> None)
with
| Some mpi -> loop (mpi::list)
| None -> List.rev list
in
loop []
(********************************************************)
let parse_pubkey_info packet =
let cin = new Channel.string_in_channel packet.packet_body 0 in
let version = cin#read_byte in
let creation_time = cin#read_int64_size 4 in
let (algorithm,mpis,expiration) =
match version with
| 4 ->
let algorithm = cin#read_byte in
let mpis = read_mpis cin in
(algorithm,mpis,None)
| 2 | 3 ->
let expiration = cin#read_int_size 2 in
let algorithm = cin#read_byte in
let mpis = read_mpis cin in
(algorithm,mpis,Some expiration)
| _ -> failwith (sprintf "Unexpected pubkey version: %d" version)
in
let mpi = List.hd mpis in
{ pk_version = version;
pk_ctime = creation_time;
pk_expiration = (match expiration with Some 0 -> None | x -> x);
pk_alg = algorithm;
pk_keylen = mpi.mpi_bits;
}
(********************************************************)
(** Parsing of signature subpackets *)
(** parse sigsubpacket length *)
let parse_sigsubpacket_length cin =
let byte1 = cin#read_byte in
if byte1 < 192 then byte1 (* one octet length *)
else if byte1 < 255 then
let byte2 = cin#read_byte in
((byte1 - 192) lsl 8) + (byte2) + 192
else if byte1 = 255 then (* five-octet length *)
let byte2 = cin#read_byte in
let byte3 = cin#read_byte in
let byte4 = cin#read_byte in
let byte5 = cin#read_byte in
(byte2 lsl 24) lor (byte3 lsl 16) lor (byte4 lsl 8) lor byte5
else
failwith "Unable to parse sigsubpacket length"
let read_sigsubpacket cin =
let length = parse_sigsubpacket_length cin in
let ssp_type = cin#read_byte land 0x7f in
let body = cin#read_string (length - 1) in
{ ssp_length = length - 1;
ssp_type = ssp_type;
ssp_body = body;
}
let get_hashed_subpacket_string cin =
let version = cin#read_byte in
if version <> 4 then
failwith "Attempt to parse non-v4 signature as v4 signature";
let _sigtype = cin#read_byte in
let _key_alg = cin#read_byte in
let _hash_alg = cin#read_byte in
let hashed_subpacket_count = cin#read_int_size 2 in
(* now we can start reading the hashed sub-packets *)
cin#read_string hashed_subpacket_count
(** return list of signature sub-packets *)
let read_subpackets cin length =
let subpacket_string = cin#read_string length in
let cin = new Channel.string_in_channel subpacket_string 0 in
let rec loop list =
match (try Some (read_sigsubpacket cin)
with End_of_file -> None)
with
| Some subpack -> loop (subpack::list)
| None -> List.rev list
in
loop []
let parse_signature packet =
let cin = new Channel.string_in_channel packet.packet_body 0 in
let version = cin#read_byte in
match version with
| 2 | 3 ->
cin#skip 1; (* length packet which must be 5 *)
let sigtype = cin#read_byte in
let ctime = cin#read_int64_size 4 in
let keyid = cin#read_string 8 in
let pk_alg = cin#read_byte in
let hash_alg = cin#read_byte in
let hash_value = cin#read_string 2 in
let mpis = read_mpis cin in
V3sig { v3s_sigtype = sigtype;
v3s_ctime = ctime;
v3s_keyid = keyid;
v3s_pk_alg = pk_alg;
v3s_hash_alg = hash_alg;
v3s_hash_value = hash_value;
v3s_mpis = mpis;
}
| 4 ->
let sigtype = cin#read_byte in
let pk_alg = cin#read_byte in
let _hash_alg = cin#read_byte in
let hashed_subpacket_bytes = cin#read_int_size 2 in
let hashed_subpackets = read_subpackets cin hashed_subpacket_bytes in
let unhashed_subpacket_bytes = cin#read_int_size 2 in
let unhashed_subpackets = read_subpackets cin unhashed_subpacket_bytes in
let hash_value = cin#read_string 2 in
let mpis = read_mpis cin in
V4sig { v4s_sigtype = sigtype;
v4s_pk_alg = pk_alg;
v4s_hashed_subpackets = hashed_subpackets;
v4s_unhashed_subpackets = unhashed_subpackets;
v4s_hash_value = hash_value;
v4s_mpis = mpis;
}
| _ -> failwith (sprintf "Unexpected signature version: %d" version)
let ssp_ctime_id = 2
let ssp_exptime_id = 3
let int32_of_string s =
let cin = new Channel.string_in_channel s 0 in
cin#read_int32
let int64_of_string s =
let cin = new Channel.string_in_channel s 0 in
cin#read_int64_size (String.length s)
let get_times sign = match sign with
| V3sig sign ->
(Some sign.v3s_ctime, None)
| V4sig sign ->
let hashed_subpackets = sign.v4s_hashed_subpackets in
let (ctime,exptime_delta) =
List.fold_left hashed_subpackets ~init:(None,None)
~f:(fun (ctime,exptime) ssp ->
if ssp.ssp_type = ssp_ctime_id && ssp.ssp_length = 4 then
(Some (int64_of_string ssp.ssp_body),exptime)
else if ssp.ssp_type = ssp_exptime_id && ssp.ssp_length = 4 then
(ctime,Some (int64_of_string ssp.ssp_body))
else
(ctime,exptime)
)
in
match (ctime,exptime_delta) with
| (Some x,None) -> (Some x,None)
| (None,_) -> (None,None)
| (Some x,Some y) -> (Some x,Some (Int64.add x y))