PageRenderTime 25ms CodeModel.GetById 2ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/include/ipxe/pccrc.h

https://bitbucket.org/dnelson/ipxe
C++ Header | 447 lines | 124 code | 39 blank | 284 comment | 0 complexity | bfa5ea59007a2c852f68595ce3d16da0 MD5 | raw file
  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
 10FILE_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 */
 28union 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 */
 62struct 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 */
109struct 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 */
157struct 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 */
196struct 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 */
231struct 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 */
263struct 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 */
301struct peerdist_raw {
302	/** Data buffer */
303	userptr_t data;
304	/** Length of data buffer */
305	size_t len;
306};
307
308/** A content range */
309struct peerdist_range {
310	/** Start offset */
311	size_t start;
312	/** End offset */
313	size_t end;
314};
315
316/** Content information */
317struct 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 */
347struct 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 */
394struct 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 */
412struct 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
436extern struct digest_algorithm sha512_trunc_algorithm;
437
438extern int peerdist_info ( userptr_t data, size_t len,
439			   struct peerdist_info *info );
440extern int peerdist_info_segment ( const struct peerdist_info *info,
441				   struct peerdist_info_segment *segment,
442				   unsigned int index );
443extern 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 */