Merge origin/feature into helpphil and fix a build break on Win32 builds.

This commit is contained in:
Phil Smith 2017-12-01 11:19:44 -08:00
Родитель 26fe5cd627 f4767095cf
Коммит a3b8592d02
42 изменённых файлов: 1337 добавлений и 494 удалений

4
.gitignore поставляемый
Просмотреть файл

@ -395,4 +395,6 @@ __pycache__/
# to avoid seeing deleted: ../lib/zlib/zconf.h in the working directory
# run git update-index --assume-unchanged lib/zlib/zconf.h
lib/zlib
src/inc/AppxCerts.hpp
# Files generated by CMake
src/inc/AppxCerts.hpp
src/inc/AppxBlockMapSchemas.hpp

Просмотреть файл

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/appx/2017/blockmap"
targetNamespace="http://schemas.microsoft.com/appx/2017/blockmap"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="BlockMap" type="CT_BlockMap"/>
<xs:complexType name="CT_BlockMap">
<xs:sequence>
<xs:element name="File" type="CT_File" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="HashMethod" type="xs:anyURI" use="required">
<xs:annotation>
<xs:documentation>The method used for computing cryptographic hashes.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PackageEncrypted" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Flag, indicates if the package is encrypted or not.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="CT_File">
<xs:sequence>
<xs:element name="Block" type="CT_Block" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="FileHash" type="CT_FileHash" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="Name" type="ST_FileName" use="required">
<xs:annotation>
<xs:documentation>Root path and file name.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Id" type="ST_FileId" use="optional">
<xs:annotation>
<xs:documentation>Id of the file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Size" type="xs:nonNegativeInteger" use="required">
<xs:annotation>
<xs:documentation>Size, in bytes, of the file's uncompressed data.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Encrypted" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Flag, indicates if file within the package is encrypted or not.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DeltaUpdated" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Flag, indicates if file within the package is delta updated.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LfhSize" type="ST_HeaderSize" use="optional">
<xs:annotation>
<xs:documentation>Size, in bytes, of the file's Local File Header.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="CT_FileHash">
<xs:attribute name="Hash" type="xs:base64Binary" use="required">
<xs:annotation>
<xs:documentation>The hash value of the *uncompressed* data.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:simpleType name="ST_FileId">
<xs:annotation>
<xs:documentation>The Id of the file must be non-empty and no more than 16 characters. It is a HEX encoding of a UINT64 integer, which the max length will be 16 characters "FFFFFFFFFFFFFFFF"</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="[0-9a-fA-F]+"/>
<xs:minLength value="1"/>
<xs:maxLength value="16"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="CT_Block">
<xs:attribute name="Hash" type="xs:base64Binary" use="required">
<xs:annotation>
<xs:documentation>The hash value of the *uncompressed* data.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Size" type="xs:positiveInteger" use="optional">
<xs:annotation>
<xs:documentation>Size, in bytes, of the *compressed* data sequence for this block.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreDeltaUpdateSize" type="xs:positiveInteger" use="optional">
<xs:annotation>
<xs:documentation>Size before delta update, in bytes, of the *compressed* data sequence for this block.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PackageOffset" type="xs:positiveInteger" use="optional">
<xs:annotation>
<xs:documentation>The offset value of the block within the package.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:simpleType name="ST_FileName">
<xs:annotation>
<xs:documentation>The name of the file must be non-empty and no more than 32767 (UNICODE_STRING_MAX_CHARS) characters supported by the APPX package format (even though ZIP format supports up to 65535 bytes).</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="32767"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ST_HeaderSize">
<xs:annotation>
<xs:documentation>The size of the Local File Header for a file must be at least 30 bytes and no more than 64KB as required by the ZIP format.</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:positiveInteger">
<xs:minInclusive value="30"/>
<xs:maxInclusive value="65536"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>

Просмотреть файл

@ -110,6 +110,14 @@ ENDIF()
MESSAGE (STATUS "Build type: ${CMAKE_BUILD_TYPE}")
IF ((CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) OR (CMAKE_BUILD_TYPE MATCHES Release) OR (CMAKE_BUILD_TYPE MATCHES MinSizeRel))
ADD_DEFINITIONS(-DNDEBUG)
MESSAGE (STATUS "NDEBUG defined, assert should be turned-off" )
ELSE()
MESSAGE (STATUS "NDEBUG NOT defined, assert should be turned-on" )
ENDIF()
IF(MACOS)
# Incredibly, for both clang and g++, while a single compile-and-link
# invocation will create an executable.dSYM/ dir with debug info,

Просмотреть файл

@ -33,6 +33,22 @@
included that in the larger CMake project.
* Make Nuget Package to target the native framework.
0.8 - Implement Signature validation (AppxSignature.p7x) on Win32, and put down the foundation for
blockmap validation. Code now compiles and links against OpenSSL on non-Win32 platforms.
Zlib was moved from a git submodule to a subtree so that we can now build on Ubuntu. Also,
Zip64 support was added to the underlying OPC layer. Also, multiple fixes to the nuget package
were added to address early adopter feedback. Plus, many bug fixes along the way
0.9 - Implement BlockMap validation and fix a number of various memory leaks. Added additional test
collateral to cover negative cases for blockmap validation. Payload file names now come from
the appxblockmap.xml file instead of from the underlying zip central directory. Xerces is now
fully integrated as the XML parser, which we use to do XML parsing, XSD semantic validation, and
Base64 decoding of manifested digest data. We also use Xerces to validate that the content type
XML file is well-formed XML. We also implemented a number of the AppxBlockMap related interfaces
that correspond to the new functionality provided in this version. Finally, we incorportated the
latest Windows XSDs for appxblockmap.xml.
</releaseNotes>
<copyright>Copyright (C) 2017 Microsoft</copyright>
<tags>xPlatAppx Appx AppxPackaging native</tags>

Просмотреть файл

@ -6,5 +6,5 @@ cd .vs
# clean up any old builds of xPlatAppx modules
find . -name *xPlatAppx* -d | xargs rm -r
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLINUX=on ..
cmake -DCMAKE_BUILD_TYPE=Release -DLINUX=on ..
make

Просмотреть файл

@ -6,5 +6,5 @@ cd .vs
# clean up any old builds of xPlatAppx modules
find . -name *xPlatAppx* -d | xargs rm -r
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMACOS=on ..
cmake -DCMAKE_BUILD_TYPE=Release -DMACOS=on ..
make

Просмотреть файл

@ -4,5 +4,5 @@ cd .vs
if exist CMakeFiles rd /s /q CMakeFiles
if exist CMakeCache.txt del CMakeCache.txt
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat"
cmake -DWIN32=on -DCMAKE_BUILD_TYPE=RelWithDebInfo -G"NMake Makefiles" ..
cmake -DWIN32=on -DCMAKE_BUILD_TYPE=Release -G"NMake Makefiles" ..
nmake

Просмотреть файл

@ -1,72 +1,233 @@
#pragma once
#include <algorithm>
#include <string>
#include <map>
#include <vector>
#include <iterator>
#include "StreamBase.hpp"
#include "StorageObject.hpp"
#include "VerifierObject.hpp"
#include "AppxPackaging.hpp"
#include "ComHelper.hpp"
#include "UnicodeConversion.hpp"
#include "AppxFactory.hpp"
#include "XmlObject.hpp"
#include "BlockMapStream.hpp"
#include "xercesc/util/XMLString.hpp"
namespace xPlat {
class AppxBlockMapBlock : public xPlat::ComClass<AppxBlockMapBlock, IAppxBlockMapBlock>
{
public:
AppxBlockMapBlock() :
m_offset(0),
m_size(0),
m_encryptedSize(0),
m_hashLength(0),
m_packageOffset(0)
AppxBlockMapBlock(IxPlatFactory* factory, Block* block) :
m_factory(factory),
m_block(block)
{}
// IAppxBlockMapBlock
HRESULT STDMETHODCALLTYPE GetHash(UINT32* bufferSize, BYTE** buffer);
HRESULT STDMETHODCALLTYPE GetCompressedSize(UINT32* size);
HRESULT STDMETHODCALLTYPE GetHash(UINT32* bufferSize, BYTE** buffer) override
{
return ResultOf([&]{
ThrowHrIfFailed(m_factory->MarshalOutBytes(m_block->hash, bufferSize, buffer));
});
}
HRESULT STDMETHODCALLTYPE GetCompressedSize(UINT32* size) override
{
return ResultOf([&]{
ThrowErrorIf(Error::InvalidParameter, (size == nullptr), "bad pointer");
*size = static_cast<UINT32>(m_block->compressedSize);
});
}
private:
std::uint64_t m_offset;
std::uint32_t m_size; // If the block is compressed, means the compressed size of the block. Otherwise, it is the uncompressed size.
std::uint32_t m_encryptedSize; // Size of the encrypted block. It is the raw data size of the block if it is encrypted.
std::uint32_t m_hashLength;
std::uint32_t m_packageOffset;
std::vector<unsigned char> hash;
IxPlatFactory* m_factory;
Block* m_block;
};
class AppxBlockMapFile : public xPlat::ComClass<AppxBlockMapFile, IAppxBlockMapFile, IAppxBlockMapFilesEnumerator>
class AppxBlockMapBlocksEnumerator : public xPlat::ComClass<AppxBlockMapBlocksEnumerator, IAppxBlockMapBlocksEnumerator>
{
protected:
std::vector<ComPtr<IAppxBlockMapBlock>>* m_blocks;
std::size_t m_cursor = 0;
public:
AppxBlockMapBlocksEnumerator(std::vector<ComPtr<IAppxBlockMapBlock>>* blocks) :
m_blocks(blocks)
{}
// IAppxBlockMapBlocksEnumerator
HRESULT STDMETHODCALLTYPE GetCurrent(IAppxBlockMapBlock** block) override
{
return ResultOf([&]{
ThrowErrorIf(Error::InvalidParameter, (block == nullptr || *block != nullptr), "bad pointer");
*block = m_blocks->at(m_cursor).Get();
(*block)->AddRef();
});
}
HRESULT STDMETHODCALLTYPE GetHasCurrent(BOOL* hasCurrent) override
{ return ResultOf([&]{
ThrowErrorIfNot(Error::InvalidParameter, (hasCurrent), "bad pointer");
*hasCurrent = (m_cursor != m_blocks->size()) ? TRUE : FALSE;
});
}
HRESULT STDMETHODCALLTYPE MoveNext(BOOL* hasNext) override
{ return ResultOf([&]{
ThrowErrorIfNot(Error::InvalidParameter, (hasNext), "bad pointer");
*hasNext = (++m_cursor != m_blocks->size()) ? TRUE : FALSE;
});
}
};
class AppxBlockMapFile : public xPlat::ComClass<AppxBlockMapFile, IAppxBlockMapFile>
{
public:
// IAppxBlockMapFile
HRESULT STDMETHODCALLTYPE GetBlocks(IAppxBlockMapBlocksEnumerator **blocks) override;
HRESULT STDMETHODCALLTYPE GetLocalFileHeaderSize(UINT32* lfhSize) override;
HRESULT STDMETHODCALLTYPE GetName(LPWSTR *name) override;
HRESULT STDMETHODCALLTYPE GetUncompressedSize(UINT64 *size) override;
HRESULT STDMETHODCALLTYPE ValidateFileHash(IStream *fileStream, BOOL *isValid) override;
AppxBlockMapFile(
IxPlatFactory* factory,
std::vector<Block>* blocks,
std::uint32_t localFileHeaderSize,
const std::string& name,
std::uint64_t uncompressedSize
) :
m_factory(factory),
m_blocks(blocks),
m_localFileHeaderSize(localFileHeaderSize),
m_name(name),
m_uncompressedSize(uncompressedSize)
{
}
// IAppxBlockMapFilesEnumerator
HRESULT STDMETHODCALLTYPE GetCurrent(IAppxBlockMapFile** block) override;
HRESULT STDMETHODCALLTYPE GetHasCurrent(BOOL* hasCurrent) override;
HRESULT STDMETHODCALLTYPE MoveNext(BOOL* hasNext) override;
// IAppxBlockMapFile
HRESULT STDMETHODCALLTYPE GetBlocks(IAppxBlockMapBlocksEnumerator **blocks) override
{
return ResultOf([&]{
if (m_blockMapBlocks.empty())
{ m_blockMapBlocks.reserve(m_blocks->size());
std::transform(
m_blocks->begin(),
m_blocks->end(),
std::back_inserter(m_blockMapBlocks),
[&](auto item){
return ComPtr<IAppxBlockMapBlock>::Make<AppxBlockMapBlock>(m_factory, &item);
}
);
}
ThrowErrorIf(Error::InvalidParameter, (blocks == nullptr || *blocks != nullptr), "bad pointer.");
*blocks = ComPtr<IAppxBlockMapBlocksEnumerator>::Make<AppxBlockMapBlocksEnumerator>(&m_blockMapBlocks).Detach();
});
}
HRESULT STDMETHODCALLTYPE GetLocalFileHeaderSize(UINT32* lfhSize) override
{ // Retrieves the size of the zip local file header of the associated zip file item
return ResultOf([&]{
ThrowErrorIf(Error::InvalidParameter, (lfhSize == nullptr), "bad pointer");
*lfhSize = static_cast<UINT32>(m_localFileHeaderSize);
});
}
HRESULT STDMETHODCALLTYPE GetName(LPWSTR *name) override
{
return ResultOf([&]{
ThrowHrIfFailed(m_factory->MarshalOutString(m_name, name));
});
}
HRESULT STDMETHODCALLTYPE GetUncompressedSize(UINT64 *size) override
{
return ResultOf([&]{
ThrowErrorIf(Error::InvalidParameter, (size == nullptr), "bad pointer");
*size = static_cast<UINT64>(m_uncompressedSize);
});
}
HRESULT STDMETHODCALLTYPE ValidateFileHash(IStream *fileStream, BOOL *isValid) override
{
return ResultOf([&]{
// TODO: Implement...
throw Exception(Error::NotImplemented);
});
}
private:
std::vector<std::shared_ptr<AppxBlockMapBlock>> m_blocks;
std::vector<ComPtr<IAppxBlockMapBlock>> m_blockMapBlocks;
std::vector<Block>* m_blocks;
IxPlatFactory* m_factory;
std::uint32_t m_localFileHeaderSize;
std::string m_name;
std::uint64_t m_uncompressedSize;
};
class AppxBlockMapFilesEnumerator : public xPlat::ComClass<AppxBlockMapFilesEnumerator, IAppxBlockMapFilesEnumerator>
{
protected:
ComPtr<IAppxBlockMapReader> m_reader;
std::vector<std::string> m_files;
std::size_t m_cursor = 0;
public:
AppxBlockMapFilesEnumerator(IAppxBlockMapReader* reader, std::vector<std::string>&& files) :
m_reader(reader),
m_files(files)
{}
// IAppxBlockMapFilesEnumerator
HRESULT STDMETHODCALLTYPE GetCurrent(IAppxBlockMapFile** block) override
{
return ResultOf([&]{
ThrowErrorIf(Error::Unexpected, (m_cursor >= m_files.size()), "index out of range");
ThrowHrIfFailed(m_reader->GetFile(utf8_to_utf16(m_files.at(m_cursor)).c_str(), block));
});
}
HRESULT STDMETHODCALLTYPE GetHasCurrent(BOOL* hasCurrent) override
{ return ResultOf([&]{
ThrowErrorIfNot(Error::InvalidParameter, (hasCurrent), "bad pointer");
*hasCurrent = (m_cursor != m_files.size()) ? TRUE : FALSE;
});
}
HRESULT STDMETHODCALLTYPE MoveNext(BOOL* hasNext) override
{ return ResultOf([&]{
ThrowErrorIfNot(Error::InvalidParameter, (hasNext), "bad pointer");
*hasNext = (++m_cursor != m_files.size()) ? TRUE : FALSE;
});
}
};
// Object backed by AppxBlockMap.xml
class AppxBlockMapObject : public VerifierObject, public xPlat::ComClass<AppxBlockMapObject, IAppxBlockMapBlocksEnumerator>
class AppxBlockMapObject : public xPlat::ComClass<AppxBlockMapObject, IAppxBlockMapReader, IVerifierObject, IStorageObject>
{
public:
AppxBlockMapObject(IStream* stream);
AppxBlockMapObject(IxPlatFactory* factory, ComPtr<IStream>& stream);
IStream* GetValidationStream(const std::string& part, IStream* stream) override;
// IVerifierObject
bool HasStream() override { return m_stream.Get() != nullptr; }
xPlat::ComPtr<IStream> GetStream() override { return m_stream; }
xPlat::ComPtr<IStream> GetValidationStream(const std::string& part, IStream* stream) override;
// IAppxBlockMapBlocksEnumerator
HRESULT STDMETHODCALLTYPE GetCurrent(IAppxBlockMapBlock** block) override;
HRESULT STDMETHODCALLTYPE GetHasCurrent(BOOL* hasCurrent) override;
HRESULT STDMETHODCALLTYPE MoveNext(BOOL* hasNext) override;
// IAppxBlockMapReader
HRESULT STDMETHODCALLTYPE GetFile(LPCWSTR filename, IAppxBlockMapFile **file) override;
HRESULT STDMETHODCALLTYPE GetFiles(IAppxBlockMapFilesEnumerator **enumerator) override;
HRESULT STDMETHODCALLTYPE GetHashMethod(IUri **hashMethod) override;
HRESULT STDMETHODCALLTYPE GetStream(IStream **blockMapStream) override;
// IStorageObject methods
std::string GetPathSeparator() override;
std::vector<std::string> GetFileNames(FileNameOptions options) override;
IStream* GetFile(const std::string& fileName) override;
void RemoveFile(const std::string& fileName) override;
IStream* OpenFile(const std::string& fileName, xPlat::FileStream::Mode mode) override;
void CommitChanges() override;
protected:
std::map<std::string, std::shared_ptr<AppxBlockMapFile>> m_blockMapfiles;
std::map<std::string, std::vector<Block>> m_blockMap;
std::map<std::string, ComPtr<IAppxBlockMapFile>> m_blockMapfiles;
IxPlatFactory* m_factory;
ComPtr<IStream> m_stream;
};
}

