mirror of https://github.com/ipxe/ipxe.git
[peerdist] Add support for decoding PeerDist Content Information
Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/35/merge
parent
755d2b8f6b
commit
d9166bbcae
|
@ -237,6 +237,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
#define ERRFILE_dhcpv6 ( ERRFILE_NET | 0x003b0000 )
|
||||
#define ERRFILE_nfs_uri ( ERRFILE_NET | 0x003c0000 )
|
||||
#define ERRFILE_rndis ( ERRFILE_NET | 0x003d0000 )
|
||||
#define ERRFILE_pccrc ( ERRFILE_NET | 0x003e0000 )
|
||||
|
||||
#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
|
||||
#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )
|
||||
|
|
|
@ -0,0 +1,445 @@
|
|||
#ifndef _IPXE_PCCRC_H
|
||||
#define _IPXE_PCCRC_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Peer Content Caching and Retrieval: Content Identification [MS-PCCRC]
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stdint.h>
|
||||
#include <byteswap.h>
|
||||
#include <ipxe/uaccess.h>
|
||||
#include <ipxe/crypto.h>
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Content Information versioning
|
||||
*
|
||||
******************************************************************************
|
||||
*
|
||||
* Note that version 1 data structures are little-endian, but version
|
||||
* 2 data structures are big-endian.
|
||||
*/
|
||||
|
||||
/** Content Information version number */
|
||||
union peerdist_info_version {
|
||||
/** Raw version number
|
||||
*
|
||||
* Always little-endian, regardless of whether the
|
||||
* encompassing structure is version 1 (little-endian) or
|
||||
* version 2 (big-endian).
|
||||
*/
|
||||
uint16_t raw;
|
||||
/** Major:minor version number */
|
||||
struct {
|
||||
/** Minor version number */
|
||||
uint8_t minor;
|
||||
/** Major version number */
|
||||
uint8_t major;
|
||||
} __attribute__ (( packed ));
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** Content Information version 1 */
|
||||
#define PEERDIST_INFO_V1 0x0100
|
||||
|
||||
/** Content Information version 2 */
|
||||
#define PEERDIST_INFO_V2 0x0200
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Content Information version 1
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/** Content Information version 1 data structure header
|
||||
*
|
||||
* All fields are little-endian.
|
||||
*/
|
||||
struct peerdist_info_v1 {
|
||||
/** Version number */
|
||||
union peerdist_info_version version;
|
||||
/** Hash algorithm
|
||||
*
|
||||
* This is a @c PEERDIST_INFO_V1_HASH_XXX constant.
|
||||
*/
|
||||
uint32_t hash;
|
||||
/** Length to skip in first segment
|
||||
*
|
||||
* Length at the start of the first segment which is not
|
||||
* included within the content range.
|
||||
*/
|
||||
uint32_t first;
|
||||
/** Length to read in last segment, or zero
|
||||
*
|
||||
* Length within the last segment which is included within the
|
||||
* content range. A zero value indicates that the whole of
|
||||
* the last segment is included within the content range.
|
||||
*/
|
||||
uint32_t last;
|
||||
/** Number of segments within the content information */
|
||||
uint32_t segments;
|
||||
/* Followed by a variable-length array of segment descriptions
|
||||
* and a list of variable-length block descriptions:
|
||||
*
|
||||
* peerdist_info_v1_segment_t(digestsize) segment[segments];
|
||||
* peerdist_info_v1_block_t(digestsize, block0.blocks) block0;
|
||||
* peerdist_info_v1_block_t(digestsize, block1.blocks) block1;
|
||||
* ...
|
||||
* peerdist_info_v1_block_t(digestsize, blockN.blocks) blockN;
|
||||
*/
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** SHA-256 hash algorithm */
|
||||
#define PEERDIST_INFO_V1_HASH_SHA256 0x0000800cUL
|
||||
|
||||
/** SHA-384 hash algorithm */
|
||||
#define PEERDIST_INFO_V1_HASH_SHA384 0x0000800dUL
|
||||
|
||||
/** SHA-512 hash algorithm */
|
||||
#define PEERDIST_INFO_V1_HASH_SHA512 0x0000800eUL
|
||||
|
||||
/** Content Information version 1 segment description header
|
||||
*
|
||||
* All fields are little-endian.
|
||||
*/
|
||||
struct peerdist_info_v1_segment {
|
||||
/** Offset of this segment within the content */
|
||||
uint64_t offset;
|
||||
/** Length of this segment
|
||||
*
|
||||
* Should always be 32MB, except for the last segment within
|
||||
* the content.
|
||||
*/
|
||||
uint32_t len;
|
||||
/** Block size for this segment
|
||||
*
|
||||
* Should always be 64kB. Note that the last block within the
|
||||
* last segment may actually be less than 64kB.
|
||||
*/
|
||||
uint32_t blksize;
|
||||
/* Followed by two variable-length hashes:
|
||||
*
|
||||
* uint8_t hash[digestsize];
|
||||
* uint8_t secret[digestsize];
|
||||
*
|
||||
* where digestsize is the digest size for the selected hash
|
||||
* algorithm.
|
||||
*
|
||||
* Note that the hash is taken over (the hashes of all blocks
|
||||
* within) the entire segment, even if the blocks do not
|
||||
* intersect the content range (and so do not appear within
|
||||
* the block list). It therefore functions only as a segment
|
||||
* identifier; it cannot be used to verify the content of the
|
||||
* segment (since we may not download all blocks within the
|
||||
* segment).
|
||||
*/
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** Content Information version 1 segment description
|
||||
*
|
||||
* @v digestsize Digest size
|
||||
*/
|
||||
#define peerdist_info_v1_segment_t( digestsize ) \
|
||||
struct { \
|
||||
struct peerdist_info_v1_segment segment; \
|
||||
uint8_t hash[digestsize]; \
|
||||
uint8_t secret[digestsize]; \
|
||||
} __attribute__ (( packed ))
|
||||
|
||||
/** Content Information version 1 block description header
|
||||
*
|
||||
* All fields are little-endian.
|
||||
*/
|
||||
struct peerdist_info_v1_block {
|
||||
/** Number of blocks within the block description
|
||||
*
|
||||
* This is the number of blocks within the segment which
|
||||
* overlap the content range. It may therefore be less than
|
||||
* the number of blocks within the segment.
|
||||
*/
|
||||
uint32_t blocks;
|
||||
/* Followed by an array of variable-length hashes:
|
||||
*
|
||||
* uint8_t hash[blocks][digestsize];
|
||||
*
|
||||
* where digestsize is the digest size for the selected hash
|
||||
* algorithm.
|
||||
*/
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** Content Information version 1 block description
|
||||
*
|
||||
* @v digestsize Digest size
|
||||
* @v blocks Number of blocks
|
||||
*/
|
||||
#define peerdist_info_v1_block_t( digestsize, blocks ) \
|
||||
struct { \
|
||||
struct peerdist_info_v1_block block; \
|
||||
uint8_t hash[blocks][digestsize]; \
|
||||
} __attribute__ (( packed ))
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Content Information version 2
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/** Content Information version 2 data structure header
|
||||
*
|
||||
* All fields are big-endian.
|
||||
*/
|
||||
struct peerdist_info_v2 {
|
||||
/** Version number */
|
||||
union peerdist_info_version version;
|
||||
/** Hash algorithm
|
||||
*
|
||||
* This is a @c PEERDIST_INFO_V2_HASH_XXX constant.
|
||||
*/
|
||||
uint8_t hash;
|
||||
/** Offset of the first segment within the content */
|
||||
uint64_t offset;
|
||||
/** Index of the first segment within the content */
|
||||
uint64_t index;
|
||||
/** Length to skip in first segment
|
||||
*
|
||||
* Length at the start of the first segment which is not
|
||||
* included within the content range.
|
||||
*/
|
||||
uint32_t first;
|
||||
/** Length of content range, or zero
|
||||
*
|
||||
* Length of the content range. A zero indicates that
|
||||
* everything up to the end of the last segment is included in
|
||||
* the content range.
|
||||
*/
|
||||
uint64_t len;
|
||||
/* Followed by a list of chunk descriptions */
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** SHA-512 hash algorithm with output truncated to first 256 bits */
|
||||
#define PEERDIST_INFO_V2_HASH_SHA512_TRUNC 0x04
|
||||
|
||||
/** Content Information version 2 chunk description header
|
||||
*
|
||||
* All fields are big-endian.
|
||||
*/
|
||||
struct peerdist_info_v2_chunk {
|
||||
/** Chunk type */
|
||||
uint8_t type;
|
||||
/** Chunk data length */
|
||||
uint32_t len;
|
||||
/* Followed by an array of segment descriptions:
|
||||
*
|
||||
* peerdist_info_v2_segment_t(digestsize) segment[segments]
|
||||
*
|
||||
* where digestsize is the digest size for the selected hash
|
||||
* algorithm, and segments is equal to @c len divided by the
|
||||
* size of each segment array entry.
|
||||
*/
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** Content Information version 2 chunk description
|
||||
*
|
||||
* @v digestsize Digest size
|
||||
*/
|
||||
#define peerdist_info_v2_chunk_t( digestsize ) \
|
||||
struct { \
|
||||
struct peerdist_info_v2_chunk chunk; \
|
||||
peerdist_info_v2_segment_t ( digestsize ) segment[0]; \
|
||||
} __attribute__ (( packed ))
|
||||
|
||||
/** Chunk type */
|
||||
#define PEERDIST_INFO_V2_CHUNK_TYPE 0x00
|
||||
|
||||
/** Content Information version 2 segment description header
|
||||
*
|
||||
* All fields are big-endian.
|
||||
*/
|
||||
struct peerdist_info_v2_segment {
|
||||
/** Segment length */
|
||||
uint32_t len;
|
||||
/* Followed by two variable-length hashes:
|
||||
*
|
||||
* uint8_t hash[digestsize];
|
||||
* uint8_t secret[digestsize];
|
||||
*
|
||||
* where digestsize is the digest size for the selected hash
|
||||
* algorithm.
|
||||
*/
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** Content Information version 2 segment description
|
||||
*
|
||||
* @v digestsize Digest size
|
||||
*/
|
||||
#define peerdist_info_v2_segment_t( digestsize ) \
|
||||
struct { \
|
||||
struct peerdist_info_v2_segment segment; \
|
||||
uint8_t hash[digestsize]; \
|
||||
uint8_t secret[digestsize]; \
|
||||
} __attribute__ (( packed ))
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Content Information
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/** Maximum digest size for any supported algorithm
|
||||
*
|
||||
* The largest digest size that we support is for SHA-512 at 64 bytes
|
||||
*/
|
||||
#define PEERDIST_DIGEST_MAX_SIZE 64
|
||||
|
||||
/** Raw content information */
|
||||
struct peerdist_raw {
|
||||
/** Data buffer */
|
||||
userptr_t data;
|
||||
/** Length of data buffer */
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/** A content range */
|
||||
struct peerdist_range {
|
||||
/** Start offset */
|
||||
size_t start;
|
||||
/** End offset */
|
||||
size_t end;
|
||||
};
|
||||
|
||||
/** Content information */
|
||||
struct peerdist_info {
|
||||
/** Raw content information */
|
||||
struct peerdist_raw raw;
|
||||
|
||||
/** Content information operations */
|
||||
struct peerdist_info_operations *op;
|
||||
/** Digest algorithm */
|
||||
struct digest_algorithm *digest;
|
||||
/** Digest size
|
||||
*
|
||||
* Note that this may be shorter than the digest size of the
|
||||
* digest algorithm. The truncation does not always take
|
||||
* place as soon as a digest is calculated. For example,
|
||||
* version 2 content information uses SHA-512 with a truncated
|
||||
* digest size of 32 (256 bits), but the segment identifier
|
||||
* ("HoHoDk") is calculated by using HMAC with the full
|
||||
* SHA-512 digest and then truncating the HMAC output, rather
|
||||
* than by simply using HMAC with the truncated SHA-512
|
||||
* digest. This is, of course, totally undocumented.
|
||||
*/
|
||||
size_t digestsize;
|
||||
/** Content range */
|
||||
struct peerdist_range range;
|
||||
/** Trimmed content range */
|
||||
struct peerdist_range trim;
|
||||
/** Number of segments within the content information */
|
||||
unsigned int segments;
|
||||
};
|
||||
|
||||
/** A content information segment */
|
||||
struct peerdist_info_segment {
|
||||
/** Content information */
|
||||
const struct peerdist_info *info;
|
||||
/** Segment index */
|
||||
unsigned int index;
|
||||
|
||||
/** Content range
|
||||
*
|
||||
* Note that this range may exceed the overall content range.
|
||||
*/
|
||||
struct peerdist_range range;
|
||||
/** Number of blocks within this segment */
|
||||
unsigned int blocks;
|
||||
/** Block size */
|
||||
size_t blksize;
|
||||
/** Segment hash of data
|
||||
*
|
||||
* This is MS-PCCRC's "HoD".
|
||||
*/
|
||||
uint8_t hash[PEERDIST_DIGEST_MAX_SIZE];
|
||||
/** Segment secret
|
||||
*
|
||||
* This is MS-PCCRC's "Ke = Kp".
|
||||
*/
|
||||
uint8_t secret[PEERDIST_DIGEST_MAX_SIZE];
|
||||
/** Segment identifier
|
||||
*
|
||||
* This is MS-PCCRC's "HoHoDk".
|
||||
*/
|
||||
uint8_t id[PEERDIST_DIGEST_MAX_SIZE];
|
||||
};
|
||||
|
||||
/** Magic string constant used to calculate segment identifier
|
||||
*
|
||||
* Note that the MS-PCCRC specification states that this constant is
|
||||
*
|
||||
* "the null-terminated ASCII string constant "MS_P2P_CACHING";
|
||||
* string literals are all ASCII strings with NULL terminators
|
||||
* unless otherwise noted."
|
||||
*
|
||||
* The specification lies. This constant is a UTF-16LE string, not an
|
||||
* ASCII string. The terminating wNUL *is* included within the
|
||||
* constant.
|
||||
*/
|
||||
#define PEERDIST_SEGMENT_ID_MAGIC L"MS_P2P_CACHING"
|
||||
|
||||
/** A content information block */
|
||||
struct peerdist_info_block {
|
||||
/** Content information segment */
|
||||
const struct peerdist_info_segment *segment;
|
||||
/** Block index */
|
||||
unsigned int index;
|
||||
|
||||
/** Content range
|
||||
*
|
||||
* Note that this range may exceed the overall content range.
|
||||
*/
|
||||
struct peerdist_range range;
|
||||
/** Block hash */
|
||||
uint8_t hash[PEERDIST_DIGEST_MAX_SIZE];
|
||||
};
|
||||
|
||||
/** Content information operations */
|
||||
struct peerdist_info_operations {
|
||||
/**
|
||||
* Populate content information
|
||||
*
|
||||
* @v info Content information to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ( * info ) ( struct peerdist_info *info );
|
||||
/**
|
||||
* Populate content information segment
|
||||
*
|
||||
* @v segment Content information segment to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ( * segment ) ( struct peerdist_info_segment *segment );
|
||||
/**
|
||||
* Populate content information block
|
||||
*
|
||||
* @v block Content information block to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ( * block ) ( struct peerdist_info_block *block );
|
||||
};
|
||||
|
||||
extern struct digest_algorithm sha512_trunc_algorithm;
|
||||
|
||||
extern int peerdist_info ( userptr_t data, size_t len,
|
||||
struct peerdist_info *info );
|
||||
extern int peerdist_info_segment ( const struct peerdist_info *info,
|
||||
struct peerdist_info_segment *segment,
|
||||
unsigned int index );
|
||||
extern int peerdist_info_block ( const struct peerdist_info_segment *segment,
|
||||
struct peerdist_info_block *block,
|
||||
unsigned int index );
|
||||
|
||||
#endif /* _IPXE_PCCRC_H */
|
|
@ -0,0 +1,803 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <ipxe/uaccess.h>
|
||||
#include <ipxe/sha256.h>
|
||||
#include <ipxe/sha512.h>
|
||||
#include <ipxe/hmac.h>
|
||||
#include <ipxe/base16.h>
|
||||
#include <ipxe/pccrc.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Peer Content Caching and Retrieval: Content Identification [MS-PCCRC]
|
||||
*
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Utility functions
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transcribe hash value (for debugging)
|
||||
*
|
||||
* @v info Content information
|
||||
* @v hash Hash value
|
||||
* @ret string Hash value string
|
||||
*/
|
||||
static inline const char *
|
||||
peerdist_info_hash_ntoa ( const struct peerdist_info *info, const void *hash ) {
|
||||
static char buf[ ( 2 * PEERDIST_DIGEST_MAX_SIZE ) + 1 /* NUL */ ];
|
||||
size_t digestsize = info->digestsize;
|
||||
|
||||
/* Sanity check */
|
||||
assert ( info != NULL );
|
||||
assert ( digestsize != 0 );
|
||||
assert ( base16_encoded_len ( digestsize ) < sizeof ( buf ) );
|
||||
|
||||
/* Transcribe hash value */
|
||||
base16_encode ( hash, digestsize, buf );
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw data
|
||||
*
|
||||
* @v info Content information
|
||||
* @v data Data buffer
|
||||
* @v offset Starting offset
|
||||
* @v len Length
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int peerdist_info_get ( const struct peerdist_info *info, void *data,
|
||||
size_t offset, size_t len ) {
|
||||
|
||||
/* Sanity check */
|
||||
if ( ( offset > info->raw.len ) ||
|
||||
( len > ( info->raw.len - offset ) ) ) {
|
||||
DBGC ( info, "PCCRC %p data underrun at [%zx,%zx) of %zx\n",
|
||||
info, offset, ( offset + len ), info->raw.len );
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Copy data */
|
||||
copy_from_user ( data, info->raw.data, offset, len );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate segment hashes
|
||||
*
|
||||
* @v segment Content information segment to fill in
|
||||
* @v hash Segment hash of data
|
||||
* @v secret Segment secret
|
||||
*/
|
||||
static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment,
|
||||
const void *hash, const void *secret ){
|
||||
const struct peerdist_info *info = segment->info;
|
||||
struct digest_algorithm *digest = info->digest;
|
||||
uint8_t ctx[digest->ctxsize];
|
||||
size_t digestsize = info->digestsize;
|
||||
size_t secretsize = digestsize;
|
||||
static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC;
|
||||
|
||||
/* Sanity check */
|
||||
assert ( digestsize <= sizeof ( segment->hash ) );
|
||||
assert ( digestsize <= sizeof ( segment->secret ) );
|
||||
assert ( digestsize <= sizeof ( segment->id ) );
|
||||
|
||||
/* Get segment hash of data */
|
||||
memcpy ( segment->hash, hash, digestsize );
|
||||
|
||||
/* Get segment secret */
|
||||
memcpy ( segment->secret, secret, digestsize );
|
||||
|
||||
/* Calculate segment identifier */
|
||||
hmac_init ( digest, ctx, segment->secret, &secretsize );
|
||||
assert ( secretsize == digestsize );
|
||||
hmac_update ( digest, ctx, segment->hash, digestsize );
|
||||
hmac_update ( digest, ctx, magic, sizeof ( magic ) );
|
||||
hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id );
|
||||
assert ( secretsize == digestsize );
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Content Information version 1
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get number of blocks within a block description
|
||||
*
|
||||
* @v info Content information
|
||||
* @v offset Block description offset
|
||||
* @ret blocks Number of blocks, or negative error
|
||||
*/
|
||||
static int peerdist_info_v1_blocks ( const struct peerdist_info *info,
|
||||
size_t offset ) {
|
||||
struct peerdist_info_v1_block raw;
|
||||
unsigned int blocks;
|
||||
int rc;
|
||||
|
||||
/* Get block description header */
|
||||
if ( ( rc = peerdist_info_get ( info, &raw, offset,
|
||||
sizeof ( raw ) ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Calculate number of blocks */
|
||||
blocks = le32_to_cpu ( raw.blocks );
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate block description
|
||||
*
|
||||
* @v info Content information
|
||||
* @v index Segment index
|
||||
* @ret offset Block description offset, or negative error
|
||||
*/
|
||||
static ssize_t peerdist_info_v1_block_offset ( const struct peerdist_info *info,
|
||||
unsigned int index ) {
|
||||
size_t digestsize = info->digestsize;
|
||||
unsigned int i;
|
||||
size_t offset;
|
||||
int blocks;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
assert ( index < info->segments );
|
||||
|
||||
/* Calculate offset of first block description */
|
||||
offset = ( sizeof ( struct peerdist_info_v1 ) +
|
||||
( info->segments *
|
||||
sizeof ( peerdist_info_v1_segment_t ( digestsize ) ) ) );
|
||||
|
||||
/* Iterate over block descriptions until we find this segment */
|
||||
for ( i = 0 ; i < index ; i++ ) {
|
||||
|
||||
/* Get number of blocks */
|
||||
blocks = peerdist_info_v1_blocks ( info, offset );
|
||||
if ( blocks < 0 ) {
|
||||
rc = blocks;
|
||||
DBGC ( info, "PCCRC %p segment %d could not get number "
|
||||
"of blocks: %s\n", info, i, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Move to next block description */
|
||||
offset += sizeof ( peerdist_info_v1_block_t ( digestsize,
|
||||
blocks ) );
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate content information
|
||||
*
|
||||
* @v info Content information to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int peerdist_info_v1 ( struct peerdist_info *info ) {
|
||||
struct peerdist_info_v1 raw;
|
||||
struct peerdist_info_segment first;
|
||||
struct peerdist_info_segment last;
|
||||
size_t first_skip;
|
||||
size_t last_skip;
|
||||
size_t last_read;
|
||||
int rc;
|
||||
|
||||
/* Get raw header */
|
||||
if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){
|
||||
DBGC ( info, "PCCRC %p could not get V1 content information: "
|
||||
"%s\n", info, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V1 ) );
|
||||
|
||||
/* Determine hash algorithm */
|
||||
switch ( raw.hash ) {
|
||||
case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA256 ) :
|
||||
info->digest = &sha256_algorithm;
|
||||
break;
|
||||
case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA384 ) :
|
||||
info->digest = &sha384_algorithm;
|
||||
break;
|
||||
case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA512 ) :
|
||||
info->digest = &sha512_algorithm;
|
||||
break;
|
||||
default:
|
||||
DBGC ( info, "PCCRC %p unsupported hash algorithm %#08x\n",
|
||||
info, le32_to_cpu ( raw.hash ) );
|
||||
return -ENOTSUP;
|
||||
}
|
||||
info->digestsize = info->digest->digestsize;
|
||||
assert ( info->digest != NULL );
|
||||
DBGC2 ( info, "PCCRC %p using %s[%zd]\n",
|
||||
info, info->digest->name, ( info->digestsize * 8 ) );
|
||||
|
||||
/* Calculate number of segments */
|
||||
info->segments = le32_to_cpu ( raw.segments );
|
||||
|
||||
/* Get first segment */
|
||||
if ( ( rc = peerdist_info_segment ( info, &first, 0 ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Calculate range start offset */
|
||||
info->range.start = first.range.start;
|
||||
|
||||
/* Calculate trimmed range start offset */
|
||||
first_skip = le32_to_cpu ( raw.first );
|
||||
info->trim.start = ( first.range.start + first_skip );
|
||||
|
||||
/* Get last segment */
|
||||
if ( ( rc = peerdist_info_segment ( info, &last,
|
||||
( info->segments - 1 ) ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Calculate range end offset */
|
||||
info->range.end = last.range.end;
|
||||
|
||||
/* Calculate trimmed range end offset */
|
||||
if ( raw.last ) {
|
||||
/* Explicit length to include from last segment is given */
|
||||
last_read = le32_to_cpu ( raw.last );
|
||||
last_skip = ( last.index ? 0 : first_skip );
|
||||
info->trim.end = ( last.range.start + last_skip + last_read );
|
||||
} else {
|
||||
/* No explicit length given: range extends to end of segment */
|
||||
info->trim.end = last.range.end;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate content information segment
|
||||
*
|
||||
* @v segment Content information segment to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int peerdist_info_v1_segment ( struct peerdist_info_segment *segment ) {
|
||||
const struct peerdist_info *info = segment->info;
|
||||
size_t digestsize = info->digestsize;
|
||||
peerdist_info_v1_segment_t ( digestsize ) raw;
|
||||
ssize_t raw_offset;
|
||||
int blocks;
|
||||
int rc;
|
||||
|
||||
/* Sanity checks */
|
||||
assert ( segment->index < info->segments );
|
||||
|
||||
/* Get raw description */
|
||||
raw_offset = ( sizeof ( struct peerdist_info_v1 ) +
|
||||
( segment->index * sizeof ( raw ) ) );
|
||||
if ( ( rc = peerdist_info_get ( info, &raw, raw_offset,
|
||||
sizeof ( raw ) ) ) != 0 ) {
|
||||
DBGC ( info, "PCCRC %p segment %d could not get segment "
|
||||
"description: %s\n", info, segment->index,
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Calculate start offset of this segment */
|
||||
segment->range.start = le64_to_cpu ( raw.segment.offset );
|
||||
|
||||
/* Calculate end offset of this segment */
|
||||
segment->range.end = ( segment->range.start +
|
||||
le32_to_cpu ( raw.segment.len ) );
|
||||
|
||||
/* Calculate block size of this segment */
|
||||
segment->blksize = le32_to_cpu ( raw.segment.blksize );
|
||||
|
||||
/* Locate block description for this segment */
|
||||
raw_offset = peerdist_info_v1_block_offset ( info, segment->index );
|
||||
if ( raw_offset < 0 ) {
|
||||
rc = raw_offset;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Get number of blocks */
|
||||
blocks = peerdist_info_v1_blocks ( info, raw_offset );
|
||||
if ( blocks < 0 ) {
|
||||
rc = blocks;
|
||||
DBGC ( info, "PCCRC %p segment %d could not get number of "
|
||||
"blocks: %s\n", info, segment->index, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
segment->blocks = blocks;
|
||||
|
||||
/* Calculate segment hashes */
|
||||
peerdist_info_segment_hash ( segment, raw.hash, raw.secret );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate content information block
|
||||
*
|
||||
* @v block Content information block to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int peerdist_info_v1_block ( struct peerdist_info_block *block ) {
|
||||
const struct peerdist_info_segment *segment = block->segment;
|
||||
const struct peerdist_info *info = segment->info;
|
||||
size_t digestsize = info->digestsize;
|
||||
peerdist_info_v1_block_t ( digestsize, segment->blocks ) raw;
|
||||
ssize_t raw_offset;
|
||||
int rc;
|
||||
|
||||
/* Sanity checks */
|
||||
assert ( block->index < segment->blocks );
|
||||
|
||||
/* Calculate start offset of this block */
|
||||
block->range.start = ( segment->range.start +
|
||||
( block->index * segment->blksize ) );
|
||||
|
||||
/* Calculate end offset of this block */
|
||||
block->range.end = ( block->range.start + segment->blksize );
|
||||
if ( block->range.end > segment->range.end )
|
||||
block->range.end = segment->range.end;
|
||||
|
||||
/* Locate block description */
|
||||
raw_offset = peerdist_info_v1_block_offset ( info, segment->index );
|
||||
if ( raw_offset < 0 ) {
|
||||
rc = raw_offset;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Get block hash */
|
||||
raw_offset += offsetof ( typeof ( raw ), hash[block->index] );
|
||||
if ( ( rc = peerdist_info_get ( info, block->hash, raw_offset,
|
||||
digestsize ) ) != 0 ) {
|
||||
DBGC ( info, "PCCRC %p segment %d block %d could not get "
|
||||
"hash: %s\n", info, segment->index, block->index,
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Content information version 1 operations */
|
||||
static struct peerdist_info_operations peerdist_info_v1_operations = {
|
||||
.info = peerdist_info_v1,
|
||||
.segment = peerdist_info_v1_segment,
|
||||
.block = peerdist_info_v1_block,
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Content Information version 2
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/** A segment cursor */
|
||||
struct peerdist_info_v2_cursor {
|
||||
/** Raw data offset */
|
||||
size_t offset;
|
||||
/** Number of segments remaining within this chunk */
|
||||
unsigned int remaining;
|
||||
/** Accumulated segment length */
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise segment cursor
|
||||
*
|
||||
* @v cursor Segment cursor
|
||||
*/
|
||||
static inline void
|
||||
peerdist_info_v2_cursor_init ( struct peerdist_info_v2_cursor *cursor ) {
|
||||
|
||||
/* Initialise cursor */
|
||||
cursor->offset = ( sizeof ( struct peerdist_info_v2 ) +
|
||||
sizeof ( struct peerdist_info_v2_chunk ) );
|
||||
cursor->remaining = 0;
|
||||
cursor->len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update segment cursor to next segment description
|
||||
*
|
||||
* @v info Content information
|
||||
* @v offset Current offset
|
||||
* @v remaining Number of segments remaining within this chunk
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int
|
||||
peerdist_info_v2_cursor_next ( const struct peerdist_info *info,
|
||||
struct peerdist_info_v2_cursor *cursor ) {
|
||||
size_t digestsize = info->digestsize;
|
||||
peerdist_info_v2_segment_t ( digestsize ) raw;
|
||||
struct peerdist_info_v2_chunk chunk;
|
||||
int rc;
|
||||
|
||||
/* Get chunk description if applicable */
|
||||
if ( ! cursor->remaining ) {
|
||||
|
||||
/* Get chunk description */
|
||||
if ( ( rc = peerdist_info_get ( info, &chunk,
|
||||
( cursor->offset -
|
||||
sizeof ( chunk ) ),
|
||||
sizeof ( chunk ) ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Update number of segments remaining */
|
||||
cursor->remaining = ( be32_to_cpu ( chunk.len ) /
|
||||
sizeof ( raw ) );
|
||||
}
|
||||
|
||||
/* Get segment description header */
|
||||
if ( ( rc = peerdist_info_get ( info, &raw.segment, cursor->offset,
|
||||
sizeof ( raw.segment ) ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Update cursor */
|
||||
cursor->offset += sizeof ( raw );
|
||||
cursor->remaining--;
|
||||
if ( ! cursor->remaining )
|
||||
cursor->offset += sizeof ( chunk );
|
||||
cursor->len += be32_to_cpu ( raw.segment.len );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of segments and total length
|
||||
*
|
||||
* @v info Content information
|
||||
* @v len Length to fill in
|
||||
* @ret rc Number of segments, or negative error
|
||||
*/
|
||||
static int peerdist_info_v2_segments ( const struct peerdist_info *info,
|
||||
size_t *len ) {
|
||||
struct peerdist_info_v2_cursor cursor;
|
||||
unsigned int segments;
|
||||
int rc;
|
||||
|
||||
/* Iterate over all segments */
|
||||
for ( peerdist_info_v2_cursor_init ( &cursor ), segments = 0 ;
|
||||
cursor.offset < info->raw.len ; segments++ ) {
|
||||
|
||||
/* Update segment cursor */
|
||||
if ( ( rc = peerdist_info_v2_cursor_next ( info,
|
||||
&cursor ) ) != 0 ) {
|
||||
DBGC ( info, "PCCRC %p segment %d could not update "
|
||||
"segment cursor: %s\n",
|
||||
info, segments, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Record accumulated length */
|
||||
*len = cursor.len;
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate content information
|
||||
*
|
||||
* @v info Content information to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int peerdist_info_v2 ( struct peerdist_info *info ) {
|
||||
struct peerdist_info_v2 raw;
|
||||
size_t len = 0;
|
||||
int segments;
|
||||
int rc;
|
||||
|
||||
/* Get raw header */
|
||||
if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){
|
||||
DBGC ( info, "PCCRC %p could not get V2 content information: "
|
||||
"%s\n", info, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V2 ) );
|
||||
|
||||
/* Determine hash algorithm */
|
||||
switch ( raw.hash ) {
|
||||
case PEERDIST_INFO_V2_HASH_SHA512_TRUNC :
|
||||
info->digest = &sha512_algorithm;
|
||||
info->digestsize = ( 256 / 8 );
|
||||
break;
|
||||
default:
|
||||
DBGC ( info, "PCCRC %p unsupported hash algorithm %#02x\n",
|
||||
info, raw.hash );
|
||||
return -ENOTSUP;
|
||||
}
|
||||
assert ( info->digest != NULL );
|
||||
DBGC2 ( info, "PCCRC %p using %s[%zd]\n",
|
||||
info, info->digest->name, ( info->digestsize * 8 ) );
|
||||
|
||||
/* Calculate number of segments and total length */
|
||||
segments = peerdist_info_v2_segments ( info, &len );
|
||||
if ( segments < 0 ) {
|
||||
rc = segments;
|
||||
DBGC ( info, "PCCRC %p could not get segment count and length: "
|
||||
"%s\n", info, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
info->segments = segments;
|
||||
|
||||
/* Calculate range start offset */
|
||||
info->range.start = be64_to_cpu ( raw.offset );
|
||||
|
||||
/* Calculate trimmed range start offset */
|
||||
info->trim.start = ( info->range.start + be32_to_cpu ( raw.first ) );
|
||||
|
||||
/* Calculate range end offset */
|
||||
info->range.end = ( info->range.start + len );
|
||||
|
||||
/* Calculate trimmed range end offset */
|
||||
info->trim.end = ( raw.len ? be64_to_cpu ( raw.len ) :
|
||||
info->range.end );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate content information segment
|
||||
*
|
||||
* @v segment Content information segment to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int peerdist_info_v2_segment ( struct peerdist_info_segment *segment ) {
|
||||
const struct peerdist_info *info = segment->info;
|
||||
size_t digestsize = info->digestsize;
|
||||
peerdist_info_v2_segment_t ( digestsize ) raw;
|
||||
struct peerdist_info_v2_cursor cursor;
|
||||
unsigned int index;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
/* Sanity checks */
|
||||
assert ( segment->index < info->segments );
|
||||
|
||||
/* Iterate over all segments before the target segment */
|
||||
for ( peerdist_info_v2_cursor_init ( &cursor ), index = 0 ;
|
||||
index < segment->index ; index++ ) {
|
||||
|
||||
/* Update segment cursor */
|
||||
if ( ( rc = peerdist_info_v2_cursor_next ( info,
|
||||
&cursor ) ) != 0 ) {
|
||||
DBGC ( info, "PCCRC %p segment %d could not update "
|
||||
"segment cursor: %s\n",
|
||||
info, index, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get raw description */
|
||||
if ( ( rc = peerdist_info_get ( info, &raw, cursor.offset,
|
||||
sizeof ( raw ) ) ) != 0 ) {
|
||||
DBGC ( info, "PCCRC %p segment %d could not get segment "
|
||||
"description: %s\n",
|
||||
info, segment->index, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Calculate start offset of this segment */
|
||||
segment->range.start = ( info->range.start + cursor.len );
|
||||
|
||||
/* Calculate end offset of this segment */
|
||||
len = be32_to_cpu ( raw.segment.len );
|
||||
segment->range.end = ( segment->range.start + len );
|
||||
|
||||
/* Model as a segment containing a single block */
|
||||
segment->blocks = 1;
|
||||
segment->blksize = len;
|
||||
|
||||
/* Calculate segment hashes */
|
||||
peerdist_info_segment_hash ( segment, raw.hash, raw.secret );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate content information block
|
||||
*
|
||||
* @v block Content information block to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int peerdist_info_v2_block ( struct peerdist_info_block *block ) {
|
||||
const struct peerdist_info_segment *segment = block->segment;
|
||||
const struct peerdist_info *info = segment->info;
|
||||
size_t digestsize = info->digestsize;
|
||||
|
||||
/* Sanity checks */
|
||||
assert ( block->index < segment->blocks );
|
||||
|
||||
/* Model as a block covering the whole segment */
|
||||
memcpy ( &block->range, &segment->range, sizeof ( block->range ) );
|
||||
memcpy ( block->hash, segment->hash, digestsize );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Content information version 2 operations */
|
||||
static struct peerdist_info_operations peerdist_info_v2_operations = {
|
||||
.block = peerdist_info_v2_block,
|
||||
.segment = peerdist_info_v2_segment,
|
||||
.info = peerdist_info_v2,
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Content Information
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Populate content information
|
||||
*
|
||||
* @v data Raw data
|
||||
* @v len Length of raw data
|
||||
* @v info Content information to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int peerdist_info ( userptr_t data, size_t len, struct peerdist_info *info ) {
|
||||
union peerdist_info_version version;
|
||||
int rc;
|
||||
|
||||
/* Initialise structure */
|
||||
memset ( info, 0, sizeof ( *info ) );
|
||||
info->raw.data = data;
|
||||
info->raw.len = len;
|
||||
|
||||
/* Get version */
|
||||
if ( ( rc = peerdist_info_get ( info, &version, 0,
|
||||
sizeof ( version ) ) ) != 0 ) {
|
||||
DBGC ( info, "PCCRC %p could not get version: %s\n",
|
||||
info, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
DBGC2 ( info, "PCCRC %p version %d.%d\n",
|
||||
info, version.major, version.minor );
|
||||
|
||||
/* Determine version */
|
||||
switch ( version.raw ) {
|
||||
case cpu_to_le16 ( PEERDIST_INFO_V1 ) :
|
||||
info->op = &peerdist_info_v1_operations;
|
||||
break;
|
||||
case cpu_to_le16 ( PEERDIST_INFO_V2 ) :
|
||||
info->op = &peerdist_info_v2_operations;
|
||||
break;
|
||||
default:
|
||||
DBGC ( info, "PCCRC %p unsupported version %d.%d\n",
|
||||
info, version.major, version.minor );
|
||||
return -ENOTSUP;
|
||||
}
|
||||
assert ( info->op != NULL );
|
||||
assert ( info->op->info != NULL );
|
||||
|
||||
/* Populate content information */
|
||||
if ( ( rc = info->op->info ( info ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
DBGC2 ( info, "PCCRC %p range [%08zx,%08zx) covers [%08zx,%08zx) with "
|
||||
"%d segments\n", info, info->range.start, info->range.end,
|
||||
info->trim.start, info->trim.end, info->segments );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate content information segment
|
||||
*
|
||||
* @v info Content information
|
||||
* @v segment Content information segment to fill in
|
||||
* @v index Segment index
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int peerdist_info_segment ( const struct peerdist_info *info,
|
||||
struct peerdist_info_segment *segment,
|
||||
unsigned int index ) {
|
||||
int rc;
|
||||
|
||||
/* Sanity checks */
|
||||
assert ( info != NULL );
|
||||
assert ( info->op != NULL );
|
||||
assert ( info->op->segment != NULL );
|
||||
if ( index >= info->segments ) {
|
||||
DBGC ( info, "PCCRC %p segment %d of [0,%d) out of range\n",
|
||||
info, index, info->segments );
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Initialise structure */
|
||||
memset ( segment, 0, sizeof ( *segment ) );
|
||||
segment->info = info;
|
||||
segment->index = index;
|
||||
|
||||
/* Populate content information segment */
|
||||
if ( ( rc = info->op->segment ( segment ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
DBGC2 ( info, "PCCRC %p segment %d covers [%08zx,%08zx) with %d "
|
||||
"blocks\n", info, segment->index, segment->range.start,
|
||||
segment->range.end, segment->blocks );
|
||||
DBGC2 ( info, "PCCRC %p segment %d digest %s\n", info, segment->index,
|
||||
peerdist_info_hash_ntoa ( info, segment->hash ) );
|
||||
DBGC2 ( info, "PCCRC %p segment %d secret %s\n", info, segment->index,
|
||||
peerdist_info_hash_ntoa ( info, segment->secret ) );
|
||||
DBGC2 ( info, "PCCRC %p segment %d identf %s\n", info, segment->index,
|
||||
peerdist_info_hash_ntoa ( info, segment->id ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate content information block
|
||||
*
|
||||
* @v segment Content information segment
|
||||
* @v block Content information block to fill in
|
||||
* @v index Block index
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int peerdist_info_block ( const struct peerdist_info_segment *segment,
|
||||
struct peerdist_info_block *block,
|
||||
unsigned int index ) {
|
||||
const struct peerdist_info *info = segment->info;
|
||||
int rc;
|
||||
|
||||
/* Sanity checks */
|
||||
assert ( segment != NULL );
|
||||
assert ( info != NULL );
|
||||
assert ( info->op != NULL );
|
||||
assert ( info->op->block != NULL );
|
||||
if ( index >= segment->blocks ) {
|
||||
DBGC ( info, "PCCRC %p segment %d block %d of [0,%d) out of "
|
||||
"range\n", info, segment->index, index, segment->blocks);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Initialise structure */
|
||||
memset ( block, 0, sizeof ( *block ) );
|
||||
block->segment = segment;
|
||||
block->index = index;
|
||||
|
||||
/* Populate content information block */
|
||||
if ( ( rc = info->op->block ( block ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
DBGC2 ( info, "PCCRC %p segment %d block %d hash %s\n",
|
||||
info, segment->index, block->index,
|
||||
peerdist_info_hash_ntoa ( info, block->hash ) );
|
||||
DBGC2 ( info, "PCCRC %p segment %d block %d covers [%08zx,%08zx)\n",
|
||||
info, segment->index, block->index, block->range.start,
|
||||
block->range.end );
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] tests
|
||||
*
|
||||
*/
|
||||
|
||||
/* Forcibly enable assertions */
|
||||
#undef NDEBUG
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <ipxe/uaccess.h>
|
||||
#include <ipxe/pccrc.h>
|
||||
#include <ipxe/sha256.h>
|
||||
#include <ipxe/sha512.h>
|
||||
#include <ipxe/hmac.h>
|
||||
#include <ipxe/test.h>
|
||||
|
||||
/** Define inline raw data */
|
||||
#define DATA(...) { __VA_ARGS__ }
|
||||
|
||||
/** A content information test */
|
||||
struct peerdist_info_test {
|
||||
/** Raw content information */
|
||||
const void *data;
|
||||
/** Length of raw content information */
|
||||
size_t len;
|
||||
/** Expected digest algorithm */
|
||||
struct digest_algorithm *expected_digest;
|
||||
/** Expected digest size */
|
||||
size_t expected_digestsize;
|
||||
/** Expected trimmed content range */
|
||||
struct peerdist_range expected_trim;
|
||||
/** Expected number of segments */
|
||||
unsigned int expected_segments;
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a content information test
|
||||
*
|
||||
* @v name Test name
|
||||
* @v DATA Raw content information
|
||||
* @v DIGEST Expected digest algorithm
|
||||
* @v DIGESTSIZE Expected digest size
|
||||
* @v START Expected trimmed content range start offset
|
||||
* @v END Expected trimmed content range end offset
|
||||
* @v SEGMENTS Expected number of segments
|
||||
* @ret test Content information test
|
||||
*
|
||||
* Raw content information can be obtained from PeerDist-capable web
|
||||
* servers using wget's "--header" option to inject the relevant
|
||||
* PeerDist headers. For example:
|
||||
*
|
||||
* wget --header "Accept-Encoding: peerdist" \
|
||||
* --header "X-P2P-PeerDist: Version=1.0" \
|
||||
* http://peerdist.server.address/test.url -O - | xxd -i -c 11
|
||||
*
|
||||
* Version 1 content information can be retrieved using the headers:
|
||||
*
|
||||
* Accept-Encoding: peerdist
|
||||
* X-P2P-PeerDist: Version=1.0
|
||||
*
|
||||
* Version 2 content information can be retrieved (from compatible
|
||||
* servers) using the headers:
|
||||
*
|
||||
* Accept-Encoding: peerdist
|
||||
* X-P2P-PeerDist: Version=1.1
|
||||
* X-P2P-PeerDistEx: MinContentInformation=2.0, MaxContentInformation=2.0
|
||||
*/
|
||||
#define PEERDIST_INFO_TEST( name, DATA, DIGEST, DIGESTSIZE, START, END, \
|
||||
SEGMENTS ) \
|
||||
static const uint8_t name ## _data[] = DATA; \
|
||||
static struct peerdist_info_test name = { \
|
||||
.data = name ## _data, \
|
||||
.len = sizeof ( name ## _data ), \
|
||||
.expected_digest = DIGEST, \
|
||||
.expected_digestsize = DIGESTSIZE, \
|
||||
.expected_trim = { \
|
||||
.start = START, \
|
||||
.end = END, \
|
||||
}, \
|
||||
.expected_segments = SEGMENTS, \
|
||||
}
|
||||
|
||||
/** A content information segment test */
|
||||
struct peerdist_info_segment_test {
|
||||
/** Segment index */
|
||||
unsigned int index;
|
||||
/** Expected content range */
|
||||
struct peerdist_range expected_range;
|
||||
/** Expected number of blocks */
|
||||
unsigned int expected_blocks;
|
||||
/** Expected block size */
|
||||
size_t expected_blksize;
|
||||
/** Expected segment hash of data */
|
||||
uint8_t expected_hash[PEERDIST_DIGEST_MAX_SIZE];
|
||||
/** Expected segment secret */
|
||||
uint8_t expected_secret[PEERDIST_DIGEST_MAX_SIZE];
|
||||
/** Expected segment identifier */
|
||||
uint8_t expected_id[PEERDIST_DIGEST_MAX_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a content information segment test
|
||||
*
|
||||
* @v name Test name
|
||||
* @v INDEX Segment index
|
||||
* @v START Expected content range start offset
|
||||
* @v END Expected content range end offset
|
||||
* @v BLOCKS Expected number of blocks
|
||||
* @v BLKSIZE Expected block size
|
||||
* @v HASH Expected segment hash of data
|
||||
* @v SECRET Expected segment secret
|
||||
* @v ID Expected segment identifier
|
||||
* @ret test Content information segment test
|
||||
*/
|
||||
#define PEERDIST_INFO_SEGMENT_TEST( name, INDEX, START, END, BLOCKS, \
|
||||
BLKSIZE, HASH, SECRET, ID ) \
|
||||
static struct peerdist_info_segment_test name = { \
|
||||
.index = INDEX, \
|
||||
.expected_range = { \
|
||||
.start = START, \
|
||||
.end = END, \
|
||||
}, \
|
||||
.expected_blocks = BLOCKS, \
|
||||
.expected_blksize = BLKSIZE, \
|
||||
.expected_hash = HASH, \
|
||||
.expected_secret = SECRET, \
|
||||
.expected_id = ID, \
|
||||
}
|
||||
|
||||
/** A content information block test */
|
||||
struct peerdist_info_block_test {
|
||||
/** Block index */
|
||||
unsigned int index;
|
||||
/** Expected content range */
|
||||
struct peerdist_range expected_range;
|
||||
/** Expected hash of data */
|
||||
uint8_t expected_hash[PEERDIST_DIGEST_MAX_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a content information block test
|
||||
*
|
||||
* @v name Test name
|
||||
* @v INDEX Block index
|
||||
* @v START Expected content range start offset
|
||||
* @v END Expected content range end offset
|
||||
* @v HASH Expected hash of data
|
||||
* @ret test Content information block test
|
||||
*/
|
||||
#define PEERDIST_INFO_BLOCK_TEST( name, INDEX, START, END, HASH ) \
|
||||
static struct peerdist_info_block_test name = { \
|
||||
.index = INDEX, \
|
||||
.expected_range = { \
|
||||
.start = START, \
|
||||
.end = END, \
|
||||
}, \
|
||||
.expected_hash = HASH, \
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a server passphrase
|
||||
*
|
||||
* @v name Server passphrase name
|
||||
* @v DATA Raw server passphrase
|
||||
*
|
||||
* The server passphrase can be exported from a Windows BranchCache
|
||||
* server using the command:
|
||||
*
|
||||
* netsh branchcache exportkey exported.key somepassword
|
||||
*
|
||||
* and this encrypted exported key can be decrypted using the
|
||||
* oSSL_key_dx or mcrypt_key_dx utilities found in the (prototype)
|
||||
* Prequel project at https://fedorahosted.org/prequel/ :
|
||||
*
|
||||
* oSSL_key_dx exported.key somepassword
|
||||
* or
|
||||
* mcrypt_key_dx exported.key somepassword
|
||||
*
|
||||
* Either command will display both the server passphrase and the
|
||||
* "Server Secret". Note that this latter is the version 1 server
|
||||
* secret (i.e. the SHA-256 of the server passphrase); the
|
||||
* corresponding version 2 server secret can be obtained by
|
||||
* calculating the truncated SHA-512 of the server passphrase.
|
||||
*
|
||||
* We do not know the server passphrase during normal operation. We
|
||||
* use it in the self-tests only to check for typos and other errors
|
||||
* in the test vectors, by checking that the segment secret defined in
|
||||
* a content information segment test is as expected.
|
||||
*/
|
||||
#define SERVER_PASSPHRASE( name, DATA ) \
|
||||
static uint8_t name[] = DATA
|
||||
|
||||
/** Server passphrase used for these test vectors */
|
||||
SERVER_PASSPHRASE ( passphrase,
|
||||
DATA ( 0x2a, 0x3d, 0x73, 0xeb, 0x43, 0x5e, 0x9f, 0x2b, 0x8a, 0x34, 0x42,
|
||||
0x67, 0xe7, 0x46, 0x7a, 0x3c, 0x73, 0x85, 0xc6, 0xe0, 0x55, 0xe2,
|
||||
0xb4, 0xd3, 0x0d, 0xfe, 0xc7, 0xc3, 0x8b, 0x0e, 0xd7, 0x2c ) );
|
||||
|
||||
/** IIS logo (iis-85.png) content information version 1 */
|
||||
PEERDIST_INFO_TEST ( iis_85_png_v1,
|
||||
DATA ( 0x00, 0x01, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x7e, 0x85, 0x01, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0xd8, 0xd9, 0x76, 0x35, 0x4a, 0x48, 0x72, 0xe9, 0x25, 0x76,
|
||||
0x18, 0x03, 0xf4, 0x58, 0xd9, 0xda, 0xaa, 0x67, 0xf8, 0xe3, 0x1c,
|
||||
0x63, 0x0f, 0xb7, 0x4e, 0x6a, 0x31, 0x2e, 0xf8, 0xa2, 0x5a, 0xba,
|
||||
0x11, 0xaf, 0xc0, 0xd7, 0x94, 0x92, 0x43, 0xf9, 0x4f, 0x9c, 0x1f,
|
||||
0xab, 0x35, 0xd9, 0xfd, 0x1e, 0x33, 0x1f, 0xcf, 0x78, 0x11, 0xa2,
|
||||
0xe0, 0x1d, 0x35, 0x87, 0xb3, 0x8d, 0x77, 0x0a, 0x29, 0xe2, 0x02,
|
||||
0x00, 0x00, 0x00, 0x73, 0xc1, 0x8a, 0xb8, 0x54, 0x91, 0x10, 0xf8,
|
||||
0xe9, 0x0e, 0x71, 0xbb, 0xc3, 0xab, 0x2a, 0xa8, 0xc4, 0x4d, 0x13,
|
||||
0xf4, 0x92, 0x94, 0x99, 0x25, 0x5b, 0x66, 0x0f, 0x24, 0xec, 0x77,
|
||||
0x80, 0x0b, 0x97, 0x4b, 0xdd, 0x65, 0x56, 0x7f, 0xde, 0xec, 0xcd,
|
||||
0xaf, 0xe4, 0x57, 0xa9, 0x50, 0x3b, 0x45, 0x48, 0xf6, 0x6e, 0xd3,
|
||||
0xb1, 0x88, 0xdc, 0xfd, 0xa0, 0xac, 0x38, 0x2b, 0x09, 0x71, 0x1a,
|
||||
0xcc ),
|
||||
&sha256_algorithm, 32, 0, 99710, 1 );
|
||||
|
||||
/** IIS logo (iis-85.png) content information version 1 segment 0 */
|
||||
PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v1_s0, 0,
|
||||
0, 99710, 2, 65536,
|
||||
DATA ( 0xd8, 0xd9, 0x76, 0x35, 0x4a, 0x48, 0x72, 0xe9, 0x25, 0x76, 0x18,
|
||||
0x03, 0xf4, 0x58, 0xd9, 0xda, 0xaa, 0x67, 0xf8, 0xe3, 0x1c, 0x63,
|
||||
0x0f, 0xb7, 0x4e, 0x6a, 0x31, 0x2e, 0xf8, 0xa2, 0x5a, 0xba ),
|
||||
DATA ( 0x11, 0xaf, 0xc0, 0xd7, 0x94, 0x92, 0x43, 0xf9, 0x4f, 0x9c, 0x1f,
|
||||
0xab, 0x35, 0xd9, 0xfd, 0x1e, 0x33, 0x1f, 0xcf, 0x78, 0x11, 0xa2,
|
||||
0xe0, 0x1d, 0x35, 0x87, 0xb3, 0x8d, 0x77, 0x0a, 0x29, 0xe2 ),
|
||||
DATA ( 0x49, 0x1b, 0x21, 0x7d, 0xbe, 0xe2, 0xb5, 0xf1, 0x2c, 0xa7, 0x9b,
|
||||
0x01, 0x5e, 0x06, 0xf4, 0xbb, 0xe6, 0x4f, 0x97, 0x45, 0xba, 0xd7,
|
||||
0x86, 0x7a, 0xef, 0x17, 0xde, 0x59, 0x92, 0x7e, 0xdc, 0xe9 ) );
|
||||
|
||||
/** IIS logo (iis-85.png) content information version 1 segment 0 block 0 */
|
||||
PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v1_s0_b0, 0,
|
||||
0, 65536,
|
||||
DATA ( 0x73, 0xc1, 0x8a, 0xb8, 0x54, 0x91, 0x10, 0xf8, 0xe9, 0x0e, 0x71,
|
||||
0xbb, 0xc3, 0xab, 0x2a, 0xa8, 0xc4, 0x4d, 0x13, 0xf4, 0x92, 0x94,
|
||||
0x99, 0x25, 0x5b, 0x66, 0x0f, 0x24, 0xec, 0x77, 0x80, 0x0b ) );
|
||||
|
||||
/** IIS logo (iis-85.png) content information version 1 segment 0 block 1 */
|
||||
PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v1_s0_b1, 1,
|
||||
65536, 99710,
|
||||
DATA ( 0x97, 0x4b, 0xdd, 0x65, 0x56, 0x7f, 0xde, 0xec, 0xcd, 0xaf, 0xe4,
|
||||
0x57, 0xa9, 0x50, 0x3b, 0x45, 0x48, 0xf6, 0x6e, 0xd3, 0xb1, 0x88,
|
||||
0xdc, 0xfd, 0xa0, 0xac, 0x38, 0x2b, 0x09, 0x71, 0x1a, 0xcc ) );
|
||||
|
||||
/** IIS logo (iis-85.png) content information version 2 */
|
||||
PEERDIST_INFO_TEST ( iis_85_png_v2,
|
||||
DATA ( 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x88, 0x00, 0x00, 0x99, 0xde, 0xe0, 0xd0, 0xc3, 0x58,
|
||||
0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32, 0xb5, 0xf1, 0x97, 0x87,
|
||||
0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e, 0x78, 0x1f, 0xae, 0x71,
|
||||
0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4, 0x58, 0x03, 0x7e, 0xd4, 0x04,
|
||||
0x11, 0x6b, 0xb6, 0x16, 0xd9, 0xb1, 0x41, 0x16, 0x08, 0x85, 0x20,
|
||||
0xc4, 0x7c, 0xdc, 0x50, 0xab, 0xce, 0xa3, 0xfa, 0xe1, 0x88, 0xa9,
|
||||
0x8e, 0xa2, 0x2d, 0xf3, 0xc0, 0x00, 0x00, 0xeb, 0xa0, 0x33, 0x81,
|
||||
0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21, 0x0f, 0x37,
|
||||
0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96, 0xa1, 0x30,
|
||||
0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc, 0xb8, 0xb6, 0xeb,
|
||||
0x77, 0x83, 0xe4, 0xf8, 0x07, 0x64, 0x7b, 0x63, 0xf1, 0x46, 0xb5,
|
||||
0x2f, 0x4a, 0xc8, 0x9c, 0xcc, 0x7a, 0xbf, 0x5f, 0xa1, 0x1a, 0xca,
|
||||
0xfc, 0x2a, 0xcf, 0x50, 0x28, 0x58, 0x6c ),
|
||||
&sha512_algorithm, 32, 0, 99710, 2 );
|
||||
|
||||
/** IIS logo (iis-85.png) content information version 2 segment 0 */
|
||||
PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v2_s0, 0,
|
||||
0, 39390, 1, 39390,
|
||||
DATA ( 0xe0, 0xd0, 0xc3, 0x58, 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32,
|
||||
0xb5, 0xf1, 0x97, 0x87, 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e,
|
||||
0x78, 0x1f, 0xae, 0x71, 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4 ),
|
||||
DATA ( 0x58, 0x03, 0x7e, 0xd4, 0x04, 0x11, 0x6b, 0xb6, 0x16, 0xd9, 0xb1,
|
||||
0x41, 0x16, 0x08, 0x85, 0x20, 0xc4, 0x7c, 0xdc, 0x50, 0xab, 0xce,
|
||||
0xa3, 0xfa, 0xe1, 0x88, 0xa9, 0x8e, 0xa2, 0x2d, 0xf3, 0xc0 ),
|
||||
DATA ( 0x33, 0x71, 0xbb, 0xea, 0xdd, 0xb6, 0x23, 0x53, 0xad, 0xce, 0xf9,
|
||||
0x70, 0xa0, 0x6f, 0xdf, 0x65, 0x00, 0x1e, 0x04, 0x21, 0xf4, 0xc7,
|
||||
0x10, 0x82, 0x76, 0xb0, 0xc3, 0x7a, 0x9f, 0x9e, 0xc1, 0x0f ) );
|
||||
|
||||
/** IIS logo (iis-85.png) content information version 2 segment 0 block 0 */
|
||||
PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v2_s0_b0, 0,
|
||||
0, 39390,
|
||||
DATA ( 0xe0, 0xd0, 0xc3, 0x58, 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32,
|
||||
0xb5, 0xf1, 0x97, 0x87, 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e,
|
||||
0x78, 0x1f, 0xae, 0x71, 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4 ) );
|
||||
|
||||
/** IIS logo (iis-85.png) content information version 2 segment 1 */
|
||||
PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v2_s1, 1,
|
||||
39390, 99710, 1, 60320,
|
||||
DATA ( 0x33, 0x81, 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21,
|
||||
0x0f, 0x37, 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96,
|
||||
0xa1, 0x30, 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc ),
|
||||
DATA ( 0xb8, 0xb6, 0xeb, 0x77, 0x83, 0xe4, 0xf8, 0x07, 0x64, 0x7b, 0x63,
|
||||
0xf1, 0x46, 0xb5, 0x2f, 0x4a, 0xc8, 0x9c, 0xcc, 0x7a, 0xbf, 0x5f,
|
||||
0xa1, 0x1a, 0xca, 0xfc, 0x2a, 0xcf, 0x50, 0x28, 0x58, 0x6c ),
|
||||
DATA ( 0xd7, 0xe9, 0x24, 0x42, 0x5e, 0x8f, 0x4f, 0x88, 0xf0, 0x1d, 0xc6,
|
||||
0xa9, 0xbb, 0x1b, 0xc3, 0x7b, 0xe1, 0x13, 0xec, 0x79, 0x17, 0xc7,
|
||||
0x45, 0xd4, 0x96, 0x5c, 0x2b, 0x55, 0xfa, 0x16, 0x3a, 0x6e ) );
|
||||
|
||||
/** IIS logo (iis-85.png) content information version 2 segment 1 block 0 */
|
||||
PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v2_s1_b0, 0,
|
||||
39390, 99710,
|
||||
DATA ( 0x33, 0x81, 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21,
|
||||
0x0f, 0x37, 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96,
|
||||
0xa1, 0x30, 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc ) );
|
||||
|
||||
/**
|
||||
* Report content information test result
|
||||
*
|
||||
* @v test Content information test
|
||||
* @v info Content information to fill in
|
||||
* @v file Test code file
|
||||
* @v line Test code line
|
||||
*/
|
||||
static void peerdist_info_okx ( struct peerdist_info_test *test,
|
||||
struct peerdist_info *info,
|
||||
const char *file, unsigned int line ) {
|
||||
|
||||
/* Parse content information */
|
||||
okx ( peerdist_info ( virt_to_user ( test->data ), test->len,
|
||||
info ) == 0, file, line );
|
||||
|
||||
/* Verify content information */
|
||||
okx ( info->raw.data == virt_to_user ( test->data ), file, line );
|
||||
okx ( info->raw.len == test->len, file, line );
|
||||
okx ( info->digest == test->expected_digest, file, line );
|
||||
okx ( info->digestsize == test->expected_digestsize, file, line );
|
||||
okx ( info->trim.start >= info->range.start, file, line );
|
||||
okx ( info->trim.start == test->expected_trim.start, file, line );
|
||||
okx ( info->trim.end <= info->range.end, file, line );
|
||||
okx ( info->trim.end == test->expected_trim.end, file, line );
|
||||
okx ( info->segments == test->expected_segments, file, line );
|
||||
}
|
||||
#define peerdist_info_ok( test, info ) \
|
||||
peerdist_info_okx ( test, info, __FILE__, __LINE__ )
|
||||
|
||||
/**
|
||||
* Report content information segment test result
|
||||
*
|
||||
* @v test Content information segment test
|
||||
* @v info Content information
|
||||
* @v segment Segment information to fill in
|
||||
* @v file Test code file
|
||||
* @v line Test code line
|
||||
*/
|
||||
static void peerdist_info_segment_okx ( struct peerdist_info_segment_test *test,
|
||||
const struct peerdist_info *info,
|
||||
struct peerdist_info_segment *segment,
|
||||
const char *file, unsigned int line ) {
|
||||
size_t digestsize = info->digestsize;
|
||||
|
||||
/* Parse content information segment */
|
||||
okx ( peerdist_info_segment ( info, segment, test->index ) == 0,
|
||||
file, line );
|
||||
|
||||
/* Verify content information segment */
|
||||
okx ( segment->info == info, file, line );
|
||||
okx ( segment->index == test->index, file, line );
|
||||
okx ( segment->range.start == test->expected_range.start, file, line );
|
||||
okx ( segment->range.end == test->expected_range.end, file, line );
|
||||
okx ( segment->blocks == test->expected_blocks, file, line );
|
||||
okx ( segment->blksize == test->expected_blksize, file, line );
|
||||
okx ( memcmp ( segment->hash, test->expected_hash,
|
||||
digestsize ) == 0, file, line );
|
||||
okx ( memcmp ( segment->secret, test->expected_secret,
|
||||
digestsize ) == 0, file, line );
|
||||
okx ( memcmp ( segment->id, test->expected_id,
|
||||
digestsize ) == 0, file, line );
|
||||
}
|
||||
#define peerdist_info_segment_ok( test, info, segment ) \
|
||||
peerdist_info_segment_okx ( test, info, segment, __FILE__, __LINE__ )
|
||||
|
||||
/**
|
||||
* Report content information block test result
|
||||
*
|
||||
* @v test Content information block test
|
||||
* @v segment Segment information
|
||||
* @v block Block information to fill in
|
||||
* @v file Test code file
|
||||
* @v line Test code line
|
||||
*/
|
||||
static void
|
||||
peerdist_info_block_okx ( struct peerdist_info_block_test *test,
|
||||
const struct peerdist_info_segment *segment,
|
||||
struct peerdist_info_block *block,
|
||||
const char *file, unsigned int line ) {
|
||||
const struct peerdist_info *info = segment->info;
|
||||
size_t digestsize = info->digestsize;
|
||||
|
||||
/* Parse content information block */
|
||||
okx ( peerdist_info_block ( segment, block, test->index ) == 0,
|
||||
file, line );
|
||||
|
||||
/* Verify content information block */
|
||||
okx ( block->segment == segment, file, line );
|
||||
okx ( block->index == test->index, file, line );
|
||||
okx ( block->range.start == test->expected_range.start, file, line );
|
||||
okx ( block->range.end == test->expected_range.end, file, line );
|
||||
okx ( memcmp ( block->hash, test->expected_hash,
|
||||
digestsize ) == 0, file, line );
|
||||
}
|
||||
#define peerdist_info_block_ok( test, segment, block ) \
|
||||
peerdist_info_block_okx ( test, segment, block, __FILE__, __LINE__ )
|
||||
|
||||
/**
|
||||
* Report server passphrase test result
|
||||
*
|
||||
* @v test Content information segment test
|
||||
* @v info Content information
|
||||
* @v pass Server passphrase
|
||||
* @v pass_len Length of server passphrase
|
||||
* @v file Test code file
|
||||
* @v line Test code line
|
||||
*/
|
||||
static void
|
||||
peerdist_info_passphrase_okx ( struct peerdist_info_segment_test *test,
|
||||
const struct peerdist_info *info,
|
||||
uint8_t *pass, size_t pass_len,
|
||||
const char *file, unsigned int line ) {
|
||||
struct digest_algorithm *digest = info->digest;
|
||||
uint8_t ctx[digest->ctxsize];
|
||||
uint8_t secret[digest->digestsize];
|
||||
uint8_t expected[digest->digestsize];
|
||||
size_t digestsize = info->digestsize;
|
||||
size_t secretsize = digestsize;
|
||||
|
||||
/* Calculate server secret */
|
||||
digest_init ( digest, ctx );
|
||||
digest_update ( digest, ctx, pass, pass_len );
|
||||
digest_final ( digest, ctx, secret );
|
||||
|
||||
/* Calculate expected segment secret */
|
||||
hmac_init ( digest, ctx, secret, &secretsize );
|
||||
assert ( secretsize == digestsize );
|
||||
hmac_update ( digest, ctx, test->expected_hash, digestsize );
|
||||
hmac_final ( digest, ctx, secret, &secretsize, expected );
|
||||
assert ( secretsize == digestsize );
|
||||
|
||||
/* Verify segment secret */
|
||||
okx ( memcmp ( test->expected_secret, expected, digestsize ) == 0,
|
||||
file, line );
|
||||
}
|
||||
#define peerdist_info_passphrase_ok( test, info, pass, pass_len ) \
|
||||
peerdist_info_passphrase_okx ( test, info, pass, pass_len, \
|
||||
__FILE__, __LINE__ )
|
||||
|
||||
/**
|
||||
* Perform content information self-tests
|
||||
*
|
||||
*/
|
||||
static void peerdist_info_test_exec ( void ) {
|
||||
struct peerdist_info info;
|
||||
struct peerdist_info_segment segment;
|
||||
struct peerdist_info_block block;
|
||||
|
||||
/* IIS logo (iis-85.png) content information version 1 */
|
||||
peerdist_info_ok ( &iis_85_png_v1, &info );
|
||||
peerdist_info_passphrase_ok ( &iis_85_png_v1_s0, &info,
|
||||
passphrase, sizeof ( passphrase ) );
|
||||
peerdist_info_segment_ok ( &iis_85_png_v1_s0, &info, &segment );
|
||||
peerdist_info_block_ok ( &iis_85_png_v1_s0_b0, &segment, &block );
|
||||
peerdist_info_block_ok ( &iis_85_png_v1_s0_b1, &segment, &block );
|
||||
|
||||
/* IIS logo (iis-85.png) content information version 2 */
|
||||
peerdist_info_ok ( &iis_85_png_v2, &info );
|
||||
peerdist_info_passphrase_ok ( &iis_85_png_v2_s0, &info,
|
||||
passphrase, sizeof ( passphrase ) );
|
||||
peerdist_info_segment_ok ( &iis_85_png_v2_s0, &info, &segment );
|
||||
peerdist_info_block_ok ( &iis_85_png_v2_s0_b0, &segment, &block );
|
||||
peerdist_info_passphrase_ok ( &iis_85_png_v2_s1, &info,
|
||||
passphrase, sizeof ( passphrase ) );
|
||||
peerdist_info_segment_ok ( &iis_85_png_v2_s1, &info, &segment );
|
||||
peerdist_info_block_ok ( &iis_85_png_v2_s1_b0, &segment, &block );
|
||||
}
|
||||
|
||||
/** Content information self-test */
|
||||
struct self_test peerdist_info_test __self_test = {
|
||||
.name = "pccrc",
|
||||
.exec = peerdist_info_test_exec,
|
||||
};
|
|
@ -65,3 +65,4 @@ REQUIRE_OBJECT ( dns_test );
|
|||
REQUIRE_OBJECT ( uri_test );
|
||||
REQUIRE_OBJECT ( profile_test );
|
||||
REQUIRE_OBJECT ( setjmp_test );
|
||||
REQUIRE_OBJECT ( pccrc_test );
|
||||
|
|
Loading…
Reference in New Issue