#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 #include #include #include /****************************************************************************** * * 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; /** Trimmed content range */ struct peerdist_range trim; /** 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 */