Просмотреть файл

@ -3,8 +3,10 @@
#include "AppxPackaging.hpp"
#include "AppxWindows.hpp"
#include "ComHelper.hpp"
#include "xercesc/util/PlatformUtils.hpp"
#include <string>
#include <vector>
// internal interface
EXTERN_C const IID IID_IxPlatFactory;
@ -18,10 +20,9 @@ class IxPlatFactory : public IUnknown
#endif
{
public:
#ifdef WIN32
virtual ~IxPlatFactory() {}
#endif
virtual HRESULT MarshalOutString(std::string& internal, LPWSTR *result) = 0;
virtual HRESULT MarshalOutBytes(std::vector<std::uint8_t>& data, UINT32* size, BYTE** buffer) = 0;
virtual APPX_VALIDATION_OPTION GetValidationOptions() = 0;
};
SpecializeUuidOfImpl(IxPlatFactory);
@ -34,6 +35,12 @@ namespace xPlat {
m_validationOptions(validationOptions), m_memalloc(memalloc), m_memfree(memfree)
{
ThrowErrorIf(Error::InvalidParameter, (m_memalloc == nullptr || m_memfree == nullptr), "allocator/deallocator pair not specified.")
XERCES_CPP_NAMESPACE::XMLPlatformUtils::Initialize();
}
~AppxFactory()
{
XERCES_CPP_NAMESPACE::XMLPlatformUtils::Terminate();
}
// IAppxFactory
@ -53,6 +60,8 @@ namespace xPlat {
// IxPlatFactory
HRESULT MarshalOutString(std::string& internal, LPWSTR *result) override;
HRESULT MarshalOutBytes(std::vector<std::uint8_t>& data, UINT32* size, BYTE** buffer) override;
APPX_VALIDATION_OPTION GetValidationOptions() override { return m_validationOptions; }
COTASKMEMALLOC* m_memalloc;
COTASKMEMFREE* m_memfree;

Просмотреть файл

@ -16,6 +16,7 @@
#include "XmlObject.hpp"
#include "AppxBlockMapObject.hpp"
#include "AppxSignature.hpp"
#include "AppxFactory.hpp"
// internal interface
EXTERN_C const IID IID_IAppxPackage;
@ -29,10 +30,6 @@ class IAppxPackage : public IUnknown
#endif
{
public:
#ifdef WIN32
virtual ~IAppxPackage() {}
#endif
virtual void Pack(APPX_PACKUNPACK_OPTION options, const std::string& certFile, IStorageObject* from) = 0;
virtual void Unpack(APPX_PACKUNPACK_OPTION options, IStorageObject* to) = 0;
virtual std::vector<std::string>& GetFootprintFiles() = 0;
@ -69,12 +66,15 @@ namespace xPlat {
};
// Object backed by AppxManifest.xml
class AppxManifestObject : public VerifierObject
class AppxManifestObject : public ComClass<AppxManifestObject, IVerifierObject>
{
public:
AppxManifestObject(IStream* stream);
AppxManifestObject(ComPtr<IStream>& stream);
IStream* GetValidationStream(const std::string& part, IStream* stream) override
// IVerifierObject
bool HasStream() override { return m_stream.Get() != nullptr; }
xPlat::ComPtr<IStream> GetStream() override { return m_stream; }
xPlat::ComPtr<IStream> GetValidationStream(const std::string& part, IStream* stream) override
{
throw Exception(Error::NotSupported);
}
@ -88,19 +88,16 @@ namespace xPlat {
};
// Storage object representing the entire AppxPackage
class AppxPackageObject : public xPlat::ComClass<AppxPackageObject, IAppxPackageReader, IAppxPackage, IStorageObject>
class AppxPackageObject : public ComClass<AppxPackageObject, IAppxPackageReader, IAppxPackage, IStorageObject>
{
public:
AppxPackageObject(APPX_VALIDATION_OPTION validation, IStorageObject* container);
AppxPackageObject(IxPlatFactory* factory, APPX_VALIDATION_OPTION validation, IStorageObject* container);
~AppxPackageObject() {}
// internal IxPlatAppxPackage methods
void Pack(APPX_PACKUNPACK_OPTION options, const std::string& certFile, IStorageObject* from) override;
void Unpack(APPX_PACKUNPACK_OPTION options, IStorageObject* to) override;
AppxSignatureObject* GetAppxSignature() const { return m_appxSignature.get(); }
AppxBlockMapObject* GetAppxBlockMap() const { return m_appxBlockMap.get(); }
AppxManifestObject* GetAppxManifest() const { return m_appxManifest.get(); }
// IAppxPackageReader
HRESULT STDMETHODCALLTYPE GetBlockMap(IAppxBlockMapReader** blockMapReader) override;
HRESULT STDMETHODCALLTYPE GetFootprintFile(APPX_FOOTPRINT_FILE_TYPE type, IAppxFile** file) override;
@ -121,16 +118,17 @@ namespace xPlat {
protected:
std::map<std::string, ComPtr<IStream>> m_streams;
APPX_VALIDATION_OPTION m_validation = APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_FULL;
std::unique_ptr<AppxSignatureObject> m_appxSignature;
std::unique_ptr<AppxBlockMapObject> m_appxBlockMap;
std::unique_ptr<AppxManifestObject> m_appxManifest;
ComPtr<IStorageObject> m_container;
std::vector<std::string> m_payloadFiles;
std::vector<std::string> m_footprintFiles;
std::unique_ptr<XmlObject> m_contentType;
APPX_VALIDATION_OPTION m_validation = APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_FULL;
ComPtr<IxPlatFactory> m_factory;
ComPtr<IVerifierObject> m_appxSignature;
ComPtr<IVerifierObject> m_appxBlockMap;
ComPtr<IVerifierObject> m_appxManifest;
ComPtr<IVerifierObject> m_contentType;
ComPtr<IStorageObject> m_container;
std::vector<std::string> m_payloadFiles;
std::vector<std::string> m_footprintFiles;
};
class AppxFilesEnumerator : public xPlat::ComClass<AppxFilesEnumerator, IAppxFilesEnumerator>

Просмотреть файл

@ -35,9 +35,9 @@ namespace xPlat {
const DWORD P7X_FILE_ID = 0x58434b50;
// Object backed by AppxSignature.p7x
class AppxSignatureObject : public VerifierObject
class AppxSignatureObject : public ComClass<AppxSignatureObject, IVerifierObject>
{
public:
public:
enum DigestName : std::uint32_t
{
HEAD = 0x58505041, // APPX
@ -50,7 +50,10 @@ namespace xPlat {
AppxSignatureObject(APPX_VALIDATION_OPTION validationOptions, IStream* stream);
IStream* GetValidationStream(const std::string& part, IStream* stream) override;
// IVerifierObject
bool HasStream() override { return m_stream.Get() != nullptr; }
xPlat::ComPtr<IStream> GetStream() override { return m_stream; }
xPlat::ComPtr<IStream> GetValidationStream(const std::string& part, IStream* stream) override;
using Digest = std::vector<std::uint8_t>;
@ -66,5 +69,6 @@ namespace xPlat {
std::map<DigestName, Digest> m_digests;
SignatureOrigin m_signatureOrigin = SignatureOrigin::Unsigned; // assume unsigned until proven otherwise.
APPX_VALIDATION_OPTION m_validationOptions;
ComPtr<IStream> m_stream;
};
} // namespace xPlat

Просмотреть файл

@ -12,29 +12,22 @@
#include <map>
#include <functional>
#include <algorithm>
#include <set>
#include <vector>
namespace xPlat {
const std::uint32_t BLOCKMAP_BLOCK_SIZE = 65535;
const std::uint64_t BLOCKMAP_BLOCK_SIZE = 65536; // 64KB
typedef struct Block
{
std::uint64_t size;
std::uint64_t offset;
std::uint64_t compressedSize;
std::vector<std::uint8_t> hash;
} Block;
typedef struct BlockCompare
{
bool operator()(const Block& lhs, const Block& rhs) const
{
return lhs.offset < rhs.offset;
}
} BlockCompare;
typedef struct BlockPlusStream : Block
{
std::uint64_t size;
std::uint64_t offset;
ComPtr<IStream> stream;
} BlockPlusStream;
@ -42,25 +35,8 @@ namespace xPlat {
class BlockMapStream : public StreamBase
{
public:
BlockMapStream(/*[In]*/ IStream* stream, /*[In]*/ std::set<Block, BlockCompare>& blocks)
BlockMapStream(IStream* stream, std::vector<Block>& blocks)
{
// Build a vector of all HashStream->RangeStream's for the blocks in the blockmap
for (auto block = blocks.begin(); block != blocks.end(); block++)
{
ThrowErrorIfNot(xPlat::Error::AppxSignatureInvalid,
((block->offset % BLOCKMAP_BLOCK_SIZE == 0) && (block->size <= BLOCKMAP_BLOCK_SIZE)),
"block size must be less than 65535");
auto rangeStream = ComPtr<IStream>::Make<RangeStream>(block->offset, block->size, stream);
auto hashStream = ComPtr<IStream>::Make<HashStream>(rangeStream.Get(), block->hash);
BlockPlusStream bs;
bs.offset = block->offset;
bs.size = block->size;
bs.stream = hashStream;
bs.hash.assign(&block->hash[0], &block->hash[block->hash.size()]);
m_blockStreams.push_back(bs);
}
// Determine overall stream size
ULARGE_INTEGER uli;
LARGE_INTEGER li;
@ -72,6 +48,30 @@ namespace xPlat {
// Reset seek position to beginning
li.QuadPart = 0;
ThrowHrIfFailed(stream->Seek(li, STREAM_SEEK_SET, nullptr));
// Build a vector of all HashStream->RangeStream's for the blocks in the blockmap
std::uint64_t offset = 0;
std::uint64_t sizeRemaining = m_streamSize;
for (auto block = blocks.begin(); ((sizeRemaining != 0) && (block != blocks.end())); block++)
{
auto rangeStream = ComPtr<IStream>::Make<RangeStream>(offset, std::min(sizeRemaining, BLOCKMAP_BLOCK_SIZE), stream);
auto hashStream = ComPtr<IStream>::Make<HashStream>(rangeStream.Get(), block->hash);
std::uint64_t blockSize = std::min(sizeRemaining, BLOCKMAP_BLOCK_SIZE);
BlockPlusStream bs;
bs.offset = offset;
bs.size = blockSize;
bs.stream = hashStream;
bs.hash = block->hash;
m_blockStreams.emplace_back(std::move(bs));
offset += blockSize;
sizeRemaining -= blockSize;
}
// Reset seek position to beginning
ThrowHrIfFailed(stream->Seek(li, STREAM_SEEK_SET, nullptr));
ThrowHrIfFailed(Seek(li, STREAM_SEEK_SET, nullptr));
}
HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newPosition) override
@ -91,6 +91,8 @@ namespace xPlat {
}
m_relativePosition = std::max((std::uint64_t)0, std::min(m_relativePosition, m_streamSize));
if (newPosition) { newPosition->QuadPart = m_relativePosition; }
m_currentBlock = m_blockStreams.begin();
return S_OK;
}
@ -100,25 +102,32 @@ namespace xPlat {
if (m_relativePosition < m_streamSize)
{
std::uint32_t bytesToRead = std::min(static_cast<std::uint32_t>(countBytes), static_cast<std::uint32_t>(m_streamSize - m_relativePosition));
for (auto block = m_blockStreams.begin(); block != m_blockStreams.end() && bytesToRead > 0; block++)
while (m_currentBlock != m_blockStreams.end() && bytesToRead > 0)
{
if (block->offset <= m_relativePosition)
if ((m_currentBlock->offset + m_currentBlock->size) <= m_relativePosition)
{
LARGE_INTEGER li;
Seek(li, STREAM_SEEK_SET, nullptr);
li.QuadPart = (m_relativePosition - block->offset);
ThrowHrIfFailed(block->stream.Get()->Seek(li, STREAM_SEEK_SET, nullptr));
m_currentBlock++;
}
else if (m_currentBlock->offset <= m_relativePosition)
{
std::uint64_t positionInBlock = m_relativePosition - m_currentBlock->offset;
LARGE_INTEGER li{0};
li.QuadPart = positionInBlock;
ThrowHrIfFailed(m_currentBlock->stream->Seek(li, STREAM_SEEK_SET, nullptr));
std::uint32_t count = std::min(bytesToRead, static_cast<std::uint32_t>(block->size - (m_relativePosition - block->offset)));
std::uint32_t count = std::min(bytesToRead, static_cast<std::uint32_t>(m_currentBlock->size - positionInBlock));
ULONG actual = 0;
ThrowHrIfFailed(block->stream.Get()->Read(buffer, count, &actual));
ThrowHrIfFailed(m_currentBlock->stream->Read(buffer, count, &actual));
buffer = static_cast<std::uint8_t*>(buffer) + actual;
m_relativePosition += actual;
bytesToRead -= actual;
bytesRead += actual;
}
else
{
m_currentBlock = m_blockStreams.begin();
}
}
}
if (actualRead) { *actualRead = bytesRead; }
@ -126,6 +135,7 @@ namespace xPlat {
}
protected:
std::vector<BlockPlusStream>::iterator m_currentBlock;
std::vector<BlockPlusStream> m_blockStreams;
std::uint64_t m_relativePosition;
std::uint64_t m_streamSize;

Просмотреть файл

@ -7,6 +7,7 @@
#include "Exceptions.hpp"
#include "AppxPackaging.hpp"
#include "xercesc/util/XMLString.hpp"
namespace xPlat {
@ -90,7 +91,7 @@ namespace xPlat {
// Assignment operator...
ComPtr& operator=(const ComPtr& right)
{
if (m_ptr != right.m_ptr) { ComPtr(right).Swap(*this); }
if (m_ptr != right.m_ptr) { ComPtr(right).Swap(*this); }
return *this;
}
@ -167,4 +168,136 @@ namespace xPlat {
std::atomic<std::uint32_t> m_ref;
ComClass() : m_ref(1) {}
};
template<class T>
class XercesPtr
{
public:
XercesPtr() : m_ptr(nullptr) {}
XercesPtr(T* p) : m_ptr(p) {}
// move ctor
XercesPtr(XercesPtr &&right) : m_ptr(nullptr)
{
if (this != reinterpret_cast<XercesPtr*>(&reinterpret_cast<std::int8_t&>(right)))
{ Swap(right);
}
}
virtual ~XercesPtr() { InternalRelease(); }
void InternalRelease()
{
T* temp = m_ptr;
if (temp)
{
m_ptr = nullptr;
temp->release();
}
}
XercesPtr& operator=(XercesPtr&& right)
{ XercesPtr(std::move(right)).Swap(*this);
return *this;
}
T* operator->() const { return m_ptr; }
T* Get() const { return m_ptr; }
protected:
inline void Swap(XercesPtr& right ) { std::swap(m_ptr, right.m_ptr); }
T* m_ptr = nullptr;
};
class XercesCharPtr
{
public:
XercesCharPtr(char* c) : m_ptr(c) {};
~XercesCharPtr() { InternalRelease(); }
// move ctor
XercesCharPtr(XercesCharPtr &&right) : m_ptr(nullptr)
{
if (this != reinterpret_cast<XercesCharPtr*>(&reinterpret_cast<std::int8_t&>(right)))
{ Swap(right);
}
}
void InternalRelease()
{ XERCES_CPP_NAMESPACE::XMLString::release(&m_ptr);
m_ptr = nullptr;
}
XercesCharPtr& operator=(XercesCharPtr&& right)
{ XercesCharPtr(std::move(right)).Swap(*this);
return *this;
}
char* operator->() const { return m_ptr; }
char* Get() const { return m_ptr; }
protected:
inline void Swap(XercesCharPtr& right ) { std::swap(m_ptr, right.m_ptr); }
char* m_ptr = nullptr;
};
class XercesXMLChPtr
{
public:
XercesXMLChPtr(XMLCh* c) : m_ptr(c) {}
~XercesXMLChPtr() { InternalRelease(); }
// move ctor
XercesXMLChPtr(XercesXMLChPtr &&right) : m_ptr(nullptr)
{
if (this != reinterpret_cast<XercesXMLChPtr*>(&reinterpret_cast<std::int8_t&>(right)))
{ Swap(right);
}
}
void InternalRelease()
{ XERCES_CPP_NAMESPACE::XMLString::release(&m_ptr);
m_ptr = nullptr;
}
XercesXMLChPtr& operator=(XercesXMLChPtr&& right)
{ XercesXMLChPtr(std::move(right)).Swap(*this);
return *this;
}
XMLCh* operator->() const { return m_ptr; }
XMLCh* Get() const { return m_ptr; }
protected:
inline void Swap(XercesXMLChPtr& right ) { std::swap(m_ptr, right.m_ptr); }
XMLCh* m_ptr = nullptr;
};
class XercesXMLBytePtr
{
public:
XercesXMLBytePtr(XMLByte* c) : m_ptr(c) {}
~XercesXMLBytePtr() { InternalRelease(); }
// move ctor
XercesXMLBytePtr(XercesXMLBytePtr &&right) : m_ptr(nullptr)
{
if (this != reinterpret_cast<XercesXMLBytePtr*>(&reinterpret_cast<std::int8_t&>(right)))
{ Swap(right);
}
}
void InternalRelease()
{ delete(m_ptr);
m_ptr = nullptr;
}
XercesXMLBytePtr& operator=(XercesXMLBytePtr&& right)
{ XercesXMLBytePtr(std::move(right)).Swap(*this);
return *this;
}
XMLByte* operator->() const { return m_ptr; }
XMLByte* Get() const { return m_ptr; }
protected:
inline void Swap(XercesXMLBytePtr& right ) { std::swap(m_ptr, right.m_ptr); }
XMLByte* m_ptr = nullptr;
};
}

Просмотреть файл

@ -7,10 +7,17 @@
#include <functional>
#include "AppxWindows.hpp"
#include "xercesc/util/PlatformUtils.hpp"
#include "xercesc/sax/ErrorHandler.hpp"
#include "xercesc/sax/SAXParseException.hpp"
#include "xercesc/dom/DOM.hpp"
namespace xPlat {
static const std::uint32_t ERROR_FACILITY = 0x8BAD0000; // Facility 2989
static const std::uint32_t ERROR_FACILITY = 0x8BAD0000; // Facility 2989
static const std::uint32_t XERCES_SAX_FACILITY = ERROR_FACILITY + 0x1000; // Xerces XMLException. 0x8BAD1000 + XMLException error code
static const std::uint32_t XERCES_XML_FACILITY = ERROR_FACILITY + 0x2000;
static const std::uint32_t XERCES_DOM_FACILITY = ERROR_FACILITY + 0x3000;
// defines error codes
enum class Error : std::uint32_t
@ -60,11 +67,19 @@ namespace xPlat {
AppxMissingBlockMapXML = ERROR_FACILITY + 0x0033,
AppxMissingAppxManifestXML = ERROR_FACILITY + 0x0034,
AppxDuplicateFootprintFile = ERROR_FACILITY + 0x0035,
AppxUnknownFileNameEncoding = ERROR_FACILITY + 0x0036,
// Signature errors
AppxSignatureInvalid = ERROR_FACILITY + 0x0041,
AppxCertNotTrusted = ERROR_FACILITY + 0x0042,
// Blockmap semantic errors
BlockMapSemanticError = ERROR_FACILITY + 0x0051,
// XML parsing errors
XercesWarning = XERCES_SAX_FACILITY + 0x0001,
XercesError = XERCES_SAX_FACILITY + 0x0002,
XercesFatal = XERCES_SAX_FACILITY + 0x0003,
};
// Defines a common exception type to throw in exceptional cases. DO NOT USE FOR FLOW CONTROL!
@ -96,7 +111,7 @@ namespace xPlat {
Exception(HRESULT error, const char* message) :
m_code(error),
m_message(message)
{}
{}
uint32_t Code() { return m_code; }
std::string& Message() { return m_message; }
@ -118,6 +133,36 @@ namespace xPlat {
{}
};
class ParsingException : public XERCES_CPP_NAMESPACE::ErrorHandler
{
public:
ParsingException() {};
~ParsingException() {};
void warning(const XERCES_CPP_NAMESPACE::SAXParseException& exp) override
{
// TODO: add message, line number and column
assert(false);
throw Exception(xPlat::Error::XercesWarning);
}
void error(const XERCES_CPP_NAMESPACE::SAXParseException& exp) override
{
// TODO: add message, line number and column
assert(false);
throw Exception(xPlat::Error::XercesError);
}
void fatalError(const XERCES_CPP_NAMESPACE::SAXParseException& exp) override
{
// TODO: add message, line number and column
assert(false);
throw Exception(xPlat::Error::XercesFatal);
}
void resetErrors() override {}
};
// Provides an ABI exception boundary with parameter validation
template <class Lambda>
inline HRESULT ResultOf(Lambda lambda)
@ -139,6 +184,16 @@ namespace xPlat {
{
hr = static_cast<HRESULT>(xPlat::Error::Unexpected);
}
catch (const XERCES_CPP_NAMESPACE::XMLException& e)
{
hr = static_cast<HRESULT>(xPlat::XERCES_XML_FACILITY) +
static_cast<HRESULT>(e.getCode());
}
catch (const XERCES_CPP_NAMESPACE::DOMException& e)
{
hr = static_cast<HRESULT>(xPlat::XERCES_DOM_FACILITY) +
static_cast<HRESULT>(e.code);
}
return hr;
}

Просмотреть файл

@ -16,45 +16,56 @@ namespace xPlat {
// This represents a subset of a Stream
class HashStream : public StreamBase
{
protected:
bool m_validated;
ComPtr<IStream> m_stream;
std::vector<std::uint8_t>& m_expectedHash;
std::unique_ptr<std::vector<std::uint8_t>> m_cacheBuffer;
std::uint64_t m_relativePosition;
size_t m_streamSize;
public:
HashStream(IStream* stream, const std::vector<std::uint8_t>& expectedHash) :
m_relativePosition(0)
HashStream(IStream* stream, std::vector<std::uint8_t>& expectedHash) :
m_validated(false),
m_stream(stream),
m_expectedHash(expectedHash),
m_relativePosition(0),
m_streamSize(0)
{
ULARGE_INTEGER uli;
LARGE_INTEGER li;
std::uint64_t streamSize;
li.QuadPart = 0;
ThrowHrIfFailed(stream->Seek(li, STREAM_SEEK_END, &uli));
streamSize = uli.u.LowPart;
ThrowHrIfFailed(stream->Seek(li, STREAM_SEEK_SET, &uli));
m_cacheBuffer.resize(static_cast<std::uint32_t>(streamSize));
ULONG bytesRead = 0;
ThrowHrIfFailed(stream->Read(m_cacheBuffer.data(), m_cacheBuffer.size(), &bytesRead));
ThrowErrorIfNot(xPlat::Error::AppxSignatureInvalid,
bytesRead == streamSize,
"Invalid signature");
std::vector<std::uint8_t> hash;
ThrowErrorIfNot(xPlat::Error::AppxSignatureInvalid,
xPlat::SHA256::ComputeHash(m_cacheBuffer.data(), m_cacheBuffer.size(), hash),
"Invalid signature");
ThrowErrorIfNot(xPlat::Error::AppxSignatureInvalid,
expectedHash.size() == hash.size(),
"Signature is corrupt");
ThrowErrorIfNot(
xPlat::Error::AppxSignatureInvalid,
memcmp(expectedHash.data(), hash.data(), hash.size()) == 0,
"Signature hash doesn't match digest hash"); //TODO: better exception
ThrowHrIfFailed(m_stream->Seek(li, StreamBase::Reference::END, &uli));
ThrowHrIfFailed(m_stream->Seek(li, StreamBase::Reference::START, nullptr));
m_streamSize = static_cast<size_t>(uli.u.LowPart);
}
HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newPosition) override
void Validate()
{
if (m_validated) { return; }
// read stream into cache buffer
m_cacheBuffer = std::make_unique<std::vector<std::uint8_t>>(m_streamSize);
ULONG bytesRead = 0;
ThrowHrIfFailed(m_stream->Read(m_cacheBuffer->data(), m_cacheBuffer->size(), &bytesRead));
ThrowErrorIfNot(xPlat::Error::AppxSignatureInvalid, bytesRead == m_streamSize, "read failed");
// compute digest and compare against expected digest
std::vector<std::uint8_t> hash;
ThrowErrorIfNot(xPlat::Error::AppxSignatureInvalid,
xPlat::SHA256::ComputeHash(m_cacheBuffer->data(), m_cacheBuffer->size(), hash),
"Invalid signature");
ThrowErrorIfNot(xPlat::Error::AppxSignatureInvalid, m_expectedHash.size() == hash.size(), "Signature is corrupt");
ThrowErrorIfNot(
xPlat::Error::AppxSignatureInvalid,
memcmp(m_expectedHash.data(), hash.data(), hash.size()) == 0,
"Signature hash doesn't match digest hash"); //TODO: better exception
m_validated = true;
}
void CacheSeek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newPosition)
{
LARGE_INTEGER newPos = { 0 };
switch (origin)
@ -66,33 +77,49 @@ namespace xPlat {
m_relativePosition = move.u.LowPart;
break;
case Reference::END:
m_relativePosition = m_cacheBuffer.size();
m_relativePosition = m_streamSize;
break;
}
m_relativePosition = std::max((std::uint64_t)0, std::min(m_relativePosition, (std::uint64_t)m_cacheBuffer.size()));
m_relativePosition = std::max((std::uint64_t)0, std::min(m_relativePosition, static_cast<std::uint64_t>(m_streamSize)));
if (newPosition) { newPosition->QuadPart = (std::uint64_t)m_relativePosition; }
return S_OK;
}
HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newPosition) override
{
return ResultOf([&]{
if (m_cacheBuffer.get() == nullptr)
{ ThrowHrIfFailed(m_stream->Seek(move, origin, newPosition));
}
// always call into cache seek to keep cache state aligned with the underlying stream state.
CacheSeek(move, origin, newPosition);
});
}
void CacheRead(void* buffer, ULONG countBytes, ULONG* actualRead)
{
ThrowErrorIf(Error::Stg_E_Invalidpointer, (buffer == nullptr), "bad input");
ULONG bytesToRead = std::min((std::uint32_t)countBytes, static_cast<std::uint32_t>((std::uint64_t)m_cacheBuffer->size() - m_relativePosition));
if (bytesToRead)
{
memcpy(buffer, reinterpret_cast<BYTE*>(m_cacheBuffer->data()) + m_relativePosition, bytesToRead);
}
m_relativePosition += bytesToRead;
if (m_streamSize == m_relativePosition) { m_cacheBuffer = nullptr; }
if (actualRead) { *actualRead = bytesToRead; }
}
HRESULT STDMETHODCALLTYPE Read(void* buffer, ULONG countBytes, ULONG* actualRead) override
{
HRESULT hr = static_cast<HRESULT>(Error::Stg_E_Invalidpointer);
if (buffer)
{
ULONG bytesToRead = std::min((std::uint32_t)countBytes, static_cast<std::uint32_t>((std::uint64_t)m_cacheBuffer.size() - m_relativePosition));
if (bytesToRead)
{
memcpy(buffer, reinterpret_cast<BYTE*>(m_cacheBuffer.data()) + m_relativePosition, bytesToRead);
return ResultOf([&]{
Validate();
if (m_cacheBuffer.get() == nullptr)
{ ThrowHrIfFailed(m_stream->Read(buffer, countBytes, actualRead));
}
m_relativePosition += bytesToRead;
if (actualRead) { *actualRead = bytesToRead; }
hr = (countBytes == bytesToRead) ? S_OK : S_FALSE;
}
return hr;
else
{ CacheRead(buffer, countBytes, actualRead);
}
});
}
protected:
std::vector<std::uint8_t> m_cacheBuffer;
std::uint64_t m_relativePosition;
};
}

Просмотреть файл

@ -23,7 +23,7 @@ namespace xPlat {
{
public:
InflateStream(IStream* stream, std::uint64_t uncompressedSize);
virtual ~InflateStream();
~InflateStream();
HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newPosition) override;
HRESULT STDMETHODCALLTYPE Read(void* buffer, ULONG countBytes, ULONG* bytesRead) override;

Просмотреть файл

@ -38,10 +38,6 @@ class IStorageObject : public IUnknown
#endif
{
public:
#ifdef WIN32
virtual ~IStorageObject() {}
#endif
virtual std::string GetPathSeparator() = 0;
// Obtains a vector of UTF-8 formatted string names contained in the storage object

Просмотреть файл

@ -4,22 +4,24 @@
#include "Exceptions.hpp"
#include "StreamBase.hpp"
#include "ComHelper.hpp"
namespace xPlat {
// internal interface
EXTERN_C const IID IID_IVerifierObject;
#ifndef WIN32
MIDL_INTERFACE("cb0a105c-3a6c-4e48-9351-377c4dccd890")
interface IVerifierObject : public IUnknown
#else
#include "Unknwn.h"
#include "Objidl.h"
class IVerifierObject : public IUnknown
#endif
// An internal interface for objects that are used to verify structured data represented by an underlying stream.
{
public:
virtual bool HasStream() = 0;
virtual xPlat::ComPtr<IStream> GetStream() = 0;
virtual xPlat::ComPtr<IStream> GetValidationStream(const std::string& part, IStream* stream) = 0;
};
// A base class for objects that are used to verify structured data represented by an underlying stream.
class VerifierObject
{
public:
VerifierObject(IStream* stream) : m_stream(stream) {}
inline bool HasStream() { return m_stream.Get() != nullptr; }
inline IStream* GetStream() { return m_stream.Get(); }
virtual IStream* GetValidationStream(const std::string& part, IStream* stream) = 0;
protected:
ComPtr<IStream> m_stream;
};
} // namespace xPlat
SpecializeUuidOfImpl(IVerifierObject);

Просмотреть файл

@ -8,38 +8,114 @@
#include "VerifierObject.hpp"
// Mandatory for using any feature of Xerces.
#include "xercesc/util/PlatformUtils.hpp"
#include "xercesc/dom/DOM.hpp"
#include "xercesc/framework/MemBufInputSource.hpp"
#include "xercesc/framework/XMLGrammarPoolImpl.hpp"
#include "xercesc/parsers/AbstractDOMParser.hpp"
#include "xercesc/parsers/XercesDOMParser.hpp"
#include "xercesc/util/PlatformUtils.hpp"
#include "xercesc/util/XMLString.hpp"
// internal interface
EXTERN_C const IID IID_IXmlObject;
#ifndef WIN32
MIDL_INTERFACE("0e7a446e-baf7-44c1-b38a-216bfa18a1a8")
interface IXmlObject : public IUnknown
#else
#include "Unknwn.h"
#include "Objidl.h"
class IXmlObject : public IUnknown
#endif
// An internal interface for XML
{
public:
virtual void Write() = 0;
virtual XERCES_CPP_NAMESPACE::DOMDocument* Document() = 0;
};
SpecializeUuidOfImpl(IXmlObject);
namespace xPlat {
// XML de-serialization happens during construction, of this object.
// XML serialization happens through the Write method
class XmlObject : public VerifierObject
class XmlObject : public ComClass<XmlObject, IXmlObject, IVerifierObject>
{
public:
// TODO: Implement actual XML validation....
XmlObject(IStream* stream) : VerifierObject(stream) {}
XmlObject(ComPtr<IStream>& stream, std::map<std::string, std::string>* schemas = nullptr) : m_stream(stream)
{
// Create buffer from stream
LARGE_INTEGER start = { 0 };
ULARGE_INTEGER end = { 0 };
ThrowHrIfFailed(stream->Seek(start, StreamBase::Reference::END, &end));
ThrowHrIfFailed(stream->Seek(start, StreamBase::Reference::START, nullptr));
// TODO: Implement: Writes the contents of the DOM Document to the underlying stream.
void Write()
{
throw Exception(Error::NotImplemented);
std::uint32_t streamSize = end.u.LowPart;
std::vector<std::uint8_t> buffer(streamSize);
ULONG actualRead = 0;
ThrowHrIfFailed(stream->Read(buffer.data(), streamSize, &actualRead));
ThrowErrorIf(Error::FileRead, (actualRead != streamSize), "read error");
// move the underlying stream back to the begginning.
ThrowHrIfFailed(stream->Seek(start, StreamBase::Reference::START, nullptr));
std::unique_ptr<XERCES_CPP_NAMESPACE::MemBufInputSource> source = std::make_unique<XERCES_CPP_NAMESPACE::MemBufInputSource>(
reinterpret_cast<const XMLByte*>(&buffer[0]), actualRead, "XML File");
// Create parser and grammar pool
auto grammarPool = std::make_unique<XERCES_CPP_NAMESPACE::XMLGrammarPoolImpl>(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager);
m_parser = std::make_unique<XERCES_CPP_NAMESPACE::XercesDOMParser>(nullptr, XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager, grammarPool.get());
bool HasSchemas = ((schemas != nullptr) && (schemas->begin() != schemas->end()));
m_parser->setValidationScheme(HasSchemas ?
XERCES_CPP_NAMESPACE::AbstractDOMParser::ValSchemes::Val_Always :
XERCES_CPP_NAMESPACE::AbstractDOMParser::ValSchemes::Val_Never
);
m_parser->cacheGrammarFromParse(HasSchemas);
m_parser->setDoSchema(HasSchemas);
m_parser->setDoNamespaces(HasSchemas);
m_parser->setHandleMultipleImports(HasSchemas); // TODO: do we need to handle the case where there aren't multiple schemas with the same namespace?
m_parser->setValidationSchemaFullChecking(HasSchemas);
if (HasSchemas)
{ // Disable DTD and prevent XXE attacks. See https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#libxerces-c for additional details.
m_parser->setIgnoreCachedDTD(true);
m_parser->setSkipDTDValidation(true);
m_parser->setCreateEntityReferenceNodes(false);
}
// Add schemas
if (schemas != nullptr)
{ for (auto index = schemas->begin(); index != schemas->end(); index++)
{ auto item = std::make_unique<XERCES_CPP_NAMESPACE::MemBufInputSource>(
reinterpret_cast<const XMLByte*>(index->second.c_str()),
index->second.length(),
index->first.c_str());
m_parser->loadGrammar(*item, XERCES_CPP_NAMESPACE::Grammar::GrammarType::SchemaGrammarType, true);
}
}
// Set the error handler for the parser
auto errorHandler = std::make_unique<ParsingException>();
m_parser->setErrorHandler(errorHandler.get());
m_parser->parse(*source);
}
// Returns a shared pointer to the DOMDocument representing the contents of this stream
std::shared_ptr<XERCES_CPP_NAMESPACE::DOMDocument> Document() { return m_DOMDocument;}
// IXmlObject
void Write() override { throw Exception(Error::NotImplemented); }
XERCES_CPP_NAMESPACE::DOMDocument* Document() override { return m_parser->getDocument();}
IStream* GetValidationStream(const std::string& part, IStream* stream) override
// IVerifierObject
bool HasStream() override { return m_stream.Get() != nullptr; }
xPlat::ComPtr<IStream> GetStream() override { return m_stream; }
xPlat::ComPtr<IStream> GetValidationStream(const std::string& part, IStream* stream) override
{
throw Exception(Error::NotSupported);
}
protected:
std::unique_ptr<XERCES_CPP_NAMESPACE::XercesDOMParser> m_parser;
ComPtr<IStream> m_stream;
std::shared_ptr<XERCES_CPP_NAMESPACE::DOMImplementation> m_DOMImplementation;
std::shared_ptr<XERCES_CPP_NAMESPACE::DOMDocument> m_DOMDocument;
};
} // namespace xPlat
} // namespace xPlat

Просмотреть файл

@ -45,9 +45,9 @@ namespace xPlat {
inline bool IsCompressed() { return m_isCompressed; }
protected:
ComPtr<IxPlatFactory> m_factory;
std::string m_name;
std::string m_contentType;
bool m_isCompressed = false;
IxPlatFactory* m_factory;
std::string m_name;
std::string m_contentType;
bool m_isCompressed = false;
};
}

Просмотреть файл

@ -26,7 +26,7 @@ namespace xPlat {
void CommitChanges() override;
protected:
ComPtr<IxPlatFactory> m_factory;
IxPlatFactory* m_factory;
ComPtr<IStream> m_stream;
std::map<std::string, ComPtr<IStream>> m_streams;
};//class ZipObject

Просмотреть файл

@ -1,123 +1,213 @@
#include "AppxBlockMapObject.hpp"
#include "AppxBlockMapSchemas.hpp"
#include "xercesc/framework/MemBufInputSource.hpp"
#include "xercesc/framework/XMLGrammarPoolImpl.hpp"
#include "xercesc/parsers/XercesDOMParser.hpp"
#include "xercesc/util/Base64.hpp"
#include "xercesc/util/XMLString.hpp"
#include <algorithm>
#include <iterator>
#include "BlockMapStream.hpp"
/* Example XML:
<?xml version="1.0" encoding="UTF-8"?>
<BlockMap HashMethod="http://www.w3.org/2001/04/xmlenc#sha256" xmlns="http://schemas.microsoft.com/appx/2010/blockmap">
...
<File Name="assets\icon150.png" Size="0" LfhSize="48"/>
...
<File LfhSize="65" Size="187761" Name="Assets\video_offline_demo_page1.jpg">
<Block Hash="NQL/PSheCSB3yZzKyZ6nHbsfzJt1EZJxOXLllMVvtEI="/>
<Block Hash="2Udxo8Nwie7rvy4g0T5yfz9qccDNMVWh2mfMD1YCQao="/>
<Block Hash="MmXnlptT/u+ilMKCIriWR49k99rBqwXKO3s60zGwZKg="/>
</File>
...
<File LfhSize="57" Size="47352" Name="Resources\Fonts\SegMVR2.ttf">
<Block Size="27777" Hash="LGaGnk3EtFymriM9cRmeX7eZI+b2hpwOIlJIXdeE1ik="/>
</File>
...
</BlockMap>
*/
XERCES_CPP_NAMESPACE_USE
namespace xPlat {
// IAppxBlockMapBlock
HRESULT STDMETHODCALLTYPE AppxBlockMapBlock::GetHash(UINT32* bufferSize, BYTE** buffer)
static std::uint32_t GetLocalFileHeaderSize(XERCES_CPP_NAMESPACE::DOMElement* element)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
XercesXMLChPtr nameAttr(XERCES_CPP_NAMESPACE::XMLString::transcode("LfhSize"));
XercesCharPtr name(XERCES_CPP_NAMESPACE::XMLString::transcode(element->getAttribute(nameAttr.Get())));
std::string attributeValue(name.Get());
bool hasValue = !attributeValue.empty();
std::uint32_t value = 0;
if (hasValue) { value = static_cast<std::uint32_t>(std::stoul(attributeValue)); }
return value;
}
static std::string GetName(XERCES_CPP_NAMESPACE::DOMElement* element)
{
XercesXMLChPtr nameAttr(XERCES_CPP_NAMESPACE::XMLString::transcode("Name"));
XercesCharPtr name(XERCES_CPP_NAMESPACE::XMLString::transcode(element->getAttribute(nameAttr.Get())));
return std::string (name.Get());
}
static std::uint64_t GetSize(XERCES_CPP_NAMESPACE::DOMElement* element)
{
XercesXMLChPtr nameAttr(XERCES_CPP_NAMESPACE::XMLString::transcode("Size"));
XercesCharPtr name(XERCES_CPP_NAMESPACE::XMLString::transcode(element->getAttribute(nameAttr.Get())));
std::string attributeValue(name.Get());
std::uint64_t value = (BLOCKMAP_BLOCK_SIZE); // size of block not always specified, in which case, it's 64k
if (!attributeValue.empty())
{ value = static_cast<std::uint64_t>(std::stoull(attributeValue));
}
return value;
}
static std::vector<std::uint8_t> GetDigestData(XERCES_CPP_NAMESPACE::DOMElement* element)
{
XercesXMLChPtr nameAttr(XMLString::transcode("Hash"));
XMLSize_t len = 0;
XercesXMLBytePtr decodedData(XERCES_CPP_NAMESPACE::Base64::decodeToXMLByte(
element->getAttribute(nameAttr.Get()),
&len));
std::vector<std::uint8_t> result(len);
for(XMLSize_t index=0; index < len; index++)
{ result[index] = static_cast<std::uint8_t>(decodedData.Get()[index]);
}
return result;
}
static Block GetBlock(XERCES_CPP_NAMESPACE::DOMElement* element)
{
Block result {0};
result.compressedSize = GetSize(element);
result.hash = GetDigestData(element);
return result;
}
AppxBlockMapObject::AppxBlockMapObject(IxPlatFactory* factory, ComPtr<IStream>& stream) : m_factory(factory), m_stream(stream)
{
auto dom = ComPtr<IXmlObject>::Make<XmlObject>(stream, &blockMapSchema);
// Create xPath query over blockmap file.
XercesXMLChPtr fileXPath(XMLString::transcode("/BlockMap/File"));
XercesPtr<DOMXPathNSResolver> resolver(dom->Document()->createNSResolver(dom->Document()->getDocumentElement()));
XercesPtr<DOMXPathResult> fileResult(dom->Document()->evaluate(
fileXPath.Get(),
dom->Document()->getDocumentElement(),
resolver.Get(),
DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
nullptr));
// Create IAppxBlockMapFiles
for (XMLSize_t i = 0; i < fileResult->getSnapshotLength(); i++)
{
fileResult->snapshotItem(i);
auto fileNode = static_cast<DOMElement*>(fileResult->getNodeValue());
// Get blocks elements
XercesXMLChPtr blockXPath(XMLString::transcode("./Block"));
XercesPtr<DOMXPathResult> blockResult = dom->Document()->evaluate(
blockXPath.Get(),
fileNode,
resolver.Get(),
DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
nullptr);
// get all the blocks for the file.
std::vector<Block> blocks(blockResult->getSnapshotLength());
for (XMLSize_t j = 0; j < blockResult->getSnapshotLength(); j++)
{
blockResult->snapshotItem(j);
auto blockNode = static_cast<DOMElement*>(blockResult->getNodeValue());
blocks[j] = GetBlock(blockNode);
}
auto name = GetName(fileNode);
auto existing = m_blockMap.find(name);
ThrowErrorIf(Error::BlockMapSemanticError, (existing != m_blockMap.end()), "duplicate file name specified.");
m_blockMap.insert(std::make_pair(name, std::move(blocks)));
m_blockMapfiles.insert(std::make_pair(name,
ComPtr<IAppxBlockMapFile>::Make<AppxBlockMapFile>(
factory,
&(m_blockMap[name]),
GetLocalFileHeaderSize(fileNode),
name,
GetSize(fileNode))));
}
}
xPlat::ComPtr<IStream> AppxBlockMapObject::GetValidationStream(const std::string& part, IStream* stream)
{
ThrowErrorIf(Error::InvalidParameter, (part.empty() || stream == nullptr), "bad input");
auto item = m_blockMap.find(part);
ThrowErrorIf(Error::BlockMapSemanticError, item == m_blockMap.end(), "file not tracked by blockmap");
return ComPtr<IStream>::Make<BlockMapStream>(stream, item->second);
}
HRESULT STDMETHODCALLTYPE AppxBlockMapObject::GetFile(LPCWSTR filename, IAppxBlockMapFile **file)
{
return ResultOf([&]{
ThrowErrorIf(Error::InvalidParameter, (
filename == nullptr || *filename == '\0' || file == nullptr || *file != nullptr
), "bad pointer");
ComPtr<IStream> stream = GetFile(utf16_to_utf8(filename));
*file = stream.As<IAppxBlockMapFile>().Detach();
});
}
HRESULT STDMETHODCALLTYPE AppxBlockMapObject::GetFiles(IAppxBlockMapFilesEnumerator **enumerator)
{
return ResultOf([&]{
ThrowErrorIf(Error::InvalidParameter, (enumerator == nullptr || *enumerator != nullptr), "bad pointer");
ComPtr<IAppxBlockMapReader> self;
ThrowHrIfFailed(QueryInterface(UuidOfImpl<IAppxBlockMapReader>::iid, reinterpret_cast<void**>(&self)));
*enumerator = ComPtr<IAppxBlockMapFilesEnumerator>::Make<AppxBlockMapFilesEnumerator>(
self.Get(),
std::move(GetFileNames(FileNameOptions::All))).Detach();
});
}
HRESULT STDMETHODCALLTYPE AppxBlockMapObject::GetHashMethod(IUri **hashMethod)
{ // Ultimately, this IUri object represents the HashMethod attribute in the blockmap:
return ResultOf([&]{
// TODO: Implement...
throw Exception(Error::NotImplemented);
});
}
HRESULT STDMETHODCALLTYPE AppxBlockMapBlock::GetCompressedSize(UINT32* size)
HRESULT STDMETHODCALLTYPE AppxBlockMapObject::GetStream(IStream **blockMapStream)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
return ResultOf([&]{
ThrowErrorIf(Error::InvalidParameter, (blockMapStream == nullptr || *blockMapStream != nullptr), "bad pointer");
auto stream = GetStream();
LARGE_INTEGER li{0};
ThrowHrIfFailed(stream->Seek(li, StreamBase::Reference::START, nullptr));
*blockMapStream = stream.Detach();
});
}
// IAppxBlockMapFile
HRESULT STDMETHODCALLTYPE AppxBlockMapFile::GetBlocks(IAppxBlockMapBlocksEnumerator** blocks)
// IStorageObject methods
std::string AppxBlockMapObject::GetPathSeparator() { return "\\"; }
std::vector<std::string> AppxBlockMapObject::GetFileNames(FileNameOptions)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
std::vector<std::string> fileNames;
std::transform(
m_blockMapfiles.begin(),
m_blockMapfiles.end(),
std::back_inserter(fileNames),
[](auto keyValuePair){ return keyValuePair.first; }
);
return fileNames;
}
HRESULT STDMETHODCALLTYPE AppxBlockMapFile::GetLocalFileHeaderSize(UINT32* lfhSize)
IStream* AppxBlockMapObject::GetFile(const std::string& fileName)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
auto index = m_blockMapfiles.find(fileName);
ThrowErrorIf(Error::FileNotFound, (index == m_blockMapfiles.end()), "named file not in blockmap");
return index->second.As<IStream>().Detach();
}
HRESULT STDMETHODCALLTYPE AppxBlockMapFile::GetName(LPWSTR* name)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
}
HRESULT STDMETHODCALLTYPE AppxBlockMapFile::GetUncompressedSize(UINT64* size)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
}
HRESULT STDMETHODCALLTYPE AppxBlockMapFile::ValidateFileHash(IStream* fileStream, BOOL* isValid)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
}
// IAppxBlockMapFilesEnumerator
HRESULT STDMETHODCALLTYPE AppxBlockMapFile::GetCurrent(IAppxBlockMapFile** block)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
}
HRESULT STDMETHODCALLTYPE AppxBlockMapFile::GetHasCurrent(BOOL* hasCurrent)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
}
HRESULT STDMETHODCALLTYPE AppxBlockMapFile::MoveNext(BOOL* hasNext)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
}
AppxBlockMapObject::AppxBlockMapObject(IStream* stream) : VerifierObject(stream)
{
// TODO: Implement
}
IStream* AppxBlockMapObject::GetValidationStream(const std::string& part, IStream* stream)
{
// TODO: Implement -- for now, just pass through.
return stream;
}
// IAppxBlockMapBlocksEnumerator
HRESULT STDMETHODCALLTYPE AppxBlockMapObject::GetCurrent(IAppxBlockMapBlock** block)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
}
HRESULT STDMETHODCALLTYPE AppxBlockMapObject::GetHasCurrent(BOOL* hasCurrent)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
}
HRESULT STDMETHODCALLTYPE AppxBlockMapObject::MoveNext(BOOL* hasNext)
{
return xPlat::ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
});
}
void AppxBlockMapObject::RemoveFile(const std::string& ) { throw Exception(Error::NotImplemented); }
IStream* AppxBlockMapObject::OpenFile(const std::string& ,xPlat::FileStream::Mode) { throw Exception(Error::NotImplemented); }
void AppxBlockMapObject::CommitChanges() { throw Exception(Error::NotImplemented); }
}

