/src/wireshark/epan/dissectors/packet-hdcp.c
Line | Count | Source |
1 | | /* packet-hdcp.c |
2 | | * Routines for HDCP dissection |
3 | | * Copyright 2011-2014, Martin Kaiser <martin@kaiser.cx> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | /* |
13 | | * This dissector supports HDCP (version 1) over I2C. For now, only the |
14 | | * most common protocol messages are recognized. |
15 | | * |
16 | | * The specification of the version 1 protocol can be found at |
17 | | * http://www.digital-cp.com/files/static_page_files/5C3DC13B-9F6B-D82E-D77D8ACA08A448BF/HDCP Specification Rev1_4.pdf |
18 | | */ |
19 | | |
20 | | #include "config.h" |
21 | | |
22 | | #include <epan/packet.h> |
23 | | #include <epan/ptvcursor.h> |
24 | | void proto_register_hdcp(void); |
25 | | |
26 | | static int proto_hdcp; |
27 | | |
28 | | static wmem_tree_t *transactions; |
29 | | |
30 | | static int ett_hdcp; |
31 | | |
32 | | static int hf_hdcp_reg; |
33 | | static int hf_hdcp_resp_in; |
34 | | static int hf_hdcp_resp_to; |
35 | | static int hf_hdcp_a_ksv; |
36 | | static int hf_hdcp_b_ksv; |
37 | | static int hf_hdcp_an; |
38 | | static int hf_hdcp_hdmi_reserved; |
39 | | static int hf_hdcp_repeater; |
40 | | static int hf_hdcp_ksv_fifo; |
41 | | static int hf_hdcp_fast_trans; |
42 | | static int hf_hdcp_features; |
43 | | static int hf_hdcp_fast_reauth; |
44 | | static int hf_hdcp_hdmi_mode; |
45 | | static int hf_hdcp_max_casc_exc; |
46 | | static int hf_hdcp_depth; |
47 | | static int hf_hdcp_max_devs_exc; |
48 | | static int hf_hdcp_downstream; |
49 | | static int hf_hdcp_link_vfy; |
50 | | |
51 | 0 | #define REG_BKSV 0x0 |
52 | 0 | #define REG_AKSV 0x10 |
53 | 0 | #define REG_AN 0x18 |
54 | 0 | #define REG_BCAPS 0x40 |
55 | 0 | #define REG_BSTATUS 0x41 |
56 | | |
57 | | typedef struct _hdcp_transaction_t { |
58 | | uint32_t rqst_frame; |
59 | | uint32_t resp_frame; |
60 | | uint8_t rqst_type; |
61 | | } hdcp_transaction_t; |
62 | | |
63 | | static const value_string hdcp_reg[] = { |
64 | | { REG_BKSV, "B_ksv" }, |
65 | | { REG_AKSV, "A_ksv" }, |
66 | | { REG_AN, "An" }, |
67 | | { REG_BCAPS, "B_caps"}, |
68 | | { REG_BSTATUS, "B_status"}, |
69 | | { 0, NULL } |
70 | | }; |
71 | | |
72 | | |
73 | | /* the input tvb contains an HDCP message without the leading address byte |
74 | | (the address byte is handled by the HDMI dissector) |
75 | | the caller must set the direction in pinfo */ |
76 | | static int |
77 | | dissect_hdcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
78 | 0 | { |
79 | 0 | uint8_t reg; |
80 | 0 | proto_item *pi; |
81 | 0 | ptvcursor_t *cursor; |
82 | 0 | proto_tree *hdcp_tree; |
83 | 0 | hdcp_transaction_t *hdcp_trans; |
84 | 0 | proto_item *it; |
85 | 0 | uint64_t a_ksv, b_ksv; |
86 | | |
87 | | /* XXX check if the packet is really HDCP? */ |
88 | |
|
89 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "HDCP"); |
90 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
91 | |
|
92 | 0 | pi = proto_tree_add_protocol_format(tree, proto_hdcp, |
93 | 0 | tvb, 0, tvb_reported_length(tvb), "HDCP"); |
94 | 0 | hdcp_tree = proto_item_add_subtree(pi, ett_hdcp); |
95 | |
|
96 | 0 | cursor = ptvcursor_new(pinfo->pool, hdcp_tree, tvb, 0); |
97 | |
|
98 | 0 | if (pinfo->p2p_dir==P2P_DIR_SENT) { |
99 | | /* transmitter sends data to the receiver */ |
100 | |
|
101 | 0 | reg = tvb_get_uint8(tvb, ptvcursor_current_offset(cursor)); |
102 | | /* all values in HDCP are little endian */ |
103 | 0 | ptvcursor_add(cursor, hf_hdcp_reg, 1, ENC_LITTLE_ENDIAN); |
104 | |
|
105 | 0 | if (tvb_reported_length_remaining(tvb, |
106 | 0 | ptvcursor_current_offset(cursor)) == 0) { |
107 | | /* transmitter requests the content of a register */ |
108 | 0 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "request %s", |
109 | 0 | val_to_str(pinfo->pool, reg, hdcp_reg, "unknown (0x%x)")); |
110 | |
|
111 | 0 | if (PINFO_FD_VISITED(pinfo)) { |
112 | | /* we've already dissected the receiver's response */ |
113 | 0 | hdcp_trans = (hdcp_transaction_t *)wmem_tree_lookup32( |
114 | 0 | transactions, pinfo->num); |
115 | 0 | if (hdcp_trans && hdcp_trans->rqst_frame==pinfo->num && |
116 | 0 | hdcp_trans->resp_frame!=0) { |
117 | |
|
118 | 0 | it = proto_tree_add_uint_format(hdcp_tree, hf_hdcp_resp_in, |
119 | 0 | NULL, 0, 0, hdcp_trans->resp_frame, |
120 | 0 | "Request to get the content of register %s, " |
121 | 0 | "response in frame %d", |
122 | 0 | val_to_str_const(hdcp_trans->rqst_type, |
123 | 0 | hdcp_reg, "unknown (0x%x)"), |
124 | 0 | hdcp_trans->resp_frame); |
125 | 0 | proto_item_set_generated(it); |
126 | 0 | } |
127 | 0 | } |
128 | 0 | else { |
129 | | /* we've not yet dissected the response */ |
130 | 0 | hdcp_trans = wmem_new(wmem_file_scope(), hdcp_transaction_t); |
131 | 0 | hdcp_trans->rqst_frame = pinfo->num; |
132 | 0 | hdcp_trans->resp_frame = 0; |
133 | 0 | hdcp_trans->rqst_type = reg; |
134 | 0 | wmem_tree_insert32(transactions, |
135 | 0 | hdcp_trans->rqst_frame, (void *)hdcp_trans); |
136 | 0 | } |
137 | 0 | } |
138 | 0 | else { |
139 | | /* transmitter actually sends protocol data */ |
140 | 0 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "send %s", |
141 | 0 | val_to_str(pinfo->pool, reg, hdcp_reg, "unknown (0x%x)")); |
142 | 0 | switch (reg) { |
143 | 0 | case REG_AKSV: |
144 | 0 | a_ksv = tvb_get_letoh40(tvb, |
145 | 0 | ptvcursor_current_offset(cursor)); |
146 | 0 | proto_tree_add_uint64_format(hdcp_tree, hf_hdcp_a_ksv, |
147 | 0 | tvb, ptvcursor_current_offset(cursor), 5, |
148 | 0 | a_ksv, "A_ksv 0x%010" PRIx64, a_ksv); |
149 | 0 | ptvcursor_advance(cursor, 5); |
150 | 0 | break; |
151 | 0 | case REG_AN: |
152 | 0 | ptvcursor_add(cursor, hf_hdcp_an, 8, ENC_LITTLE_ENDIAN); |
153 | 0 | break; |
154 | 0 | default: |
155 | 0 | break; |
156 | 0 | } |
157 | 0 | } |
158 | 0 | } |
159 | 0 | else { |
160 | | /* transmitter reads from receiver */ |
161 | |
|
162 | 0 | hdcp_trans = (hdcp_transaction_t *)wmem_tree_lookup32_le( |
163 | 0 | transactions, pinfo->num); |
164 | 0 | if (hdcp_trans) { |
165 | 0 | if (hdcp_trans->resp_frame==0) { |
166 | | /* there's a pending request, this packet is the response */ |
167 | 0 | hdcp_trans->resp_frame = pinfo->num; |
168 | 0 | } |
169 | |
|
170 | 0 | if (hdcp_trans->resp_frame== pinfo->num) { |
171 | | /* we found the request that corresponds to our response */ |
172 | 0 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "send %s", |
173 | 0 | val_to_str_const(hdcp_trans->rqst_type, |
174 | 0 | hdcp_reg, "unknown (0x%x)")); |
175 | 0 | it = proto_tree_add_uint_format(hdcp_tree, hf_hdcp_resp_to, |
176 | 0 | NULL, 0, 0, hdcp_trans->rqst_frame, |
177 | 0 | "Response to frame %d (content of register %s)", |
178 | 0 | hdcp_trans->rqst_frame, |
179 | 0 | val_to_str_const(hdcp_trans->rqst_type, |
180 | 0 | hdcp_reg, "unknown (0x%x)")); |
181 | 0 | proto_item_set_generated(it); |
182 | 0 | switch (hdcp_trans->rqst_type) { |
183 | 0 | case REG_BKSV: |
184 | 0 | b_ksv = tvb_get_letoh40(tvb, |
185 | 0 | ptvcursor_current_offset(cursor)); |
186 | 0 | proto_tree_add_uint64_format(hdcp_tree, hf_hdcp_b_ksv, |
187 | 0 | tvb, ptvcursor_current_offset(cursor), 5, |
188 | 0 | b_ksv, "B_ksv 0x%010" PRIx64, |
189 | 0 | b_ksv); |
190 | 0 | ptvcursor_advance(cursor, 5); |
191 | 0 | break; |
192 | 0 | case REG_BCAPS: |
193 | 0 | ptvcursor_add_no_advance(cursor, |
194 | 0 | hf_hdcp_hdmi_reserved, 1, ENC_LITTLE_ENDIAN); |
195 | 0 | ptvcursor_add_no_advance(cursor, |
196 | 0 | hf_hdcp_repeater, 1, ENC_LITTLE_ENDIAN); |
197 | 0 | ptvcursor_add_no_advance(cursor, |
198 | 0 | hf_hdcp_ksv_fifo, 1, ENC_LITTLE_ENDIAN); |
199 | 0 | ptvcursor_add_no_advance(cursor, |
200 | 0 | hf_hdcp_fast_trans, 1, ENC_LITTLE_ENDIAN); |
201 | 0 | ptvcursor_add_no_advance(cursor, |
202 | 0 | hf_hdcp_features, 1, ENC_LITTLE_ENDIAN); |
203 | 0 | ptvcursor_add_no_advance(cursor, |
204 | 0 | hf_hdcp_fast_reauth, 1, ENC_LITTLE_ENDIAN); |
205 | 0 | break; |
206 | 0 | case REG_BSTATUS: |
207 | 0 | ptvcursor_add_no_advance(cursor, |
208 | 0 | hf_hdcp_hdmi_mode, 2, ENC_LITTLE_ENDIAN); |
209 | 0 | ptvcursor_add_no_advance(cursor, |
210 | 0 | hf_hdcp_max_casc_exc, 2, ENC_LITTLE_ENDIAN); |
211 | 0 | ptvcursor_add_no_advance(cursor, |
212 | 0 | hf_hdcp_depth, 2, ENC_LITTLE_ENDIAN); |
213 | 0 | ptvcursor_add_no_advance(cursor, |
214 | 0 | hf_hdcp_max_devs_exc, 2, ENC_LITTLE_ENDIAN); |
215 | 0 | ptvcursor_add_no_advance(cursor, |
216 | 0 | hf_hdcp_downstream, 2, ENC_LITTLE_ENDIAN); |
217 | 0 | break; |
218 | 0 | } |
219 | 0 | } |
220 | 0 | } |
221 | | |
222 | 0 | if (!hdcp_trans || hdcp_trans->resp_frame!=pinfo->num) { |
223 | | /* the packet isn't a response to a request from the |
224 | | * transmitter; it must be a link verification */ |
225 | 0 | if (tvb_reported_length_remaining( |
226 | 0 | tvb, ptvcursor_current_offset(cursor)) == 2) { |
227 | 0 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, |
228 | 0 | "send link verification Ri'"); |
229 | 0 | ptvcursor_add_no_advance(cursor, |
230 | 0 | hf_hdcp_link_vfy, 2, ENC_LITTLE_ENDIAN); |
231 | 0 | } |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | 0 | ptvcursor_free(cursor); |
236 | 0 | return tvb_reported_length(tvb); |
237 | 0 | } |
238 | | |
239 | | |
240 | | void |
241 | | proto_register_hdcp(void) |
242 | 15 | { |
243 | 15 | static hf_register_info hf[] = { |
244 | 15 | { &hf_hdcp_reg, |
245 | 15 | { "Register offset", "hdcp.reg", FT_UINT8, BASE_HEX, |
246 | 15 | VALS(hdcp_reg), 0, NULL, HFILL } }, |
247 | 15 | { &hf_hdcp_resp_in, |
248 | 15 | { "Response In", "hdcp.resp_in", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, |
249 | 15 | "The response to this request is in this frame", HFILL }}, |
250 | 15 | { &hf_hdcp_resp_to, |
251 | 15 | { "Response To", "hdcp.resp_to", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, |
252 | 15 | "This is the response to the request in this frame", HFILL }}, |
253 | 15 | { &hf_hdcp_a_ksv, |
254 | 15 | { "Transmitter's key selection vector", "hdcp.a_ksv", FT_UINT40, |
255 | 15 | BASE_HEX, NULL, 0, NULL, HFILL } }, |
256 | 15 | { &hf_hdcp_b_ksv, |
257 | 15 | { "Receiver's key selection vector", "hdcp.b_ksv", FT_UINT64, |
258 | 15 | BASE_HEX, NULL, 0, NULL, HFILL } }, |
259 | 15 | { &hf_hdcp_an, |
260 | 15 | { "Random number for the session", "hdcp.an", FT_UINT64, |
261 | 15 | BASE_HEX, NULL, 0, NULL, HFILL } }, |
262 | 15 | { &hf_hdcp_hdmi_reserved, |
263 | 15 | { "HDMI reserved", "hdcp.hdmi_reserved", FT_UINT8, BASE_DEC, |
264 | 15 | NULL, 0x80, NULL, HFILL } }, |
265 | 15 | { &hf_hdcp_repeater, |
266 | 15 | { "Repeater", "hdcp.repeater", FT_UINT8, BASE_DEC, |
267 | 15 | NULL, 0x40, NULL, HFILL } }, |
268 | 15 | { &hf_hdcp_ksv_fifo, |
269 | 15 | { "KSV fifo ready", "hdcp.ksv_fifo", FT_UINT8, BASE_DEC, |
270 | 15 | NULL, 0x20, NULL, HFILL } }, |
271 | 15 | { &hf_hdcp_fast_trans, |
272 | 15 | { "Support for 400KHz transfers", "hdcp.fast_trans", |
273 | 15 | FT_UINT8, BASE_DEC, NULL, 0x10, NULL, HFILL } }, |
274 | 15 | { &hf_hdcp_features, |
275 | 15 | { "Support for additional features", "hdcp.features", |
276 | 15 | FT_UINT8, BASE_DEC, NULL, 0x02, NULL, HFILL } }, |
277 | 15 | { &hf_hdcp_fast_reauth, |
278 | 15 | { "Support for fast re-authentication", "hdcp.fast_reauth", |
279 | 15 | FT_UINT8, BASE_DEC, NULL, 0x01, NULL, HFILL } }, |
280 | 15 | { &hf_hdcp_hdmi_mode, |
281 | 15 | { "HDMI mode", "hdcp.hdmi_mode", |
282 | 15 | FT_UINT16, BASE_DEC, NULL, 0x1000, NULL, HFILL } }, |
283 | 15 | { &hf_hdcp_max_casc_exc, |
284 | 15 | { "Maximum cascading depth exceeded", "hdcp.max_casc_exc", |
285 | 15 | FT_UINT16, BASE_DEC, NULL, 0x0800, NULL, HFILL } }, |
286 | 15 | { &hf_hdcp_depth, |
287 | 15 | { "Repeater cascade depth", "hdcp.depth", |
288 | 15 | FT_UINT16, BASE_DEC, NULL, 0x0700, NULL, HFILL } }, |
289 | 15 | { &hf_hdcp_max_devs_exc, |
290 | 15 | { "Maximum number of devices exceeded", "hdcp.max_devs_exc", |
291 | 15 | FT_UINT16, BASE_DEC, NULL, 0x0080, NULL, HFILL } }, |
292 | 15 | { &hf_hdcp_downstream, |
293 | 15 | { "Number of downstream receivers", "hdcp.downstream", |
294 | 15 | FT_UINT16, BASE_DEC, NULL, 0x007F, NULL, HFILL } }, |
295 | 15 | { &hf_hdcp_link_vfy, |
296 | 15 | { "Link verification response Ri'", "hdcp.link_vfy", |
297 | 15 | FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } } |
298 | 15 | }; |
299 | | |
300 | 15 | static int *ett[] = { |
301 | 15 | &ett_hdcp |
302 | 15 | }; |
303 | | |
304 | | |
305 | 15 | proto_hdcp = proto_register_protocol("High bandwidth Digital Content Protection", "HDCP", "hdcp"); |
306 | | |
307 | 15 | proto_register_field_array(proto_hdcp, hf, array_length(hf)); |
308 | 15 | proto_register_subtree_array(ett, array_length(ett)); |
309 | | |
310 | 15 | register_dissector("hdcp", dissect_hdcp, proto_hdcp); |
311 | | |
312 | 15 | transactions = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
313 | 15 | } |
314 | | |
315 | | /* |
316 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
317 | | * |
318 | | * Local variables: |
319 | | * c-basic-offset: 4 |
320 | | * tab-width: 8 |
321 | | * indent-tabs-mode: nil |
322 | | * End: |
323 | | * |
324 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
325 | | * :indentSize=4:tabSize=8:noTabs=true: |
326 | | */ |