/*--------------------------------------------------------------------------*/ /* Copyright 2008 Joe Lowe */ /* */ /* Permission is granted to any person obtaining a copy of this Software, */ /* to deal in the Software without restriction, including the rights to use,*/ /* copy, modify, merge, publish, distribute, sublicense, and sell copies of */ /* the Software. */ /* */ /* The above copyright and permission notice must be left intact in all */ /* copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS WITHOUT WARRANTY. */ /*--------------------------------------------------------------------------*/ /* file name: ciso.h */ /* created: 2008.01.21 */ /* */ /* On-media structure definitions and implementation notes for */ /* Compact ISO file format. */ /* revised 2008.06.18 */ /*--------------------------------------------------------------------------*/ #ifndef CISO_H #define CISO_H /* The following type macros are intended only to enhance portability of this header file. Implementors are not expected to use the type macros in implementation code. Implementors should use the type names that fit the compiler/platform/style of the implementation. */ #define _CISO_UINT8 unsigned char #define _CISO_UINT16 unsigned short #define _CISO_UINT32 unsigned #if defined(_MSC_VER) #define _CISO_UINT64 unsigned __int64 #elif LONG_MAX > INT_MAX #define _CISO_UINT64 unsigned long #else #define _CISO_UINT64 unsigned long long #endif #define _CISO_MAKETAGUINT32(a,b,c,d) ((_CISO_UINT32)((a)|((b)<<8)|((c)<<16)|((d)<<24))) #ifdef C_ASSERT #define _CISO_C_ASSERT(a) C_ASSERT(a) #else #define _CISO_C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] #endif #ifdef __cplusplus #define _CISO_INLINE inline #else #define _CISO_INLINE static #endif /* Byte order portability */ #if defined(PORTABLEINT_H) #define CISO_UINT16LE UINT16LE #define CISO_UINT32LE UINT32LE #define CISO_UINT64LE UINT64LE #define CISO_UUIDLE UUIDLE #define _CISO_SETUINT16LE(d,v) setuint16le(d,v) #define _CISO_SETUINT32LE(d,v) setuint32le(d,v) #define _CISO_SETUINT64LE(d,v) setuint64le(d,v) #define _CISO_GETUINT16LE(s) getuint16le(s) #define _CISO_GETUINT32LE(s) getuint32le(s) #define _CISO_GETUINT64LE(s) getuint64le(s) #else #if defined(LITTLEENDIAN) && !defined(DEBUG) #define CISO_UINT16LE _CISO_UINT16 #define CISO_UINT32LE _CISO_UINT32 #define CISO_UINT64LE _CISO_UINT64 #define _CISO_SETUINT16LE(d,v) { *(d) = v; } #define _CISO_SETUINT32LE(d,v) { *(d) = v; } #define _CISO_SETUINT64LE(d,v) { *(d) = v; } #define _CISO_GETUINT16LE(s) (*(s)) #define _CISO_GETUINT32LE(s) (*(s)) #define _CISO_GETUINT64LE(s) (*(s)) #else typedef union { _CISO_UINT16 oe; struct { _CISO_UINT8 b0; _CISO_UINT8 b1; } u; } CfsUint16Le; typedef union { _CISO_UINT32 oe; struct { _CISO_UINT8 b0; _CISO_UINT8 b1; _CISO_UINT8 b2; _CISO_UINT8 b3; } u; } CfsUint32Le; typedef union { _CISO_UINT64 oe; struct { _CISO_UINT8 b0; _CISO_UINT8 b1; _CISO_UINT8 b2; _CISO_UINT8 b3; _CISO_UINT8 b4; _CISO_UINT8 b5; _CISO_UINT8 b6; _CISO_UINT8 b7;} u; } CfsUint64Le; #define CISO_UINT16LE CfsUint16Le #define CISO_UINT32LE CfsUint32Le #define CISO_UINT64LE CfsUint64Le #define _CISO_SETUINT16LE(d,v) { (d)->u.b0 = (_CISO_UINT8)(v); (d)->u.b1 = (_CISO_UINT8)((v)>>8); } #define _CISO_SETUINT32LE(d,v) { (d)->u.b0 = (_CISO_UINT8)(v); (d)->u.b1 = (_CISO_UINT8)((v)>>8); (d)->u.b2 = (_CISO_UINT8)((v)>>16); (d)->u.b3 = (_CISO_UINT8)((v)>>24); } #define _CISO_SETUINT64LE(d,v) { (d)->u.b0 = (_CISO_UINT8)(v); (d)->u.b1 = (_CISO_UINT8)((_CISO_UINT32)(v)>>8); (d)->u.b2 = (_CISO_UINT8)((_CISO_UINT32)(v)>>16); (d)->u.b3 = (_CISO_UINT8)((_CISO_UINT32)(v)>>24); (d)->u.b4 = (_CISO_UINT8)((v)>>32); (d)->u.b5 = (_CISO_UINT8)((v)>>40); (d)->u.b6 = (_CISO_UINT8)((v)>>48); (d)->u.b7 = (_CISO_UINT8)((v)>>56); } #define _CISO_GETUINT16LE(s) ((s)->u.b0|((_CISO_UINT16)((s)->u.b1)<<8)) #define _CISO_GETUINT32LE(s) ((s)->u.b0|((_CISO_UINT32)((s)->u.b1)<<8)|((_CISO_UINT32)((s)->u.b2)<<16)|((_CISO_UINT32)((s)->u.b3)<<24)) #define _CISO_GETUINT64LE(s) ((s)->u.b0|((_CISO_UINT32)((s)->u.b1)<<8)|((_CISO_UINT32)((s)->u.b2)<<16)|((_CISO_UINT32)((s)->u.b3)<<24)|((_CISO_UINT64)((s)->u.b4)<<32)|((_CISO_UINT64)((s)->u.b5)<<40)|((_CISO_UINT64)((s)->u.b6)<<48)|((_CISO_UINT64)((s)->u.b7)<<56)) #endif #if defined(_MSC_VER) && defined(GUID_DEFINED) #define CISO_UUIDLE GUID #else typedef struct { CISO_UINT32LE data1; CISO_UINT16LE data2; CISO_UINT16LE data3; _CISO_UINT8 data4[8]; } CisoUuidLe; #define CISO_UUIDLE CisoUuidLe #endif #endif _CISO_INLINE _CISO_UINT16 CisoGetUint16Le(const CISO_UINT16LE* s) { return _CISO_GETUINT16LE(s); } _CISO_INLINE _CISO_UINT32 CisoGetUint32Le(const CISO_UINT32LE* s) { return _CISO_GETUINT32LE(s); } _CISO_INLINE _CISO_UINT64 CisoGetUint64Le(const CISO_UINT64LE* s) { return _CISO_GETUINT64LE(s); } _CISO_INLINE void CisoSetUint16Le(CISO_UINT16LE* d,_CISO_UINT16 v) { _CISO_SETUINT16LE(d,v); } _CISO_INLINE void CisoSetUint32Le(CISO_UINT32LE* d,_CISO_UINT32 v) { _CISO_SETUINT32LE(d,v); } _CISO_INLINE void CisoSetUint64Le(CISO_UINT64LE* d,_CISO_UINT64 v) { _CISO_SETUINT64LE(d,v); } /* CISO definitions */ enum { cisoAlignment = 0x20, cisoFormatterVersion = 1, cisoMinReadCompatFormatVersion = 1, cisoMinWriteCompatFormatVersion = 1, cisoInitFormatVersion = 1, cisoInitMinReadCompatFormatterVersion = 1, cisoInitMinWriteCompatFormatterVersion = 1, cisoPartHeaderTag = _CISO_MAKETAGUINT32('c','i','%','p'), cisoMediaInfoTag = _CISO_MAKETAGUINT32('c','i','%','m'), cisoSecurityHeaderTag = _CISO_MAKETAGUINT32('c','i','%','x'), cisoPatchHeaderTag = _CISO_MAKETAGUINT32('p','t','c','h'), cisoCompressTypeNone = 0, cisoCompressTypeZip = 1, cisoCompressTypeBzraw = 2, cisoCompressTypeLzma = 3, cisoEncryptTypeNone = 0, cisoEncryptTypeXtsAes128 = 1, cisoHashTypeNone = 0, cisoHashTypeCrc32 = 1, cisoHashTypeSha256 = 2, cisoMinFrameSize = 0x800, /* 2K */ cisoMaxFrameSize = 0x400000, /* 4M */ cisoInitFrameSize = 0x20000, /* 128K */ cisoEncryptInitFrameSize = 0x20000, /* 128K */ cisoSecurityTypeEnd = 1, cisoSecurityTypeKeyBox = 2, cisoMaxHashSize = 0x40, cisoMaxKeySize = 0x40, cisoMaxPatchesSize = 0x400000, /* 4M */ }; _CISO_INLINE size_t CisoAlignUp(size_t n) { return (n+cisoAlignment-1)&~(cisoAlignment-1); } _CISO_INLINE size_t CisoAlignDown(size_t n) { return n&~(cisoAlignment-1); } _CISO_INLINE _CISO_UINT32 CisoAlignUp32(_CISO_UINT32 n) { return (n+cisoAlignment-1)&~(cisoAlignment-1); } _CISO_INLINE _CISO_UINT32 CisoAlignDown32(_CISO_UINT32 n) { return n&~(cisoAlignment-1); } _CISO_INLINE _CISO_UINT64 CisoAlignUp64(_CISO_UINT64 n) { return (n+cisoAlignment-1)&~(cisoAlignment-1); } _CISO_INLINE _CISO_UINT64 CisoAlignDown64(_CISO_UINT64 n) { return n&~(cisoAlignment-1); } _CISO_INLINE unsigned CisoHashSize(uint8_t hashType) { switch(hashType) { default: return UINT_MAX; case cisoHashTypeCrc32: return 4; case cisoHashTypeSha256: return 32; case cisoHashTypeNone: break; } return 0; } #define CISO_DATAFORMAT_ISO "iso" #ifdef __cplusplus static const char cisoDataFormatIso[5] = CISO_DATAFORMAT_ISO; #endif typedef struct { _CISO_UINT32 tag; CISO_UINT32LE crc; /* begin crc-ed */ _CISO_UINT8 formatVersion; _CISO_UINT8 minReadCompatFormatterVersion; _CISO_UINT8 minWriteCompatFormatterVersion; char dataFormat[5]; CISO_UUIDLE mediaId; CISO_UUIDLE instanceId; CISO_UINT64LE mediaStartOffset; /* assert(mediaStartOffset%cisoAlignment == 0) */ CISO_UINT32LE partNumber; /* zero based */ _CISO_UINT8 reserved2[36]; /* end crc-ed */ } CisoPartHeader; _CISO_C_ASSERT(sizeof(CisoPartHeader) == 96); _CISO_C_ASSERT(sizeof(CisoPartHeader)%cisoAlignment == 0); typedef struct { _CISO_UINT32 tag; CISO_UINT32LE crc; /* begin crc-ed */ /* begin media hash-ed */ _CISO_UINT8 formatVersion; _CISO_UINT8 minReadCompatFormatterVersion; _CISO_UINT8 minWriteCompatFormatterVersion; char dataFormat[5]; CISO_UUIDLE mediaId; _CISO_UINT8 hashType; _CISO_UINT8 encryptType; _CISO_UINT8 reserved2[30]; /* begin encrypted */ struct { CISO_UINT64LE frameCount; CISO_UINT32LE frameSize; /* assert(frameSize%cisoAlignment == 0) */ CISO_UINT32LE endFrameSize; /* assert(patchesMediaSize%cisoAlignment == 0) */ CISO_UINT32LE patchesSize; /* assert(patchesSize%cisoAlignment == 0) */ _CISO_UINT8 compressType; _CISO_UINT8 compressParameter; _CISO_UINT8 reserved4[26]; _CISO_UINT8 salt[16]; } encrypt; /* end encrypted */ /* end media hash-ed */ _CISO_UINT8 mediaHash[cisoMaxHashSize]; /* end crc-ed */ } CisoMediaInfo; _CISO_C_ASSERT(sizeof(CisoMediaInfo) == 192); _CISO_C_ASSERT(sizeof(CisoMediaInfo)%cisoAlignment == 0); typedef struct { _CISO_UINT32 tag; CISO_UINT32LE dataSize; CISO_UINT64LE offset; /* uint8_t data[dataSize]; */ /* uint8_t zeros[CisoAlignUp(offsetof(zeros))-offsetof(zeros)]; */ } CisoPatchHeader; typedef struct { _CISO_UINT64 zero; CISO_UINT64LE mediaOffset; /* assert(mediaOffset%cisoAlignment == 0) */ } CisoXtsEncryptTweak; typedef CISO_UINT32LE CisoCrc32Hash; typedef CISO_UINT32LE CisoFrameSize; /* assert(frameCounts[i]%cisoAlignment == 0) */ typedef struct { _CISO_UINT32 tag; CISO_UINT32LE crc; /* begin crc-ed */ CISO_UINT32LE size; /* assert(size%cisoAlignment == 0) */ _CISO_UINT8 securityType; _CISO_UINT8 reserved1[3]; /* _CISO_UINT8 typeSpecific[size-offsetof(typeSpecific)]; */ /* end crc-ed */ } CisoSecurityHeader; _CISO_C_ASSERT(sizeof(CisoSecurityHeader) == 16); typedef struct { CisoSecurityHeader header; CISO_UUIDLE mediaId; CISO_UINT64LE totalSecuritySize; /* assert(totalSecuritySize%cisoAlignment == 0) */ _CISO_UINT8 reserved2[24]; } CisoSecurityEnd; _CISO_C_ASSERT(sizeof(CisoSecurityEnd) == 64); _CISO_C_ASSERT(sizeof(CisoSecurityEnd)%cisoAlignment == 0); typedef struct { CisoSecurityHeader header; _CISO_UINT32 check; CISO_UINT32LE rounds; _CISO_UINT64 salt; _CISO_UINT8 mediaKey[cisoMaxKeySize]; } CisoSecurityKeyBox; _CISO_C_ASSERT(sizeof(CisoSecurityKeyBox) == 96); _CISO_C_ASSERT(sizeof(CisoSecurityKeyBox)%cisoAlignment == 0); #undef _CISO_UINT8 #undef _CISO_UINT16 #undef _CISO_UINT32 #undef _CISO_UINT64 #undef _CISO_SETUINT16LE #undef _CISO_SETUINT32LE #undef _CISO_SETUINT64LE #undef _CISO_GETUINT16LE #undef _CISO_GETUINT32LE #undef _CISO_GETUINT64LE #if 0 // // Compact ISO Specification. // 2008.04.14 // // // CISO is a CD/DVD file system image container format. It exists to // address specific functional requirements not met by existing CD/DVD // image container file formats. // // // Requirements. // // 1. Open and documented standard. // - Required encryption and compression algorithms available // in public domain or with open (non-restrictive) licenses. // 2. Compression. // - Large frame sizes for increased compression efficiency. // 3. Efficient random read access. // - Indexed compression. // 4. Digital signature, authenticity. // - Compatibile with existing signature validation mechanisms. // - On-the-fly validation during read. // 5. Splitting of image into multiple files (spanning). // 6. Executable stub, for self extracting and self mounting images. // 7. Encryption. // - Strong encryption algorithm. // - Secure password based key derivation. // - No leakage. For example, exact image size, // compressibility of sectors, hashes or CRCs of data, sectors // containing identical data, etc.. // // Desired features. // // 1. Support creation of image to non seeking file or pipe (no forward // references in format). // 3. Extensible to use as wrapper for data formats other than CD/DVD // images. // 4. Allow re-splitting images without invalidating media hash. // 5. Allow changing media password without reencrypting the entire // media. // // Non requirements. // // 1. Ability to read image from non-seeking file or pipe. // - Image is expected to be used primarily in environments // where random access is required. If random access were // not required it would be more appropriate to place a // normal ISO image inside a compressed archive such as // a ZIP or GZ file. // 2. Efficient dynamic modification of existing image. // - Doing dynamic modification efficiently becomes file system // development. This is especially true of compressed data // formats. // - Efficient dynamic modification increases the complexity of // the format and the amount of code required in // implementations. // // // Format highlights. // // Key media headers are tagged and crc-ed. Media ID is duplicated // in key headers to facilitate positive identification. // // Sliding window version mechanism, to allow precise control over // forward and backward compatibility when format enhancements are // introduced. // // All headers, tables, and data are 32 byte aligned. All header // fields are aligned to their type size. // // Binary values are stored with little-endian byte ordering. // // Choice of lzma, zip (deflate), or bzraw (modified bzip2) // compression algorithms. Bzraw is deprecated. Lzma compresses // tighter than bzraw with much better read performance. // // SHA256 hash algorithm is used for authenticity. Each frame of // image data is individually hashed to allow validation during // random access. Frame hash table is hashed along with other // media tables and headers to create media hash. // // AES 128 encryption is used with an XTS chaining mode. // // PKCS5V2 password based key derivation. Password is used to decrypt // the media key. This facilitates future enhancements such as master // passwords and certificate based encryption. // // Media can be split into multiple part files at any 32 byte aligned // offset after the first part header up to the start of the media // info. At each split an additional part header is inserted, so each // part file begins with a part header. // // Security data is a tagged-sized chain of structures, allowing // for parsing past unknown security structure types. Simple // implementations can ignore all security structures if support for // encrypted media is not needed. // // Last (or only) part of media will always end with either a // media info structure or a security end structure. The location of // the media info structure is indicated in the security end structure. // // Order of data processing during write is compress, hash, encrypt. // // Patch mechanism allows small updates to image data without requiring // seeking and recompression. This allows mastering processes to update // the ptr to the root directory in the volume descriptor after the bulk // of the image is written, even when writing to non-seeking devices. // // Only frame data is compressed, not headers, patches, or tables. This // allows optimization and specialization of the compression algorithms // for fixed size blocks. // // Image can be attached to two different types of stubs, PE format // executable or a minimal CAB (MS Cabinet archive file) stub. These // stubs both allow an Authenticode digital signature to be attached // to the image that can be validated by stock signature validation // code on Windows. // // Media layout. // // CisoSecurity // { // optional CisoSecurity??? futureSecurity // ... // optional CisoSecurityKeyBox keyBox // ... // optional CisoSecurity??? futureSecurity // ... // CisoSecurityEnd securityEnd // } // // CisoPatch // { // CisoPatchHeader patchHeader // uint8_t patchData[patchHeader.dataSize] // uint8_t alignPad[CisoAlignUp(offsetof(alignPad))-offsetof(alignPad)] // } // // CisoMedia // { // // start of file // CisoPartHeader partHeader // // begin splittable area // uint8_t codedFrameData[frameCodedSizes[0]+...+frameCodedSizes[mediaInfo.frameCount]] // uint8_t frameHashes[CisoAlignUp(mediaInfo.encrypt.frameCount*CisoHashSize(mediaInfo.hashType))] // CisoFrameSize frameCodedSizes[(mediaInfo.encrypt.compressType == cisoCompressTypeNone)?0:(CisoAlignUp(mediaInfo.frameCount*sizeof(CisoFrameSize))/sizeof(CisoFrameSize))] // uint8_t/*CisoPatch*/ patches[mediaInfo.encrypt.patchesSize]; // // end splittable area // CisoMediaInfo mediaInfo // optional CisoSecurity security // } // // Note: Stub payload start, payload end, and unsigned payload start, are // calculated from the exe image header using the logic in pestub.c, or // from the CAB stub using logic in cabstub.c . Simple reader implementations // may choose to search for the part header tags by scanning through the file // for the structure tags and validating via the structure crcs. // // CisoStubbedInternal // { // // start of file // uint8_t exeOrCabStub[stubPayloadStart] // uint8_t alignPad[CisoAlignUp(stubPayloadStart)-stubPayloadStart] // CisoPartHeader partHeader // // begin splittable area // uint8_t codedFrameData[...] // uint8_t frameHashes[...] // CisoFrameSize frameCodedSizes[...] // uint8_t patches[...] // // end splittable area // CisoMediaInfo mediaInfo // optional CisoSecurity security // // stub signature start // optional uint8_t stubSignature[] // // stub signature end // } // // An alternate stub layout allows for high performance authentication // by placing only the media hash within the signed portion of the // stub. With this layout a stock signature validating application insures // to the users that no tampered data will be returned by a CISO reader, but // does not indicate that no tampering has occurred. // // CisoStubbedExternal // { // // start of file // uint8_t exeOrCabStub[stubPayloadStart] // uint8_t alignPad[CisoAlignUp(stubPayloadStart)-stubPayloadStart] // CisoPartHeader lastPartHeader // CisoMediaInfo mediaInfo // optional CisoSecurity security // // stub signature start // uint8_t stubSignature[stubUnsignedPayloadStart-offsetof(stubSignature)] // // stub unsigned payload start // uint8_t alignPad2[CisoAlignUp(stubUnsignedPayloadStart)-stubUnsignedPayloadStart] // CisoPartHeader firstPartHeader // // begin splittable area // uint8_t codedFrameData[...] // uint8_t frameHashes[...] // UINT32LE frameCodedSizes[...] // uint8_t patches[...] // // end splittable area // } // // Note: Implementations must refuse to read media attached to a signed // exe unless 1) the media has a single part that is entirely within the // signed portion of the exe, or 2) the last part of the media (contains // the media info) is within the signed portion of the exe and the media // was created with a strong hash. Otherwise data in subsequent part // files or in the unsigned area of the exe could be modified without // detection. This condition applies even where the implementation does // not verify the exe signature. The user must be able to assume that // tampered data will not be returned from an image attached to an // executable where any tool may have indicated to the user that the // executable has a valid signature. // // // Change history: // // 2008.06.18 // // Added LZMA compression support. // // 2008.04.14 // // First version. // #endif #endif