Просмотреть файл

@ -23,7 +23,7 @@ namespace xPlat {
ComPtr<IxPlatFactory> self;
ThrowHrIfFailed(QueryInterface(UuidOfImpl<IxPlatFactory>::iid, reinterpret_cast<void**>(&self)));
auto zip = ComPtr<IStorageObject>::Make<ZipObject>(self.Get(), inputStream);
auto result = ComPtr<IAppxPackageReader>::Make<AppxPackageObject>(m_validationOptions, zip.Get());
auto result = ComPtr<IAppxPackageReader>::Make<AppxPackageObject>(self.Get(), m_validationOptions, zip.Get());
*packageReader = result.Detach();
});
}
@ -43,26 +43,46 @@ namespace xPlat {
IAppxBlockMapReader** blockMapReader)
{
return ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
ThrowErrorIf(Error::InvalidParameter, (
inputStream == nullptr ||
blockMapReader == nullptr ||
*blockMapReader != nullptr
),"bad pointer.");
ComPtr<IxPlatFactory> self;
ThrowHrIfFailed(QueryInterface(UuidOfImpl<IxPlatFactory>::iid, reinterpret_cast<void**>(&self)));
ComPtr<IStream> stream(inputStream);
*blockMapReader = ComPtr<IAppxBlockMapReader>::Make<AppxBlockMapObject>(self.Get(), stream).Detach();
});
}
HRESULT STDMETHODCALLTYPE AppxFactory::CreateValidatedBlockMapReader (
IStream* blockMapStream,
IStream* inputStream,
LPCWSTR signatureFileName,
IAppxBlockMapReader** blockMapReader)
{
return ResultOf([&]() {
// TODO: Implement
throw Exception(Error::NotImplemented);
ThrowErrorIf(Error::InvalidParameter, (
inputStream == nullptr ||
signatureFileName == nullptr ||
*signatureFileName == '\0' ||
blockMapReader == nullptr ||
*blockMapReader != nullptr
),"bad pointer.");
ComPtr<IxPlatFactory> self;
ThrowHrIfFailed(QueryInterface(UuidOfImpl<IxPlatFactory>::iid, reinterpret_cast<void**>(&self)));
auto stream = ComPtr<IStream>::Make<FileStream>(utf16_to_utf8(signatureFileName), FileStream::Mode::READ);
auto signature = ComPtr<IVerifierObject>::Make<AppxSignatureObject>(self->GetValidationOptions(), stream.Get());
auto validatedStream = signature->GetValidationStream("AppxBlockMap.xml", inputStream);
*blockMapReader = ComPtr<IAppxBlockMapReader>::Make<AppxBlockMapObject>(self.Get(), validatedStream).Detach();
});
}
HRESULT AppxFactory::MarshalOutString(std::string& internal, LPWSTR *result)
{
return ResultOf([&]() {
ThrowErrorIf(Error::InvalidParameter, (result == nullptr || *result != nullptr), "bad pointer" );
ThrowErrorIf(Error::InvalidParameter, (result == nullptr || *result != nullptr), "bad pointer" );
auto intermediate = utf8_to_utf16(internal);
std::size_t countBytes = sizeof(wchar_t)*(internal.size()+1);
*result = reinterpret_cast<LPWSTR>(m_memalloc(countBytes));
@ -74,4 +94,17 @@ namespace xPlat {
});
}
HRESULT AppxFactory::MarshalOutBytes(std::vector<std::uint8_t>& data, UINT32* size, BYTE** buffer)
{
return ResultOf([&]{
ThrowErrorIf(Error::InvalidParameter, (size==nullptr || buffer == nullptr || *buffer != nullptr), "Bad pointer");
*size = static_cast<UINT32>(data.size());
*buffer = reinterpret_cast<BYTE*>(m_memalloc(data.size()));
ThrowErrorIfNot(Error::OutOfMemory, (*buffer), "Allocation failed");
std::memcpy(reinterpret_cast<void*>(*buffer),
reinterpret_cast<void*>(data.data()),
data.size());
});
}
} // namespace xPlat

