1 #ifndef _IPXE_PCCRC_H
2 #define _IPXE_PCCRC_H
3 
4 /** @file
5  *
6  * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC]
7  *
8  */
9 
10 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11 
12 #include <stdint.h>
13 #include <byteswap.h>
14 #include <ipxe/uaccess.h>
15 #include <ipxe/crypto.h>
16 
17 /******************************************************************************
18  *
19  * Content Information versioning
20  *
21  ******************************************************************************
22  *
23  * Note that version 1 data structures are little-endian, but version
24  * 2 data structures are big-endian.
25  */
26 
27 /** Content Information version number */
28 union peerdist_info_version {
29 	/** Raw version number
30 	 *
31 	 * Always little-endian, regardless of whether the
32 	 * encompassing structure is version 1 (little-endian) or
33 	 * version 2 (big-endian).
34 	 */
35 	uint16_t raw;
36 	/** Major:minor version number */
37 	struct {
38 		/** Minor version number */
39 		uint8_t minor;
40 		/** Major version number */
41 		uint8_t major;
42 	} __attribute__ (( packed ));
43 } __attribute__ (( packed ));
44 
45 /** Content Information version 1 */
46 #define PEERDIST_INFO_V1 0x0100
47 
48 /** Content Information version 2 */
49 #define PEERDIST_INFO_V2 0x0200
50 
51 /******************************************************************************
52  *
53  * Content Information version 1
54  *
55  ******************************************************************************
56  */
57 
58 /** Content Information version 1 data structure header
59  *
60  * All fields are little-endian.
61  */
62 struct peerdist_info_v1 {
63 	/** Version number */
64 	union peerdist_info_version version;
65 	/** Hash algorithm
66 	 *
67 	 * This is a @c PEERDIST_INFO_V1_HASH_XXX constant.
68 	 */
69 	uint32_t hash;
70 	/** Length to skip in first segment
71 	 *
72 	 * Length at the start of the first segment which is not
73 	 * included within the content range.
74 	 */
75 	uint32_t first;
76 	/** Length to read in last segment, or zero
77 	 *
78 	 * Length within the last segment which is included within the
79 	 * content range.  A zero value indicates that the whole of
80 	 * the last segment is included within the content range.
81 	 */
82 	uint32_t last;
83 	/** Number of segments within the content information */
84 	uint32_t segments;
85 	/* Followed by a variable-length array of segment descriptions
86 	 * and a list of variable-length block descriptions:
87 	 *
88 	 * peerdist_info_v1_segment_t(digestsize) segment[segments];
89 	 * peerdist_info_v1_block_t(digestsize, block0.blocks) block0;
90 	 * peerdist_info_v1_block_t(digestsize, block1.blocks) block1;
91 	 * ...
92 	 * peerdist_info_v1_block_t(digestsize, blockN.blocks) blockN;
93 	 */
94 } __attribute__ (( packed ));
95 
96 /** SHA-256 hash algorithm */
97 #define PEERDIST_INFO_V1_HASH_SHA256 0x0000800cUL
98 
99 /** SHA-384 hash algorithm */
100 #define PEERDIST_INFO_V1_HASH_SHA384 0x0000800dUL
101 
102 /** SHA-512 hash algorithm */
103 #define PEERDIST_INFO_V1_HASH_SHA512 0x0000800eUL
104 
105 /** Content Information version 1 segment description header
106  *
107  * All fields are little-endian.
108  */
109 struct peerdist_info_v1_segment {
110 	/** Offset of this segment within the content */
111 	uint64_t offset;
112 	/** Length of this segment
113 	 *
114 	 * Should always be 32MB, except for the last segment within
115 	 * the content.
116 	 */
117 	uint32_t len;
118 	/** Block size for this segment
119 	 *
120 	 * Should always be 64kB.  Note that the last block within the
121 	 * last segment may actually be less than 64kB.
122 	 */
123 	uint32_t blksize;
124 	/* Followed by two variable-length hashes:
125 	 *
126 	 * uint8_t hash[digestsize];
127 	 * uint8_t secret[digestsize];
128 	 *
129 	 * where digestsize is the digest size for the selected hash
130 	 * algorithm.
131 	 *
132 	 * Note that the hash is taken over (the hashes of all blocks
133 	 * within) the entire segment, even if the blocks do not
134 	 * intersect the content range (and so do not appear within
135 	 * the block list).  It therefore functions only as a segment
136 	 * identifier; it cannot be used to verify the content of the
137 	 * segment (since we may not download all blocks within the
138 	 * segment).
139 	 */
140 } __attribute__ (( packed ));
141 
142 /** Content Information version 1 segment description
143  *
144  * @v digestsize	Digest size
145  */
146 #define peerdist_info_v1_segment_t( digestsize )			\
147 	struct {							\
148 		struct peerdist_info_v1_segment segment;		\
149 		uint8_t hash[digestsize];				\
150 		uint8_t secret[digestsize];				\
151 	} __attribute__ (( packed ))
152 
153 /** Content Information version 1 block description header
154  *
155  * All fields are little-endian.
156  */
157 struct peerdist_info_v1_block {
158 	/** Number of blocks within the block description
159 	 *
160 	 * This is the number of blocks within the segment which
161 	 * overlap the content range.  It may therefore be less than
162 	 * the number of blocks within the segment.
163 	 */
164 	uint32_t blocks;
165 	/* Followed by an array of variable-length hashes:
166 	 *
167 	 * uint8_t hash[blocks][digestsize];
168 	 *
169 	 * where digestsize is the digest size for the selected hash
170 	 * algorithm.
171 	 */
172  } __attribute__ (( packed ));
173 
174 /** Content Information version 1 block description
175  *
176  * @v digestsize	Digest size
177  * @v blocks		Number of blocks
178  */
179 #define peerdist_info_v1_block_t( digestsize, blocks )			\
180 	struct {							\
181 		struct peerdist_info_v1_block block;			\
182 		uint8_t hash[blocks][digestsize];			\
183 	} __attribute__ (( packed ))
184 
185 /******************************************************************************
186  *
187  * Content Information version 2
188  *
189  ******************************************************************************
190  */
191 
192 /** Content Information version 2 data structure header
193  *
194  * All fields are big-endian.
195  */
196 struct peerdist_info_v2 {
197 	/** Version number */
198 	union peerdist_info_version version;
199 	/** Hash algorithm
200 	 *
201 	 * This is a @c PEERDIST_INFO_V2_HASH_XXX constant.
202 	 */
203 	uint8_t hash;
204 	/** Offset of the first segment within the content */
205 	uint64_t offset;
206 	/** Index of the first segment within the content */
207 	uint64_t index;
208 	/** Length to skip in first segment
209 	 *
210 	 * Length at the start of the first segment which is not
211 	 * included within the content range.
212 	 */
213 	uint32_t first;
214 	/** Length of content range, or zero
215 	 *
216 	 * Length of the content range.  A zero indicates that
217 	 * everything up to the end of the last segment is included in
218 	 * the content range.
219 	 */
220 	uint64_t len;
221 	/* Followed by a list of chunk descriptions */
222 } __attribute__ (( packed ));
223 
224 /** SHA-512 hash algorithm with output truncated to first 256 bits */
225 #define PEERDIST_INFO_V2_HASH_SHA512_TRUNC 0x04
226 
227 /** Content Information version 2 chunk description header
228  *
229  * All fields are big-endian.
230  */
231 struct peerdist_info_v2_chunk {
232 	/** Chunk type */
233 	uint8_t type;
234 	/** Chunk data length */
235 	uint32_t len;
236 	/* Followed by an array of segment descriptions:
237 	 *
238 	 * peerdist_info_v2_segment_t(digestsize) segment[segments]
239 	 *
240 	 * where digestsize is the digest size for the selected hash
241 	 * algorithm, and segments is equal to @c len divided by the
242 	 * size of each segment array entry.
243 	 */
244 } __attribute__ (( packed ));
245 
246 /** Content Information version 2 chunk description
247  *
248  * @v digestsize	Digest size
249  */
250 #define peerdist_info_v2_chunk_t( digestsize )				\
251 	struct {							\
252 		struct peerdist_info_v2_chunk chunk;			\
253 		peerdist_info_v2_segment_t ( digestsize ) segment[0];	\
254 	} __attribute__ (( packed ))
255 
256 /** Chunk type */
257 #define PEERDIST_INFO_V2_CHUNK_TYPE 0x00
258 
259 /** Content Information version 2 segment description header
260  *
261  * All fields are big-endian.
262  */
263 struct peerdist_info_v2_segment {
264 	/** Segment length */
265 	uint32_t len;
266 	/* Followed by two variable-length hashes:
267 	 *
268 	 * uint8_t hash[digestsize];
269 	 * uint8_t secret[digestsize];
270 	 *
271 	 * where digestsize is the digest size for the selected hash
272 	 * algorithm.
273 	 */
274 } __attribute__ (( packed ));
275 
276 /** Content Information version 2 segment description
277  *
278  * @v digestsize	Digest size
279  */
280 #define peerdist_info_v2_segment_t( digestsize )			\
281 	struct {							\
282 		struct peerdist_info_v2_segment segment;		\
283 		uint8_t hash[digestsize];				\
284 		uint8_t secret[digestsize];				\
285 	} __attribute__ (( packed ))
286 
287 /******************************************************************************
288  *
289  * Content Information
290  *
291  ******************************************************************************
292  */
293 
294 /** Maximum digest size for any supported algorithm
295  *
296  * The largest digest size that we support is for SHA-512 at 64 bytes
297  */
298 #define PEERDIST_DIGEST_MAX_SIZE 64
299 
300 /** Raw content information */
301 struct peerdist_raw {
302 	/** Data buffer */
303 	userptr_t data;
304 	/** Length of data buffer */
305 	size_t len;
306 };
307 
308 /** A content range */
309 struct peerdist_range {
310 	/** Start offset */
311 	size_t start;
312 	/** End offset */
313 	size_t end;
314 };
315 
316 /** Content information */
317 struct peerdist_info {
318 	/** Raw content information */
319 	struct peerdist_raw raw;
320 
321 	/** Content information operations */
322 	struct peerdist_info_operations *op;
323 	/** Digest algorithm */
324 	struct digest_algorithm *digest;
325 	/** Digest size
326 	 *
327 	 * Note that this may be shorter than the digest size of the
328 	 * digest algorithm.  The truncation does not always take
329 	 * place as soon as a digest is calculated.  For example,
330 	 * version 2 content information uses SHA-512 with a truncated
331 	 * digest size of 32 (256 bits), but the segment identifier
332 	 * ("HoHoDk") is calculated by using HMAC with the full
333 	 * SHA-512 digest and then truncating the HMAC output, rather
334 	 * than by simply using HMAC with the truncated SHA-512
335 	 * digest.  This is, of course, totally undocumented.
336 	 */
337 	size_t digestsize;
338 	/** Content range */
339 	struct peerdist_range range;
340 	/** Trimmed content range */
341 	struct peerdist_range trim;
342 	/** Number of segments within the content information */
343 	unsigned int segments;
344 };
345 
346 /** A content information segment */
347 struct peerdist_info_segment {
348 	/** Content information */
349 	const struct peerdist_info *info;
350 	/** Segment index */
351 	unsigned int index;
352 
353 	/** Content range
354 	 *
355 	 * Note that this range may exceed the overall content range.
356 	 */
357 	struct peerdist_range range;
358 	/** Number of blocks within this segment */
359 	unsigned int blocks;
360 	/** Block size */
361 	size_t blksize;
362 	/** Segment hash of data
363 	 *
364 	 * This is MS-PCCRC's "HoD".
365 	 */
366 	uint8_t hash[PEERDIST_DIGEST_MAX_SIZE];
367 	/** Segment secret
368 	 *
369 	 * This is MS-PCCRC's "Ke = Kp".
370 	 */
371 	uint8_t secret[PEERDIST_DIGEST_MAX_SIZE];
372 	/** Segment identifier
373 	 *
374 	 * This is MS-PCCRC's "HoHoDk".
375 	 */
376 	uint8_t id[PEERDIST_DIGEST_MAX_SIZE];
377 };
378 
379 /** Magic string constant used to calculate segment identifier
380  *
381  * Note that the MS-PCCRC specification states that this constant is
382  *
383  *   "the null-terminated ASCII string constant "MS_P2P_CACHING";
384  *    string literals are all ASCII strings with NULL terminators
385  *    unless otherwise noted."
386  *
387  * The specification lies.  This constant is a UTF-16LE string, not an
388  * ASCII string.  The terminating wNUL *is* included within the
389  * constant.
390  */
391 #define PEERDIST_SEGMENT_ID_MAGIC L"MS_P2P_CACHING"
392 
393 /** A content information block */
394 struct peerdist_info_block {
395 	/** Content information segment */
396 	const struct peerdist_info_segment *segment;
397 	/** Block index */
398 	unsigned int index;
399 
400 	/** Content range
401 	 *
402 	 * Note that this range may exceed the overall content range.
403 	 */
404 	struct peerdist_range range;
405 	/** Trimmed content range */
406 	struct peerdist_range trim;
407 	/** Block hash */
408 	uint8_t hash[PEERDIST_DIGEST_MAX_SIZE];
409 };
410 
411 /** Content information operations */
412 struct peerdist_info_operations {
413 	/**
414 	 * Populate content information
415 	 *
416 	 * @v info		Content information to fill in
417 	 * @ret rc		Return status code
418 	 */
419 	int ( * info ) ( struct peerdist_info *info );
420 	/**
421 	 * Populate content information segment
422 	 *
423 	 * @v segment		Content information segment to fill in
424 	 * @ret rc		Return status code
425 	 */
426 	int ( * segment ) ( struct peerdist_info_segment *segment );
427 	/**
428 	 * Populate content information block
429 	 *
430 	 * @v block		Content information block to fill in
431 	 * @ret rc		Return status code
432 	 */
433 	int ( * block ) ( struct peerdist_info_block *block );
434 };
435 
436 extern struct digest_algorithm sha512_trunc_algorithm;
437 
438 extern int peerdist_info ( userptr_t data, size_t len,
439 			   struct peerdist_info *info );
440 extern int peerdist_info_segment ( const struct peerdist_info *info,
441 				   struct peerdist_info_segment *segment,
442 				   unsigned int index );
443 extern int peerdist_info_block ( const struct peerdist_info_segment *segment,
444 				 struct peerdist_info_block *block,
445 				 unsigned int index );
446 
447 #endif /* _IPXE_PCCRC_H */
448