Browse Source

[peerdist] Add support for decoding PeerDist Content Information

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
d9166bbcae
5 changed files with 1757 additions and 0 deletions
  1. 1
    0
      src/include/ipxe/errfile.h
  2. 445
    0
      src/include/ipxe/pccrc.h
  3. 803
    0
      src/net/pccrc.c
  4. 507
    0
      src/tests/pccrc_test.c
  5. 1
    0
      src/tests/tests.c

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -237,6 +237,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
237 237
 #define ERRFILE_dhcpv6			( ERRFILE_NET | 0x003b0000 )
238 238
 #define ERRFILE_nfs_uri			( ERRFILE_NET | 0x003c0000 )
239 239
 #define ERRFILE_rndis			( ERRFILE_NET | 0x003d0000 )
240
+#define ERRFILE_pccrc			( ERRFILE_NET | 0x003e0000 )
240 241
 
241 242
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
242 243
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 445
- 0
src/include/ipxe/pccrc.h View File

@@ -0,0 +1,445 @@
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
+	/** Block hash */
406
+	uint8_t hash[PEERDIST_DIGEST_MAX_SIZE];
407
+};
408
+
409
+/** Content information operations */
410
+struct peerdist_info_operations {
411
+	/**
412
+	 * Populate content information
413
+	 *
414
+	 * @v info		Content information to fill in
415
+	 * @ret rc		Return status code
416
+	 */
417
+	int ( * info ) ( struct peerdist_info *info );
418
+	/**
419
+	 * Populate content information segment
420
+	 *
421
+	 * @v segment		Content information segment to fill in
422
+	 * @ret rc		Return status code
423
+	 */
424
+	int ( * segment ) ( struct peerdist_info_segment *segment );
425
+	/**
426
+	 * Populate content information block
427
+	 *
428
+	 * @v block		Content information block to fill in
429
+	 * @ret rc		Return status code
430
+	 */
431
+	int ( * block ) ( struct peerdist_info_block *block );
432
+};
433
+
434
+extern struct digest_algorithm sha512_trunc_algorithm;
435
+
436
+extern int peerdist_info ( userptr_t data, size_t len,
437
+			   struct peerdist_info *info );
438
+extern int peerdist_info_segment ( const struct peerdist_info *info,
439
+				   struct peerdist_info_segment *segment,
440
+				   unsigned int index );
441
+extern int peerdist_info_block ( const struct peerdist_info_segment *segment,
442
+				 struct peerdist_info_block *block,
443
+				 unsigned int index );
444
+
445
+#endif /* _IPXE_PCCRC_H */

+ 803
- 0
src/net/pccrc.c View File

