iPXE
pccrc.h
Go to the documentation of this file.
00001 #ifndef _IPXE_PCCRC_H
00002 #define _IPXE_PCCRC_H
00003 
00004 /** @file
00005  *
00006  * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC]
00007  *
00008  */
00009 
00010 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00011 
00012 #include <stdint.h>
00013 #include <byteswap.h>
00014 #include <ipxe/uaccess.h>
00015 #include <ipxe/crypto.h>
00016 
00017 /******************************************************************************
00018  *
00019  * Content Information versioning
00020  *
00021  ******************************************************************************
00022  *
00023  * Note that version 1 data structures are little-endian, but version
00024  * 2 data structures are big-endian.
00025  */
00026 
00027 /** Content Information version number */
00028 union peerdist_info_version {
00029         /** Raw version number
00030          *
00031          * Always little-endian, regardless of whether the
00032          * encompassing structure is version 1 (little-endian) or
00033          * version 2 (big-endian).
00034          */
00035         uint16_t raw;
00036         /** Major:minor version number */
00037         struct {
00038                 /** Minor version number */
00039                 uint8_t minor;
00040                 /** Major version number */
00041                 uint8_t major;
00042         } __attribute__ (( packed ));
00043 } __attribute__ (( packed ));
00044 
00045 /** Content Information version 1 */
00046 #define PEERDIST_INFO_V1 0x0100
00047 
00048 /** Content Information version 2 */
00049 #define PEERDIST_INFO_V2 0x0200
00050 
00051 /******************************************************************************
00052  *
00053  * Content Information version 1
00054  *
00055  ******************************************************************************
00056  */
00057 
00058 /** Content Information version 1 data structure header
00059  *
00060  * All fields are little-endian.
00061  */
00062 struct peerdist_info_v1 {
00063         /** Version number */
00064         union peerdist_info_version version;
00065         /** Hash algorithm
00066          *
00067          * This is a @c PEERDIST_INFO_V1_HASH_XXX constant.
00068          */
00069         uint32_t hash;
00070         /** Length to skip in first segment
00071          *
00072          * Length at the start of the first segment which is not
00073          * included within the content range.
00074          */
00075         uint32_t first;
00076         /** Length to read in last segment, or zero
00077          *
00078          * Length within the last segment which is included within the
00079          * content range.  A zero value indicates that the whole of
00080          * the last segment is included within the content range.
00081          */
00082         uint32_t last;
00083         /** Number of segments within the content information */
00084         uint32_t segments;
00085         /* Followed by a variable-length array of segment descriptions
00086          * and a list of variable-length block descriptions:
00087          *
00088          * peerdist_info_v1_segment_t(digestsize) segment[segments];
00089          * peerdist_info_v1_block_t(digestsize, block0.blocks) block0;
00090          * peerdist_info_v1_block_t(digestsize, block1.blocks) block1;
00091          * ...
00092          * peerdist_info_v1_block_t(digestsize, blockN.blocks) blockN;
00093          */
00094 } __attribute__ (( packed ));
00095 
00096 /** SHA-256 hash algorithm */
00097 #define PEERDIST_INFO_V1_HASH_SHA256 0x0000800cUL
00098 
00099 /** SHA-384 hash algorithm */
00100 #define PEERDIST_INFO_V1_HASH_SHA384 0x0000800dUL
00101 
00102 /** SHA-512 hash algorithm */
00103 #define PEERDIST_INFO_V1_HASH_SHA512 0x0000800eUL
00104 
00105 /** Content Information version 1 segment description header
00106  *
00107  * All fields are little-endian.
00108  */
00109 struct peerdist_info_v1_segment {
00110         /** Offset of this segment within the content */
00111         uint64_t offset;
00112         /** Length of this segment
00113          *
00114          * Should always be 32MB, except for the last segment within
00115          * the content.
00116          */
00117         uint32_t len;
00118         /** Block size for this segment
00119          *
00120          * Should always be 64kB.  Note that the last block within the
00121          * last segment may actually be less than 64kB.
00122          */
00123         uint32_t blksize;
00124         /* Followed by two variable-length hashes:
00125          *
00126          * uint8_t hash[digestsize];
00127          * uint8_t secret[digestsize];
00128          *
00129          * where digestsize is the digest size for the selected hash
00130          * algorithm.
00131          *
00132          * Note that the hash is taken over (the hashes of all blocks
00133          * within) the entire segment, even if the blocks do not
00134          * intersect the content range (and so do not appear within
00135          * the block list).  It therefore functions only as a segment
00136          * identifier; it cannot be used to verify the content of the
00137          * segment (since we may not download all blocks within the
00138          * segment).
00139          */
00140 } __attribute__ (( packed ));
00141 
00142 /** Content Information version 1 segment description
00143  *
00144  * @v digestsize        Digest size
00145  */
00146 #define peerdist_info_v1_segment_t( digestsize )                        \
00147         struct {                                                        \
00148                 struct peerdist_info_v1_segment segment;                \
00149                 uint8_t hash[digestsize];                               \
00150                 uint8_t secret[digestsize];                             \
00151         } __attribute__ (( packed ))
00152 
00153 /** Content Information version 1 block description header
00154  *
00155  * All fields are little-endian.
00156  */
00157 struct peerdist_info_v1_block {
00158         /** Number of blocks within the block description
00159          *
00160          * This is the number of blocks within the segment which
00161          * overlap the content range.  It may therefore be less than
00162          * the number of blocks within the segment.
00163          */
00164         uint32_t blocks;
00165         /* Followed by an array of variable-length hashes:
00166          *
00167          * uint8_t hash[blocks][digestsize];
00168          *
00169          * where digestsize is the digest size for the selected hash
00170          * algorithm.
00171          */
00172  } __attribute__ (( packed ));
00173 
00174 /** Content Information version 1 block description
00175  *
00176  * @v digestsize        Digest size
00177  * @v blocks            Number of blocks
00178  */
00179 #define peerdist_info_v1_block_t( digestsize, blocks )                  \
00180         struct {                                                        \
00181                 struct peerdist_info_v1_block block;                    \
00182                 uint8_t hash[blocks][digestsize];                       \
00183         } __attribute__ (( packed ))
00184 
00185 /******************************************************************************
00186  *
00187  * Content Information version 2
00188  *
00189  ******************************************************************************
00190  */
00191 
00192 /** Content Information version 2 data structure header
00193  *
00194  * All fields are big-endian.
00195  */
00196 struct peerdist_info_v2 {
00197         /** Version number */
00198         union peerdist_info_version version;
00199         /** Hash algorithm
00200          *
00201          * This is a @c PEERDIST_INFO_V2_HASH_XXX constant.
00202          */
00203         uint8_t hash;
00204         /** Offset of the first segment within the content */
00205         uint64_t offset;
00206         /** Index of the first segment within the content */
00207         uint64_t index;
00208         /** Length to skip in first segment
00209          *
00210          * Length at the start of the first segment which is not
00211          * included within the content range.
00212          */
00213         uint32_t first;
00214         /** Length of content range, or zero
00215          *
00216          * Length of the content range.  A zero indicates that
00217          * everything up to the end of the last segment is included in
00218          * the content range.
00219          */
00220         uint64_t len;
00221         /* Followed by a list of chunk descriptions */
00222 } __attribute__ (( packed ));
00223 
00224 /** SHA-512 hash algorithm with output truncated to first 256 bits */
00225 #define PEERDIST_INFO_V2_HASH_SHA512_TRUNC 0x04
00226 
00227 /** Content Information version 2 chunk description header
00228  *
00229  * All fields are big-endian.
00230  */
00231 struct peerdist_info_v2_chunk {
00232         /** Chunk type */
00233         uint8_t type;
00234         /** Chunk data length */
00235         uint32_t len;
00236         /* Followed by an array of segment descriptions:
00237          *
00238          * peerdist_info_v2_segment_t(digestsize) segment[segments]
00239          *
00240          * where digestsize is the digest size for the selected hash
00241          * algorithm, and segments is equal to @c len divided by the
00242          * size of each segment array entry.
00243          */
00244 } __attribute__ (( packed ));
00245 
00246 /** Content Information version 2 chunk description
00247  *
00248  * @v digestsize        Digest size
00249  */
00250 #define peerdist_info_v2_chunk_t( digestsize )                          \
00251         struct {                                                        \
00252                 struct peerdist_info_v2_chunk chunk;                    \
00253                 peerdist_info_v2_segment_t ( digestsize ) segment[0];   \
00254         } __attribute__ (( packed ))
00255 
00256 /** Chunk type */
00257 #define PEERDIST_INFO_V2_CHUNK_TYPE 0x00
00258 
00259 /** Content Information version 2 segment description header
00260  *
00261  * All fields are big-endian.
00262  */
00263 struct peerdist_info_v2_segment {
00264         /** Segment length */
00265         uint32_t len;
00266         /* Followed by two variable-length hashes:
00267          *
00268          * uint8_t hash[digestsize];
00269          * uint8_t secret[digestsize];
00270          *
00271          * where digestsize is the digest size for the selected hash
00272          * algorithm.
00273          */
00274 } __attribute__ (( packed ));
00275 
00276 /** Content Information version 2 segment description
00277  *
00278  * @v digestsize        Digest size
00279  */
00280 #define peerdist_info_v2_segment_t( digestsize )                        \
00281         struct {                                                        \
00282                 struct peerdist_info_v2_segment segment;                \
00283                 uint8_t hash[digestsize];                               \
00284                 uint8_t secret[digestsize];                             \
00285         } __attribute__ (( packed ))
00286 
00287 /******************************************************************************
00288  *
00289  * Content Information
00290  *
00291  ******************************************************************************
00292  */
00293 
00294 /** Maximum digest size for any supported algorithm
00295  *
00296  * The largest digest size that we support is for SHA-512 at 64 bytes
00297  */
00298 #define PEERDIST_DIGEST_MAX_SIZE 64
00299 
00300 /** Raw content information */
00301 struct peerdist_raw {
00302         /** Data buffer */
00303         userptr_t data;
00304         /** Length of data buffer */
00305         size_t len;
00306 };
00307 
00308 /** A content range */
00309 struct peerdist_range {
00310         /** Start offset */
00311         size_t start;
00312         /** End offset */
00313         size_t end;
00314 };
00315 
00316 /** Content information */
00317 struct peerdist_info {
00318         /** Raw content information */
00319         struct peerdist_raw raw;
00320 
00321         /** Content information operations */
00322         struct peerdist_info_operations *op;
00323         /** Digest algorithm */
00324         struct digest_algorithm *digest;
00325         /** Digest size
00326          *
00327          * Note that this may be shorter than the digest size of the
00328          * digest algorithm.  The truncation does not always take
00329          * place as soon as a digest is calculated.  For example,
00330          * version 2 content information uses SHA-512 with a truncated
00331          * digest size of 32 (256 bits), but the segment identifier
00332          * ("HoHoDk") is calculated by using HMAC with the full
00333          * SHA-512 digest and then truncating the HMAC output, rather
00334          * than by simply using HMAC with the truncated SHA-512
00335          * digest.  This is, of course, totally undocumented.
00336          */
00337         size_t digestsize;
00338         /** Content range */
00339         struct peerdist_range range;
00340         /** Trimmed content range */
00341         struct peerdist_range trim;
00342         /** Number of segments within the content information */
00343         unsigned int segments;
00344 };
00345 
00346 /** A content information segment */
00347 struct peerdist_info_segment {
00348         /** Content information */
00349         const struct peerdist_info *info;
00350         /** Segment index */
00351         unsigned int index;
00352 
00353         /** Content range
00354          *
00355          * Note that this range may exceed the overall content range.
00356          */
00357         struct peerdist_range range;
00358         /** Number of blocks within this segment */
00359         unsigned int blocks;
00360         /** Block size */
00361         size_t blksize;
00362         /** Segment hash of data
00363          *
00364          * This is MS-PCCRC's "HoD".
00365          */
00366         uint8_t hash[PEERDIST_DIGEST_MAX_SIZE];
00367         /** Segment secret
00368          *
00369          * This is MS-PCCRC's "Ke = Kp".
00370          */
00371         uint8_t secret[PEERDIST_DIGEST_MAX_SIZE];
00372         /** Segment identifier
00373          *
00374          * This is MS-PCCRC's "HoHoDk".
00375          */
00376         uint8_t id[PEERDIST_DIGEST_MAX_SIZE];
00377 };
00378 
00379 /** Magic string constant used to calculate segment identifier
00380  *
00381  * Note that the MS-PCCRC specification states that this constant is
00382  *
00383  *   "the null-terminated ASCII string constant "MS_P2P_CACHING";
00384  *    string literals are all ASCII strings with NULL terminators
00385  *    unless otherwise noted."
00386  *
00387  * The specification lies.  This constant is a UTF-16LE string, not an
00388  * ASCII string.  The terminating wNUL *is* included within the
00389  * constant.
00390  */
00391 #define PEERDIST_SEGMENT_ID_MAGIC L"MS_P2P_CACHING"
00392 
00393 /** A content information block */
00394 struct peerdist_info_block {
00395         /** Content information segment */
00396         const struct peerdist_info_segment *segment;
00397         /** Block index */
00398         unsigned int index;
00399 
00400         /** Content range
00401          *
00402          * Note that this range may exceed the overall content range.
00403          */
00404         struct peerdist_range range;
00405         /** Trimmed content range */
00406         struct peerdist_range trim;
00407         /** Block hash */
00408         uint8_t hash[PEERDIST_DIGEST_MAX_SIZE];
00409 };
00410 
00411 /** Content information operations */
00412 struct peerdist_info_operations {
00413         /**
00414          * Populate content information
00415          *
00416          * @v info              Content information to fill in
00417          * @ret rc              Return status code
00418          */
00419         int ( * info ) ( struct peerdist_info *info );
00420         /**
00421          * Populate content information segment
00422          *
00423          * @v segment           Content information segment to fill in
00424          * @ret rc              Return status code
00425          */
00426         int ( * segment ) ( struct peerdist_info_segment *segment );
00427         /**
00428          * Populate content information block
00429          *
00430          * @v block             Content information block to fill in
00431          * @ret rc              Return status code
00432          */
00433         int ( * block ) ( struct peerdist_info_block *block );
00434 };
00435 
00436 extern struct digest_algorithm sha512_trunc_algorithm;
00437 
00438 extern int peerdist_info ( userptr_t data, size_t len,
00439                            struct peerdist_info *info );
00440 extern int peerdist_info_segment ( const struct peerdist_info *info,
00441                                    struct peerdist_info_segment *segment,
00442                                    unsigned int index );
00443 extern int peerdist_info_block ( const struct peerdist_info_segment *segment,
00444                                  struct peerdist_info_block *block,
00445                                  unsigned int index );
00446 
00447 #endif /* _IPXE_PCCRC_H */