6 * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC]
10 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
14 #include <ipxe/uaccess.h>
15 #include <ipxe/crypto.h>
17 /******************************************************************************
19 * Content Information versioning
21 ******************************************************************************
23 * Note that version 1 data structures are little-endian, but version
24 * 2 data structures are big-endian.
27 /** Content Information version number */
28 union peerdist_info_version {
29 /** Raw version number
31 * Always little-endian, regardless of whether the
32 * encompassing structure is version 1 (little-endian) or
33 * version 2 (big-endian).
36 /** Major:minor version number */
38 /** Minor version number */
40 /** Major version number */
42 } __attribute__ (( packed ));
43 } __attribute__ (( packed ));
45 /** Content Information version 1 */
46 #define PEERDIST_INFO_V1 0x0100
48 /** Content Information version 2 */
49 #define PEERDIST_INFO_V2 0x0200
51 /******************************************************************************
53 * Content Information version 1
55 ******************************************************************************
58 /** Content Information version 1 data structure header
60 * All fields are little-endian.
62 struct peerdist_info_v1 {
64 union peerdist_info_version version;
67 * This is a @c PEERDIST_INFO_V1_HASH_XXX constant.
70 /** Length to skip in first segment
72 * Length at the start of the first segment which is not
73 * included within the content range.
76 /** Length to read in last segment, or zero
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.
83 /** Number of segments within the content information */
85 /* Followed by a variable-length array of segment descriptions
86 * and a list of variable-length block descriptions:
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;
92 * peerdist_info_v1_block_t(digestsize, blockN.blocks) blockN;
94 } __attribute__ (( packed ));
96 /** SHA-256 hash algorithm */
97 #define PEERDIST_INFO_V1_HASH_SHA256 0x0000800cUL
99 /** SHA-384 hash algorithm */
100 #define PEERDIST_INFO_V1_HASH_SHA384 0x0000800dUL
102 /** SHA-512 hash algorithm */
103 #define PEERDIST_INFO_V1_HASH_SHA512 0x0000800eUL
105 /** Content Information version 1 segment description header
107 * All fields are little-endian.
109 struct peerdist_info_v1_segment {
110 /** Offset of this segment within the content */
112 /** Length of this segment
114 * Should always be 32MB, except for the last segment within
118 /** Block size for this segment
120 * Should always be 64kB. Note that the last block within the
121 * last segment may actually be less than 64kB.
124 /* Followed by two variable-length hashes:
126 * uint8_t hash[digestsize];
127 * uint8_t secret[digestsize];
129 * where digestsize is the digest size for the selected hash
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
140 } __attribute__ (( packed ));
142 /** Content Information version 1 segment description
144 * @v digestsize Digest size
146 #define peerdist_info_v1_segment_t( digestsize ) \
148 struct peerdist_info_v1_segment segment; \
149 uint8_t hash[digestsize]; \
150 uint8_t secret[digestsize]; \
151 } __attribute__ (( packed ))
153 /** Content Information version 1 block description header
155 * All fields are little-endian.
157 struct peerdist_info_v1_block {
158 /** Number of blocks within the block description
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.
165 /* Followed by an array of variable-length hashes:
167 * uint8_t hash[blocks][digestsize];
169 * where digestsize is the digest size for the selected hash
172 } __attribute__ (( packed ));
174 /** Content Information version 1 block description
176 * @v digestsize Digest size
177 * @v blocks Number of blocks
179 #define peerdist_info_v1_block_t( digestsize, blocks ) \
181 struct peerdist_info_v1_block block; \
182 uint8_t hash[blocks][digestsize]; \
183 } __attribute__ (( packed ))
185 /******************************************************************************
187 * Content Information version 2
189 ******************************************************************************
192 /** Content Information version 2 data structure header
194 * All fields are big-endian.
196 struct peerdist_info_v2 {
197 /** Version number */
198 union peerdist_info_version version;
201 * This is a @c PEERDIST_INFO_V2_HASH_XXX constant.
204 /** Offset of the first segment within the content */
206 /** Index of the first segment within the content */
208 /** Length to skip in first segment
210 * Length at the start of the first segment which is not
211 * included within the content range.
214 /** Length of content range, or zero
216 * Length of the content range. A zero indicates that
217 * everything up to the end of the last segment is included in
221 /* Followed by a list of chunk descriptions */
222 } __attribute__ (( packed ));
224 /** SHA-512 hash algorithm with output truncated to first 256 bits */
225 #define PEERDIST_INFO_V2_HASH_SHA512_TRUNC 0x04
227 /** Content Information version 2 chunk description header
229 * All fields are big-endian.
231 struct peerdist_info_v2_chunk {
234 /** Chunk data length */
236 /* Followed by an array of segment descriptions:
238 * peerdist_info_v2_segment_t(digestsize) segment[segments]
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.
244 } __attribute__ (( packed ));
246 /** Content Information version 2 chunk description
248 * @v digestsize Digest size
250 #define peerdist_info_v2_chunk_t( digestsize ) \
252 struct peerdist_info_v2_chunk chunk; \
253 peerdist_info_v2_segment_t ( digestsize ) segment[0]; \
254 } __attribute__ (( packed ))
257 #define PEERDIST_INFO_V2_CHUNK_TYPE 0x00
259 /** Content Information version 2 segment description header
261 * All fields are big-endian.
263 struct peerdist_info_v2_segment {
264 /** Segment length */
266 /* Followed by two variable-length hashes:
268 * uint8_t hash[digestsize];
269 * uint8_t secret[digestsize];
271 * where digestsize is the digest size for the selected hash
274 } __attribute__ (( packed ));
276 /** Content Information version 2 segment description
278 * @v digestsize Digest size
280 #define peerdist_info_v2_segment_t( digestsize ) \
282 struct peerdist_info_v2_segment segment; \
283 uint8_t hash[digestsize]; \
284 uint8_t secret[digestsize]; \
285 } __attribute__ (( packed ))
287 /******************************************************************************
289 * Content Information
291 ******************************************************************************
294 /** Maximum digest size for any supported algorithm
296 * The largest digest size that we support is for SHA-512 at 64 bytes
298 #define PEERDIST_DIGEST_MAX_SIZE 64
300 /** Raw content information */
301 struct peerdist_raw {
304 /** Length of data buffer */
308 /** A content range */
309 struct peerdist_range {
316 /** Content information */
317 struct peerdist_info {
318 /** Raw content information */
319 struct peerdist_raw raw;
321 /** Content information operations */
322 struct peerdist_info_operations *op;
323 /** Digest algorithm */
324 struct digest_algorithm *digest;
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.
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;
346 /** A content information segment */
347 struct peerdist_info_segment {
348 /** Content information */
349 const struct peerdist_info *info;
355 * Note that this range may exceed the overall content range.
357 struct peerdist_range range;
358 /** Number of blocks within this segment */
362 /** Segment hash of data
364 * This is MS-PCCRC's "HoD".
366 uint8_t hash[PEERDIST_DIGEST_MAX_SIZE];
369 * This is MS-PCCRC's "Ke = Kp".
371 uint8_t secret[PEERDIST_DIGEST_MAX_SIZE];
372 /** Segment identifier
374 * This is MS-PCCRC's "HoHoDk".
376 uint8_t id[PEERDIST_DIGEST_MAX_SIZE];
379 /** Magic string constant used to calculate segment identifier
381 * Note that the MS-PCCRC specification states that this constant is
383 * "the null-terminated ASCII string constant "MS_P2P_CACHING";
384 * string literals are all ASCII strings with NULL terminators
385 * unless otherwise noted."
387 * The specification lies. This constant is a UTF-16LE string, not an
388 * ASCII string. The terminating wNUL *is* included within the
391 #define PEERDIST_SEGMENT_ID_MAGIC L"MS_P2P_CACHING"
393 /** A content information block */
394 struct peerdist_info_block {
395 /** Content information segment */
396 const struct peerdist_info_segment *segment;
402 * Note that this range may exceed the overall content range.
404 struct peerdist_range range;
405 /** Trimmed content range */
406 struct peerdist_range trim;
408 uint8_t hash[PEERDIST_DIGEST_MAX_SIZE];
411 /** Content information operations */
412 struct peerdist_info_operations {
414 * Populate content information
416 * @v info Content information to fill in
417 * @ret rc Return status code
419 int ( * info ) ( struct peerdist_info *info );
421 * Populate content information segment
423 * @v segment Content information segment to fill in
424 * @ret rc Return status code
426 int ( * segment ) ( struct peerdist_info_segment *segment );
428 * Populate content information block
430 * @v block Content information block to fill in
431 * @ret rc Return status code
433 int ( * block ) ( struct peerdist_info_block *block );
436 extern struct digest_algorithm sha512_trunc_algorithm;
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 );
447 #endif /* _IPXE_PCCRC_H */