Просмотреть файл

@ -30,6 +30,83 @@ namespace xPlat {
{APPX_FOOTPRINT_FILE_TYPE_CODEINTEGRITY, CODEINTEGRITY_CAT},
};
static const std::uint8_t PercentangeEncodingTableSize = 0x5E;
static const std::vector<std::string> PercentangeEncoding = {
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"%20", "%21", "", "%23", "%24", "%25", "%26", "%27", // [space] ! # $ % & '
"%28", "%29", "", "%2B", "%2C", "", "", "", // ( ) + ,
"", "", "", "", "", "", "", "",
"", "", "", "%3B", "", "%3D", "", "", // ; =
"%40", "", "", "", "", "", "", "", // @
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "%5B", "", "%5D" // [ ]
};
static const std::map<std::string, char> EncodingToChar =
{
{"20", ' '}, {"21", '!'}, {"23", '#'}, {"24", '$'},
{"25", '%'}, {"26", '&'}, {"27", '\''}, {"28", '('},
{"29", ')'}, {"25", '+'}, {"2B", '%'}, {"2C", ','},
{"3B", ';'}, {"3D", '='}, {"40", '@'}, {"5B", '['},
{"5D", ']'}
};
static std::string EncodeFileName(std::string fileName)
{
std::string result;
for (std::uint32_t position = 0; position < fileName.length(); ++position)
{
std::uint8_t index = static_cast<std::uint8_t>(fileName[position]);
if(fileName[position] < PercentangeEncodingTableSize && index < PercentangeEncoding.size() && !PercentangeEncoding[index].empty())
{
result += PercentangeEncoding[index];
}
else if (fileName[position] == '\\') // Remove Windows file name
{
result += '/';
}
else
{
result += fileName[position];
}
}
return result;
}
static std::string DecodeFileName(std::string fileName)
{
std::string result;
for (std::uint32_t i = 0; i < fileName.length(); ++i)
{
if(fileName[i] == '%')
{
auto found = EncodingToChar.find(fileName.substr(i+1, 2));
if (found != EncodingToChar.end())
{
result += found->second;
}
else
{
throw Exception(Error::AppxUnknownFileNameEncoding, fileName);
}
i += 2;
}
else if (fileName[i] == '/') // Windows file name
{
result += '\\';
}
else
{
result += fileName[i];
}
}
return result;
}
AppxPackageId::AppxPackageId(
const std::string& name,
const std::string& version,
@ -41,18 +118,21 @@ namespace xPlat {
// TODO: Implement validation?
}
AppxManifestObject::AppxManifestObject(IStream* stream) : VerifierObject(stream)
AppxManifestObject::AppxManifestObject(ComPtr<IStream>& stream) : m_stream(stream)
{
// TODO: Implement
}
AppxPackageObject::AppxPackageObject(APPX_VALIDATION_OPTION validation, IStorageObject* container) :
AppxPackageObject::AppxPackageObject(IxPlatFactory* factory, APPX_VALIDATION_OPTION validation, IStorageObject* container) :
m_factory(factory),
m_validation(validation),
m_container(container)
{
// 1. Get the appx signature from the container and parse it
// TODO: pass validation flags and other necessary goodness through.
m_appxSignature = std::make_unique<AppxSignatureObject>(validation, m_container->GetFile(APPXSIGNATURE_P7X));
m_appxSignature = ComPtr<IVerifierObject>::Make<AppxSignatureObject>(validation,
((validation & APPX_VALIDATION_OPTION_SKIPSIGNATURE) == 0) ? m_container->GetFile(APPXSIGNATURE_P7X) : nullptr
);
if ((validation & APPX_VALIDATION_OPTION_SKIPSIGNATURE) == 0)
{ ThrowErrorIfNot(Error::AppxMissingSignatureP7X, (m_appxSignature->HasStream()), "AppxSignature.p7x not in archive!");
@ -60,24 +140,24 @@ namespace xPlat {
// 2. Get content type using signature object for validation
// TODO: switch underlying type of m_contentType to something more specific.
m_contentType = std::make_unique<XmlObject>(m_appxSignature->GetValidationStream(
CONTENT_TYPES_XML, m_container->GetFile(CONTENT_TYPES_XML)));
auto temp = m_appxSignature->GetValidationStream(CONTENT_TYPES_XML, m_container->GetFile(CONTENT_TYPES_XML));
m_contentType = ComPtr<IVerifierObject>::Make<XmlObject>(temp);
ThrowErrorIfNot(Error::AppxMissingContentTypesXML, (m_contentType->HasStream()), "[Content_Types].xml not in archive!");
// 3. Get blockmap object using signature object for validation
m_appxBlockMap = std::make_unique<AppxBlockMapObject>(m_appxSignature->GetValidationStream(
APPXBLOCKMAP_XML, m_container->GetFile(APPXBLOCKMAP_XML)));
temp = m_appxSignature->GetValidationStream(APPXBLOCKMAP_XML, m_container->GetFile(APPXBLOCKMAP_XML));
m_appxBlockMap = ComPtr<IVerifierObject>::Make<AppxBlockMapObject>(factory, temp);
ThrowErrorIfNot(Error::AppxMissingBlockMapXML, (m_appxBlockMap->HasStream()), "AppxBlockMap.xml not in archive!");
// 4. Get manifest object using blockmap object for validation
// TODO: pass validation flags and other necessary goodness through.
m_appxManifest = std::make_unique<AppxManifestObject>(m_appxBlockMap->GetValidationStream(
APPXMANIFEST_XML, m_container->GetFile(APPXMANIFEST_XML)));
temp = m_appxBlockMap->GetValidationStream(APPXMANIFEST_XML, m_container->GetFile(APPXMANIFEST_XML));
m_appxManifest = ComPtr<IVerifierObject>::Make<AppxManifestObject>(temp);
ThrowErrorIfNot(Error::AppxMissingAppxManifestXML, (m_appxBlockMap->HasStream()), "AppxManifest.xml not in archive!");
struct Config
{
using lambda = std::function<IStream*()>;
using lambda = std::function<xPlat::ComPtr<IStream>()>;
Config(lambda f) : GetValidationStream(f) {}
lambda GetValidationStream;
};
@ -85,35 +165,36 @@ namespace xPlat {
std::map<std::string, Config> footPrintFileNames = {
{ APPXBLOCKMAP_XML, Config([&](){ m_footprintFiles.push_back(APPXBLOCKMAP_XML); return m_appxBlockMap->GetStream();}) },
{ APPXMANIFEST_XML, Config([&](){ m_footprintFiles.push_back(APPXMANIFEST_XML); return m_appxManifest->GetStream();}) },
{ APPXSIGNATURE_P7X, Config([&](){ if (m_appxSignature->GetStream()){m_footprintFiles.push_back(APPXSIGNATURE_P7X);} return m_appxSignature->GetStream();}) },
{ APPXSIGNATURE_P7X, Config([&](){ if (m_appxSignature->GetStream().Get()){m_footprintFiles.push_back(APPXSIGNATURE_P7X);} return m_appxSignature->GetStream();}) },
{ CODEINTEGRITY_CAT, Config([&](){ m_footprintFiles.push_back(CODEINTEGRITY_CAT); return m_appxSignature->GetValidationStream(CODEINTEGRITY_CAT, std::move(m_container->GetFile(CODEINTEGRITY_CAT)));}) },
{ CONTENT_TYPES_XML, Config([&]()->IStream*{ return nullptr;}) }, // content types is never implicitly unpacked
};
// 5. Ensure that the stream collection contains streams wired up for their appropriate validation
// and partition the container's file names into footprint and payload files.
for (const auto& fileName : m_container->GetFileNames(FileNameOptions::All))
{
ComPtr<IStream> stream;
auto footPrintFile = footPrintFileNames.find(fileName);
// and partition the container's file names into footprint and payload files. First by going through
// the footprint files, and then by going through the payload files.
auto filesToProcess = m_container->GetFileNames(FileNameOptions::All);
for (const auto& fileName : m_container->GetFileNames(FileNameOptions::FootPrintOnly))
{ auto footPrintFile = footPrintFileNames.find(fileName);
if (footPrintFile != footPrintFileNames.end())
{
stream = footPrintFile->second.GetValidationStream();
}
else
{
m_payloadFiles.push_back(fileName);
stream = m_appxBlockMap->GetValidationStream(fileName, m_container->GetFile(fileName));
}
if (stream.Get() != nullptr)
{
LARGE_INTEGER pos = {0};
ThrowHrIfFailed(stream->Seek(pos, StreamBase::Reference::START, nullptr));
m_streams[fileName] = stream.Get();
{ m_streams[fileName] = footPrintFile->second.GetValidationStream();
filesToProcess.erase(std::remove(filesToProcess.begin(), filesToProcess.end(), fileName), filesToProcess.end());
}
}
auto blockMapStorage = m_appxBlockMap.As<IStorageObject>();
for (const auto& fileName : blockMapStorage->GetFileNames(FileNameOptions::PayloadOnly))
{ auto footPrintFile = footPrintFileNames.find(fileName);
if (footPrintFile == footPrintFileNames.end())
{ std::string containerFileName = EncodeFileName(fileName);
m_payloadFiles.push_back(containerFileName);
m_streams[containerFileName] = m_appxBlockMap->GetValidationStream(fileName, m_container->GetFile(containerFileName));
filesToProcess.erase(std::remove(filesToProcess.begin(), filesToProcess.end(), containerFileName), filesToProcess.end());
}
}
// If the map is not empty, there's a file in the container that didn't go to the footprint or payload
// files. (eg. payload file missing in the AppxBlockMap.xml)
ThrowErrorIfNot(Error::BlockMapSemanticError, (filesToProcess.empty()), "Package not valid!");
}
void AppxPackageObject::Pack(APPX_PACKUNPACK_OPTION options, const std::string& certFile, IStorageObject* from)
@ -130,7 +211,8 @@ namespace xPlat {
std::string targetName;
if (options & APPX_PACKUNPACK_OPTION_CREATEPACKAGESUBFOLDER)
{
targetName = GetAppxManifest()->GetPackageFullName() + to->GetPathSeparator() + fileName;
throw Exception(Error::NotImplemented);
//targetName = GetAppxManifest()->GetPackageFullName() + to->GetPathSeparator() + fileName;
}
else
{

Просмотреть файл

@ -93,9 +93,11 @@ MIDL_DEFINE_GUID(IID, IID_IAppxEncryptedBundleWriter2,0xE644BE82,0xF0FA,0x42B8,0
MIDL_DEFINE_GUID(IID, IID_IAppxPackageEditor,0xE2ADB6DC,0x5E71,0x4416,0x86,0xB6,0x86,0xE5,0xF5,0x29,0x1A,0x6B);
// internal interfaces.
MIDL_DEFINE_GUID(IID, IID_IAppxPackage,0x51B2C456,0xAAA9,0x46D6,0x8E,0xC9,0x29,0x82,0x20,0x55,0x91,0x89);
MIDL_DEFINE_GUID(IID, IID_IStorageObject,0xEC25B96E,0x0DB1,0x4483,0xBD,0xB1,0xCA,0xB1,0x10,0x9C,0xB7,0x41);
MIDL_DEFINE_GUID(IID, IID_IxPlatFactory, 0x1f850db4,0x32b8,0x4db6,0x8b,0xf4,0x5a,0x89,0x7e,0xb6,0x11,0xf1);
MIDL_DEFINE_GUID(IID, IID_IAppxPackage, 0x51B2C456,0xAAA9,0x46D6,0x8E,0xC9,0x29,0x82,0x20,0x55,0x91,0x89);
MIDL_DEFINE_GUID(IID, IID_IStorageObject, 0xEC25B96E,0x0DB1,0x4483,0xBD,0xB1,0xCA,0xB1,0x10,0x9C,0xB7,0x41);
MIDL_DEFINE_GUID(IID, IID_IxPlatFactory, 0x1f850db4,0x32b8,0x4db6,0x8b,0xf4,0x5a,0x89,0x7e,0xb6,0x11,0xf1);
MIDL_DEFINE_GUID(IID, IID_IVerifierObject, 0xcb0a105c,0x3a6c,0x4e48,0x93,0x51,0x37,0x7c,0x4d,0xcc,0xd8,0x90);
MIDL_DEFINE_GUID(IID, IID_IXmlObject, 0x0e7a446e,0xbaf7,0x44c1,0xb3,0x8a,0x21,0x6b,0xfa,0x18,0xa1,0xa8);
#undef MIDL_DEFINE_GUID
}

Просмотреть файл

@ -18,7 +18,7 @@
namespace xPlat {
AppxSignatureObject::AppxSignatureObject(APPX_VALIDATION_OPTION validationOptions, IStream* stream) :
VerifierObject(stream),
m_stream(stream),
m_validationOptions(validationOptions)
{
m_hasDigests = SignatureValidator::Validate(validationOptions, stream, m_digests, m_signatureOrigin);
@ -30,28 +30,22 @@ AppxSignatureObject::AppxSignatureObject(APPX_VALIDATION_OPTION validationOption
}
}
IStream* AppxSignatureObject::GetValidationStream(const std::string& part, IStream* stream)
xPlat::ComPtr<IStream> AppxSignatureObject::GetValidationStream(const std::string& part, IStream* stream)
{
if (m_hasDigests)
{
if (part == std::string("AppxBlockMap.xml"))
{
// This stream implementation will throw if the underlying stream does not match the digest
auto result = ComPtr<IStream>::Make<HashStream>(stream, this->GetAppxBlockMapDigest());
return result.Detach();
{ // This stream implementation will throw if the underlying stream does not match the digest
return ComPtr<IStream>::Make<HashStream>(stream, this->GetAppxBlockMapDigest());
}
else if (part == std::string("[Content_Types].xml"))
{
// This stream implementation will throw if the underlying stream does not match the digest'
auto result = ComPtr<IStream>::Make<HashStream>(stream, this->GetContentTypesDigest());
return result.Detach();
{ // This stream implementation will throw if the underlying stream does not match the digest'
return ComPtr<IStream>::Make<HashStream>(stream, this->GetContentTypesDigest());
}
else if (part == std::string("CodeIntegrity.cat"))
{
// This stream implementation will throw if the underlying stream does not match the digest
auto result = ComPtr<IStream>::Make<HashStream>(stream, this->GetCodeIntegrityDigest());
return result.Detach();
}
{ // This stream implementation will throw if the underlying stream does not match the digest
return ComPtr<IStream>::Make<HashStream>(stream, this->GetCodeIntegrityDigest());
}
// TODO: unnamed stream for central directory?
}
return stream;

Просмотреть файл

@ -37,6 +37,21 @@ MESSAGE (STATUS "PAL: DirectoryObject = ${DirectoryObject}")
MESSAGE (STATUS "PAL: SHA256 = ${SHA256}")
MESSAGE (STATUS "PAL: Signature = ${Signature}")
# Create header for BlockMap schemas
file(READ "${CMAKE_PROJECT_ROOT}/AppxPackaging/BlockMap/schema/BlockMapSchema.xsd" BLOCKMAP_SCHEMA)
file(READ "${CMAKE_PROJECT_ROOT}/AppxPackaging/BlockMap/schema/BlockMapSchema2015.xsd" BLOCKMAP_SCHEMA_2015)
file(READ "${CMAKE_PROJECT_ROOT}/AppxPackaging/BlockMap/schema/BlockMapSchema2017.xsd" BLOCKMAP_SCHEMA_2017)
set(BLOCKMAP_HEADER "// This file is generated by CMake and contains XSDs for parsing the AppxBlockMap.xml. Do not edit!!
#include <string>
#include <map>
std::map<std::string, std::string> blockMapSchema = {
{\"blockMapSchemaRaw\", R\"(${BLOCKMAP_SCHEMA})\" },
{\"blockMapSchema2015Raw\", R\"(${BLOCKMAP_SCHEMA_2015})\"},
{\"blockMapSchema2017Raw\", R\"(${BLOCKMAP_SCHEMA_2017})\"}
};
")
file(WRITE "../inc/AppxBlockMapSchemas.hpp" "${BLOCKMAP_HEADER}")
set(LIB_PUBLIC_HEADERS
../inc/AppxPackaging.hpp
../inc/AppxWindows.hpp
@ -90,7 +105,7 @@ add_library(${LIBRARY_NAME} SHARED ${LIB_SOURCES} ${LIB_PUBLIC_HEADERS} ${LIB_PR
set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 14)
# Set the build version. It will be used in the name of the lib, with corresponding
# symlinks created. SOVERSION could also be specified for api version.
# symlinks created. SOVERSION could also be specified for api version.
set_target_properties(${LIBRARY_NAME} PROPERTIES
VERSION ${VERSION} # ${VERSION} was defined in the main CMakeLists.
FRAMEWORK FALSE

Просмотреть файл

@ -106,6 +106,11 @@ namespace xPlat {
m_fileCurrentPosition += bytesToCopy;
}
if (m_fileCurrentPosition == m_uncompressedSize)
{
Cleanup();
return std::make_pair(false, State::UNINITIALIZED);
}
return std::make_pair(countBytes != 0, State::READY_TO_COPY);
}
} // State::READY_TO_COPY

Просмотреть файл

@ -131,18 +131,17 @@ namespace xPlat
DWORD cbExtensionUsage = 0;
std::vector<byte> extensionUsage(0);
if (!CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, NULL, &cbExtensionUsage))
{
extensionUsage.resize(cbExtensionUsage);
ThrowErrorIf(Error::AppxSignatureInvalid, (
!CertGetEnhancedKeyUsage(
pCertContext,
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
reinterpret_cast<PCERT_ENHKEY_USAGE>(extensionUsage.data()),
&cbExtensionUsage) &&
GetLastError() != CRYPT_E_NOT_FOUND
), "CertGetEnhacnedKeyUsage on extension usage failed.");
}
CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, NULL, &cbExtensionUsage);
extensionUsage.resize(cbExtensionUsage);
ThrowErrorIf(Error::AppxSignatureInvalid, (
!CertGetEnhancedKeyUsage(
pCertContext,
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
reinterpret_cast<PCERT_ENHKEY_USAGE>(extensionUsage.data()),
&cbExtensionUsage) &&
GetLastError() != CRYPT_E_NOT_FOUND
), "CertGetEnhacnedKeyUsage on extension usage failed.");
if (extensionUsage.size() > 0)
{
PCERT_ENHKEY_USAGE pExtensionUsageT = reinterpret_cast<PCERT_ENHKEY_USAGE>(extensionUsage.data());
@ -154,18 +153,17 @@ namespace xPlat
DWORD cbPropertyUsage = 0;
std::vector<byte> propertyUsage(0);
if (!CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, NULL, &cbPropertyUsage))
{
propertyUsage.resize(cbPropertyUsage);
ThrowErrorIf(Error::AppxSignatureInvalid, (
!CertGetEnhancedKeyUsage(
pCertContext,
CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
reinterpret_cast<PCERT_ENHKEY_USAGE>(propertyUsage.data()),
&cbPropertyUsage) &&
GetLastError() != CRYPT_E_NOT_FOUND
), "CertGetEnhancedKeyUsage on property usage failed.");
}
CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, NULL, &cbPropertyUsage);
propertyUsage.resize(cbPropertyUsage);
ThrowErrorIf(Error::AppxSignatureInvalid, (
!CertGetEnhancedKeyUsage(
pCertContext,
CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
reinterpret_cast<PCERT_ENHKEY_USAGE>(propertyUsage.data()),
&cbPropertyUsage) &&
GetLastError() != CRYPT_E_NOT_FOUND
), "CertGetEnhancedKeyUsage on property usage failed.");
if (propertyUsage.size() > 0)
{
PCERT_ENHKEY_USAGE pPropertyUsageT = reinterpret_cast<PCERT_ENHKEY_USAGE>(propertyUsage.data());
@ -187,11 +185,15 @@ namespace xPlat
policyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS);
policyParameters.dwFlags = MICROSOFT_ROOT_CERT_CHAIN_POLICY_CHECK_APPLICATION_ROOT_FLAG;
return CertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_MICROSOFT_ROOT,
certChainContext,
&policyParameters,
&policyStatus);
ThrowErrorIfNot(Error::AppxSignatureInvalid,
CertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_MICROSOFT_ROOT,
certChainContext,
&policyParameters,
&policyStatus),
"CertVerifyCertificateChainPolicy failed");
return ERROR_SUCCESS == policyStatus.dwError;
}
static bool IsAuthenticodeTrustedChain(_In_ PCCERT_CHAIN_CONTEXT certChainContext)
@ -201,11 +203,31 @@ namespace xPlat
CERT_CHAIN_POLICY_STATUS policyStatus = { 0 };
policyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS);
return CertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_AUTHENTICODE,
certChainContext,
&policyParameters,
&policyStatus);
//policyParameters.dwFlags = MICROSOFT_ROOT_CERT_CHAIN_POLICY_CHECK_APPLICATION_ROOT_FLAG;
ThrowErrorIfNot(Error::AppxSignatureInvalid,
CertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_AUTHENTICODE,
certChainContext,
&policyParameters,
&policyStatus),
"CertVerifyCertificateChainPolicy failed");
bool isAuthenticode = (ERROR_SUCCESS == policyStatus.dwError);
policyParameters = { 0 };
policyParameters.cbSize = sizeof(CERT_CHAIN_POLICY_PARA);
policyStatus = {0};
policyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS);
ThrowErrorIfNot(Error::AppxSignatureInvalid,
CertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_BASE,
certChainContext,
&policyParameters,
&policyStatus),
"CertVerifyCertificateChainPolicy failed");
bool chainsToTrustedRoot = (ERROR_SUCCESS == policyStatus.dwError);
return isAuthenticode && chainsToTrustedRoot;
}
static bool IsCACert(_In_ PCCERT_CONTEXT pCertContext)
@ -250,7 +272,7 @@ namespace xPlat
return(true);
}
static PCCERT_CONTEXT GetCertContext(BYTE *signatureBuffer, ULONG cbSignatureBuffer)
static PCCERT_CONTEXT GetCertContext(BYTE *signatureBuffer, ULONG cbSignatureBuffer)
{
//get cert context from strCertificate;
DWORD dwExpectedContentType = CERT_QUERY_CONTENT_FLAG_CERT |
@ -460,8 +482,8 @@ static PCCERT_CONTEXT GetCertContext(BYTE *signatureBuffer, ULONG cbSignatureBuf
), "CryptDecodeObjectEx failed");
DigestHeader *header = reinterpret_cast<DigestHeader*>(indirectContent->Digest.pbData);
std::uint32_t numberOfHashes = (indirectContent->Digest.cbData - sizeof(DWORD)) / sizeof(Digest);
std::uint32_t modHashes = (indirectContent->Digest.cbData - sizeof(DWORD)) % sizeof(Digest);
std::uint32_t numberOfHashes = (indirectContent->Digest.cbData - sizeof(DWORD)) / sizeof(DigestHash);
std::uint32_t modHashes = (indirectContent->Digest.cbData - sizeof(DWORD)) % sizeof(DigestHash);
ThrowErrorIf(Error::AppxSignatureInvalid, (
(header->name != xPlat::AppxSignatureObject::DigestName::HEAD) &&
(numberOfHashes != 4 && numberOfHashes != 5) &&
@ -487,45 +509,15 @@ static PCCERT_CONTEXT GetCertContext(BYTE *signatureBuffer, ULONG cbSignatureBuf
}
}
// If the caller allows unknown origin certs, don't bother with the certificate check
if (!(option & APPX_VALIDATION_OPTION_ALLOWSIGNATUREORIGINUNKNOWN))
{
// Build wintrust data to pass to WinVerifyTrust in order to validate signature
// TODO: we cannot use SIP 5598CFF1-68DB-4340-B57F-1CACF88C9A51 on anything older than Win8!!!
GUID P7xSipGuid = { 0x5598cff1, 0x68db, 0x4340,{ 0xb5, 0x7f, 0x1c, 0xac, 0xf8, 0x8c, 0x9a, 0x51 } };
WINTRUST_BLOB_INFO signatureBlobInfo = { 0 };
signatureBlobInfo.cbStruct = sizeof(WINTRUST_BLOB_INFO);
signatureBlobInfo.gSubject = P7xSipGuid;
signatureBlobInfo.cbMemObject = p7x.size();
signatureBlobInfo.pbMemObject = p7x.data();
WINTRUST_DATA trustData = { 0 };
trustData.cbStruct = sizeof(WINTRUST_DATA);
trustData.dwUIChoice = WTD_UI_NONE;
trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
trustData.dwUnionChoice = WTD_CHOICE_BLOB;
trustData.dwStateAction = WTD_STATEACTION_VERIFY;
trustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL | WTD_REVOCATION_CHECK_NONE;
trustData.pBlob = &signatureBlobInfo;
// Verify whether we trust the certificate. If it fails,
GUID wintrustActionVerify = WINTRUST_ACTION_GENERIC_VERIFY_V2;
ThrowErrorIf(Error::AppxCertNotTrusted, (
0 != WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &wintrustActionVerify, &trustData)
), "WinVerifyTrust failed");
// Close trustData.hWVTStateData -- returned by previous WinVerifyTrust call
trustData.cbStruct = sizeof(WINTRUST_DATA);
trustData.dwStateAction = WTD_STATEACTION_CLOSE;
ThrowErrorIf(Error::AppxCertNotTrusted, (
0 != WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &wintrustActionVerify, &trustData)
), "WinVerifyTrust StateAction Close failed");
}
origin = xPlat::SignatureOrigin::Unknown;
if (IsStoreOrigin(p7s, p7sSize)) { origin = xPlat::SignatureOrigin::Store; }
else if (IsAuthenticodeOrigin(p7s, p7sSize)) { origin = xPlat::SignatureOrigin::LOB; }
bool SignatureOriginUnknownAllowed = (option & APPX_VALIDATION_OPTION_ALLOWSIGNATUREORIGINUNKNOWN) == APPX_VALIDATION_OPTION_ALLOWSIGNATUREORIGINUNKNOWN;
ThrowErrorIf(Error::AppxCertNotTrusted,
((xPlat::SignatureOrigin::Unknown == origin) && !SignatureOriginUnknownAllowed),
"Unknown signature origin");
return true;
}
} // namespace xPlat

Просмотреть файл

@ -287,19 +287,21 @@ namespace xPlat {
(v == static_cast<std::uint32_t>(Signatures::CentralFileHeader)),
"CDFH Signature");
};
// 1 - version made by 2 bytes
Field<1>().validation = [](std::uint16_t& v)
{ ThrowErrorIfNot(Error::ZipCentralDirectoryHeader,
(v == static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension)),
"unsupported version made by");
};
// 2 - version needed to extract 2 bytes
Field<2>().validation = [](std::uint16_t& v)
{ ThrowErrorIfNot(Error::ZipCentralDirectoryHeader,
((v == static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion)) ||
(v == static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension))),
"unsupported version needed to extract");
};
// we actually do not base any decisions on these values, and OPC and Appx both do not
// consider the values in these fields to be all that interesting either apparently.
// // 1 - version made by 2 bytes
// Field<1>().validation = [](std::uint16_t& v)
// { ThrowErrorIfNot(Error::ZipCentralDirectoryHeader,
// (v == static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension)),
// "unsupported version made by");
// };
// // 2 - version needed to extract 2 bytes
// Field<2>().validation = [](std::uint16_t& v)
// { ThrowErrorIfNot(Error::ZipCentralDirectoryHeader,
// ((v == static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion)) ||
// (v == static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension))),
// "unsupported version needed to extract");
// };
// 3 - general purpose bit flag 2 bytes
Field<3>().validation = [](std::uint16_t& v)
{ ThrowErrorIfNot(Error::ZipCentralDirectoryHeader,
@ -336,10 +338,10 @@ namespace xPlat {
Field<13>().validation = [](std::uint16_t& v)
{ ThrowErrorIfNot(Error::ZipCentralDirectoryHeader, (v == 0), "unsupported disk number start");
};
//14 - internal file attributes 2 bytes
Field<14>().validation = [](std::uint16_t& v)
{ ThrowErrorIfNot(Error::ZipCentralDirectoryHeader, (v == 0), "unsupported internal file attributes");
};
// //14 - internal file attributes 2 bytes
// Field<14>().validation = [](std::uint16_t& v)
// { ThrowErrorIfNot(Error::ZipCentralDirectoryHeader, (v == 0), "unsupported internal file attributes");
// };
//15 - external file attributes 4 bytes
//16 - relative offset of local header 4 bytes
Field<16>().validation = [&](std::uint32_t& v)
@ -701,7 +703,7 @@ namespace xPlat {
std::uint64_t GetSizeOfCD() { return Field<8>().value; }
void SetSizeOfCD(std::uint64_t value) { Field<8>().value = value; }
std::uint64_t GetOffsetfStartOfCD() { return Field<9>().value; }
std::uint64_t GetOffsetStartOfCD() { return Field<9>().value; }
void SetOffsetfStartOfCD(std::uint64_t value) { Field<9>().value = value; }
private:
@ -803,39 +805,26 @@ namespace xPlat {
};
// 3 - total number of entries in the central directory on this disk 2 bytes
Field<3>().validation = [&](std::uint16_t& v)
{ ThrowErrorIfNot(Error::ZipEOCDRecord,
((v == 0) || (v == 0xFFFF)),
"unsupported total number of entries on this disk");
// ThrowErrorIfNot(Error::ZipEOCDRecord, (
// v == Field<2>().value
// ), "previous field value does not match this field");
{
if (v != 0 && v != 0xFFFF) { m_archiveHasZip64Locator = false; }
};
// 4 - total number of entries in the central directory 2 bytes
Field<4>().validation = [&](std::uint16_t& v)
{ ThrowErrorIfNot(Error::ZipEOCDRecord,
((v == 0) || (v == 0xFFFF)),
"unsupported total number of entries");
ThrowErrorIfNot(Error::ZipEOCDRecord, (
{ ThrowErrorIfNot(Error::ZipEOCDRecord, (
v == Field<3>().value
), "previous field value does not match this field");
};
// 5 - size of the central directory 4 bytes
Field<5>().validation = [&](std::uint32_t& v)
{ ThrowErrorIfNot(Error::ZipEOCDRecord,
((v == 0) || (v == 0xFFFFFFFF)),
{ ThrowErrorIf(Error::ZipEOCDRecord,
(m_archiveHasZip64Locator && (v != 0) && (v != 0xFFFFFFFF)),
"unsupported size of central directory");
ThrowErrorIfNot(Error::ZipEOCDRecord, (
v == 0 ? Field<4>().value == 0 : Field<4>().value == 0xFFFF
), "previous field value does not match this field");
};
// 6 - offset of start of central directory with respect to the starting disk number 4 bytes
Field<6>().validation = [&](std::uint32_t& v)
{ ThrowErrorIfNot(Error::ZipEOCDRecord,
((v == 0) || (v == 0xFFFFFFFF)),
{ ThrowErrorIf(Error::ZipEOCDRecord,
(m_archiveHasZip64Locator && (v != 0) && (v != 0xFFFFFFFF)),
"unsupported offset of start of central directory");
ThrowErrorIfNot(Error::ZipEOCDRecord, (
v == Field<5>().value
), "previous field value does not match this field");
};
// 7 - .ZIP file comment length 2 bytes
Field<7>().validation = [&](std::uint16_t& v)
@ -849,7 +838,7 @@ namespace xPlat {
SetSignature(static_cast<std::uint32_t>(Signatures::EndOfCentralDirectory));
SetNumberOfDisk(0);
SetDiskStart(0);
// next 12 bytes need to be: FFFF FFFF FFFF FFFF FFFF FFFF
// by default, the next 12 bytes need to be: FFFF FFFF FFFF FFFF FFFF FFFF
SetTotalNumberOfEntries (std::numeric_limits<std::uint16_t>::max());
SetTotalEntriesInCentralDirectory(std::numeric_limits<std::uint16_t>::max());
SetSizeOfCentralDirectory (std::numeric_limits<std::uint32_t>::max());
@ -858,7 +847,12 @@ namespace xPlat {
SetCommentLength(0);
}
bool GetIsZip64() { return m_isZip64; }
bool GetArchiveHasZip64Locator() { return m_archiveHasZip64Locator; }
bool GetIsZip64() { return m_isZip64; }
std::uint64_t GetNumberOfCentralDirectoryEntries() { return static_cast<std::uint64_t>(Field<3>().value); }
std::uint64_t GetStartOfCentralDirectory() { return static_cast<std::uint64_t>(Field<6>().value); }
private:
void SetSignature(std::uint32_t value) { Field<0>().value = value; }
void SetNumberOfDisk(std::uint16_t value) { Field<1>().value = value; }
@ -872,6 +866,7 @@ namespace xPlat {
void SetCommentLength(std::uint16_t value) { Field<7>().value = value; }
bool m_isZip64 = false;
bool m_archiveHasZip64Locator = true;
};//class EndOfCentralDirectoryRecord
std::vector<std::string> ZipObject::GetFileNames(FileNameOptions)
@ -887,7 +882,9 @@ namespace xPlat {
IStream* ZipObject::GetFile(const std::string& fileName)
{
// TODO: Make this on-demand populate m_streams and then pull from there.
return m_streams[fileName].Get();
auto result = m_streams.find(fileName);
ThrowErrorIf(Error::FileNotFound, (result == m_streams.end()), "file not in archive");
return result->second.Get();
}
void ZipObject::RemoveFile(const std::string& fileName)
@ -917,22 +914,34 @@ namespace xPlat {
endCentralDirectoryRecord.Read(m_stream.Get());
// find where the zip central directory exists.
Zip64EndOfCentralDirectoryLocator zip64Locator(m_stream.Get());
pos.QuadPart = -1*(endCentralDirectoryRecord.Size() + zip64Locator.Size());
ThrowHrIfFailed(m_stream->Seek(pos, StreamBase::Reference::END, nullptr));
zip64Locator.Read(m_stream.Get());
std::uint64_t offsetStartOfCD = 0;
std::uint64_t totalNumberOfEntries = 0;
Zip64EndOfCentralDirectoryLocator zip64Locator(m_stream.Get());
if (!endCentralDirectoryRecord.GetArchiveHasZip64Locator())
{
offsetStartOfCD = endCentralDirectoryRecord.GetStartOfCentralDirectory();
totalNumberOfEntries = endCentralDirectoryRecord.GetNumberOfCentralDirectoryEntries();
}
else
{ // Make sure that we have a zip64 end of central directory locator
pos.QuadPart = -1*(endCentralDirectoryRecord.Size() + zip64Locator.Size());
ThrowHrIfFailed(m_stream->Seek(pos, StreamBase::Reference::END, nullptr));
zip64Locator.Read(m_stream.Get());
// now read the zip central directory
Zip64EndOfCentralDirectoryRecord zip64EndOfCentralDirectory(m_stream.Get());
pos.QuadPart = zip64Locator.GetRelativeOffset();
ThrowHrIfFailed(m_stream->Seek(pos, StreamBase::Reference::START, nullptr));
zip64EndOfCentralDirectory.Read(m_stream.Get());
// now read the end of zip central directory record
Zip64EndOfCentralDirectoryRecord zip64EndOfCentralDirectory(m_stream.Get());
pos.QuadPart = zip64Locator.GetRelativeOffset();
ThrowHrIfFailed(m_stream->Seek(pos, StreamBase::Reference::START, nullptr));
zip64EndOfCentralDirectory.Read(m_stream.Get());
offsetStartOfCD = zip64EndOfCentralDirectory.GetOffsetStartOfCD();
totalNumberOfEntries = zip64EndOfCentralDirectory.GetTotalNumberOfEntries();
}
// read the zip central directory
std::map<std::string, std::shared_ptr<CentralDirectoryFileHeader>> centralDirectory;
pos.QuadPart = zip64EndOfCentralDirectory.GetOffsetfStartOfCD();
pos.QuadPart = offsetStartOfCD;
ThrowHrIfFailed(m_stream->Seek(pos, StreamBase::Reference::START, nullptr));
for (std::uint32_t index = 0; index < zip64EndOfCentralDirectory.GetTotalNumberOfEntries(); index++)
for (std::uint32_t index = 0; index < totalNumberOfEntries; index++)
{
auto centralFileHeader = std::make_shared<CentralDirectoryFileHeader>(endCentralDirectoryRecord.GetIsZip64(), m_stream.Get());
centralFileHeader->Read(m_stream.Get());
@ -940,14 +949,14 @@ namespace xPlat {
centralDirectory.insert(std::make_pair(centralFileHeader->GetFileName(), centralFileHeader));
}
// We should have no data between the end of the last central directory header and the start of the EoCD
ULARGE_INTEGER uPos = {0};
ThrowHrIfFailed(m_stream->Seek({0}, StreamBase::Reference::CURRENT, &uPos));
ThrowErrorIfNot(Error::ZipHiddenData, (uPos.QuadPart == zip64Locator.GetRelativeOffset()), "hidden data unsupported");
if (endCentralDirectoryRecord.GetArchiveHasZip64Locator())
{ // We should have no data between the end of the last central directory header and the start of the EoCD
ULARGE_INTEGER uPos = {0};
ThrowHrIfFailed(m_stream->Seek({0}, StreamBase::Reference::CURRENT, &uPos));
ThrowErrorIfNot(Error::ZipHiddenData, (uPos.QuadPart == zip64Locator.GetRelativeOffset()), "hidden data unsupported");
}
// TODO: change to uint64_t when adding full zip64 support
std::map<std::uint64_t, std::shared_ptr<LocalFileHeader>> fileRepository;
// TODO: change population of m_streams into cache semantics and move into ZipObject::GetFile
// Read the file repository
for (const auto& centralFileHeader : centralDirectory)
@ -963,7 +972,7 @@ namespace xPlat {
auto fileStream = ComPtr<IStream>::Make<ZipFileStream>(
centralFileHeader.second->GetFileName(),
"TODO: Implement", // TODO: put value from content type
m_factory.Get(),
m_factory,
localFileHeader->GetCompressionType() == CompressionType::Deflate,
centralFileHeader.second->GetRelativeOffsetOfLocalHeader() + localFileHeader->Size(),
localFileHeader->GetCompressedSize(),

Двоичные данные
test/appx/BlockMap/Bad_Namespace_Blockmap.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/ContentTypes_in_blockmap.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/Duplicate_file_in_blockmap.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/Extra_file_in_blockmap.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/File_missing_from_blockmap.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/HelloWorld.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/Invalid_Bad_Block.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/Missing_Manifest_in_blockmap.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/No_blockmap.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/Size_wrong_uncompressed.appx Normal file

Двоичный файл не отображается.

Двоичные данные
test/appx/BlockMap/TODAVIANO/Signature_in_BlockMap.appx Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.