1 #ifndef _IPXE_PCCRC_H 2 #define _IPXE_PCCRC_H 3 4 /** @file 5 * 6 * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] 7 * 8 */ 9 10 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); 11 12 #include <stdint.h> 13 #include <byteswap.h> 14 #include <ipxe/uaccess.h> 15 #include <ipxe/crypto.h> 16 17 /****************************************************************************** 18 * 19 * Content Information versioning 20 * 21 ****************************************************************************** 22 * 23 * Note that version 1 data structures are little-endian, but version 24 * 2 data structures are big-endian. 25 */ 26 27 /** Content Information version number */ 28 union peerdist_info_version { 29 /** Raw version number 30 * 31 * Always little-endian, regardless of whether the 32 * encompassing structure is version 1 (little-endian) or 33 * version 2 (big-endian). 34 */ 35 uint16_t raw; 36 /** Major:minor version number */ 37 struct { 38 /** Minor version number */ 39 uint8_t minor; 40 /** Major version number */ 41 uint8_t major; 42 } __attribute__ (( packed )); 43 } __attribute__ (( packed )); 44 45 /** Content Information version 1 */ 46 #define PEERDIST_INFO_V1 0x0100 47 48 /** Content Information version 2 */ 49 #define PEERDIST_INFO_V2 0x0200 50 51 /****************************************************************************** 52 * 53 * Content Information version 1 54 * 55 ****************************************************************************** 56 */ 57 58 /** Content Information version 1 data structure header 59 * 60 * All fields are little-endian. 61 */ 62 struct peerdist_info_v1 { 63 /** Version number */ 64 union peerdist_info_version version; 65 /** Hash algorithm 66 * 67 * This is a @c PEERDIST_INFO_V1_HASH_XXX constant. 68 */ 69 uint32_t hash; 70 /** Length to skip in first segment 71 * 72 * Length at the start of the first segment which is not 73 * included within the content range. 74 */ 75 uint32_t first; 76 /** Length to read in last segment, or zero 77 * 78 * Length within the last segment which is included within the 79 * content range. A zero value indicates that the whole of 80 * the last segment is included within the content range. 81 */ 82 uint32_t last; 83 /** Number of segments within the content information */ 84 uint32_t segments; 85 /* Followed by a variable-length array of segment descriptions 86 * and a list of variable-length block descriptions: 87 * 88 * peerdist_info_v1_segment_t(digestsize) segment[segments]; 89 * peerdist_info_v1_block_t(digestsize, block0.blocks) block0; 90 * peerdist_info_v1_block_t(digestsize, block1.blocks) block1; 91 * ... 92 * peerdist_info_v1_block_t(digestsize, blockN.blocks) blockN; 93 */ 94 } __attribute__ (( packed )); 95 96 /** SHA-256 hash algorithm */ 97 #define PEERDIST_INFO_V1_HASH_SHA256 0x0000800cUL 98 99 /** SHA-384 hash algorithm */ 100 #define PEERDIST_INFO_V1_HASH_SHA384 0x0000800dUL 101 102 /** SHA-512 hash algorithm */ 103 #define PEERDIST_INFO_V1_HASH_SHA512 0x0000800eUL 104 105 /** Content Information version 1 segment description header 106 * 107 * All fields are little-endian. 108 */ 109 struct peerdist_info_v1_segment { 110 /** Offset of this segment within the content */ 111 uint64_t offset; 112 /** Length of this segment 113 * 114 * Should always be 32MB, except for the last segment within 115 * the content. 116 */ 117 uint32_t len; 118 /** Block size for this segment 119 * 120 * Should always be 64kB. Note that the last block within the 121 * last segment may actually be less than 64kB. 122 */ 123 uint32_t blksize; 124 /* Followed by two variable-length hashes: 125 * 126 * uint8_t hash[digestsize]; 127 * uint8_t secret[digestsize]; 128 * 129 * where digestsize is the digest size for the selected hash 130 * algorithm. 131 * 132 * Note that the hash is taken over (the hashes of all blocks 133 * within) the entire segment, even if the blocks do not 134 * intersect the content range (and so do not appear within 135 * the block list). It therefore functions only as a segment 136 * identifier; it cannot be used to verify the content of the 137 * segment (since we may not download all blocks within the 138 * segment). 139 */ 140 } __attribute__ (( packed )); 141 142 /** Content Information version 1 segment description 143 * 144 * @v digestsize Digest size 145 */ 146 #define peerdist_info_v1_segment_t( digestsize ) \ 147 struct { \ 148 struct peerdist_info_v1_segment segment; \ 149 uint8_t hash[digestsize]; \ 150 uint8_t secret[digestsize]; \ 151 } __attribute__ (( packed )) 152 153 /** Content Information version 1 block description header 154 * 155 * All fields are little-endian. 156 */ 157 struct peerdist_info_v1_block { 158 /** Number of blocks within the block description 159 * 160 * This is the number of blocks within the segment which 161 * overlap the content range. It may therefore be less than 162 * the number of blocks within the segment. 163 */ 164 uint32_t blocks; 165 /* Followed by an array of variable-length hashes: 166 * 167 * uint8_t hash[blocks][digestsize]; 168 * 169 * where digestsize is the digest size for the selected hash 170 * algorithm. 171 */ 172 } __attribute__ (( packed )); 173 174 /** Content Information version 1 block description 175 * 176 * @v digestsize Digest size 177 * @v blocks Number of blocks 178 */ 179 #define peerdist_info_v1_block_t( digestsize, blocks ) \ 180 struct { \ 181 struct peerdist_info_v1_block block; \ 182 uint8_t hash[blocks][digestsize]; \ 183 } __attribute__ (( packed )) 184 185 /****************************************************************************** 186 * 187 * Content Information version 2 188 * 189 ****************************************************************************** 190 */ 191 192 /** Content Information version 2 data structure header 193 * 194 * All fields are big-endian. 195 */ 196 struct peerdist_info_v2 { 197 /** Version number */ 198 union peerdist_info_version version; 199 /** Hash algorithm 200 * 201 * This is a @c PEERDIST_INFO_V2_HASH_XXX constant. 202 */ 203 uint8_t hash; 204 /** Offset of the first segment within the content */ 205 uint64_t offset; 206 /** Index of the first segment within the content */ 207 uint64_t index; 208 /** Length to skip in first segment 209 * 210 * Length at the start of the first segment which is not 211 * included within the content range. 212 */ 213 uint32_t first; 214 /** Length of content range, or zero 215 * 216 * Length of the content range. A zero indicates that 217 * everything up to the end of the last segment is included in 218 * the content range. 219 */ 220 uint64_t len; 221 /* Followed by a list of chunk descriptions */ 222 } __attribute__ (( packed )); 223 224 /** SHA-512 hash algorithm with output truncated to first 256 bits */ 225 #define PEERDIST_INFO_V2_HASH_SHA512_TRUNC 0x04 226 227 /** Content Information version 2 chunk description header 228 * 229 * All fields are big-endian. 230 */ 231 struct peerdist_info_v2_chunk { 232 /** Chunk type */ 233 uint8_t type; 234 /** Chunk data length */ 235 uint32_t len; 236 /* Followed by an array of segment descriptions: 237 * 238 * peerdist_info_v2_segment_t(digestsize) segment[segments] 239 * 240 * where digestsize is the digest size for the selected hash 241 * algorithm, and segments is equal to @c len divided by the 242 * size of each segment array entry. 243 */ 244 } __attribute__ (( packed )); 245 246 /** Content Information version 2 chunk description 247 * 248 * @v digestsize Digest size 249 */ 250 #define peerdist_info_v2_chunk_t( digestsize ) \ 251 struct { \ 252 struct peerdist_info_v2_chunk chunk; \ 253 peerdist_info_v2_segment_t ( digestsize ) segment[0]; \ 254 } __attribute__ (( packed )) 255 256 /** Chunk type */ 257 #define PEERDIST_INFO_V2_CHUNK_TYPE 0x00 258 259 /** Content Information version 2 segment description header 260 * 261 * All fields are big-endian. 262 */ 263 struct peerdist_info_v2_segment { 264 /** Segment length */ 265 uint32_t len; 266 /* Followed by two variable-length hashes: 267 * 268 * uint8_t hash[digestsize]; 269 * uint8_t secret[digestsize]; 270 * 271 * where digestsize is the digest size for the selected hash 272 * algorithm. 273 */ 274 } __attribute__ (( packed )); 275 276 /** Content Information version 2 segment description 277 * 278 * @v digestsize Digest size 279 */ 280 #define peerdist_info_v2_segment_t( digestsize ) \ 281 struct { \ 282 struct peerdist_info_v2_segment segment; \ 283 uint8_t hash[digestsize]; \ 284 uint8_t secret[digestsize]; \ 285 } __attribute__ (( packed )) 286 287 /****************************************************************************** 288 * 289 * Content Information 290 * 291 ****************************************************************************** 292 */ 293 294 /** Maximum digest size for any supported algorithm 295 * 296 * The largest digest size that we support is for SHA-512 at 64 bytes 297 */ 298 #define PEERDIST_DIGEST_MAX_SIZE 64 299 300 /** Raw content information */ 301 struct peerdist_raw { 302 /** Data buffer */ 303 userptr_t data; 304 /** Length of data buffer */ 305 size_t len; 306 }; 307 308 /** A content range */ 309 struct peerdist_range { 310 /** Start offset */ 311 size_t start; 312 /** End offset */ 313 size_t end; 314 }; 315 316 /** Content information */ 317 struct peerdist_info { 318 /** Raw content information */ 319 struct peerdist_raw raw; 320 321 /** Content information operations */ 322 struct peerdist_info_operations *op; 323 /** Digest algorithm */ 324 struct digest_algorithm *digest; 325 /** Digest size 326 * 327 * Note that this may be shorter than the digest size of the 328 * digest algorithm. The truncation does not always take 329 * place as soon as a digest is calculated. For example, 330 * version 2 content information uses SHA-512 with a truncated 331 * digest size of 32 (256 bits), but the segment identifier 332 * ("HoHoDk") is calculated by using HMAC with the full 333 * SHA-512 digest and then truncating the HMAC output, rather 334 * than by simply using HMAC with the truncated SHA-512 335 * digest. This is, of course, totally undocumented. 336 */ 337 size_t digestsize; 338 /** Content range */ 339 struct peerdist_range range; 340 /** Trimmed content range */ 341 struct peerdist_range trim; 342 /** Number of segments within the content information */ 343 unsigned int segments; 344 }; 345 346 /** A content information segment */ 347 struct peerdist_info_segment { 348 /** Content information */ 349 const struct peerdist_info *info; 350 /** Segment index */ 351 unsigned int index; 352 353 /** Content range 354 * 355 * Note that this range may exceed the overall content range. 356 */ 357 struct peerdist_range range; 358 /** Number of blocks within this segment */ 359 unsigned int blocks; 360 /** Block size */ 361 size_t blksize; 362 /** Segment hash of data 363 * 364 * This is MS-PCCRC's "HoD". 365 */ 366 uint8_t hash[PEERDIST_DIGEST_MAX_SIZE]; 367 /** Segment secret 368 * 369 * This is MS-PCCRC's "Ke = Kp". 370 */ 371 uint8_t secret[PEERDIST_DIGEST_MAX_SIZE]; 372 /** Segment identifier 373 * 374 * This is MS-PCCRC's "HoHoDk". 375 */ 376 uint8_t id[PEERDIST_DIGEST_MAX_SIZE]; 377 }; 378 379 /** Magic string constant used to calculate segment identifier 380 * 381 * Note that the MS-PCCRC specification states that this constant is 382 * 383 * "the null-terminated ASCII string constant "MS_P2P_CACHING"; 384 * string literals are all ASCII strings with NULL terminators 385 * unless otherwise noted." 386 * 387 * The specification lies. This constant is a UTF-16LE string, not an 388 * ASCII string. The terminating wNUL *is* included within the 389 * constant. 390 */ 391 #define PEERDIST_SEGMENT_ID_MAGIC L"MS_P2P_CACHING" 392 393 /** A content information block */ 394 struct peerdist_info_block { 395 /** Content information segment */ 396 const struct peerdist_info_segment *segment; 397 /** Block index */ 398 unsigned int index; 399 400 /** Content range 401 * 402 * Note that this range may exceed the overall content range. 403 */ 404 struct peerdist_range range; 405 /** Trimmed content range */ 406 struct peerdist_range trim; 407 /** Block hash */ 408 uint8_t hash[PEERDIST_DIGEST_MAX_SIZE]; 409 }; 410 411 /** Content information operations */ 412 struct peerdist_info_operations { 413 /** 414 * Populate content information 415 * 416 * @v info Content information to fill in 417 * @ret rc Return status code 418 */ 419 int ( * info ) ( struct peerdist_info *info ); 420 /** 421 * Populate content information segment 422 * 423 * @v segment Content information segment to fill in 424 * @ret rc Return status code 425 */ 426 int ( * segment ) ( struct peerdist_info_segment *segment ); 427 /** 428 * Populate content information block 429 * 430 * @v block Content information block to fill in 431 * @ret rc Return status code 432 */ 433 int ( * block ) ( struct peerdist_info_block *block ); 434 }; 435 436 extern struct digest_algorithm sha512_trunc_algorithm; 437 438 extern int peerdist_info ( userptr_t data, size_t len, 439 struct peerdist_info *info ); 440 extern int peerdist_info_segment ( const struct peerdist_info *info, 441 struct peerdist_info_segment *segment, 442 unsigned int index ); 443 extern int peerdist_info_block ( const struct peerdist_info_segment *segment, 444 struct peerdist_info_block *block, 445 unsigned int index ); 446 447 #endif /* _IPXE_PCCRC_H */ 448