@@ -0,0 +1,803 @@
1
+/*
2
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+#include <errno.h>
27
+#include <assert.h>
28
+#include <ipxe/uaccess.h>
29
+#include <ipxe/sha256.h>
30
+#include <ipxe/sha512.h>
31
+#include <ipxe/hmac.h>
32
+#include <ipxe/base16.h>
33
+#include <ipxe/pccrc.h>
34
+
35
+/** @file
36
+ *
37
+ * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC]
38
+ *
39
+ */
40
+
41
+/******************************************************************************
42
+ *
43
+ * Utility functions
44
+ *
45
+ ******************************************************************************
46
+ */
47
+
48
+/**
49
+ * Transcribe hash value (for debugging)
50
+ *
51
+ * @v info		Content information
52
+ * @v hash		Hash value
53
+ * @ret string		Hash value string
54
+ */
55
+static inline const char *
56
+peerdist_info_hash_ntoa ( const struct peerdist_info *info, const void *hash ) {
57
+	static char buf[ ( 2 * PEERDIST_DIGEST_MAX_SIZE ) + 1 /* NUL */ ];
58
+	size_t digestsize = info->digestsize;
59
+
60
+	/* Sanity check */
61
+	assert ( info != NULL );
62
+	assert ( digestsize != 0 );
63
+	assert ( base16_encoded_len ( digestsize ) < sizeof ( buf ) );
64
+
65
+	/* Transcribe hash value */
66
+	base16_encode ( hash, digestsize, buf );
67
+	return buf;
68
+}
69
+
70
+/**
71
+ * Get raw data
72
+ *
73
+ * @v info		Content information
74
+ * @v data		Data buffer
75
+ * @v offset		Starting offset
76
+ * @v len		Length
77
+ * @ret rc		Return status code
78
+ */
79
+static int peerdist_info_get ( const struct peerdist_info *info, void *data,
80
+			       size_t offset, size_t len ) {
81
+
82
+	/* Sanity check */
83
+	if ( ( offset > info->raw.len ) ||
84
+	     ( len > ( info->raw.len - offset ) ) ) {
85
+		DBGC ( info, "PCCRC %p data underrun at [%zx,%zx) of %zx\n",
86
+		       info, offset, ( offset + len ), info->raw.len );
87
+		return -ERANGE;
88
+	}
89
+
90
+	/* Copy data */
91
+	copy_from_user ( data, info->raw.data, offset, len );
92
+
93
+	return 0;
94
+}
95
+
96
+/**
97
+ * Populate segment hashes
98
+ *
99
+ * @v segment		Content information segment to fill in
100
+ * @v hash		Segment hash of data
101
+ * @v secret		Segment secret
102
+ */
103
+static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment,
104
+					 const void *hash, const void *secret ){
105
+	const struct peerdist_info *info = segment->info;
106
+	struct digest_algorithm *digest = info->digest;
107
+	uint8_t ctx[digest->ctxsize];
108
+	size_t digestsize = info->digestsize;
109
+	size_t secretsize = digestsize;
110
+	static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC;
111
+
112
+	/* Sanity check */
113
+	assert ( digestsize <= sizeof ( segment->hash ) );
114
+	assert ( digestsize <= sizeof ( segment->secret ) );
115
+	assert ( digestsize <= sizeof ( segment->id ) );
116
+
117
+	/* Get segment hash of data */
118
+	memcpy ( segment->hash, hash, digestsize );
119
+
120
+	/* Get segment secret */
121
+	memcpy ( segment->secret, secret, digestsize );
122
+
123
+	/* Calculate segment identifier */
124
+	hmac_init ( digest, ctx, segment->secret, &secretsize );
125
+	assert ( secretsize == digestsize );
126
+	hmac_update ( digest, ctx, segment->hash, digestsize );
127
+	hmac_update ( digest, ctx, magic, sizeof ( magic ) );
128
+	hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id );
129
+	assert ( secretsize == digestsize );
130
+}
131
+
132
+/******************************************************************************
133
+ *
134
+ * Content Information version 1
135
+ *
136
+ ******************************************************************************
137
+ */
138
+
139
+/**
140
+ * Get number of blocks within a block description
141
+ *
142
+ * @v info		Content information
143
+ * @v offset		Block description offset
144
+ * @ret blocks		Number of blocks, or negative error
145
+ */
146
+static int peerdist_info_v1_blocks ( const struct peerdist_info *info,
147
+				     size_t offset ) {
148
+	struct peerdist_info_v1_block raw;
149
+	unsigned int blocks;
150
+	int rc;
151
+
152
+	/* Get block description header */
153
+	if ( ( rc = peerdist_info_get ( info, &raw, offset,
154
+					sizeof ( raw ) ) ) != 0 )
155
+		return rc;
156
+
157
+	/* Calculate number of blocks */
158
+	blocks = le32_to_cpu ( raw.blocks );
159
+
160
+	return blocks;
161
+}
162
+
163
+/**
164
+ * Locate block description
165
+ *
166
+ * @v info		Content information
167
+ * @v index		Segment index
168
+ * @ret offset		Block description offset, or negative error
169
+ */
170
+static ssize_t peerdist_info_v1_block_offset ( const struct peerdist_info *info,
171
+					       unsigned int index ) {
172
+	size_t digestsize = info->digestsize;
173
+	unsigned int i;
174
+	size_t offset;
175
+	int blocks;
176
+	int rc;
177
+
178
+	/* Sanity check */
179
+	assert ( index < info->segments );
180
+
181
+	/* Calculate offset of first block description */
182
+	offset = ( sizeof ( struct peerdist_info_v1 ) +
183
+		   ( info->segments *
184
+		     sizeof ( peerdist_info_v1_segment_t ( digestsize ) ) ) );
185
+
186
+	/* Iterate over block descriptions until we find this segment */
187
+	for ( i = 0 ; i < index ; i++ ) {
188
+
189
+		/* Get number of blocks */
190
+		blocks = peerdist_info_v1_blocks ( info, offset );
191
+		if ( blocks < 0 ) {
192
+			rc = blocks;
193
+			DBGC ( info, "PCCRC %p segment %d could not get number "
194
+			       "of blocks: %s\n", info, i, strerror ( rc ) );
195
+			return rc;
196
+		}
197
+
198
+		/* Move to next block description */
199
+		offset += sizeof ( peerdist_info_v1_block_t ( digestsize,
200
+							      blocks ) );
201
+	}
202
+
203
+	return offset;
204
+}
205
+
206
+/**
207
+ * Populate content information
208
+ *
209
+ * @v info		Content information to fill in
210
+ * @ret rc		Return status code
211
+ */
212
+static int peerdist_info_v1 ( struct peerdist_info *info ) {
213
+	struct peerdist_info_v1 raw;
214
+	struct peerdist_info_segment first;
215
+	struct peerdist_info_segment last;
216
+	size_t first_skip;
217
+	size_t last_skip;
218
+	size_t last_read;
219
+	int rc;
220
+
221
+	/* Get raw header */
222
+	if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){
223
+		DBGC ( info, "PCCRC %p could not get V1 content information: "
224
+		       "%s\n", info, strerror ( rc ) );
225
+		return rc;
226
+	}
227
+	assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V1 ) );
228
+
229
+	/* Determine hash algorithm */
230
+	switch ( raw.hash ) {
231
+	case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA256 ) :
232
+		info->digest = &sha256_algorithm;
233
+		break;
234
+	case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA384 ) :
235
+		info->digest = &sha384_algorithm;
236
+		break;
237
+	case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA512 ) :
238
+		info->digest = &sha512_algorithm;
239
+		break;
240
+	default:
241
+		DBGC ( info, "PCCRC %p unsupported hash algorithm %#08x\n",
242
+		       info, le32_to_cpu ( raw.hash ) );
243
+		return -ENOTSUP;
244
+	}
245
+	info->digestsize = info->digest->digestsize;
246
+	assert ( info->digest != NULL );
247
+	DBGC2 ( info, "PCCRC %p using %s[%zd]\n",
248
+		info, info->digest->name, ( info->digestsize * 8 ) );
249
+
250
+	/* Calculate number of segments */
251
+	info->segments = le32_to_cpu ( raw.segments );
252
+
253
+	/* Get first segment */
254
+	if ( ( rc = peerdist_info_segment ( info, &first, 0 ) ) != 0 )
255
+		return rc;
256
+
257
+	/* Calculate range start offset */
258
+	info->range.start = first.range.start;
259
+
260
+	/* Calculate trimmed range start offset */
261
+	first_skip = le32_to_cpu ( raw.first );
262
+	info->trim.start = ( first.range.start + first_skip );
263
+
264
+	/* Get last segment */
265
+	if ( ( rc = peerdist_info_segment ( info, &last,
266
+					    ( info->segments - 1 ) ) ) != 0 )
267
+		return rc;
268
+
269
+	/* Calculate range end offset */
270
+	info->range.end = last.range.end;
271
+
272
+	/* Calculate trimmed range end offset */
273
+	if ( raw.last ) {
274
+		/* Explicit length to include from last segment is given */
275
+		last_read = le32_to_cpu ( raw.last );
276
+		last_skip = ( last.index ? 0 : first_skip );
277
+		info->trim.end = ( last.range.start + last_skip + last_read );
278
+	} else {
279
+		/* No explicit length given: range extends to end of segment */
280
+		info->trim.end = last.range.end;
281
+	}
282
+
283
+	return 0;
284
+}
285
+
286
+/**
287
+ * Populate content information segment
288
+ *
289
+ * @v segment		Content information segment to fill in
290
+ * @ret rc		Return status code
291
+ */
292
+static int peerdist_info_v1_segment ( struct peerdist_info_segment *segment ) {
293
+	const struct peerdist_info *info = segment->info;
294
+	size_t digestsize = info->digestsize;
295
+	peerdist_info_v1_segment_t ( digestsize ) raw;
296
+	ssize_t raw_offset;
297
+	int blocks;
298
+	int rc;
299
+
300
+	/* Sanity checks */
301
+	assert ( segment->index < info->segments );
302
+
303
+	/* Get raw description */
304
+	raw_offset = ( sizeof ( struct peerdist_info_v1 ) +
305
+		       ( segment->index * sizeof ( raw ) ) );
306
+	if ( ( rc = peerdist_info_get ( info, &raw, raw_offset,
307
+					sizeof ( raw ) ) ) != 0 ) {
308
+		DBGC ( info, "PCCRC %p segment %d could not get segment "
309
+		       "description: %s\n", info, segment->index,
310
+		       strerror ( rc ) );
311
+		return rc;
312
+	}
313
+
314
+	/* Calculate start offset of this segment */
315
+	segment->range.start = le64_to_cpu ( raw.segment.offset );
316
+
317
+	/* Calculate end offset of this segment */
318
+	segment->range.end = ( segment->range.start +
319
+			       le32_to_cpu ( raw.segment.len ) );
320
+
321
+	/* Calculate block size of this segment */
322
+	segment->blksize = le32_to_cpu ( raw.segment.blksize );
323
+
324
+	/* Locate block description for this segment */
325
+	raw_offset = peerdist_info_v1_block_offset ( info, segment->index );
326
+	if ( raw_offset < 0 ) {
327
+		rc = raw_offset;
328
+		return rc;
329
+	}
330
+
331
+	/* Get number of blocks */
332
+	blocks = peerdist_info_v1_blocks ( info, raw_offset );
333
+	if ( blocks < 0 ) {
334
+		rc = blocks;
335
+		DBGC ( info, "PCCRC %p segment %d could not get number of "
336
+		       "blocks: %s\n", info, segment->index, strerror ( rc ) );
337
+		return rc;
338
+	}
339
+	segment->blocks = blocks;
340
+
341
+	/* Calculate segment hashes */
342
+	peerdist_info_segment_hash ( segment, raw.hash, raw.secret );
343
+
344
+	return 0;
345
+}
346
+
347
+/**
348
+ * Populate content information block
349
+ *
350
+ * @v block		Content information block to fill in
351
+ * @ret rc		Return status code
352
+ */
353
+static int peerdist_info_v1_block ( struct peerdist_info_block *block ) {
354
+	const struct peerdist_info_segment *segment = block->segment;
355
+	const struct peerdist_info *info = segment->info;
356
+	size_t digestsize = info->digestsize;
357
+	peerdist_info_v1_block_t ( digestsize, segment->blocks ) raw;
358
+	ssize_t raw_offset;
359
+	int rc;
360
+
361
+	/* Sanity checks */
362
+	assert ( block->index < segment->blocks );
363
+
364
+	/* Calculate start offset of this block */
365
+	block->range.start = ( segment->range.start +
366
+			       ( block->index * segment->blksize ) );
367
+
368
+	/* Calculate end offset of this block */
369
+	block->range.end = ( block->range.start + segment->blksize );
370
+	if ( block->range.end > segment->range.end )
371
+		block->range.end = segment->range.end;
372
+
373
+	/* Locate block description */
374
+	raw_offset = peerdist_info_v1_block_offset ( info, segment->index );
375
+	if ( raw_offset < 0 ) {
376
+		rc = raw_offset;
377
+		return rc;
378
+	}
379
+
380
+	/* Get block hash */
381
+	raw_offset += offsetof ( typeof ( raw ), hash[block->index] );
382
+	if ( ( rc = peerdist_info_get ( info, block->hash, raw_offset,
383
+					digestsize ) ) != 0 ) {
384
+		DBGC ( info, "PCCRC %p segment %d block %d could not get "
385
+		       "hash: %s\n", info, segment->index, block->index,
386
+		       strerror ( rc ) );
387
+		return rc;
388
+	}
389
+
390
+	return 0;
391
+}
392
+
393
+/** Content information version 1 operations */
394
+static struct peerdist_info_operations peerdist_info_v1_operations = {
395
+	.info = peerdist_info_v1,
396
+	.segment = peerdist_info_v1_segment,
397
+	.block = peerdist_info_v1_block,
398
+};
399
+
400
+/******************************************************************************
401
+ *
402
+ * Content Information version 2
403
+ *
404
+ ******************************************************************************
405
+ */
406
+
407
+/** A segment cursor */
408
+struct peerdist_info_v2_cursor {
409
+	/** Raw data offset */
410
+	size_t offset;
411
+	/** Number of segments remaining within this chunk */
412
+	unsigned int remaining;
413
+	/** Accumulated segment length */
414
+	size_t len;
415
+};
416
+
417
+/**
418
+ * Initialise segment cursor
419
+ *
420
+ * @v cursor		Segment cursor
421
+ */
422
+static inline void
423
+peerdist_info_v2_cursor_init ( struct peerdist_info_v2_cursor *cursor ) {
424
+
425
+	/* Initialise cursor */
426
+	cursor->offset = ( sizeof ( struct peerdist_info_v2 ) +
427
+			   sizeof ( struct peerdist_info_v2_chunk ) );
428
+	cursor->remaining = 0;
429
+	cursor->len = 0;
430
+}
431
+
432
+/**
433
+ * Update segment cursor to next segment description
434
+ *
435
+ * @v info		Content information
436
+ * @v offset		Current offset
437
+ * @v remaining		Number of segments remaining within this chunk
438
+ * @ret rc		Return status code
439
+ */
440
+static int
441
+peerdist_info_v2_cursor_next ( const struct peerdist_info *info,
442
+			       struct peerdist_info_v2_cursor *cursor ) {
443
+	size_t digestsize = info->digestsize;
444
+	peerdist_info_v2_segment_t ( digestsize ) raw;
445
+	struct peerdist_info_v2_chunk chunk;
446
+	int rc;
447
+
448
+	/* Get chunk description if applicable */
449
+	if ( ! cursor->remaining ) {
450
+
451
+		/* Get chunk description */
452
+		if ( ( rc = peerdist_info_get ( info, &chunk,
453
+						( cursor->offset -
454
+						  sizeof ( chunk ) ),
455
+						sizeof ( chunk ) ) ) != 0 )
456
+			return rc;
457
+
458
+		/* Update number of segments remaining */
459
+		cursor->remaining = ( be32_to_cpu ( chunk.len ) /
460
+				      sizeof ( raw ) );
461
+	}
462
+
463
+	/* Get segment description header */
464
+	if ( ( rc = peerdist_info_get ( info, &raw.segment, cursor->offset,
465
+					sizeof ( raw.segment ) ) ) != 0 )
466
+		return rc;
467
+
468
+	/* Update cursor */
469
+	cursor->offset += sizeof ( raw );
470
+	cursor->remaining--;
471
+	if ( ! cursor->remaining )
472
+		cursor->offset += sizeof ( chunk );
473
+	cursor->len += be32_to_cpu ( raw.segment.len );
474
+
475
+	return 0;
476
+}
477
+
478
+/**
479
+ * Get number of segments and total length
480
+ *
481
+ * @v info		Content information
482
+ * @v len		Length to fill in
483
+ * @ret rc		Number of segments, or negative error
484
+ */
485
+static int peerdist_info_v2_segments ( const struct peerdist_info *info,
486
+				       size_t *len ) {
487
+	struct peerdist_info_v2_cursor cursor;
488
+	unsigned int segments;
489
+	int rc;
490
+
491
+	/* Iterate over all segments */
492
+	for ( peerdist_info_v2_cursor_init ( &cursor ), segments = 0 ;
493
+	      cursor.offset < info->raw.len ; segments++ ) {
494
+
495
+		/* Update segment cursor */
496
+		if ( ( rc = peerdist_info_v2_cursor_next ( info,
497
+							   &cursor ) ) != 0 ) {
498
+			DBGC ( info, "PCCRC %p segment %d could not update "
499
+			       "segment cursor: %s\n",
500
+			       info, segments, strerror ( rc ) );
501
+			return rc;
502
+		}
503
+	}
504
+
505
+	/* Record accumulated length */
506
+	*len = cursor.len;
507
+
508
+	return segments;
509
+}
510
+
511
+/**
512
+ * Populate content information
513
+ *
514
+ * @v info		Content information to fill in
515
+ * @ret rc		Return status code
516
+ */
517
+static int peerdist_info_v2 ( struct peerdist_info *info ) {
518
+	struct peerdist_info_v2 raw;
519
+	size_t len = 0;
520
+	int segments;
521
+	int rc;
522
+
523
+	/* Get raw header */
524
+	if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){
525
+		DBGC ( info, "PCCRC %p could not get V2 content information: "
526
+		       "%s\n", info, strerror ( rc ) );
527
+		return rc;
528
+	}
529
+	assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V2 ) );
530
+
531
+	/* Determine hash algorithm */
532
+	switch ( raw.hash ) {
533
+	case PEERDIST_INFO_V2_HASH_SHA512_TRUNC :
534
+		info->digest = &sha512_algorithm;
535
+		info->digestsize = ( 256 / 8 );
536
+		break;
537
+	default:
538
+		DBGC ( info, "PCCRC %p unsupported hash algorithm %#02x\n",
539
+		       info, raw.hash );
540
+		return -ENOTSUP;
541
+	}
542
+	assert ( info->digest != NULL );
543
+	DBGC2 ( info, "PCCRC %p using %s[%zd]\n",
544
+		info, info->digest->name, ( info->digestsize * 8 ) );
545
+
546
+	/* Calculate number of segments and total length */
547
+	segments = peerdist_info_v2_segments ( info, &len );
548
+	if ( segments < 0 ) {
549
+		rc = segments;
550
+		DBGC ( info, "PCCRC %p could not get segment count and length: "
551
+		       "%s\n", info, strerror ( rc ) );
552
+		return rc;
553
+	}
554
+	info->segments = segments;
555
+
556
+	/* Calculate range start offset */
557
+	info->range.start = be64_to_cpu ( raw.offset );
558
+
559
+	/* Calculate trimmed range start offset */
560
+	info->trim.start = ( info->range.start + be32_to_cpu ( raw.first ) );
561
+
562
+	/* Calculate range end offset */
563
+	info->range.end = ( info->range.start + len );
564
+
565
+	/* Calculate trimmed range end offset */
566
+	info->trim.end = ( raw.len ? be64_to_cpu ( raw.len ) :
567
+			   info->range.end );
568
+
569
+	return 0;
570
+}
571
+
572
+/**
573
+ * Populate content information segment
574
+ *
575
+ * @v segment		Content information segment to fill in
576
+ * @ret rc		Return status code
577
+ */
578
+static int peerdist_info_v2_segment ( struct peerdist_info_segment *segment ) {
579
+	const struct peerdist_info *info = segment->info;
580
+	size_t digestsize = info->digestsize;
581
+	peerdist_info_v2_segment_t ( digestsize ) raw;
582
+	struct peerdist_info_v2_cursor cursor;
583
+	unsigned int index;
584
+	size_t len;
585
+	int rc;
586
+
587
+	/* Sanity checks */
588
+	assert ( segment->index < info->segments );
589
+
590
+	/* Iterate over all segments before the target segment */
591
+	for ( peerdist_info_v2_cursor_init ( &cursor ), index = 0 ;
592
+	      index < segment->index ; index++ ) {
593
+
594
+		/* Update segment cursor */
595
+		if ( ( rc = peerdist_info_v2_cursor_next ( info,
596
+							   &cursor ) ) != 0 ) {
597
+			DBGC ( info, "PCCRC %p segment %d could not update "
598
+			       "segment cursor: %s\n",
599
+			       info, index, strerror ( rc ) );
600
+			return rc;
601
+		}
602
+	}
603
+
604
+	/* Get raw description */
605
+	if ( ( rc = peerdist_info_get ( info, &raw, cursor.offset,
606
+					sizeof ( raw ) ) ) != 0 ) {
607
+		DBGC ( info, "PCCRC %p segment %d could not get segment "
608
+		       "description: %s\n",
609
+		       info, segment->index, strerror ( rc ) );
610
+		return rc;
611
+	}
612
+
613
+	/* Calculate start offset of this segment */
614
+	segment->range.start = ( info->range.start + cursor.len );
615
+
616
+	/* Calculate end offset of this segment */
617
+	len = be32_to_cpu ( raw.segment.len );
618
+	segment->range.end = ( segment->range.start + len );
619
+
620
+	/* Model as a segment containing a single block */
621
+	segment->blocks = 1;
622
+	segment->blksize = len;
623
+
624
+	/* Calculate segment hashes */
625
+	peerdist_info_segment_hash ( segment, raw.hash, raw.secret );
626
+
627
+	return 0;
628
+}
629
+
630
+/**
631
+ * Populate content information block
632
+ *
633
+ * @v block		Content information block to fill in
634
+ * @ret rc		Return status code
635
+ */
636
+static int peerdist_info_v2_block ( struct peerdist_info_block *block ) {
637
+	const struct peerdist_info_segment *segment = block->segment;
638
+	const struct peerdist_info *info = segment->info;
639
+	size_t digestsize = info->digestsize;
640
+
641
+	/* Sanity checks */
642
+	assert ( block->index < segment->blocks );
643
+
644
+	/* Model as a block covering the whole segment */
645
+	memcpy ( &block->range, &segment->range, sizeof ( block->range ) );
646
+	memcpy ( block->hash, segment->hash, digestsize );
647
+
648
+	return 0;
649
+}
650
+
651
+/** Content information version 2 operations */
652
+static struct peerdist_info_operations peerdist_info_v2_operations = {
653
+	.block = peerdist_info_v2_block,
654
+	.segment = peerdist_info_v2_segment,
655
+	.info = peerdist_info_v2,
656
+};
657
+
658
+/******************************************************************************
659
+ *
660
+ * Content Information
661
+ *
662
+ ******************************************************************************
663
+ */
664
+
665
+/**
666
+ * Populate content information
667
+ *
668
+ * @v data		Raw data
669
+ * @v len		Length of raw data
670
+ * @v info		Content information to fill in
671
+ * @ret rc		Return status code
672
+ */
673
+int peerdist_info ( userptr_t data, size_t len, struct peerdist_info *info ) {
674
+	union peerdist_info_version version;
675
+	int rc;
676
+
677
+	/* Initialise structure */
678
+	memset ( info, 0, sizeof ( *info ) );
679
+	info->raw.data = data;
680
+	info->raw.len = len;
681
+
682
+	/* Get version */
683
+	if ( ( rc = peerdist_info_get ( info, &version, 0,
684
+					sizeof ( version ) ) ) != 0 ) {
685
+		DBGC ( info, "PCCRC %p could not get version: %s\n",
686
+		       info, strerror ( rc ) );
687
+		return rc;
688
+	}
689
+	DBGC2 ( info, "PCCRC %p version %d.%d\n",
690
+		info, version.major, version.minor );
691
+
692
+	/* Determine version */
693
+	switch ( version.raw ) {
694
+	case cpu_to_le16 ( PEERDIST_INFO_V1 ) :
695
+		info->op = &peerdist_info_v1_operations;
696
+		break;
697
+	case cpu_to_le16 ( PEERDIST_INFO_V2 ) :
698
+		info->op = &peerdist_info_v2_operations;
699
+		break;
700
+	default:
701
+		DBGC ( info, "PCCRC %p unsupported version %d.%d\n",
702
+		       info, version.major, version.minor );
703
+		return -ENOTSUP;
704
+	}
705
+	assert ( info->op != NULL );
706
+	assert ( info->op->info != NULL );
707
+
708
+	/* Populate content information */
709
+	if ( ( rc = info->op->info ( info ) ) != 0 )
710
+		return rc;
711
+
712
+	DBGC2 ( info, "PCCRC %p range [%08zx,%08zx) covers [%08zx,%08zx) with "
713
+		"%d segments\n", info, info->range.start, info->range.end,
714
+		info->trim.start, info->trim.end, info->segments );
715
+	return 0;
716
+}
717
+
718
+/**
719
+ * Populate content information segment
720
+ *
721
+ * @v info		Content information
722
+ * @v segment		Content information segment to fill in
723
+ * @v index		Segment index
724
+ * @ret rc		Return status code
725
+ */
726
+int peerdist_info_segment ( const struct peerdist_info *info,
727
+			    struct peerdist_info_segment *segment,
728
+			    unsigned int index ) {
729
+	int rc;
730
+
731
+	/* Sanity checks */
732
+	assert ( info != NULL );
733
+	assert ( info->op != NULL );
734
+	assert ( info->op->segment != NULL );
735
+	if ( index >= info->segments ) {
736
+		DBGC ( info, "PCCRC %p segment %d of [0,%d) out of range\n",
737
+		       info, index, info->segments );
738
+		return -ERANGE;
739
+	}
740
+
741
+	/* Initialise structure */
742
+	memset ( segment, 0, sizeof ( *segment ) );
743
+	segment->info = info;
744
+	segment->index = index;
745
+
746
+	/* Populate content information segment */
747
+	if ( ( rc = info->op->segment ( segment ) ) != 0 )
748
+		return rc;
749
+
750
+	DBGC2 ( info, "PCCRC %p segment %d covers [%08zx,%08zx) with %d "
751
+		"blocks\n", info, segment->index, segment->range.start,
752
+		segment->range.end, segment->blocks );
753
+	DBGC2 ( info, "PCCRC %p segment %d digest %s\n", info, segment->index,
754
+		peerdist_info_hash_ntoa ( info, segment->hash ) );
755
+	DBGC2 ( info, "PCCRC %p segment %d secret %s\n", info, segment->index,
756
+		peerdist_info_hash_ntoa ( info, segment->secret ) );
757
+	DBGC2 ( info, "PCCRC %p segment %d identf %s\n", info, segment->index,
758
+		peerdist_info_hash_ntoa ( info, segment->id ) );
759
+	return 0;
760
+}
761
+
762
+/**
763
+ * Populate content information block
764
+ *
765
+ * @v segment		Content information segment
766
+ * @v block		Content information block to fill in
767
+ * @v index		Block index
768
+ * @ret rc		Return status code
769
+ */
770
+int peerdist_info_block ( const struct peerdist_info_segment *segment,
771
+			  struct peerdist_info_block *block,
772
+			  unsigned int index ) {
773
+	const struct peerdist_info *info = segment->info;
774
+	int rc;
775
+
776
+	/* Sanity checks */
777
+	assert ( segment != NULL );
778
+	assert ( info != NULL );
779
+	assert ( info->op != NULL );
780
+	assert ( info->op->block != NULL );
781
+	if ( index >= segment->blocks ) {
782
+		DBGC ( info, "PCCRC %p segment %d block %d of [0,%d) out of "
783
+		       "range\n", info, segment->index, index, segment->blocks);
784
+		return -ERANGE;
785
+	}
786
+
787
+	/* Initialise structure */
788
+	memset ( block, 0, sizeof ( *block ) );
789
+	block->segment = segment;
790
+	block->index = index;
791
+
792
+	/* Populate content information block */
793
+	if ( ( rc = info->op->block ( block ) ) != 0 )
794
+		return rc;
795
+
796
+	DBGC2 ( info, "PCCRC %p segment %d block %d hash %s\n",
797
+		info, segment->index, block->index,
798
+		peerdist_info_hash_ntoa ( info, block->hash ) );
799
+	DBGC2 ( info, "PCCRC %p segment %d block %d covers [%08zx,%08zx)\n",
800
+		info, segment->index, block->index, block->range.start,
801
+		block->range.end );
802
+	return 0;
803
+}

