mirror of
https://github.com/ipxe/ipxe
synced 2025-12-30 04:28:12 +03:00
[peerdist] Add support for decoding PeerDist Content Information
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -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 )
|
||||
|
||||
445
src/include/ipxe/pccrc.h
Normal file
445
src/include/ipxe/pccrc.h
Normal file
@@ -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 */
|
||||
Reference in New Issue
Block a user