+ 507
- 0
src/tests/pccrc_test.c View File

@@ -0,0 +1,507 @@
1
+/*
2
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+/** @file
27
+ *
28
+ * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] tests
29
+ *
30
+ */
31
+
32
+/* Forcibly enable assertions */
33
+#undef NDEBUG
34
+
35
+#include <stdint.h>
36
+#include <string.h>
37
+#include <assert.h>
38
+#include <ipxe/uaccess.h>
39
+#include <ipxe/pccrc.h>
40
+#include <ipxe/sha256.h>
41
+#include <ipxe/sha512.h>
42
+#include <ipxe/hmac.h>
43
+#include <ipxe/test.h>
44
+
45
+/** Define inline raw data */
46
+#define DATA(...) { __VA_ARGS__ }
47
+
48
+/** A content information test */
49
+struct peerdist_info_test {
50
+	/** Raw content information */
51
+	const void *data;
52
+	/** Length of raw content information */
53
+	size_t len;
54
+	/** Expected digest algorithm */
55
+	struct digest_algorithm *expected_digest;
56
+	/** Expected digest size */
57
+	size_t expected_digestsize;
58
+	/** Expected trimmed content range */
59
+	struct peerdist_range expected_trim;
60
+	/** Expected number of segments */
61
+	unsigned int expected_segments;
62
+};
63
+
64
+/**
65
+ * Define a content information test
66
+ *
67
+ * @v name		Test name
68
+ * @v DATA		Raw content information
69
+ * @v DIGEST		Expected digest algorithm
70
+ * @v DIGESTSIZE	Expected digest size
71
+ * @v START		Expected trimmed content range start offset
72
+ * @v END		Expected trimmed content range end offset
73
+ * @v SEGMENTS		Expected number of segments
74
+ * @ret test		Content information test
75
+ *
76
+ * Raw content information can be obtained from PeerDist-capable web
77
+ * servers using wget's "--header" option to inject the relevant
78
+ * PeerDist headers.  For example:
79
+ *
80
+ *   wget --header "Accept-Encoding: peerdist" \
81
+ *        --header "X-P2P-PeerDist: Version=1.0" \
82
+ *	  http://peerdist.server.address/test.url -O - | xxd -i -c 11
83
+ *
84
+ * Version 1 content information can be retrieved using the headers:
85
+ *
86
+ *   Accept-Encoding: peerdist
87
+ *   X-P2P-PeerDist: Version=1.0
88
+ *
89
+ * Version 2 content information can be retrieved (from compatible
90
+ * servers) using the headers:
91
+ *
92
+ *   Accept-Encoding: peerdist
93
+ *   X-P2P-PeerDist: Version=1.1
94
+ *   X-P2P-PeerDistEx: MinContentInformation=2.0, MaxContentInformation=2.0
95
+ */
96
+#define PEERDIST_INFO_TEST( name, DATA, DIGEST, DIGESTSIZE, START, END,	\
97
+			    SEGMENTS )					\
98
+	static const uint8_t name ## _data[] = DATA;			\
99
+	static struct peerdist_info_test name = {			\
100
+		.data = name ## _data,					\
101
+		.len = sizeof ( name ## _data ),			\
102
+		.expected_digest = DIGEST,				\
103
+		.expected_digestsize = DIGESTSIZE,			\
104
+		.expected_trim = {					\
105
+			.start = START,					\
106
+			.end = END,					\
107
+		},							\
108
+		.expected_segments = SEGMENTS,				\
109
+	}
110
+
111
+/** A content information segment test */
112
+struct peerdist_info_segment_test {
113
+	/** Segment index */
114
+	unsigned int index;
115
+	/** Expected content range */
116
+	struct peerdist_range expected_range;
117
+	/** Expected number of blocks */
118
+	unsigned int expected_blocks;
119
+	/** Expected block size */
120
+	size_t expected_blksize;
121
+	/** Expected segment hash of data */
122
+	uint8_t expected_hash[PEERDIST_DIGEST_MAX_SIZE];
123
+	/** Expected segment secret */
124
+	uint8_t expected_secret[PEERDIST_DIGEST_MAX_SIZE];
125
+	/** Expected segment identifier */
126
+	uint8_t expected_id[PEERDIST_DIGEST_MAX_SIZE];
127
+};
128
+
129
+/**
130
+ * Define a content information segment test
131
+ *
132
+ * @v name		Test name
133
+ * @v INDEX		Segment index
134
+ * @v START		Expected content range start offset
135
+ * @v END		Expected content range end offset
136
+ * @v BLOCKS		Expected number of blocks
137
+ * @v BLKSIZE		Expected block size
138
+ * @v HASH		Expected segment hash of data
139
+ * @v SECRET		Expected segment secret
140
+ * @v ID		Expected segment identifier
141
+ * @ret test		Content information segment test
142
+ */
143
+#define PEERDIST_INFO_SEGMENT_TEST( name, INDEX, START, END, BLOCKS,	\
144
+				    BLKSIZE, HASH, SECRET, ID )		\
145
+	static struct peerdist_info_segment_test name = {		\
146
+		.index = INDEX,						\
147
+		.expected_range = {					\
148
+			.start = START,					\
149
+			.end = END,					\
150
+		},							\
151
+		.expected_blocks = BLOCKS,				\
152
+		.expected_blksize = BLKSIZE,				\
153
+		.expected_hash = HASH,					\
154
+		.expected_secret = SECRET,				\
155
+		.expected_id = ID,					\
156
+	}
157
+
158
+/** A content information block test */
159
+struct peerdist_info_block_test {
160
+	/** Block index */
161
+	unsigned int index;
162
+	/** Expected content range */
163
+	struct peerdist_range expected_range;
164
+	/** Expected hash of data */
165
+	uint8_t expected_hash[PEERDIST_DIGEST_MAX_SIZE];
166
+};
167
+
168
+/**
169
+ * Define a content information block test
170
+ *
171
+ * @v name		Test name
172
+ * @v INDEX		Block index
173
+ * @v START		Expected content range start offset
174
+ * @v END		Expected content range end offset
175
+ * @v HASH		Expected hash of data
176
+ * @ret test		Content information block test
177
+ */
178
+#define PEERDIST_INFO_BLOCK_TEST( name, INDEX, START, END, HASH )	\
179
+	static struct peerdist_info_block_test name = {			\
180
+		.index = INDEX,						\
181
+		.expected_range = {					\
182
+			.start = START,					\
183
+			.end = END,					\
184
+		},							\
185
+		.expected_hash = HASH,					\
186
+	}
187
+
188
+/**
189
+ * Define a server passphrase
190
+ *
191
+ * @v name		Server passphrase name
192
+ * @v DATA		Raw server passphrase
193
+ *
194
+ * The server passphrase can be exported from a Windows BranchCache
195
+ * server using the command:
196
+ *
197
+ *   netsh branchcache exportkey exported.key somepassword
198
+ *
199
+ * and this encrypted exported key can be decrypted using the
200
+ * oSSL_key_dx or mcrypt_key_dx utilities found in the (prototype)
201
+ * Prequel project at https://fedorahosted.org/prequel/ :
202
+ *
203
+ *   oSSL_key_dx exported.key somepassword
204
+ *     or
205
+ *   mcrypt_key_dx exported.key somepassword
206
+ *
207
+ * Either command will display both the server passphrase and the
208
+ * "Server Secret".  Note that this latter is the version 1 server
209
+ * secret (i.e. the SHA-256 of the server passphrase); the
210
+ * corresponding version 2 server secret can be obtained by
211
+ * calculating the truncated SHA-512 of the server passphrase.
212
+ *
213
+ * We do not know the server passphrase during normal operation.  We
214
+ * use it in the self-tests only to check for typos and other errors
215
+ * in the test vectors, by checking that the segment secret defined in
216
+ * a content information segment test is as expected.
217
+ */
218
+#define SERVER_PASSPHRASE( name, DATA )					\
219
+	static uint8_t name[] = DATA
220
+
221
+/** Server passphrase used for these test vectors */
222
+SERVER_PASSPHRASE ( passphrase,
223
+      DATA ( 0x2a, 0x3d, 0x73, 0xeb, 0x43, 0x5e, 0x9f, 0x2b, 0x8a, 0x34, 0x42,
224
+	     0x67, 0xe7, 0x46, 0x7a, 0x3c, 0x73, 0x85, 0xc6, 0xe0, 0x55, 0xe2,
225
+	     0xb4, 0xd3, 0x0d, 0xfe, 0xc7, 0xc3, 0x8b, 0x0e, 0xd7, 0x2c ) );
226
+
227
+/** IIS logo (iis-85.png) content information version 1 */
228
+PEERDIST_INFO_TEST ( iis_85_png_v1,
229
+	DATA ( 0x00, 0x01, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230
+	       0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231
+	       0x00, 0x00, 0x00, 0x00, 0x7e, 0x85, 0x01, 0x00, 0x00, 0x00, 0x01,
232
+	       0x00, 0xd8, 0xd9, 0x76, 0x35, 0x4a, 0x48, 0x72, 0xe9, 0x25, 0x76,
233
+	       0x18, 0x03, 0xf4, 0x58, 0xd9, 0xda, 0xaa, 0x67, 0xf8, 0xe3, 0x1c,
234
+	       0x63, 0x0f, 0xb7, 0x4e, 0x6a, 0x31, 0x2e, 0xf8, 0xa2, 0x5a, 0xba,
235
+	       0x11, 0xaf, 0xc0, 0xd7, 0x94, 0x92, 0x43, 0xf9, 0x4f, 0x9c, 0x1f,
236
+	       0xab, 0x35, 0xd9, 0xfd, 0x1e, 0x33, 0x1f, 0xcf, 0x78, 0x11, 0xa2,
237
+	       0xe0, 0x1d, 0x35, 0x87, 0xb3, 0x8d, 0x77, 0x0a, 0x29, 0xe2, 0x02,
238
+	       0x00, 0x00, 0x00, 0x73, 0xc1, 0x8a, 0xb8, 0x54, 0x91, 0x10, 0xf8,
239
+	       0xe9, 0x0e, 0x71, 0xbb, 0xc3, 0xab, 0x2a, 0xa8, 0xc4, 0x4d, 0x13,
240
+	       0xf4, 0x92, 0x94, 0x99, 0x25, 0x5b, 0x66, 0x0f, 0x24, 0xec, 0x77,
241
+	       0x80, 0x0b, 0x97, 0x4b, 0xdd, 0x65, 0x56, 0x7f, 0xde, 0xec, 0xcd,
242
+	       0xaf, 0xe4, 0x57, 0xa9, 0x50, 0x3b, 0x45, 0x48, 0xf6, 0x6e, 0xd3,
243
+	       0xb1, 0x88, 0xdc, 0xfd, 0xa0, 0xac, 0x38, 0x2b, 0x09, 0x71, 0x1a,
244
+	       0xcc ),
245
+	&sha256_algorithm, 32, 0, 99710, 1 );
246
+
247
+/** IIS logo (iis-85.png) content information version 1 segment 0 */
248
+PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v1_s0, 0,
249
+	0, 99710, 2, 65536,
250
+	DATA ( 0xd8, 0xd9, 0x76, 0x35, 0x4a, 0x48, 0x72, 0xe9, 0x25, 0x76, 0x18,
251
+	       0x03, 0xf4, 0x58, 0xd9, 0xda, 0xaa, 0x67, 0xf8, 0xe3, 0x1c, 0x63,
252
+	       0x0f, 0xb7, 0x4e, 0x6a, 0x31, 0x2e, 0xf8, 0xa2, 0x5a, 0xba ),
253
+	DATA ( 0x11, 0xaf, 0xc0, 0xd7, 0x94, 0x92, 0x43, 0xf9, 0x4f, 0x9c, 0x1f,
254
+	       0xab, 0x35, 0xd9, 0xfd, 0x1e, 0x33, 0x1f, 0xcf, 0x78, 0x11, 0xa2,
255
+	       0xe0, 0x1d, 0x35, 0x87, 0xb3, 0x8d, 0x77, 0x0a, 0x29, 0xe2 ),
256
+	DATA ( 0x49, 0x1b, 0x21, 0x7d, 0xbe, 0xe2, 0xb5, 0xf1, 0x2c, 0xa7, 0x9b,
257
+	       0x01, 0x5e, 0x06, 0xf4, 0xbb, 0xe6, 0x4f, 0x97, 0x45, 0xba, 0xd7,
258
+	       0x86, 0x7a, 0xef, 0x17, 0xde, 0x59, 0x92, 0x7e, 0xdc, 0xe9 ) );
259
+
260
+/** IIS logo (iis-85.png) content information version 1 segment 0 block 0 */
261
+PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v1_s0_b0, 0,
262
+	0, 65536,
263
+	DATA ( 0x73, 0xc1, 0x8a, 0xb8, 0x54, 0x91, 0x10, 0xf8, 0xe9, 0x0e, 0x71,
264
+	       0xbb, 0xc3, 0xab, 0x2a, 0xa8, 0xc4, 0x4d, 0x13, 0xf4, 0x92, 0x94,
265
+	       0x99, 0x25, 0x5b, 0x66, 0x0f, 0x24, 0xec, 0x77, 0x80, 0x0b ) );
266
+
267
+/** IIS logo (iis-85.png) content information version 1 segment 0 block 1 */
268
+PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v1_s0_b1, 1,
269
+	65536, 99710,
270
+	DATA ( 0x97, 0x4b, 0xdd, 0x65, 0x56, 0x7f, 0xde, 0xec, 0xcd, 0xaf, 0xe4,
271
+	       0x57, 0xa9, 0x50, 0x3b, 0x45, 0x48, 0xf6, 0x6e, 0xd3, 0xb1, 0x88,
272
+	       0xdc, 0xfd, 0xa0, 0xac, 0x38, 0x2b, 0x09, 0x71, 0x1a, 0xcc ) );
273
+
274
+/** IIS logo (iis-85.png) content information version 2 */
275
+PEERDIST_INFO_TEST ( iis_85_png_v2,
276
+	DATA ( 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
277
+	       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
278
+	       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
279
+	       0x00, 0x00, 0x88, 0x00, 0x00, 0x99, 0xde, 0xe0, 0xd0, 0xc3, 0x58,
280
+	       0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32, 0xb5, 0xf1, 0x97, 0x87,
281
+	       0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e, 0x78, 0x1f, 0xae, 0x71,
282
+	       0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4, 0x58, 0x03, 0x7e, 0xd4, 0x04,
283
+	       0x11, 0x6b, 0xb6, 0x16, 0xd9, 0xb1, 0x41, 0x16, 0x08, 0x85, 0x20,
284
+	       0xc4, 0x7c, 0xdc, 0x50, 0xab, 0xce, 0xa3, 0xfa, 0xe1, 0x88, 0xa9,
285
+	       0x8e, 0xa2, 0x2d, 0xf3, 0xc0, 0x00, 0x00, 0xeb, 0xa0, 0x33, 0x81,
286
+	       0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21, 0x0f, 0x37,
287
+	       0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96, 0xa1, 0x30,
288
+	       0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc, 0xb8, 0xb6, 0xeb,
289
+	       0x77, 0x83, 0xe4, 0xf8, 0x07, 0x64, 0x7b, 0x63, 0xf1, 0x46, 0xb5,
290
+	       0x2f, 0x4a, 0xc8, 0x9c, 0xcc, 0x7a, 0xbf, 0x5f, 0xa1, 0x1a, 0xca,
291
+	       0xfc, 0x2a, 0xcf, 0x50, 0x28, 0x58, 0x6c ),
292
+	&sha512_algorithm, 32, 0, 99710, 2 );
293
+
294
+/** IIS logo (iis-85.png) content information version 2 segment 0 */
295
+PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v2_s0, 0,
296
+	0, 39390, 1, 39390,
297
+	DATA ( 0xe0, 0xd0, 0xc3, 0x58, 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32,
298
+	       0xb5, 0xf1, 0x97, 0x87, 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e,
299
+	       0x78, 0x1f, 0xae, 0x71, 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4 ),
300
+	DATA ( 0x58, 0x03, 0x7e, 0xd4, 0x04, 0x11, 0x6b, 0xb6, 0x16, 0xd9, 0xb1,
301
+	       0x41, 0x16, 0x08, 0x85, 0x20, 0xc4, 0x7c, 0xdc, 0x50, 0xab, 0xce,
302
+	       0xa3, 0xfa, 0xe1, 0x88, 0xa9, 0x8e, 0xa2, 0x2d, 0xf3, 0xc0 ),
303
+	DATA ( 0x33, 0x71, 0xbb, 0xea, 0xdd, 0xb6, 0x23, 0x53, 0xad, 0xce, 0xf9,
304
+	       0x70, 0xa0, 0x6f, 0xdf, 0x65, 0x00, 0x1e, 0x04, 0x21, 0xf4, 0xc7,
305
+	       0x10, 0x82, 0x76, 0xb0, 0xc3, 0x7a, 0x9f, 0x9e, 0xc1, 0x0f ) );
306
+
307
+/** IIS logo (iis-85.png) content information version 2 segment 0 block 0 */
308
+PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v2_s0_b0, 0,
309
+	0, 39390,
310
+	DATA ( 0xe0, 0xd0, 0xc3, 0x58, 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32,
311
+	       0xb5, 0xf1, 0x97, 0x87, 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e,
312
+	       0x78, 0x1f, 0xae, 0x71, 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4 ) );
313
+
314
+/** IIS logo (iis-85.png) content information version 2 segment 1 */
315
+PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v2_s1, 1,
316
+	39390, 99710, 1, 60320,
317
+	DATA ( 0x33, 0x81, 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21,
318
+	       0x0f, 0x37, 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96,
319
+	       0xa1, 0x30, 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc ),
320
+	DATA ( 0xb8, 0xb6, 0xeb, 0x77, 0x83, 0xe4, 0xf8, 0x07, 0x64, 0x7b, 0x63,
321
+	       0xf1, 0x46, 0xb5, 0x2f, 0x4a, 0xc8, 0x9c, 0xcc, 0x7a, 0xbf, 0x5f,
322
+	       0xa1, 0x1a, 0xca, 0xfc, 0x2a, 0xcf, 0x50, 0x28, 0x58, 0x6c ),
323
+	DATA ( 0xd7, 0xe9, 0x24, 0x42, 0x5e, 0x8f, 0x4f, 0x88, 0xf0, 0x1d, 0xc6,
324
+	       0xa9, 0xbb, 0x1b, 0xc3, 0x7b, 0xe1, 0x13, 0xec, 0x79, 0x17, 0xc7,
325
+	       0x45, 0xd4, 0x96, 0x5c, 0x2b, 0x55, 0xfa, 0x16, 0x3a, 0x6e ) );
326
+
327
+/** IIS logo (iis-85.png) content information version 2 segment 1 block 0 */
328
+PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v2_s1_b0, 0,
329
+	39390, 99710,
330
+	DATA ( 0x33, 0x81, 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21,
331
+	       0x0f, 0x37, 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96,
332
+	       0xa1, 0x30, 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc ) );
333
+
334
+/**
335
+ * Report content information test result
336
+ *
337
+ * @v test		Content information test
338
+ * @v info		Content information to fill in
339
+ * @v file		Test code file
340
+ * @v line		Test code line
341
+ */
342
+static void peerdist_info_okx ( struct peerdist_info_test *test,
343
+				struct peerdist_info *info,
344
+				const char *file, unsigned int line ) {
345
+
346
+	/* Parse content information */
347
+	okx ( peerdist_info ( virt_to_user ( test->data ), test->len,
348
+			      info ) == 0, file, line );
349
+
350
+	/* Verify content information */
351
+	okx ( info->raw.data == virt_to_user ( test->data ), file, line );
352
+	okx ( info->raw.len == test->len, file, line );
353
+	okx ( info->digest == test->expected_digest, file, line );
354
+	okx ( info->digestsize == test->expected_digestsize, file, line );
355
+	okx ( info->trim.start >= info->range.start, file, line );
356
+	okx ( info->trim.start == test->expected_trim.start, file, line );
357
+	okx ( info->trim.end <= info->range.end, file, line );
358
+	okx ( info->trim.end == test->expected_trim.end, file, line );
359
+	okx ( info->segments == test->expected_segments, file, line );
360
+}
361
+#define peerdist_info_ok( test, info ) \
362
+	peerdist_info_okx ( test, info, __FILE__, __LINE__ )
363
+
364
+/**
365
+ * Report content information segment test result
366
+ *
367
+ * @v test		Content information segment test
368
+ * @v info		Content information
369
+ * @v segment		Segment information to fill in
370
+ * @v file		Test code file
371
+ * @v line		Test code line
372
+ */
373
+static void peerdist_info_segment_okx ( struct peerdist_info_segment_test *test,
374
+					const struct peerdist_info *info,
375
+					struct peerdist_info_segment *segment,
376
+					const char *file, unsigned int line ) {
377
+	size_t digestsize = info->digestsize;
378
+
379
+	/* Parse content information segment */
380
+	okx ( peerdist_info_segment ( info, segment, test->index ) == 0,
381
+	      file, line );
382
+
383
+	/* Verify content information segment */
384
+	okx ( segment->info == info, file, line );
385
+	okx ( segment->index == test->index, file, line );
386
+	okx ( segment->range.start == test->expected_range.start, file, line );
387
+	okx ( segment->range.end == test->expected_range.end, file, line );
388
+	okx ( segment->blocks == test->expected_blocks, file, line );
389
+	okx ( segment->blksize == test->expected_blksize, file, line );
390
+	okx ( memcmp ( segment->hash, test->expected_hash,
391
+		       digestsize ) == 0, file, line );
392
+	okx ( memcmp ( segment->secret, test->expected_secret,
393
+		       digestsize ) == 0, file, line );
394
+	okx ( memcmp ( segment->id, test->expected_id,
395
+		       digestsize ) == 0, file, line );
396
+}
397
+#define peerdist_info_segment_ok( test, info, segment ) \
398
+	peerdist_info_segment_okx ( test, info, segment, __FILE__, __LINE__ )
399
+
400
+/**
401
+ * Report content information block test result
402
+ *
403
+ * @v test		Content information block test
404
+ * @v segment		Segment information
405
+ * @v block		Block information to fill in
406
+ * @v file		Test code file
407
+ * @v line		Test code line
408
+ */
409
+static void
410
+peerdist_info_block_okx ( struct peerdist_info_block_test *test,
411
+			  const struct peerdist_info_segment *segment,
412
+			  struct peerdist_info_block *block,
413
+			  const char *file, unsigned int line ) {
414
+	const struct peerdist_info *info = segment->info;
415
+	size_t digestsize = info->digestsize;
416
+
417
+	/* Parse content information block */
418
+	okx ( peerdist_info_block ( segment, block, test->index ) == 0,
419
+	      file, line );
420
+
421
+	/* Verify content information block */
422
+	okx ( block->segment == segment, file, line );
423
+	okx ( block->index == test->index, file, line );
424
+	okx ( block->range.start == test->expected_range.start, file, line );
425
+	okx ( block->range.end == test->expected_range.end, file, line );
426
+	okx ( memcmp ( block->hash, test->expected_hash,
427
+		       digestsize ) == 0, file, line );
428
+}
429
+#define peerdist_info_block_ok( test, segment, block ) \
430
+	peerdist_info_block_okx ( test, segment, block, __FILE__, __LINE__ )
431
+
432
+/**
433
+ * Report server passphrase test result
434
+ *
435
+ * @v test		Content information segment test
436
+ * @v info		Content information
437
+ * @v pass		Server passphrase
438
+ * @v pass_len		Length of server passphrase
439
+ * @v file		Test code file
440
+ * @v line		Test code line
441
+ */
442
+static void
443
+peerdist_info_passphrase_okx ( struct peerdist_info_segment_test *test,
444
+			       const struct peerdist_info *info,
445
+			       uint8_t *pass, size_t pass_len,
446
+			       const char *file, unsigned int line ) {
447
+	struct digest_algorithm *digest = info->digest;
448
+	uint8_t ctx[digest->ctxsize];
449
+	uint8_t secret[digest->digestsize];
450
+	uint8_t expected[digest->digestsize];
451
+	size_t digestsize = info->digestsize;
452
+	size_t secretsize = digestsize;
453
+
454
+	/* Calculate server secret */
455
+	digest_init ( digest, ctx );
456
+	digest_update ( digest, ctx, pass, pass_len );
457
+	digest_final ( digest, ctx, secret );
458
+
459
+	/* Calculate expected segment secret */
460
+	hmac_init ( digest, ctx, secret, &secretsize );
461
+	assert ( secretsize == digestsize );
462
+	hmac_update ( digest, ctx, test->expected_hash, digestsize );
463
+	hmac_final ( digest, ctx, secret, &secretsize, expected );
464
+	assert ( secretsize == digestsize );
465
+
466
+	/* Verify segment secret */
467
+	okx ( memcmp ( test->expected_secret, expected, digestsize ) == 0,
468
+	      file, line );
469
+}
470
+#define peerdist_info_passphrase_ok( test, info, pass, pass_len )	\
471
+	peerdist_info_passphrase_okx ( test, info, pass, pass_len,	\
472
+				       __FILE__, __LINE__ )
473
+
474
+/**
475
+ * Perform content information self-tests
476
+ *
477
+ */
478
+static void peerdist_info_test_exec ( void ) {
479
+	struct peerdist_info info;
480
+	struct peerdist_info_segment segment;
481
+	struct peerdist_info_block block;
482
+
483
+	/* IIS logo (iis-85.png) content information version 1 */
484
+	peerdist_info_ok ( &iis_85_png_v1, &info );
485
+	peerdist_info_passphrase_ok ( &iis_85_png_v1_s0, &info,
486
+				      passphrase, sizeof ( passphrase ) );
487
+	peerdist_info_segment_ok ( &iis_85_png_v1_s0, &info, &segment );
488
+	peerdist_info_block_ok ( &iis_85_png_v1_s0_b0, &segment, &block );
489
+	peerdist_info_block_ok ( &iis_85_png_v1_s0_b1, &segment, &block );
490
+
491
+	/* IIS logo (iis-85.png) content information version 2 */
492
+	peerdist_info_ok ( &iis_85_png_v2, &info );
493
+	peerdist_info_passphrase_ok ( &iis_85_png_v2_s0, &info,
494
+				      passphrase, sizeof ( passphrase ) );
495
+	peerdist_info_segment_ok ( &iis_85_png_v2_s0, &info, &segment );
496
+	peerdist_info_block_ok ( &iis_85_png_v2_s0_b0, &segment, &block );
497
+	peerdist_info_passphrase_ok ( &iis_85_png_v2_s1, &info,
498
+				      passphrase, sizeof ( passphrase ) );
499
+	peerdist_info_segment_ok ( &iis_85_png_v2_s1, &info, &segment );
500
+	peerdist_info_block_ok ( &iis_85_png_v2_s1_b0, &segment, &block );
501
+}
502
+
503
+/** Content information self-test */
504
+struct self_test peerdist_info_test __self_test = {
505
+	.name = "pccrc",
506
+	.exec = peerdist_info_test_exec,
507
+};

+ 1
- 0
src/tests/tests.c View File

@@ -65,3 +65,4 @@ REQUIRE_OBJECT ( dns_test );
65 65
 REQUIRE_OBJECT ( uri_test );
66 66
 REQUIRE_OBJECT ( profile_test );
67 67
 REQUIRE_OBJECT ( setjmp_test );
68
+REQUIRE_OBJECT ( pccrc_test );

Loading…
Cancel
Save