diff options
Diffstat (limited to 'src/strongname')
33 files changed, 7418 insertions, 0 deletions
diff --git a/src/strongname/.gitmirror b/src/strongname/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/strongname/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/strongname/CMakeLists.txt b/src/strongname/CMakeLists.txt new file mode 100644 index 0000000000..dcfdb8cf57 --- /dev/null +++ b/src/strongname/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(api)
\ No newline at end of file diff --git a/src/strongname/api/.gitmirror b/src/strongname/api/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/strongname/api/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/strongname/api/CMakeLists.txt b/src/strongname/api/CMakeLists.txt new file mode 100644 index 0000000000..1eeed75267 --- /dev/null +++ b/src/strongname/api/CMakeLists.txt @@ -0,0 +1,26 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +#this will again add vm to include directories. However, this will add vm to be the first include folder. +#This is required because util.hpp is also present in ildasm folder and so due to ordering wrong util.hpp +#is pickedup. +include_directories(BEFORE ${VM_DIR}) + +include_directories(${VM_DIR}/${ARCH_SOURCES_DIR}) +include_directories(${CLR_DIR}/src/md/compiler) + +add_definitions(-DSTRONGNAME_IN_VM -DSNAPI_INTERNAL) + +set(STRONGNAME_SOURCES + strongname.cpp + strongnamecoreclr.cpp + strongnameinternal.cpp +) + +convert_to_absolute_path(STRONGNAME_SOURCES ${STRONGNAME_SOURCES}) + +if(CLR_CMAKE_PLATFORM_UNIX) + add_compile_options(-fPIC) +endif(CLR_CMAKE_PLATFORM_UNIX) + +add_subdirectory(dac) +add_subdirectory(wks)
\ No newline at end of file diff --git a/src/strongname/api/api.props b/src/strongname/api/api.props new file mode 100644 index 0000000000..2c4c278a69 --- /dev/null +++ b/src/strongname/api/api.props @@ -0,0 +1,43 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + + <PropertyGroup> + <UserIncludes> + $(UserIncludes); + $(Clrbase)\src\StrongName\inc; + $(Clrbase)\src\inc; + $(Clrbase)\src\md\inc; + $(Clrbase)\src\md\compiler; + $(Clrbase)\src\vm + </UserIncludes> + + <UserIncludes Condition="'$(StrongnameInVm)' == 'true'"> + $(UserIncludes); + $(Clrbase)\src\vm\$(TargetCpu) + </UserIncludes> + + <CDefines Condition="'$(StrongnameInVm)' == 'true'">$(CDefines);STRONGNAME_IN_VM</CDefines> + + <CDefines>$(CDefines);SNAPI_INTERNAL</CDefines> + <CDefines>$(CDefines);UNICODE;_UNICODE</CDefines> + + <OutputPath>$(ClrLibDest)</OutputPath> + <TargetType>LIBRARY</TargetType> + + <PCHHeader>common.h</PCHHeader> + <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders> + <PCHCompile>$(Clrbase)\src\StrongName\api\common.cpp</PCHCompile> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="$(ClrSrcDirectory)inc\corguids.nativeproj"> + <Comment>clrinternal.h</Comment> + </ProjectReference> + </ItemGroup> + + <ItemGroup> + <CppCompile Include="$(Clrbase)\src\StrongName\api\strongname.cpp" /> + <CppCompile Include="$(Clrbase)\src\StrongName\api\strongnamecoreclr.cpp" /> + <CppCompile Include="$(Clrbase)\src\StrongName\api\strongnameinternal.cpp" /> + </ItemGroup> + +</Project>
\ No newline at end of file diff --git a/src/strongname/api/common.cpp b/src/strongname/api/common.cpp new file mode 100644 index 0000000000..f752dafb41 --- /dev/null +++ b/src/strongname/api/common.cpp @@ -0,0 +1,7 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "common.h" + diff --git a/src/strongname/api/common.h b/src/strongname/api/common.h new file mode 100644 index 0000000000..45f14a8b2b --- /dev/null +++ b/src/strongname/api/common.h @@ -0,0 +1,404 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// common.h - precompiled headers include for the COM+ Execution Engine +// + +#ifndef _common_h_ +#define _common_h_ + +#if defined(_MSC_VER) && defined(_X86_) && !defined(FPO_ON) +#pragma optimize("y", on) // Small critical routines, don't put in EBP frame +#define FPO_ON 1 +#define COMMON_TURNED_FPO_ON 1 +#endif + +#if STRONGNAME_IN_VM + +#define USE_COM_CONTEXT_DEF + +#ifdef _DEBUG +#define DEBUG_REGDISPLAY +#endif + +#ifdef _MSC_VER + + // These don't seem useful, so turning them off is no big deal +#pragma warning(disable:4244) // loss of data int -> char .. + +#ifndef DEBUG +#pragma warning(disable:4189) // local variable initialized but not used +#pragma warning(disable:4505) // unreferenced local function has been removed +#pragma warning(disable:4313) // 'format specifier' in format string conflicts with argument %d of type 'type' +#endif // !DEBUG + + +#endif // _MSC_VER + +#endif // STRONGNAME_IN_VM + +#define _CRT_DEPENDENCY_ //this code depends on the crt file functions + + +#include <stdint.h> +#include <winwrap.h> + +#include <windows.h> +#include <wincrypt.h> +#include <winnt.h> +#include <crosscomp.h> +#include <clrnt.h> +#include <stdlib.h> +#include <wchar.h> +#include <objbase.h> +#include <stddef.h> +#include <float.h> +#include <math.h> +#include <time.h> +#include <limits.h> + +#include <olectl.h> + +#ifdef _MSC_VER +//non inline intrinsics are faster +#pragma function(memcpy,memcmp,strcmp,strcpy,strlen,strcat) +#endif // _MSC_VER + +// make all the unsafe redefinitions available +#include "unsafe.h" + +//----------------------------------------------------------------------------------------------------------- + +#include "lazycow.h" + +#include "compatibilityflags.h" +extern BOOL GetCompatibilityFlag(CompatibilityFlag flag); +extern DWORD* GetGlobalCompatibilityFlags(); + +#include "strongname.h" +#include "stdmacros.h" + +#define POISONC ((UINT_PTR)((sizeof(int *) == 4)?0xCCCCCCCCL:I64(0xCCCCCCCCCCCCCCCC))) + +#include "ndpversion.h" +#include "switches.h" +#include "holder.h" + +#if STRONGNAME_IN_VM +#include "classnames.h" +#include "util.hpp" +#endif // STRONGNAME_IN_VM + +#include "corpriv.h" + +#include <daccess.h> + +#if STRONGNAME_IN_VM + +typedef VPTR(class LoaderAllocator) PTR_LoaderAllocator; +typedef VPTR(class AppDomain) PTR_AppDomain; +typedef VPTR(class AppDomainBaseObject) PTR_AppDomainBaseObject; +typedef DPTR(class ArrayBase) PTR_ArrayBase; +typedef DPTR(class ArrayTypeDesc) PTR_ArrayTypeDesc; +typedef DPTR(class Assembly) PTR_Assembly; +typedef DPTR(class AssemblyBaseObject) PTR_AssemblyBaseObject; +typedef DPTR(class AssemblyNameBaseObject) PTR_AssemblyNameBaseObject; +typedef VPTR(class BaseDomain) PTR_BaseDomain; +typedef DPTR(class MscorlibBinder) PTR_MscorlibBinder; +typedef DPTR(class ClassLoader) PTR_ClassLoader; +typedef DPTR(class ComCallMethodDesc) PTR_ComCallMethodDesc; +typedef VPTR(class CompilationDomain) PTR_CompilationDomain; +typedef DPTR(class ComPlusCallMethodDesc) PTR_ComPlusCallMethodDesc; +typedef VPTR(class DebugInterface) PTR_DebugInterface; +typedef DPTR(class Dictionary) PTR_Dictionary; +typedef VPTR(class DomainAssembly) PTR_DomainAssembly; +typedef VPTR(class DomainFile) PTR_DomainFile; +typedef VPTR(class DomainModule) PTR_DomainModule; +typedef DPTR(struct FailedAssembly) PTR_FailedAssembly; +typedef VPTR(class EditAndContinueModule) PTR_EditAndContinueModule; +typedef DPTR(class EEClass) PTR_EEClass; +typedef DPTR(class DelegateEEClass) PTR_DelegateEEClass; +typedef DPTR(struct DomainLocalModule) PTR_DomainLocalModule; +typedef VPTR(class EECodeManager) PTR_EECodeManager; +typedef DPTR(class EEConfig) PTR_EEConfig; +typedef VPTR(class EEDbgInterfaceImpl) PTR_EEDbgInterfaceImpl; +typedef VPTR(class DebugInfoManager) PTR_DebugInfoManager; +typedef DPTR(class FieldDesc) PTR_FieldDesc; +typedef VPTR(class Frame) PTR_Frame; +typedef VPTR(class ICodeManager) PTR_ICodeManager; +typedef VPTR(class IJitManager) PTR_IJitManager; +typedef DPTR(class InstMethodHashTable) PTR_InstMethodHashTable; +typedef DPTR(class MetaSig) PTR_MetaSig; +typedef DPTR(class MethodDesc) PTR_MethodDesc; +typedef DPTR(class MethodDescChunk) PTR_MethodDescChunk; +typedef DPTR(class MethodImpl) PTR_MethodImpl; +typedef DPTR(class MethodTable) PTR_MethodTable; +typedef VPTR(class Module) PTR_Module; +typedef DPTR(class NDirectMethodDesc) PTR_NDirectMethodDesc; +typedef VPTR(class Thread) PTR_Thread; +typedef DPTR(class Object) PTR_Object; +typedef DPTR(PTR_Object) PTR_PTR_Object; +typedef DPTR(class ObjHeader) PTR_ObjHeader; +typedef DPTR(class Precode) PTR_Precode; +typedef VPTR(class ReflectionModule) PTR_ReflectionModule; +typedef DPTR(class ReflectClassBaseObject) PTR_ReflectClassBaseObject; +typedef DPTR(class ReflectModuleBaseObject) PTR_ReflectModuleBaseObject; +typedef DPTR(class ReflectMethodObject) PTR_ReflectMethodObject; +typedef DPTR(class ReflectFieldObject) PTR_ReflectFieldObject; +typedef DPTR(class ReJitManager) PTR_ReJitManager; +typedef DPTR(struct ReJitInfo) PTR_ReJitInfo; +typedef DPTR(struct SharedReJitInfo) PTR_SharedReJitInfo; +typedef DPTR(class StringObject) PTR_StringObject; +typedef DPTR(class StringBufferObject) PTR_StringBufferObject; +typedef DPTR(class TypeHandle) PTR_TypeHandle; +#ifdef STUB_DISPATCH +typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager; +typedef VPTR(class VirtualCallStubManagerManager) PTR_VirtualCallStubManagerManager; +#endif +typedef VPTR(class GCHeap) PTR_GCHeap; + +// +// _UNCHECKED_OBJECTREF is for code that can't deal with DEBUG OBJECTREFs +// +typedef PTR_Object _UNCHECKED_OBJECTREF; +typedef DPTR(PTR_Object) PTR_UNCHECKED_OBJECTREF; + +#ifdef USE_CHECKED_OBJECTREFS +class OBJECTREF; +#else +typedef PTR_Object OBJECTREF; +#endif +typedef DPTR(OBJECTREF) PTR_OBJECTREF; +typedef DPTR(PTR_OBJECTREF) PTR_PTR_OBJECTREF; + + +EXTERN_C Thread* STDCALL GetThread(); + +// This is a mechanism by which macros can make the Thread pointer available to inner scopes +// that is robust to code changes. If the outer Thread no longer is available for some reason +// (e.g. code refactoring), this GET_THREAD() macro will fall back to calling GetThread(). +const bool CURRENT_THREAD_AVAILABLE = false; +Thread * const CURRENT_THREAD = NULL; +#define GET_THREAD() (CURRENT_THREAD_AVAILABLE ? CURRENT_THREAD : GetThread()) + +#define MAKE_CURRENT_THREAD_AVAILABLE() \ + Thread * __pThread = GET_THREAD(); \ + MAKE_CURRENT_THREAD_AVAILABLE_EX(__pThread) + +#define MAKE_CURRENT_THREAD_AVAILABLE_EX(__pThread) \ + Thread * CURRENT_THREAD = __pThread; \ + const bool CURRENT_THREAD_AVAILABLE = true; \ + (void)CURRENT_THREAD_AVAILABLE; /* silence "local variable initialized but not used" warning */ \ + +#ifndef DACCESS_COMPILE +EXTERN_C AppDomain* GetAppDomain(); +#endif //!DACCESS_COMPILE + +inline void RetailBreak() +{ +#if defined(_TARGET_X86_) + __asm int 3 +#else + DebugBreak(); +#endif +} + +extern BOOL isMemoryReadable(const TADDR start, unsigned len); + +#ifndef memcpyUnsafe_f +#define memcpyUnsafe_f + +// use this when you want to memcpy something that contains GC refs +inline void* memcpyUnsafe(void *dest, const void *src, size_t len) +{ + WRAPPER_NO_CONTRACT; + return memcpy(dest, src, len); +} + +#endif // !memcpyUnsafe_f + +// +// By default logging, and debug GC are enabled under debug +// +// These can be enabled in non-debug by removing the #ifdef _DEBUG +// allowing one to log/check_gc a free build. +// +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + + // You should be using CopyValueClass if you are doing an memcpy + // in the CG heap. + #if !defined(memcpy) + inline void* memcpyNoGCRefs(void * dest, const void * src, size_t len) { + WRAPPER_NO_CONTRACT; + + return memcpy(dest, src, len); + } + extern "C" void * __cdecl GCSafeMemCpy(void *, const void *, size_t); + #define memcpy(dest, src, len) GCSafeMemCpy(dest, src, len) + #endif // !defined(memcpy) + + #if !defined(CHECK_APP_DOMAIN_LEAKS) + #define CHECK_APP_DOMAIN_LEAKS 1 + #endif +#else // !_DEBUG && !DACCESS_COMPILE && !CROSSGEN_COMPILE + inline void* memcpyNoGCRefs(void * dest, const void * src, size_t len) { + WRAPPER_NO_CONTRACT; + + return memcpy(dest, src, len); + } +#endif // !_DEBUG && !DACCESS_COMPILE && !CROSSGEN_COMPILE + +namespace Loader +{ + typedef enum + { + Load, //should load + DontLoad, //should not load + SafeLookup //take no locks, no allocations + } LoadFlag; +} + +#endif // STRONGNAME_IN_VM + +// src/inc +#include "utilcode.h" +#include "log.h" +#include "loaderheap.h" + +#if STRONGNAME_IN_VM + +// src/vm +#include "util.hpp" +#include "ibclogger.h" +#include "eepolicy.h" + +#include "vars.hpp" +#include "crst.h" +#include "argslot.h" +#include "stublink.h" +#include "cgensys.h" +#include "ceemain.h" +#include "hash.h" +#include "eecontract.h" +#include "pedecoder.h" +#include "sstring.h" +#include "slist.h" + +#include "eeconfig.h" + +#include "spinlock.h" +#include "objecthandle.h" +#include "cgensys.h" +#include "declsec.h" + +#ifdef FEATURE_COMINTEROP +#include "stdinterfaces.h" +#endif + +#include "typehandle.h" +#include "perfcounters.h" +#include "methodtable.h" +#include "typectxt.h" + +#include "eehash.h" + +#include "handletable.h" +#include "vars.hpp" +#include "eventstore.hpp" + +#include "synch.h" +#include "regdisp.h" +#include "stackframe.h" +#include "gms.h" +#include "stackprobe.h" +#include "fcall.h" +#include "syncblk.h" +#include "gcdesc.h" +#include "specialstatics.h" +#include "object.h" // <NICE> We should not really need to put this so early... </NICE> +#include "gchelpers.h" +#include "pefile.h" +#include "clrex.h" +#include "clsload.hpp" // <NICE> We should not really need to put this so early... </NICE> +#include "siginfo.hpp" +#include "binder.h" +#include "jitinterface.h" // <NICE> We should not really need to put this so early... </NICE> +#include "ceeload.h" +#include "memberload.h" +#include "genericdict.h" +#include "class.h" +#include "codeman.h" +#include "threads.h" +#include "clrex.inl" +#include "loaderallocator.hpp" +#include "appdomain.hpp" +#include "assembly.hpp" +#include "pefile.inl" +#include "excep.h" +#include "method.hpp" +#include "frames.h" + +#include "stackwalk.h" +#include "stackingallocator.h" +#include "interoputil.h" +#include "wrappers.h" +#include "dynamicmethod.h" + + +HRESULT EnsureRtlFunctions(); +HINSTANCE GetModuleInst(); + + +#if defined(_DEBUG) + +// This catches CANNOTTHROW macros that occur outside the scope of a CONTRACT. +// Note that it's important for m_CannotThrowLineNums to be NULL. +struct DummyGlobalContract +{ + int *m_CannotThrowLineNums; //= NULL; + LPVOID *m_CannotThrowRecords; //= NULL; +}; + +extern DummyGlobalContract ___contract; + +#endif // defined(_DEBUG) + +// For down level platform compiles. +#if !defined(_WIN64) && (_WIN32_WINNT < 0x0500) +typedef VOID (__stdcall *WAITORTIMERCALLBACK)(PVOID, BOOL); +#endif + + + +// All files get to see all of these .inl files to make sure all files +// get the benefit of inlining. +#include "ceeload.inl" +#include "typedesc.inl" +#include "class.inl" +#include "methodtable.inl" +#include "typehandle.inl" +#include "object.inl" +#include "ceeload.inl" +#include "clsload.inl" +#include "domainfile.inl" +#include "handletable.inl" +#include "clsload.inl" +#include "method.inl" +#include "stackprobe.inl" +#include "syncblk.inl" +#include "threads.inl" +#include "eehash.inl" + +#endif // STRONGNAME_IN_VM + +#include "pedecoder.h" + +#if defined(COMMON_TURNED_FPO_ON) +#pragma optimize("", on) // Go back to command line default optimizations +#undef COMMON_TURNED_FPO_ON +#undef FPO_ON +#endif + +#endif // !_common_h_ diff --git a/src/strongname/api/crossgen/.gitmirror b/src/strongname/api/crossgen/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/strongname/api/crossgen/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/strongname/api/crossgen/strongname_crossgen.nativeproj b/src/strongname/api/crossgen/strongname_crossgen.nativeproj new file mode 100644 index 0000000000..55e7cc8cd9 --- /dev/null +++ b/src/strongname/api/crossgen/strongname_crossgen.nativeproj @@ -0,0 +1,18 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" /> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <PropertyGroup> + <StrongnameInVm>true</StrongnameInVm> + </PropertyGroup> + <Import Project="$(ClrBase)\src\strongname\api\api.props" /> + + <PropertyGroup> + <BuildSysBinaries>true</BuildSysBinaries> + <OutputName>strongname_crossgen</OutputName> + </PropertyGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/strongname/api/cryptapis.h b/src/strongname/api/cryptapis.h new file mode 100644 index 0000000000..d5b22a8abd --- /dev/null +++ b/src/strongname/api/cryptapis.h @@ -0,0 +1,48 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// =========================================================================== +// File: CryptApis.h +// +// CryptoAPI entry points used for StrongName implementation. This file is +// included multiple times with different definitions of the DEFINE_IMPORT +// macro in order handle dynamically finding these entry points. +// =========================================================================== + +#ifndef DEFINE_IMPORT +#error Must define DEFINE_IMPORT macro before including this file +#endif + +// DEFINE_IMPORT parameters are: +// 1) Function name (remember to add A to functions that take strings, don't +// use W versions since they're unsupported on Win9X). +// 2) Paranthesised argument types (return type is always assumed to be +// BOOLEAN). +// 3) TRUE if function is required, FALSE if it is optional (calls will not +// fail because the function can't be found). + +DEFINE_IMPORT(CryptAcquireContextA, (HCRYPTPROV*, LPCSTR, LPCSTR, DWORD, DWORD), TRUE) +DEFINE_IMPORT(CryptAcquireContextW, (HCRYPTPROV*, LPCWSTR, LPCWSTR, DWORD, DWORD), TRUE) +DEFINE_IMPORT(CryptReleaseContext, (HCRYPTPROV, DWORD), TRUE) +DEFINE_IMPORT(CryptCreateHash, (HCRYPTPROV, ALG_ID, HCRYPTKEY, DWORD, HCRYPTHASH*), TRUE) +DEFINE_IMPORT(CryptDestroyHash, (HCRYPTHASH), TRUE) +DEFINE_IMPORT(CryptHashData, (HCRYPTHASH, CONST BYTE*, DWORD, DWORD), TRUE) +DEFINE_IMPORT(CryptGetHashParam, (HCRYPTHASH, DWORD, BYTE*, DWORD*, DWORD), TRUE) +DEFINE_IMPORT(CryptImportKey, (HCRYPTPROV, CONST BYTE*, DWORD, HCRYPTKEY, DWORD, HCRYPTKEY*), TRUE) +DEFINE_IMPORT(CryptExportKey, (HCRYPTKEY, HCRYPTKEY, DWORD, DWORD, BYTE*, DWORD*), TRUE) +DEFINE_IMPORT(CryptGenKey, (HCRYPTPROV, ALG_ID, DWORD, HCRYPTKEY*), TRUE) +DEFINE_IMPORT(CryptGetKeyParam, (HCRYPTKEY, DWORD, BYTE*, DWORD*, DWORD), TRUE) +DEFINE_IMPORT(CryptDestroyKey, (HCRYPTKEY), TRUE) +DEFINE_IMPORT(CryptVerifySignatureA, (HCRYPTHASH, CONST BYTE*, DWORD, HCRYPTKEY, LPCSTR, DWORD), TRUE) +DEFINE_IMPORT(CryptVerifySignatureW, (HCRYPTHASH, CONST BYTE*, DWORD, HCRYPTKEY, LPCWSTR, DWORD), TRUE) +DEFINE_IMPORT(CryptSignHashA, (HCRYPTHASH, DWORD, LPCSTR, DWORD, BYTE*, DWORD*), TRUE) +DEFINE_IMPORT(CryptSignHashW, (HCRYPTHASH, DWORD, LPCWSTR, DWORD, BYTE*, DWORD*), TRUE) + +DEFINE_IMPORT(CryptGetProvParam, (HCRYPTPROV, DWORD, BYTE*, DWORD*, DWORD), TRUE) +DEFINE_IMPORT(CryptGetUserKey, (HCRYPTPROV, DWORD, HCRYPTKEY*), TRUE) +DEFINE_IMPORT(CryptEnumProvidersA, (DWORD, DWORD*, DWORD, DWORD*, LPSTR, DWORD*), FALSE) +DEFINE_IMPORT(CryptEnumProvidersW, (DWORD, DWORD*, DWORD, DWORD*, LPWSTR, DWORD*), FALSE) + +#undef DEFINE_IMPORT diff --git a/src/strongname/api/dac/.gitmirror b/src/strongname/api/dac/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/strongname/api/dac/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/strongname/api/dac/CMakeLists.txt b/src/strongname/api/dac/CMakeLists.txt new file mode 100644 index 0000000000..5278a67d83 --- /dev/null +++ b/src/strongname/api/dac/CMakeLists.txt @@ -0,0 +1,4 @@ + +include(${CLR_DIR}/dac.cmake) + +add_library(strongname_dac ${STRONGNAME_SOURCES}) diff --git a/src/strongname/api/dac/dirs.proj b/src/strongname/api/dac/dirs.proj new file mode 100644 index 0000000000..6ca01ad4ad --- /dev/null +++ b/src/strongname/api/dac/dirs.proj @@ -0,0 +1,19 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="HostLocal\strongname_dac.nativeproj" /> + </ItemGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/strongname/api/dirs.proj b/src/strongname/api/dirs.proj new file mode 100644 index 0000000000..996632d796 --- /dev/null +++ b/src/strongname/api/dirs.proj @@ -0,0 +1,22 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="dac\dirs.proj" /> + <ProjectFile Condition="'$(BuildProjectName)' != 'CoreSys'" Include="standalone\strongname.nativeproj" /> + <ProjectFile Condition="'$(BuildProjectName)' != 'CoreSys'" Include="standalone-winrt\strongname-winrt.nativeproj" /> + <ProjectFile Include="wks\strongname_wks.nativeproj" /> + </ItemGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/strongname/api/strongname.cpp b/src/strongname/api/strongname.cpp new file mode 100644 index 0000000000..ec91fe8788 --- /dev/null +++ b/src/strongname/api/strongname.cpp @@ -0,0 +1,4988 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// =========================================================================== +// File: StrongName.cpp +// +// Wrappers for signing and hashing functions needed to implement strong names +// =========================================================================== + +#include "common.h" +#include <imagehlp.h> + +#include <winwrap.h> +#include <windows.h> +#include <wincrypt.h> +#include <stddef.h> +#include <stdio.h> +#include <malloc.h> +#include <cor.h> +#include <corimage.h> +#include <metadata.h> +#include <daccess.h> +#include <limits.h> +#include <ecmakey.h> +#include <sha1.h> + +#include "strongname.h" +#include "ex.h" +#include "pedecoder.h" +#include "strongnameholders.h" +#include "strongnameinternal.h" +#include "common.h" +#include "classnames.h" + +// Debug logging. +#if !defined(_DEBUG) || defined(DACCESS_COMPILE) +#define SNLOG(args) +#endif // !_DEBUG || DACCESS_COMPILE + +#ifndef DACCESS_COMPILE + +// Debug logging. +#if defined(_DEBUG) +#include <stdarg.h> + +BOOLEAN g_fLoggingInitialized = FALSE; +DWORD g_dwLoggingFlags = FALSE; + +#define SNLOG(args) Log args + +void Log(__in_z const WCHAR *wszFormat, ...) +{ + if (g_fLoggingInitialized && !g_dwLoggingFlags) + return; + + DWORD dwError = GetLastError(); + + if (!g_fLoggingInitialized) { + g_dwLoggingFlags = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MscorsnLogging); + g_fLoggingInitialized = TRUE; + } + + if (!g_dwLoggingFlags) { + SetLastError(dwError); + return; + } + + va_list pArgs; + WCHAR wszBuffer[1024]; + static WCHAR wszPrefix[] = W("SN: "); + + wcscpy_s(wszBuffer, COUNTOF(wszBuffer), wszPrefix); + + va_start(pArgs, wszFormat); + _vsnwprintf_s(&wszBuffer[COUNTOF(wszPrefix) - 1], + COUNTOF(wszBuffer) - COUNTOF(wszPrefix), + _TRUNCATE, + wszFormat, + pArgs); + + wszBuffer[COUNTOF(wszBuffer) - 1] = W('\0'); + va_end(pArgs); + + if (g_dwLoggingFlags & 1) + wprintf(W("%s"), wszBuffer); + if (g_dwLoggingFlags & 2) + WszOutputDebugString(wszBuffer); + if (g_dwLoggingFlags & 4) + { + MAKE_UTF8PTR_FROMWIDE_NOTHROW(szMessage, wszBuffer); + if(szMessage != NULL) + LOG((LF_SECURITY, LL_INFO100, szMessage)); + } + + SetLastError(dwError); +} + +#endif // _DEBUG + +// Size in bytes of strong name token. +#define SN_SIZEOF_TOKEN 8 + +enum StrongNameCachedCsp { + None = -1, + Sha1CachedCsp = 0, + Sha2CachedCsp = Sha1CachedCsp + 1, + CachedCspCount = Sha2CachedCsp + 1 +}; + +// We cache a couple of things on a per thread basis: the last error encountered +// and (potentially) CSP contexts. The following structure tracks these and is +// allocated lazily as needed. +struct SN_THREAD_CTX { + DWORD m_dwLastError; +#if !defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) + HCRYPTPROV m_hProv[CachedCspCount]; +#endif // !FEATURE_CORECLR || CROSSGEN_COMPILE +}; + +#endif // !DACCESS_COMPILE + +// Macro containing common code used at the start of most APIs. +#define SN_COMMON_PROLOG() do { \ + HRESULT __hr = InitStrongName(); \ + if (FAILED(__hr)) { \ + SetStrongNameErrorInfo(__hr); \ + retVal = FALSE; \ + goto Exit; \ + } \ + SetStrongNameErrorInfo(S_OK); \ +} while (0) + +// Macro to return an error from a SN entrypoint API +#define SN_ERROR(__hr) do { \ + if (FAILED(__hr)) { \ + SetStrongNameErrorInfo(__hr); \ + retVal = FALSE; \ + goto Exit; \ + } \ +} while (false) + +// Determine the size of a PublicKeyBlob structure given the size of the key +// portion. +#define SN_SIZEOF_KEY(_pKeyBlob) (offsetof(PublicKeyBlob, PublicKey) + GET_UNALIGNED_VAL32(&(_pKeyBlob)->cbPublicKey)) + +// We allow a special abbreviated form of the Microsoft public key (16 bytes +// long: 0 for both alg ids, 4 for key length and 4 bytes of 0 for the key +// itself). This allows us to build references to system libraries that are +// platform neutral (so a 3rd party can build mscorlib replacements). The +// special zero PK is just shorthand for the local runtime's real system PK, +// which is always used to perform the signature verification, so no security +// hole is opened by this. Therefore we need to store a copy of the real PK (for +// this platform) here. + +// the actual definition of the microsoft key is in separate file to allow custom keys +#include "thekey.h" + + +#define SN_THE_KEY() ((PublicKeyBlob*)g_rbTheKey) +#define SN_SIZEOF_THE_KEY() sizeof(g_rbTheKey) + +#define SN_THE_KEYTOKEN() ((PublicKeyBlob*)g_rbTheKeyToken) + +// Determine if the given public key blob is the neutral key. +#define SN_IS_NEUTRAL_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbNeutralPublicKey) && \ + memcmp((_pk), g_rbNeutralPublicKey, sizeof(g_rbNeutralPublicKey)) == 0) + +#define SN_IS_THE_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbTheKey) && \ + memcmp((_pk), g_rbTheKey, sizeof(g_rbTheKey)) == 0) + + +#ifdef FEATURE_CORECLR + +// Silverlight platform key +#define SN_THE_SILVERLIGHT_PLATFORM_KEYTOKEN() ((PublicKeyBlob*)g_rbTheSilverlightPlatformKeyToken) +#define SN_IS_THE_SILVERLIGHT_PLATFORM_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbTheSilverlightPlatformKey) && \ + memcmp((_pk), g_rbTheSilverlightPlatformKey, sizeof(g_rbTheSilverlightPlatformKey)) == 0) + +// Silverlight key +#define SN_IS_THE_SILVERLIGHT_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbTheSilverlightKey) && \ + memcmp((_pk), g_rbTheSilverlightKey, sizeof(g_rbTheSilverlightKey)) == 0) + +#define SN_THE_SILVERLIGHT_KEYTOKEN() ((PublicKeyBlob*)g_rbTheSilverlightKeyToken) + +#ifdef FEATURE_WINDOWSPHONE +// Microsoft.Phone.* key +#define SN_THE_MICROSOFT_PHONE_KEYTOKEN() ((PublicKeyBlob*)g_rbTheMicrosoftPhoneKeyToken) + +#define SN_IS_THE_MICROSOFT_PHONE_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbTheMicrosoftPhoneKey) && \ + memcmp((_pk), g_rbTheMicrosoftPhoneKey, sizeof(g_rbTheMicrosoftPhoneKey)) == 0) + +// Microsoft.Xna.* key +#define SN_THE_MICROSOFT_XNA_KEYTOKEN() ((PublicKeyBlob*)g_rbTheMicrosoftXNAKeyToken) + +#define SN_IS_THE_MICROSOFT_XNA_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbTheMicrosoftXNAKey) && \ + memcmp((_pk), g_rbTheMicrosoftXNAKey, sizeof(g_rbTheMicrosoftXNAKey)) == 0) + +#endif // FEATURE_WINDOWSPHONE +#endif // FEATURE_CORECLR + +#if !defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) + +#ifdef FEATURE_STRONGNAME_MIGRATION +#include "caparser.h" +#include "custattr.h" +#include "cahlprinternal.h" +#endif // FEATURE_STRONGNAME_MIGRATION + +// The maximum length of CSP name we support (in characters). +#define SN_MAX_CSP_NAME 1024 + +// If we're being built as a standalone library, then we shouldn't redirect through the hosting APIs +#if !STRONGNAME_IN_VM + +#undef MapViewOfFile +#undef UnmapViewOfFile + +#define CLRMapViewOfFile MapViewOfFile +#define CLRUnmapViewOfFile UnmapViewOfFile + +#if FEATURE_STANDALONE_SN && !FEATURE_CORECLR + +// We will need to call into shim, therefore include new hosting APIs +#include "metahost.h" +#include "clrinternal.h" + +#endif //FEATURE_STANDALONE_SN && !FEATURE_CORECLR + +#define DONOT_DEFINE_ETW_CALLBACK + +#endif // !STRONGNAME_IN_VM +#include "eventtracebase.h" + +#ifndef DACCESS_COMPILE + +// Flag indicating whether the initialization of the strong name APIs has been completed. +BOOLEAN g_bStrongNamesInitialized = FALSE; + +// Flag indicating whether it's OK to cache the results of verifying an assembly +// whose file is accessible to users. +BOOLEAN g_fCacheVerify = TRUE; + +// Algorithm IDs for hashing and signing. Like the CSP name, these values are +// read from the registry at initialization time. +ALG_ID g_uHashAlgId; +ALG_ID g_uSignAlgId; + +// Flag read from the registry at initialization time. It controls the key spec +// to be used. AT_SIGNATURE will be the default. +DWORD g_uKeySpec; + +// CSP provider type. PROV_RSA_FULL will be the default. +DWORD g_uProvType; + +// Critical section used to serialize some non-thread safe crypto APIs. +CRITSEC_COOKIE g_rStrongNameMutex = NULL; + +// Name of CSP to use. This is read from the registry at initialization time. If +// not found we look up a CSP by hashing and signing algorithms (see below) or +// use the default CSP. + +BOOLEAN g_bHasCSPName = FALSE; +WCHAR g_wszCSPName[SN_MAX_CSP_NAME + 1] = {0}; + +// Flag read from the registry at initialization time. Controls whether we use +// machine or user based key containers. +BOOLEAN g_bUseMachineKeyset = TRUE; + +#ifdef FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED +// Verification Skip Records +// +// These are entries in the registry (usually set up by SN) that control whether +// an assembly needs to pass signature verification to be considered valid (i.e. +// return TRUE from StrongNameSignatureVerification). This is useful during +// development when it's not feasible to fully sign each assembly on each build. +// Assemblies to be skipped can be specified by name and public key token, all +// assemblies with a given public key token or just all assemblies. Each entry +// can be further qualified by a list of user names to which the records +// applies. When matching against an entry, the most specific one wins. +// +// We read these entries at startup time and place them into a global, singly +// linked, NULL terminated list. + +// Structure used to represent each record we find in the registry. +struct SN_VER_REC { + SN_VER_REC *m_pNext; // Pointer to next record (or NULL) + WCHAR *m_wszAssembly; // Assembly name/public key token as a string + WCHAR *m_mszUserList; // Pointer to multi-string list of valid users (or NULL) + WCHAR *m_wszTestPublicKey; // Test public key to use during strong name verification (or NULL) +}; + +// Head of the list of entries we found in the registry during initialization. +SN_VER_REC *g_pVerificationRecords = NULL; +#endif // FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED + +#ifdef FEATURE_STRONGNAME_MIGRATION + +struct SN_REPLACEMENT_KEY_REC { + SN_REPLACEMENT_KEY_REC *m_pNext; + BYTE *m_pbReplacementKey; + ULONG m_cbReplacementKey; +}; + +struct SN_REVOCATION_REC { + SN_REVOCATION_REC *m_pNext; + BYTE *m_pbRevokedKey; + ULONG m_cbRevokedKey; + SN_REPLACEMENT_KEY_REC *m_pReplacementKeys; +}; + +SN_REVOCATION_REC *g_pRevocationRecords = NULL; + +#endif // FEATURE_STRONGNAME_MIGRATION + +#endif // #ifndef DACCESS_COMPILE + + + +#include "thetestkey.h" + +#ifndef DACCESS_COMPILE + +// The actions that can be performed upon opening a CSP with LocateCSP. +#define SN_OPEN_CONTAINER 0 +#define SN_IGNORE_CONTAINER 1 +#define SN_CREATE_CONTAINER 2 +#define SN_DELETE_CONTAINER 3 +#define SN_HASH_SHA1_ONLY 4 + +// Macro to aid in setting flags for CryptAcquireContext based on container +// actions above. +#define SN_CAC_FLAGS(_act) \ + (((_act) == SN_OPEN_CONTAINER ? 0 : \ + ((_act) == SN_HASH_SHA1_ONLY) || ((_act) == SN_IGNORE_CONTAINER) ? CRYPT_VERIFYCONTEXT : \ + (_act) == SN_CREATE_CONTAINER ? CRYPT_NEWKEYSET : \ + (_act) == SN_DELETE_CONTAINER ? CRYPT_DELETEKEYSET : \ + 0) | \ + (g_bUseMachineKeyset ? CRYPT_MACHINE_KEYSET : 0)) + +// Substitute a strong name error if the error we're wrapping is not transient +FORCEINLINE HRESULT SubstituteErrorIfNotTransient(HRESULT hrOriginal, HRESULT hrSubstitute) +{ + return Exception::IsTransient(hrOriginal) ? hrOriginal : hrSubstitute; +} + +// Private routine prototypes. +SN_THREAD_CTX *GetThreadContext(); +VOID SetStrongNameErrorInfo(DWORD dwStatus); +HCRYPTPROV LocateCSP(LPCWSTR wszKeyContainer, + DWORD dwAction, + ALG_ID uHashAlgId = 0, + ALG_ID uSignAlgId = 0); +VOID FreeCSP(HCRYPTPROV hProv); +HCRYPTPROV LookupCachedCSP(StrongNameCachedCsp cspNumber); +VOID CacheCSP(HCRYPTPROV hProv, StrongNameCachedCsp cspNumber); +BOOLEAN IsCachedCSP(HCRYPTPROV hProv); +HRESULT ReadRegistryConfig(); +BOOLEAN LoadCryptoApis(); +#ifdef FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED +HRESULT ReadVerificationRecords(); + +#ifdef FEATURE_STRONGNAME_MIGRATION +HRESULT ReadRevocationRecords(); +#endif // FEATURE_STRONGNAME_MIGRATION +SN_VER_REC *GetVerificationRecord(__in_z __deref LPWSTR wszAssemblyName, PublicKeyBlob *pPublicKey); +#endif // FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED + +BOOLEAN IsValidUser(__in_z WCHAR *mszUserList); +BOOLEAN GetKeyContainerName(LPCWSTR *pwszKeyContainer, BOOLEAN *pbTempContainer); +VOID FreeKeyContainerName(LPCWSTR wszKeyContainer, BOOLEAN bTempContainer); +HRESULT GetMetadataImport(__in const SN_LOAD_CTX *pLoadCtx, + __in mdAssembly *ptkAssembly, + __out IMDInternalImport **ppMetaDataImport); +HRESULT FindPublicKey(const SN_LOAD_CTX *pLoadCtx, + __out_ecount_opt(cchAssemblyName) LPWSTR wszAssemblyName, + DWORD cchAssemblyName, + __out PublicKeyBlob **ppPublicKey, + DWORD *pcbPublicKey = NULL); +PublicKeyBlob *GetPublicKeyFromHex(LPCWSTR wszPublicKeyHexString); +BOOLEAN RehashModules(SN_LOAD_CTX *pLoadCtx, LPCWSTR szFilePath); +HRESULT VerifySignature(SN_LOAD_CTX *pLoadCtx, + DWORD dwInFlags, + PublicKeyBlob *pRealEcmaPublicKey, + DWORD *pdwOutFlags); +HRESULT InitStrongNameCriticalSection(); +HRESULT InitStrongName(); +typedef BOOLEAN (*HashFunc)(HCRYPTHASH hHash, PBYTE start, DWORD length, DWORD flags, void* cookie); +BOOLEAN ComputeHash(SN_LOAD_CTX *pLoadCtx, HCRYPTHASH hHash, HashFunc func, void* cookie); +bool VerifyKeyMatchesAssembly(PublicKeyBlob * pAssemblySignaturePublicKey, __in_z LPCWSTR wszKeyContainer, BYTE *pbKeyBlob, ULONG cbKeyBlob, DWORD dwFlags); + +#ifdef FEATURE_STRONGNAME_MIGRATION +HRESULT GetVerifiedSignatureKey(__in SN_LOAD_CTX *pLoadCtx, __out PublicKeyBlob **ppPublicKey, __out DWORD *pcbPublicKey = NULL); +#endif // FEATURE_STRONGNAME_MIGRATION + +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) + +void DbgCount(__in_z WCHAR *szCounterName) +{ + +#ifndef FEATURE_CORECLR + if (g_fLoggingInitialized && !(g_dwLoggingFlags & 4)) + return; + + DWORD dwError = GetLastError(); + + if (!g_fLoggingInitialized) { + g_dwLoggingFlags = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MscorsnLogging); + g_fLoggingInitialized = TRUE; + } + + if (!(g_dwLoggingFlags & 4)) { + SetLastError(dwError); + return; + } + + HKEY hKey = NULL; + DWORD dwCounter = 0; + DWORD dwBytes; + + if (WszRegCreateKeyEx(HKEY_LOCAL_MACHINE, + SN_CONFIG_KEY_W W("\\Counters"), + 0, + NULL, + 0, + KEY_ALL_ACCESS, + NULL, + &hKey, + NULL) != ERROR_SUCCESS) + goto End; + + WszRegQueryValueEx(hKey, szCounterName, NULL, NULL, (BYTE*)&dwCounter, &dwBytes); + dwCounter++; + WszRegSetValueEx(hKey, szCounterName, NULL, REG_DWORD, (BYTE*)&dwCounter, sizeof(DWORD)); + + End: + if (hKey) + RegCloseKey(hKey); + SetLastError(dwError); + +#endif //#ifndef FEATURE_CORECLR + +} + + +void HexDump(BYTE *pbData, + DWORD cbData) +{ + if (g_dwLoggingFlags == 0) + return; + + DWORD dwRow, dwCol; + WCHAR wszBuffer[1024]; + WCHAR *wszPtr = wszBuffer; + +#define SN_PUSH0(_fmt) do { wszPtr += swprintf_s(wszPtr, COUNTOF(wszBuffer) - (wszPtr - wszBuffer), _fmt); } while (false) +#define SN_PUSH1(_fmt, _arg1) do { wszPtr += swprintf_s(wszPtr, COUNTOF(wszBuffer) - (wszPtr - wszBuffer), _fmt, _arg1); } while (false) + + wszBuffer[0] = W('\0'); + + for (dwRow = 0; dwRow < ((cbData + 15) / 16); dwRow++) { + SN_PUSH1(W("%08p "), pbData + (16 * dwRow)); + for (dwCol = 0; dwCol < 16; dwCol++) + if (((dwRow * 16) + dwCol) < cbData) + SN_PUSH1(W("%02X "), pbData[(dwRow * 16) + dwCol]); + else + SN_PUSH0(W(" ")); + for (dwCol = 0; dwCol < 16; dwCol++) + if (((dwRow * 16) + dwCol) < cbData) { + unsigned char c = pbData[(dwRow * 16) + dwCol]; + if ((c >= 32) && (c <= 127)) + SN_PUSH1(W("%c"), c); + else + SN_PUSH0(W(".")); + } else + SN_PUSH0(W(" ")); + SN_PUSH0(W("\n")); + } +#undef SN_PUSH1 +#undef SN_PUSH0 + + _ASSERTE(wszPtr < &wszBuffer[COUNTOF(wszBuffer)]); + + Log(W("%s"), wszBuffer); +} + +#else // _DEBUG && !DACCESS_COMPILE + +#define HexDump(x) +#define DbgCount(x) + +#endif // _DEBUG && !DACCESS_COMPILE + + +BOOLEAN CalculateSize(HCRYPTHASH hHash, PBYTE start, DWORD length, DWORD flags, void* cookie) +{ + *(size_t*)cookie += length; + return TRUE; +} + +struct CopyDataBufferDesc +{ + PBYTE pbData; + DWORD cbDataSize; +}; + +BOOLEAN CopyData(HCRYPTHASH hHash, PBYTE start, DWORD length, DWORD flags, void* cookie) +{ + _ASSERTE(cookie); + + CopyDataBufferDesc *pBuffer = reinterpret_cast<CopyDataBufferDesc *>(cookie); + _ASSERTE(pBuffer->pbData); + + memcpy_s(pBuffer->pbData, pBuffer->cbDataSize, start, length); + pBuffer->pbData += length; + + _ASSERTE(pBuffer->cbDataSize >= length); + pBuffer->cbDataSize = pBuffer->cbDataSize >= length ? pBuffer->cbDataSize - length : 0; + + return TRUE; +} + +BOOLEAN CalcHash(HCRYPTHASH hHash, PBYTE start, DWORD length, DWORD flags, void* cookie) +{ + return CryptHashData(hHash, start, length, flags); +} + +VOID +WINAPI Fls_Callback ( + IN PVOID lpFlsData + ) +{ + STATIC_CONTRACT_SO_TOLERANT; + SN_THREAD_CTX *pThreadCtx = (SN_THREAD_CTX*)lpFlsData; + if (pThreadCtx != NULL) { + for(ULONG i = 0; i < CachedCspCount; i++) + { + if (pThreadCtx->m_hProv[i]) + CryptReleaseContext(pThreadCtx->m_hProv[i], 0); + } + + delete pThreadCtx; + } +} + +HRESULT InitStrongNameCriticalSection() +{ + if (g_rStrongNameMutex) + return S_OK; + + CRITSEC_COOKIE pv = ClrCreateCriticalSection(CrstStrongName, CRST_DEFAULT); + if (pv == NULL) + return E_OUTOFMEMORY; + + if (InterlockedCompareExchangeT(&g_rStrongNameMutex, pv, NULL) != NULL) + ClrDeleteCriticalSection(pv); + return S_OK; +} + +HRESULT InitStrongName() +{ + HRESULT hr = S_OK; + if (g_bStrongNamesInitialized) + return hr; + + // Read CSP configuration info from the registry (if provided). + hr = ReadRegistryConfig(); + if (FAILED(hr)) + return hr; + + // Associate a callback for freeing our TLS data. + ClrFlsAssociateCallback(TlsIdx_StrongName, Fls_Callback); + + g_bStrongNamesInitialized = TRUE; + + return hr; +} + +// Generate a new key pair for strong name use. +SNAPI StrongNameKeyGen(LPCWSTR wszKeyContainer, // [in] desired key container name, must be a non-empty string + DWORD dwFlags, // [in] flags (see below) + BYTE **ppbKeyBlob, // [out] public/private key blob + ULONG *pcbKeyBlob) +{ + BOOLEAN retVal = FALSE; + BEGIN_ENTRYPOINT_VOIDRET; + + SN_COMMON_PROLOG(); + + if (wszKeyContainer == NULL && ppbKeyBlob == NULL) + SN_ERROR(E_INVALIDARG); + if (ppbKeyBlob != NULL && pcbKeyBlob == NULL) + SN_ERROR(E_POINTER); + + DWORD dwKeySize; + + // We set a key size of 1024 if we're using the default + // signing algorithm (RSA), otherwise we leave it at the default. + if (g_uSignAlgId == CALG_RSA_SIGN) + dwKeySize = 1024; + else + dwKeySize = 0; + + retVal = StrongNameKeyGenEx(wszKeyContainer, dwFlags, dwKeySize, ppbKeyBlob, pcbKeyBlob); + +Exit: + END_ENTRYPOINT_VOIDRET; + return retVal; +} + +// Generate a new key pair with the specified key size for strong name use. +SNAPI StrongNameKeyGenEx(LPCWSTR wszKeyContainer, // [in] desired key container name, must be a non-empty string + DWORD dwFlags, // [in] flags (see below) + DWORD dwKeySize, // [in] desired key size. + BYTE **ppbKeyBlob, // [out] public/private key blob + ULONG *pcbKeyBlob) +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + HCRYPTPROV hProv = NULL; + HCRYPTKEY hKey = NULL; + BOOLEAN bTempContainer = FALSE; + + SNLOG((W("StrongNameKeyGenEx(\"%s\", %08X, %08X, %08X, %08X)\n"), wszKeyContainer, dwFlags, dwKeySize, ppbKeyBlob, pcbKeyBlob)); + + SN_COMMON_PROLOG(); + + if (wszKeyContainer == NULL && ppbKeyBlob == NULL) + SN_ERROR(E_INVALIDARG); + if (ppbKeyBlob != NULL && pcbKeyBlob == NULL) + SN_ERROR(E_POINTER); + + // Check to see if a temporary container name is needed. + _ASSERTE((wszKeyContainer != NULL) || !(dwFlags & SN_LEAVE_KEY)); + if (!GetKeyContainerName(&wszKeyContainer, &bTempContainer)) + { + goto Exit; + } + + // Open a CSP and container. + hProv = LocateCSP(wszKeyContainer, SN_CREATE_CONTAINER); + if (!hProv) + goto Error; + + + // Generate the new key pair, try for exportable first. + // Note: The key size in bits is encoded in the upper + // 16-bits of a DWORD (and OR'd together with other flags for the + // CryptGenKey call). + if (!CryptGenKey(hProv, g_uKeySpec, (dwKeySize << 16) | CRYPT_EXPORTABLE, &hKey)) { + SNLOG((W("Couldn't create exportable key, trying for non-exportable: %08X\n"), GetLastError())); + if (!CryptGenKey(hProv, g_uKeySpec, dwKeySize << 16, &hKey)) { + SNLOG((W("Couldn't create key pair: %08X\n"), GetLastError())); + goto Error; + } + } + +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) + if (g_bHasCSPName) { + ALG_ID uAlgId; + DWORD dwAlgIdLen = sizeof(uAlgId); + // Check that signature algorithm used was the one we expected. + if (CryptGetKeyParam(hKey, KP_ALGID, (BYTE*)&uAlgId, &dwAlgIdLen, 0)) { + _ASSERTE(uAlgId == g_uSignAlgId); + } else + SNLOG((W("Failed to get key params: %08X\n"), GetLastError())); + } +#endif // _DEBUG + + // If the user wants the key pair back, attempt to export it. + if (ppbKeyBlob) { + + // Calculate length of blob first; + if (!CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, NULL, pcbKeyBlob)) { + SNLOG((W("Couldn't export key pair: %08X\n"), GetLastError())); + goto Error; + } + + // Allocate a buffer of the right size. + *ppbKeyBlob = new (nothrow) BYTE[*pcbKeyBlob]; + if (*ppbKeyBlob == NULL) { + SetLastError(E_OUTOFMEMORY); + goto Error; + } + + // Export the key pair. + if (!CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, *ppbKeyBlob, pcbKeyBlob)) { + SNLOG((W("Couldn't export key pair: %08X\n"), GetLastError())); + delete[] *ppbKeyBlob; + *ppbKeyBlob = NULL; + goto Error; + } + } + + // Destroy the key handle (but not the key pair itself). + CryptDestroyKey(hKey); + hKey = NULL; + + // Release the CSP. + FreeCSP(hProv); + + // If the user didn't explicitly want to keep the key pair around, delete the + // key container. + if (!(dwFlags & SN_LEAVE_KEY) || bTempContainer) + LocateCSP(wszKeyContainer, SN_DELETE_CONTAINER); + + // Free temporary key container name if allocated. + FreeKeyContainerName(wszKeyContainer, bTempContainer); + retVal = TRUE; + goto Exit; + + Error: + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + if (hKey) + CryptDestroyKey(hKey); + if (hProv) { + FreeCSP(hProv); + if (!(dwFlags & SN_LEAVE_KEY) || bTempContainer) + LocateCSP(wszKeyContainer, SN_DELETE_CONTAINER); + } + FreeKeyContainerName(wszKeyContainer, bTempContainer); + + Exit: + END_ENTRYPOINT_VOIDRET; + return retVal; +} + + +// Import key pair into a key container. +SNAPI StrongNameKeyInstall(LPCWSTR wszKeyContainer,// [in] desired key container name, must be a non-empty string + BYTE *pbKeyBlob, // [in] public/private key pair blob + ULONG cbKeyBlob) +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + HCRYPTPROV hProv = NULL; + HCRYPTKEY hKey = NULL; + + SNLOG((W("StrongNameKeyInstall(\"%s\", %08X, %08X)\n"), wszKeyContainer, pbKeyBlob, cbKeyBlob)); + + SN_COMMON_PROLOG(); + + if (wszKeyContainer == NULL) + SN_ERROR(E_POINTER); + if (pbKeyBlob == NULL) + SN_ERROR(E_POINTER); + if (cbKeyBlob == 0) + SN_ERROR(E_INVALIDARG); + + // Open a CSP and container. + hProv = LocateCSP(wszKeyContainer, SN_CREATE_CONTAINER); + if (!hProv) { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + goto Exit; + } + + // Import the key pair. + if (!CryptImportKey(hProv, + pbKeyBlob, + cbKeyBlob, + 0, 0, &hKey)) { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + FreeCSP(hProv); + goto Exit; + } + + // Release the CSP. + FreeCSP(hProv); + retVal = TRUE; +Exit: + + END_ENTRYPOINT_VOIDRET; + return retVal; +} + + +// Delete a key pair. +SNAPI StrongNameKeyDelete(LPCWSTR wszKeyContainer) // [in] desired key container name +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + HCRYPTPROV hProv; + + SNLOG((W("StrongNameKeyDelete(\"%s\")\n"), wszKeyContainer)); + + SN_COMMON_PROLOG(); + + if (wszKeyContainer == NULL) + SN_ERROR(E_POINTER); + + // Open and delete the named container. + hProv = LocateCSP(wszKeyContainer, SN_DELETE_CONTAINER); + if (hProv) { + // Returned handle isn't actually valid in the delete case, so we're + // finished. + retVal = TRUE; + } else { + SetStrongNameErrorInfo(CORSEC_E_CONTAINER_NOT_FOUND); + retVal = FALSE; + } +Exit: + + END_ENTRYPOINT_VOIDRET; + return retVal; + +} + +// Retrieve the public portion of a key pair. +SNAPI StrongNameGetPublicKey (LPCWSTR wszKeyContainer, // [in] desired key container name + BYTE *pbKeyBlob, // [in] public/private key blob (optional) + ULONG cbKeyBlob, + BYTE **ppbPublicKeyBlob, // [out] public key blob + ULONG *pcbPublicKeyBlob) +{ + LIMITED_METHOD_CONTRACT; + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + retVal = StrongNameGetPublicKeyEx( + wszKeyContainer, + pbKeyBlob, + cbKeyBlob, + ppbPublicKeyBlob, + pcbPublicKeyBlob, + 0, + 0); + + END_ENTRYPOINT_VOIDRET; + return retVal; +} + +// Holder for any HCRYPTPROV handles allocated by the strong name APIs +typedef Wrapper<HCRYPTPROV, DoNothing, FreeCSP, 0> HandleStrongNameCspHolder; + + +SNAPI StrongNameGetPublicKeyEx (LPCWSTR wszKeyContainer, // [in] desired key container name + BYTE *pbKeyBlob, // [in] public/private key blob (optional) + ULONG cbKeyBlob, + BYTE **ppbPublicKeyBlob, // [out] public key blob + ULONG *pcbPublicKeyBlob, + ULONG uHashAlgId, + ULONG uReserved) // reserved for future use as uSigAlgId (signature algorithm id) +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + HandleStrongNameCspHolder hProv(NULL); + CapiKeyHolder hKey(NULL); + DWORD dwKeyLen; + PublicKeyBlob *pKeyBlob; + DWORD dwSigAlgIdLen; + + SNLOG((W("StrongNameGetPublicKeyEx(\"%s\", %08X, %08X, %08X, %08X, %08X)\n"), wszKeyContainer, pbKeyBlob, cbKeyBlob, ppbPublicKeyBlob, pcbPublicKeyBlob, uHashAlgId)); + + SN_COMMON_PROLOG(); + + if (wszKeyContainer == NULL && pbKeyBlob == NULL) + SN_ERROR(E_INVALIDARG); + if (pbKeyBlob != NULL && !(StrongNameIsEcmaKey(pbKeyBlob, cbKeyBlob) || StrongNameIsValidKeyPair(pbKeyBlob, cbKeyBlob))) + SN_ERROR(E_INVALIDARG); + if (ppbPublicKeyBlob == NULL) + SN_ERROR(E_POINTER); + if (pcbPublicKeyBlob == NULL) + SN_ERROR(E_POINTER); + if (uReserved != 0) + SN_ERROR(E_INVALIDARG); + + bool fHashAlgorithmValid; + fHashAlgorithmValid = uHashAlgId == 0 || + (GET_ALG_CLASS(uHashAlgId) == ALG_CLASS_HASH && GET_ALG_SID(uHashAlgId) >= ALG_SID_SHA1 && GET_ALG_SID(uHashAlgId) <= ALG_SID_SHA_512); + if(!fHashAlgorithmValid) + SN_ERROR(E_INVALIDARG); + + if(uHashAlgId == 0) + uHashAlgId = g_uHashAlgId; + + // If we're handed a platform neutral public key, just hand it right back to + // the user. Well, hand back a copy at least. + if (pbKeyBlob && cbKeyBlob && SN_IS_NEUTRAL_KEY(pbKeyBlob)) { + *pcbPublicKeyBlob = sizeof(g_rbNeutralPublicKey); + *ppbPublicKeyBlob = (BYTE*)g_rbNeutralPublicKey; + retVal = TRUE; + goto Exit; + } + + // Open a CSP. Create a key container if a public/private key blob is + // provided, otherwise we assume a key container already exists. + if (pbKeyBlob) + hProv = LocateCSP(NULL, SN_IGNORE_CONTAINER); + else + hProv = LocateCSP(wszKeyContainer, SN_OPEN_CONTAINER); + if (!hProv) + goto Error; + + // If a key blob was provided, import the key pair into the container. + if (pbKeyBlob) { + if (!CryptImportKey(hProv, + pbKeyBlob, + cbKeyBlob, + 0, 0, &hKey)) + goto Error; + } else { +#if !defined(FEATURE_CORESYSTEM) + // Else fetch the signature key pair from the container. + if (!CryptGetUserKey(hProv, g_uKeySpec, &hKey)) + goto Error; +#else // FEATURE_CORESYSTEM + SetLastError(E_NOTIMPL); + goto Error; +#endif // !FEATURE_CORESYSTEM + } + + // Determine the length of the public key part as a blob. + if (!CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, NULL, &dwKeyLen)) + goto Error; + + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:22011) // Suppress this PREFast warning which gets triggered by the offset macro expansion. +#endif + // And then the length of the PublicKeyBlob structure we return to the + // caller. + *pcbPublicKeyBlob = offsetof(PublicKeyBlob, PublicKey) + dwKeyLen; +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + + // Allocate a large enough buffer. + *ppbPublicKeyBlob = new (nothrow) BYTE[*pcbPublicKeyBlob]; + if (*ppbPublicKeyBlob == NULL) { + SetLastError(E_OUTOFMEMORY); + goto Error; + } + + pKeyBlob = (PublicKeyBlob*)*ppbPublicKeyBlob; + + // Extract the public part as a blob. + if (!CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, pKeyBlob->PublicKey, &dwKeyLen)) { + delete[] *ppbPublicKeyBlob; + *ppbPublicKeyBlob = NULL; + goto Error; + } + + // Extract key's signature algorithm and store it in the key blob. + dwSigAlgIdLen = sizeof(unsigned int); + ALG_ID SigAlgID; + if (!CryptGetKeyParam(hKey, KP_ALGID, (BYTE*)&SigAlgID, &dwSigAlgIdLen, 0)) { + delete[] *ppbPublicKeyBlob; + *ppbPublicKeyBlob = NULL; + goto Error; + } + SET_UNALIGNED_VAL32(&pKeyBlob->SigAlgID, SigAlgID); + + // Fill in the other public key blob fields. + SET_UNALIGNED_VAL32(&pKeyBlob->HashAlgID, uHashAlgId); + SET_UNALIGNED_VAL32(&pKeyBlob->cbPublicKey, dwKeyLen); + + retVal = TRUE; + goto Exit; + +Error: + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); +Exit: + END_ENTRYPOINT_VOIDRET; + return retVal; +} + +// Hash and sign a manifest. +SNAPI StrongNameSignatureGeneration(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + LPCWSTR wszKeyContainer, // [in] desired key container name + BYTE *pbKeyBlob, // [in] public/private key blob (optional) + ULONG cbKeyBlob, + BYTE **ppbSignatureBlob, // [out] signature blob + ULONG *pcbSignatureBlob) +{ + BOOL fRetVal = FALSE; + BEGIN_ENTRYPOINT_VOIDRET; + fRetVal = StrongNameSignatureGenerationEx(wszFilePath, wszKeyContainer, pbKeyBlob, cbKeyBlob, ppbSignatureBlob, pcbSignatureBlob, 0); + END_ENTRYPOINT_VOIDRET; + return fRetVal; +} + +HRESULT FindAssemblySignaturePublicKey(const SN_LOAD_CTX *pLoadCtx, + __out PublicKeyBlob **ppPublicKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr; + StrongNameBufferHolder<PublicKeyBlob> result = NULL; + + IfFailRet(FindPublicKey(pLoadCtx, NULL, 0, &result)); + +#ifdef FEATURE_STRONGNAME_MIGRATION + PublicKeyBlob *pSignaturePublicKey = NULL; + IfFailRet(GetVerifiedSignatureKey((SN_LOAD_CTX*) pLoadCtx, &pSignaturePublicKey)); + + if(hr != S_FALSE) + { + result = pSignaturePublicKey; + } +#endif // FEATURE_STRONGNAME_MIGRATION + + *ppPublicKey = result.Extract(); + + return S_OK; +} + +SNAPI StrongNameSignatureGenerationEx(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + LPCWSTR wszKeyContainer, // [in] desired key container name + BYTE *pbKeyBlob, // [in] public/private key blob (optional) + ULONG cbKeyBlob, + BYTE **ppbSignatureBlob, // [out] signature blob + ULONG *pcbSignatureBlob, + DWORD dwFlags) // [in] modifer flags +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + HandleStrongNameCspHolder hProv(NULL); + CapiHashHolder hHash(NULL); + NewArrayHolder<BYTE> pbSig(NULL); + ULONG cbSig = 0; + SN_LOAD_CTX sLoadCtx; + BOOLEAN bImageLoaded = FALSE; + ALG_ID uHashAlgId; + StrongNameBufferHolder<PublicKeyBlob> pSignatureKey = NULL; + + SNLOG((W("StrongNameSignatureGenerationEx(\"%s\", \"%s\", %08X, %08X, %08X, %08X, %08X)\n"), wszFilePath, wszKeyContainer, pbKeyBlob, cbKeyBlob, ppbSignatureBlob, pcbSignatureBlob, dwFlags)); + + SN_COMMON_PROLOG(); + + uHashAlgId = g_uHashAlgId; + + if (pbKeyBlob != NULL && !StrongNameIsValidKeyPair(pbKeyBlob, cbKeyBlob)) + SN_ERROR(E_INVALIDARG); + if (ppbSignatureBlob != NULL && pcbSignatureBlob == NULL) + SN_ERROR(E_POINTER); + + if (wszFilePath != NULL) { + // Map the assembly into memory. + sLoadCtx.m_fReadOnly = FALSE; + if (!LoadAssembly(&sLoadCtx, wszFilePath)) + goto Error; + bImageLoaded = TRUE; + + // If we've asked to recalculate the file hashes of linked modules we have + // to load the metadata engine and search for file references. + if (dwFlags & SN_SIGN_ALL_FILES) + if (!RehashModules(&sLoadCtx, wszFilePath)) + goto Error; + + // If no key pair is provided, then we were only called to re-compute the hashes of + // linked modules in the assembly. + if (!wszKeyContainer && !pbKeyBlob) + { + retVal = TRUE; + goto Exit; + } + + HRESULT hr; + if(FAILED(hr = FindAssemblySignaturePublicKey(&sLoadCtx, &pSignatureKey))) + { + SN_ERROR(hr); + } + + // Ecma key has an algorithm of zero, so we ignore that case. + ALG_ID uKeyHashAlgId = GET_UNALIGNED_VAL32(&pSignatureKey->HashAlgID); + if(uKeyHashAlgId != 0) + { + uHashAlgId = uKeyHashAlgId; + } + } + + if (wszKeyContainer || pbKeyBlob) { // We have a key pair in a container, or in a blob + // Open a CSP. If a public/private key blob is provided, use CRYPT_VERIFYCONTEXT, + // otherwise we assume the key container already exists. + if (pbKeyBlob) + hProv = LocateCSP(NULL, SN_IGNORE_CONTAINER, uHashAlgId); + else + hProv = LocateCSP(wszKeyContainer, SN_OPEN_CONTAINER, uHashAlgId); + + if (hProv.GetValue() == NULL) + goto Error; + + // If a key blob was provided, import the key pair into the container. + // This might be the real key or the test-sign key. In the case of test signing, + // there's no way to specify hash algorithm, so we use the one from the + // assembly signature public key (in the metadata table, or the migration attribute). + if (pbKeyBlob) { + // The provider holds a reference to the key, so we don't need to + // keep one around. + CapiKeyHolder hKey(NULL); + if (!CryptImportKey(hProv, + pbKeyBlob, + cbKeyBlob, + 0, + 0, + &hKey)) + goto Error; + } + + // Create a hash object. + if (!CryptCreateHash(hProv, uHashAlgId, 0, 0, &hHash)) + goto Error; + + // Compute size of the signature blob. + if (!CryptSignHashW(hHash, g_uKeySpec, NULL, 0, NULL, &cbSig)) + goto Error; + + // If the caller only wants the size of the signature, return it now and + // exit. + // RSA signature length is independent of the hash size, so hash algorithm + // doesn't matter here (we don't know the algorithm if the assembly path was passed in as NULL) + if (wszFilePath == NULL) { + *pcbSignatureBlob = cbSig; + retVal = TRUE; + goto Exit; + } + } + + // Verify that the public key of the assembly being signed matches the private key we're signing with + if ((wszKeyContainer != NULL || pbKeyBlob != NULL) && !VerifyKeyMatchesAssembly(pSignatureKey, wszKeyContainer, pbKeyBlob, cbKeyBlob, dwFlags)) + { + SetLastError(StrongNameErrorInfo()); + goto Error; + } + + // We set a bit in the header to indicate we're fully signing the assembly. + if (!(dwFlags & SN_TEST_SIGN)) + sLoadCtx.m_pCorHeader->Flags |= VAL32(COMIMAGE_FLAGS_STRONGNAMESIGNED); + else + sLoadCtx.m_pCorHeader->Flags &= ~VAL32(COMIMAGE_FLAGS_STRONGNAMESIGNED); + + // Destroy the old hash object and create a new one + // because CryptoAPI says you can't reuse a hash once you've signed it + // Note that this seems to work with MS-based CSPs but breaks on + // at least newer nCipher CSPs. + if (!CryptCreateHash(hProv, uHashAlgId, 0, 0, &hHash)) + goto Error; + + // Compute a hash over the image. + if (!ComputeHash(&sLoadCtx, hHash, CalcHash, NULL)) + goto Error; + + // Allocate the blob. + pbSig = new (nothrow) BYTE[cbSig]; + if (pbSig == NULL) { + SetLastError(E_OUTOFMEMORY); + goto Error; + } + + // Compute a signature blob over the hash of the manifest. + if (!CryptSignHashW(hHash, g_uKeySpec, NULL, 0, pbSig, &cbSig)) + goto Error; + + // Check the signature size + if (sLoadCtx.m_cbSignature != cbSig) { + SetLastError(CORSEC_E_SIGNATURE_MISMATCH); + goto Error; + } + + // If the user hasn't asked for the signature to be returned as a pointer, write it to file. + if (!ppbSignatureBlob) + { + memcpy_s(sLoadCtx.m_pbSignature, sLoadCtx.m_cbSignature, pbSig, cbSig); + + // + // Memory-mapped IO in Windows doesn't guarantee that it will update + // the file's "Modified" timestamp, so we update it ourselves. + // + _ASSERTE(sLoadCtx.m_hFile != INVALID_HANDLE_VALUE); + + FILETIME ft; + SYSTEMTIME st; + + GetSystemTime(&st); + + // We don't care if updating the timestamp fails for any reason. + if(SystemTimeToFileTime(&st, &ft)) + { + SetFileTime(sLoadCtx.m_hFile, (LPFILETIME) NULL, (LPFILETIME) NULL, &ft); + } + } + + // Unmap the image (automatically recalculates and updates the image + // checksum). + bImageLoaded = FALSE; + if (!UnloadAssembly(&sLoadCtx)) + goto Error; + + if (ppbSignatureBlob) { + *ppbSignatureBlob = pbSig.Extract(); + *pcbSignatureBlob = cbSig; + } + + retVal = TRUE; + goto Exit; + +Error: + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); +Exit: + if (bImageLoaded) + UnloadAssembly(&sLoadCtx); + END_ENTRYPOINT_VOIDRET; + return retVal; +} + +// +// Generate the digest of a delay signed assembly, which can be signed with StrongNameDigestSign. The digest +// algorithm is determined from the HashAlgID of the assembly's public key blob. +// +// Parameters: +// wszFilePath - path to the delay signed assembly to generate the digest of +// ppbDigestBlob - on success this will point to a buffer that contains the digest of the wszFilePath +// assembly. This buffer should be freed with StrongNameFreeBuffer. +// pcbDigestBlob - on success this will point to the size of the digest buffer in *ppbDigestBlob +// dwFlags - flags used to control signing. This is the same set of flags used by +// StrongNameSignatureGenerationEx +// + +bool StrongNameDigestGenerate_Internal(_In_z_ LPCWSTR wszFilePath, + _Outptr_result_bytebuffer_(*pcbDigestBlob) BYTE** ppbDigestBlob, + _Out_ ULONG* pcbDigestBlob, + DWORD dwFlags) +{ + // Load up the assembly and find its public key - this tells us which hash algorithm we need to use + // Note that it cannot be loaded read-only since we need to toggle the fully siged bit in order to + // calculate the correct hash for the signature. + StrongNameAssemblyLoadHolder assembly(wszFilePath, false /* read only */); + if (!assembly.IsLoaded()) + { + return false; + } + + // If we were asked to do a full rehashing of all modules that needs to be done before calculating the digest + if ((dwFlags & SN_SIGN_ALL_FILES) == SN_SIGN_ALL_FILES) + { + if (!RehashModules(assembly.GetLoadContext(), wszFilePath)) + { + return false; + } + } + + // During signature verification, the fully signed bit will be set in the assembly's COR header. + // Therefore, when calculating the digest of the assembly we must toggle this bit in order to make the + // digest match what will be calculated during verificaiton. However, we do not want to persist the + // bit flip on disk, since we're not actually signing the assembly now. We'll save the current COR + // flags, flip the bit for digesting, and then restore the COR flags before we finish calculating the + // digest. + class AssemblyFlagsHolder + { + private: + IMAGE_COR20_HEADER* m_pHeader; + DWORD m_originalFlags; + + public: + AssemblyFlagsHolder(_In_ IMAGE_COR20_HEADER* pHeader) + : m_pHeader(pHeader), + m_originalFlags(pHeader->Flags) + { + } + + ~AssemblyFlagsHolder() + { + m_pHeader->Flags = m_originalFlags; + } + } flagsHolder(assembly.GetLoadContext()->m_pCorHeader); + assembly.GetLoadContext()->m_pCorHeader->Flags |= VAL32(COMIMAGE_FLAGS_STRONGNAMESIGNED); + + StrongNameBufferHolder<PublicKeyBlob> pPublicKey; + HRESULT hrPublicKey = FindAssemblySignaturePublicKey(assembly.GetLoadContext(), &pPublicKey); + if (FAILED(hrPublicKey)) + { + SetStrongNameErrorInfo(hrPublicKey); + return false; + } + + // Generate the digest of the assembly + HandleStrongNameCspHolder hProv(LocateCSP(nullptr, SN_IGNORE_CONTAINER, pPublicKey->HashAlgID)); + if (!hProv) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + CapiHashHolder hHash; + if (!CryptCreateHash(hProv, pPublicKey->HashAlgID, NULL, 0, &hHash)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + if (!ComputeHash(assembly.GetLoadContext(), hHash, CalcHash, nullptr)) + { + return false; + } + + // Figure out how big the resulting digest is so that we can pass it back out + DWORD hashSize = 0; + DWORD dwordSize = sizeof(hashSize); + if (!CryptGetHashParam(hHash, HP_HASHSIZE, reinterpret_cast<BYTE*>(&hashSize), &dwordSize, 0)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + StrongNameBufferHolder<BYTE> pbHash(new (nothrow)BYTE[hashSize]); + if (!pbHash) + { + SetStrongNameErrorInfo(E_OUTOFMEMORY); + return false; + } + + if (!CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &hashSize, 0)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + *ppbDigestBlob = pbHash.Extract(); + *pcbDigestBlob = hashSize; + return true; +} + +SNAPI StrongNameDigestGenerate(_In_z_ LPCWSTR wszFilePath, + _Outptr_result_bytebuffer_(*pcbDigestBlob) BYTE** ppbDigestBlob, + _Out_ ULONG* pcbDigestBlob, + DWORD dwFlags) +{ + BOOLEAN retVal = FALSE; + BEGIN_ENTRYPOINT_VOIDRET; + + SNLOG((W("StrongNameDigestGenerate(\"%s\", %08X, %08X, %04X)\n"), wszFilePath, ppbDigestBlob, pcbDigestBlob, dwFlags)); + + SN_COMMON_PROLOG(); + if (wszFilePath == nullptr) + SN_ERROR(E_POINTER); + if (ppbDigestBlob == nullptr) + SN_ERROR(E_POINTER); + if (pcbDigestBlob == nullptr) + SN_ERROR(E_POINTER); + + retVal = StrongNameDigestGenerate_Internal(wszFilePath, ppbDigestBlob, pcbDigestBlob, dwFlags); + + END_ENTRYPOINT_VOIDRET; + +Exit: + return retVal; +} + +// +// Sign an the digest of an assembly calculated by StrongNameDigestGenerate +// +// Parameters: +// wszKeyContainer - name of the key container that holds the key pair used to generate the signature. If +// both a key container and key blob are specified, the key container name is ignored. +// pbKeyBlob - raw key pair to be used to generate the signature. If both a key pair and a key +// container are given, the key blob will be used. +// cbKeyBlob - size of the key pair in pbKeyBlob +// pbDigestBlob - digest of the assembly, calculated by StrongNameDigestGenerate +// cbDigestBlob - size of the digest blob +// hashAlgId - algorithm ID of the hash algorithm used to generate the digest blob +// ppbSignatureBlob - on success this will point to a buffer that contains a signature over the blob. This +// buffer should be freed with StrongNameFreeBuffer. +// pcbSignatureBlob - on success this will point to the size of the signature blob in *ppbSignatureBlob +// dwFlags - flags used to control signing. This is the same set of flags used by +// StrongNameSignatureGenerationEx +// + +bool StrongNameDigestSign_Internal(_In_opt_z_ LPCWSTR wszKeyContainer, + _In_reads_bytes_opt_(cbKeyBlob) BYTE* pbKeyBlob, + ULONG cbKeyBlob, + _In_reads_bytes_(cbDigestBlob) BYTE* pbDigestBlob, + ULONG cbDigestBlob, + DWORD hashAlgId, + _Outptr_result_bytebuffer_(*pcbSignatureBlob) BYTE** ppbSignatureBlob, + _Out_ ULONG* pcbSignatureBlob, + DWORD dwFlags) +{ + // + // Get the key we'll be signing with loaded into CAPI + // + + HandleStrongNameCspHolder hProv; + CapiKeyHolder hKey; + if (pbKeyBlob != nullptr) + { + hProv = LocateCSP(nullptr, SN_IGNORE_CONTAINER, hashAlgId); + + if (hProv != NULL) + { + if (!CryptImportKey(hProv, pbKeyBlob, cbKeyBlob, 0, 0, &hKey)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + } + } + else + { + hProv = LocateCSP(wszKeyContainer, SN_OPEN_CONTAINER, hashAlgId); + } + + if (!hProv) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + // + // Get the pre-calculated digest loaded into a CAPI Hash object + // + + CapiHashHolder hHash; + if (!CryptCreateHash(hProv, hashAlgId, 0, 0, &hHash)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + DWORD hashSize = 0; + DWORD cbHashSize = sizeof(hashSize); + if (!CryptGetHashParam(hHash, HP_HASHSIZE, reinterpret_cast<BYTE*>(&hashSize), &cbHashSize, 0)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + if (hashSize != cbDigestBlob) + { + SetStrongNameErrorInfo(NTE_BAD_HASH); + return false; + } + + if (!CryptSetHashParam(hHash, HP_HASHVAL, pbDigestBlob, 0)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + // + // Sign the hash + // + + DWORD cbSignature = 0; + if (!CryptSignHashW(hHash, g_uKeySpec, nullptr, 0, nullptr, &cbSignature)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + // CAPI has a quirk where some CSPs do not allow you to sign a hash object once you've asked for the size + // of the signature. To work in those cases, we must create a new hash object to sign. + if (!CryptCreateHash(hProv, hashAlgId, 0, 0, &hHash)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + if (!CryptGetHashParam(hHash, HP_HASHSIZE, reinterpret_cast<BYTE*>(&hashSize), &cbHashSize, 0)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + if (hashSize != cbDigestBlob) + { + SetStrongNameErrorInfo(NTE_BAD_HASH); + return false; + } + + if (!CryptSetHashParam(hHash, HP_HASHVAL, pbDigestBlob, 0)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + // Now that we've got a fresh hash object to sign, we can compute the final signature + StrongNameBufferHolder<BYTE> pbSignature(new (nothrow)BYTE[cbSignature]); + if (pbSignature == nullptr) + { + SetStrongNameErrorInfo(E_OUTOFMEMORY); + return false; + } + + if (!CryptSignHashW(hHash, g_uKeySpec, nullptr, 0, pbSignature, &cbSignature)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + *ppbSignatureBlob = pbSignature.Extract(); + *pcbSignatureBlob = cbSignature; + return true; +} + +SNAPI StrongNameDigestSign(_In_opt_z_ LPCWSTR wszKeyContainer, + _In_reads_bytes_opt_(cbKeyBlob) BYTE* pbKeyBlob, + ULONG cbKeyBlob, + _In_reads_bytes_(cbDigestBlob) BYTE* pbDigestBlob, + ULONG cbDigestBlob, + DWORD hashAlgId, + _Outptr_result_bytebuffer_(*pcbSignatureBlob) BYTE** ppbSignatureBlob, + _Out_ ULONG* pcbSignatureBlob, + DWORD dwFlags) +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + SNLOG((W("StrongNameDigestSign(\"%s\", %08X, %04X, %08X, %04X, %04X, %08X, %08X, %04X)\n"), wszKeyContainer, pbKeyBlob, cbKeyBlob, pbDigestBlob, cbDigestBlob, hashAlgId, ppbSignatureBlob, pcbSignatureBlob, dwFlags)); + SN_COMMON_PROLOG(); + + if (wszKeyContainer == nullptr && pbKeyBlob == nullptr) + SN_ERROR(E_POINTER); + if (pbKeyBlob != nullptr && !StrongNameIsValidKeyPair(pbKeyBlob, cbKeyBlob)) + SN_ERROR(E_INVALIDARG); + if (pbDigestBlob == nullptr) + SN_ERROR(E_POINTER); + if (ppbSignatureBlob == nullptr) + SN_ERROR(E_POINTER); + if (pcbSignatureBlob == nullptr) + SN_ERROR(E_POINTER); + + *ppbSignatureBlob = nullptr; + *pcbSignatureBlob = 0; + + retVal = StrongNameDigestSign_Internal(wszKeyContainer, pbKeyBlob, cbKeyBlob, pbDigestBlob, cbDigestBlob, hashAlgId, ppbSignatureBlob, pcbSignatureBlob, dwFlags); + + END_ENTRYPOINT_VOIDRET; +Exit: + return retVal; +} + +// +// Embed a digest signature generated with StrongNameDigestSign into a delay signed assembly, completing +// the signing process for that assembly. +// +// Parameters: +// wszFilePath - path to the assembly to sign +// pbSignatureBlob - signature blob to embed in the assembly +// cbSignatureBlob - size of the signature blob +// + +bool StrongNameDigestEmbed_Internal(_In_z_ LPCWSTR wszFilePath, + _In_reads_bytes_(cbSignatureBlob) BYTE* pbSignatureBlob, + ULONG cbSignatureBlob) +{ + StrongNameAssemblyLoadHolder assembly(wszFilePath, false); + if (!assembly.IsLoaded()) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + + memcpy_s(assembly.GetLoadContext()->m_pbSignature, assembly.GetLoadContext()->m_cbSignature, pbSignatureBlob, cbSignatureBlob); + assembly.GetLoadContext()->m_pCorHeader->Flags |= VAL32(COMIMAGE_FLAGS_STRONGNAMESIGNED); + + FILETIME ft = { 0 }; + SYSTEMTIME st = { 0 }; + GetSystemTime(&st); + if (SystemTimeToFileTime(&st, &ft)) + { + SetFileTime(assembly.GetLoadContext()->m_hFile, nullptr, nullptr, &ft); + } + + return true; +} + +SNAPI StrongNameDigestEmbed(_In_z_ LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly to update + _In_reads_bytes_(cbSignatureBlob) BYTE* pbSignatureBlob, // [in] signatuer blob for the assembly + ULONG cbSignatureBlob) +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + SNLOG((W("StrongNameDigestEmbed(\"%s\", %08X, %04X)\n"), wszFilePath, pbSignatureBlob, cbSignatureBlob)); + SN_COMMON_PROLOG(); + + if (wszFilePath == nullptr) + SN_ERROR(E_POINTER); + if (pbSignatureBlob == nullptr) + SN_ERROR(E_POINTER); + + retVal = StrongNameDigestEmbed_Internal(wszFilePath, pbSignatureBlob, cbSignatureBlob); + + END_ENTRYPOINT_VOIDRET; +Exit: + return retVal; +} + +// Create a strong name token from an assembly file. +SNAPI StrongNameTokenFromAssembly(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + BYTE **ppbStrongNameToken, // [out] strong name token + ULONG *pcbStrongNameToken) +{ + BOOL fRetValue = FALSE; + BEGIN_ENTRYPOINT_VOIDRET; + fRetValue = StrongNameTokenFromAssemblyEx(wszFilePath, + ppbStrongNameToken, + pcbStrongNameToken, + NULL, + NULL); + END_ENTRYPOINT_VOIDRET; + return fRetValue; +} + +// Create a strong name token from an assembly file and additionally return the full public key. +SNAPI StrongNameTokenFromAssemblyEx(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + BYTE **ppbStrongNameToken, // [out] strong name token + ULONG *pcbStrongNameToken, + BYTE **ppbPublicKeyBlob, // [out] public key blob + ULONG *pcbPublicKeyBlob) +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + SN_LOAD_CTX sLoadCtx; + BOOLEAN fMapped = FALSE; + BOOLEAN fSetErrorInfo = TRUE; + PublicKeyBlob *pPublicKey = NULL; + HRESULT hrKey = S_OK; + + SNLOG((W("StrongNameTokenFromAssemblyEx(\"%s\", %08X, %08X, %08X, %08X)\n"), wszFilePath, ppbStrongNameToken, pcbStrongNameToken, ppbPublicKeyBlob, pcbPublicKeyBlob)); + + SN_COMMON_PROLOG(); + + if (wszFilePath == NULL) + SN_ERROR(E_POINTER); + if (ppbStrongNameToken == NULL) + SN_ERROR(E_POINTER); + if (pcbStrongNameToken == NULL) + SN_ERROR(E_POINTER); + + // Map the assembly into memory. + sLoadCtx.m_fReadOnly = TRUE; + if (!LoadAssembly(&sLoadCtx, wszFilePath)) + goto Error; + fMapped = TRUE; + + // Read the public key used to sign the assembly from the assembly metadata. + hrKey = FindPublicKey(&sLoadCtx, NULL, 0, &pPublicKey); + if (FAILED(hrKey)) + { + SetStrongNameErrorInfo(hrKey); + fSetErrorInfo = FALSE; + goto Error; + } + + // Unload the assembly. + fMapped = FALSE; + if (!UnloadAssembly(&sLoadCtx)) + goto Error; + + // Now we have a public key blob, we can call our more direct API to do the + // actual work. + if (!StrongNameTokenFromPublicKey((BYTE*)pPublicKey, + SN_SIZEOF_KEY(pPublicKey), + ppbStrongNameToken, + pcbStrongNameToken)) { + fSetErrorInfo = FALSE; + goto Error; + } + + if (pcbPublicKeyBlob) + *pcbPublicKeyBlob = SN_SIZEOF_KEY(pPublicKey); + + // Return public key information. + if (ppbPublicKeyBlob) + *ppbPublicKeyBlob = (BYTE*)pPublicKey; + else + delete [] (BYTE*)pPublicKey; + + retVal = TRUE; + goto Exit; + + Error: + if (fSetErrorInfo) + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + if (pPublicKey) + delete [] (BYTE*)pPublicKey; + if (fMapped) + UnloadAssembly(&sLoadCtx); + +Exit: + END_ENTRYPOINT_VOIDRET; + + return retVal; +} + +bool StrongNameSignatureVerificationEx2_Internal(LPCWSTR wszFilePath, + BOOLEAN fForceVerification, + BYTE *pbEcmaPublicKey, + DWORD cbEcmaPublicKey, + BOOLEAN *pfWasVerified) +{ + StrongNameAssemblyLoadHolder assembly(wszFilePath, true); + if (!assembly.IsLoaded()) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + return false; + } + else + { + DWORD dwOutFlags = 0; + HRESULT hrVerify = VerifySignature(assembly.GetLoadContext(), + SN_INFLAG_INSTALL | SN_INFLAG_ALL_ACCESS | (fForceVerification ? SN_INFLAG_FORCE_VER : 0), + reinterpret_cast<PublicKeyBlob *>(pbEcmaPublicKey), + &dwOutFlags); + if (FAILED(hrVerify)) + { + SetStrongNameErrorInfo(hrVerify); + return false; + } + + if (pfWasVerified) + { + *pfWasVerified = (dwOutFlags & SN_OUTFLAG_WAS_VERIFIED) != 0; + } + + return true; + } +} + +// +// Verify the signature of a strongly named assembly, providing a mapping from the ECMA key to a real key +// +// Arguments: +// wszFilePath - valid path to the PE file for the assembly +// fForceVerification - verify even if settings in the registry disable it +// pbEcmaPublicKey - mapping from the ECMA public key to the real key used for verification +// cbEcmaPublicKey - length of the real ECMA public key +// fWasVerified - [out] set to false if verify succeeded due to registry settings +// +// Return Value: +// TRUE if the signature was successfully verified, FALSE otherwise +// + +SNAPI StrongNameSignatureVerificationEx2(LPCWSTR wszFilePath, + BOOLEAN fForceVerification, + BYTE *pbEcmaPublicKey, + DWORD cbEcmaPublicKey, + BOOLEAN *pfWasVerified) +{ + BOOLEAN retVal = FALSE; + BEGIN_ENTRYPOINT_VOIDRET; + + SNLOG((W("StrongNameSignatureVerificationEx2(\"%s\", %d, %08X, %08X, %08X)\n"), wszFilePath, fForceVerification, pbEcmaPublicKey, cbEcmaPublicKey, pfWasVerified)); + + SN_COMMON_PROLOG(); + + if (wszFilePath == NULL) + SN_ERROR(E_POINTER); + if (pbEcmaPublicKey == NULL) + SN_ERROR(E_POINTER); + if (!StrongNameIsValidPublicKey(pbEcmaPublicKey, cbEcmaPublicKey, false)) + SN_ERROR(CORSEC_E_INVALID_PUBLICKEY); + + retVal = StrongNameSignatureVerificationEx2_Internal(wszFilePath, fForceVerification, pbEcmaPublicKey, cbEcmaPublicKey, pfWasVerified); + +Exit: + END_ENTRYPOINT_VOIDRET; + return retVal; +} + +// Verify a strong name/manifest against a public key blob. +SNAPI StrongNameSignatureVerificationEx(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + BOOLEAN fForceVerification, // [in] verify even if settings in the registry disable it + BOOLEAN *pfWasVerified) // [out] set to false if verify succeeded due to registry settings +{ + BOOLEAN fRet = FALSE; + BEGIN_ENTRYPOINT_VOIDRET; + + fRet = StrongNameSignatureVerificationEx2(wszFilePath, + fForceVerification, + const_cast<BYTE *>(g_rbTheKey), + COUNTOF(g_rbTheKey), + pfWasVerified); + + END_ENTRYPOINT_VOIDRET; + return fRet; +} + + +// Verify a strong name/manifest against a public key blob. +SNAPI StrongNameSignatureVerification(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + DWORD dwInFlags, // [in] flags modifying behaviour + DWORD *pdwOutFlags) // [out] additional output info +{ + BOOLEAN retVal = TRUE; + + BEGIN_ENTRYPOINT_VOIDRET; + + SN_LOAD_CTX sLoadCtx; + BOOLEAN fMapped = FALSE; + + SNLOG((W("StrongNameSignatureVerification(\"%s\", %08X, %08X, %08X)\n"), wszFilePath, dwInFlags, pdwOutFlags)); + + SN_COMMON_PROLOG(); + + if (wszFilePath == NULL) + SN_ERROR(E_POINTER); + + // Map the assembly into memory. + sLoadCtx.m_fReadOnly = TRUE; + if (LoadAssembly(&sLoadCtx, wszFilePath)) + { + fMapped = TRUE; + } + else + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + retVal = FALSE; + } + + // Go to common code to process the verification. + if (fMapped) + { + HRESULT hrVerify = VerifySignature(&sLoadCtx, dwInFlags, reinterpret_cast<PublicKeyBlob *>(const_cast<BYTE *>(g_rbTheKey)), pdwOutFlags); + if (FAILED(hrVerify)) + { + SetStrongNameErrorInfo(hrVerify); + retVal = FALSE; + } + + // Unmap the image. Only set error information if VerifySignature succeeded, since we do not want to + // overwrite its error information with the error code from UnloadAssembly. + if (!UnloadAssembly(&sLoadCtx)) + { + if (retVal) + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + retVal = FALSE; + } + } + + // SN_COMMON_PROLOG requires an Exit location +Exit: + END_ENTRYPOINT_VOIDRET; + return retVal; +} + + +// Verify a strong name/manifest against a public key blob when the assembly is +// already memory mapped. +SNAPI StrongNameSignatureVerificationFromImage(BYTE *pbBase, // [in] base address of mapped manifest file + DWORD dwLength, // [in] length of mapped image in bytes + DWORD dwInFlags, // [in] flags modifying behaviour + DWORD *pdwOutFlags) // [out] additional output info +{ + BOOLEAN retVal = TRUE; + + BEGIN_ENTRYPOINT_VOIDRET + + SN_LOAD_CTX sLoadCtx; + BOOLEAN fMapped = FALSE; + + SNLOG((W("StrongNameSignatureVerificationFromImage(%08X, %08X, %08X, %08X)\n"), pbBase, dwLength, dwInFlags, pdwOutFlags)); + + SN_COMMON_PROLOG(); + + if (pbBase == NULL) + SN_ERROR(E_POINTER); + + // We don't need to map the image, it's already in memory. But we do need to + // set up a load context for some of the following routines. LoadAssembly + // copes with this case for us. + sLoadCtx.m_pbBase = pbBase; + sLoadCtx.m_dwLength = dwLength; + sLoadCtx.m_fReadOnly = TRUE; + if (LoadAssembly(&sLoadCtx, NULL, dwInFlags)) + { + fMapped = TRUE; + } + else + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + retVal = FALSE; + } + + if (fMapped) + { + // Go to common code to process the verification. + HRESULT hrVerify = VerifySignature(&sLoadCtx, dwInFlags, reinterpret_cast<PublicKeyBlob *>(const_cast<BYTE *>(g_rbTheKey)), pdwOutFlags); + if (FAILED(hrVerify)) + { + SetStrongNameErrorInfo(hrVerify); + retVal = FALSE; + } + + // Unmap the image. + if (!UnloadAssembly(&sLoadCtx)) + { + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + retVal = FALSE; + } + } + + // SN_COMMON_PROLOG requires an Exit location +Exit: + END_ENTRYPOINT_VOIDRET; + return retVal; +} + +// Find portions of an assembly to hash. +BOOLEAN CollectBlob(SN_LOAD_CTX *pLoadCtx, PBYTE pbBlob, DWORD* pcbBlob) +{ + // Calculate the required size + DWORD cbRequired = 0; + BOOLEAN bRetval = ComputeHash(pLoadCtx, (HCRYPTHASH)INVALID_HANDLE_VALUE, CalculateSize, &cbRequired); + if (!bRetval) + return FALSE; + if (*pcbBlob < cbRequired) { + *pcbBlob = cbRequired; + SetLastError( E_INVALIDARG ); + return FALSE; + } + + CopyDataBufferDesc buffer = { pbBlob, *pcbBlob }; + if (!ComputeHash(pLoadCtx, (HCRYPTHASH)INVALID_HANDLE_VALUE, CopyData, &buffer)) + return FALSE; + + *pcbBlob = cbRequired; + return TRUE; +} + +// ensure that the symbol will be exported properly +extern "C" SNAPI StrongNameGetBlob(LPCWSTR wszFilePath, + PBYTE pbBlob, + DWORD *cbBlob); + +SNAPI StrongNameGetBlob(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + PBYTE pbBlob, // [in] buffer to fill with blob + DWORD *cbBlob) // [in/out] size of buffer/number of bytes put into buffer +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + SN_LOAD_CTX sLoadCtx; + BOOLEAN fMapped = FALSE; + + SNLOG((W("StrongNameGetBlob(\"%s\", %08X, %08X)\n"), wszFilePath, pbBlob, cbBlob)); + + SN_COMMON_PROLOG(); + + if (wszFilePath == NULL) + SN_ERROR(E_POINTER); + if (pbBlob == NULL) + SN_ERROR(E_POINTER); + if (cbBlob == NULL) + SN_ERROR(E_POINTER); + + // Map the assembly into memory. + sLoadCtx.m_fReadOnly = TRUE; + if (!LoadAssembly(&sLoadCtx, wszFilePath, 0, FALSE)) + goto Error; + fMapped = TRUE; + + if (!CollectBlob(&sLoadCtx, pbBlob, cbBlob)) + goto Error; + + // Unmap the image. + fMapped = FALSE; + if (!UnloadAssembly(&sLoadCtx)) + goto Error; + + retVal = TRUE; + goto Exit; + + Error: + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + if (fMapped) + UnloadAssembly(&sLoadCtx); +Exit: + END_ENTRYPOINT_VOIDRET; + return retVal; +} + +// ensure that the symbol will be exported properly +extern "C" SNAPI StrongNameGetBlobFromImage(BYTE *pbBase, + DWORD dwLength, + PBYTE pbBlob, + DWORD *cbBlob); + +SNAPI StrongNameGetBlobFromImage(BYTE *pbBase, // [in] base address of mapped manifest file + DWORD dwLength, // [in] length of mapped image in bytes + PBYTE pbBlob, // [in] buffer to fill with blob + DWORD *cbBlob) // [in/out] size of buffer/number of bytes put into buffer +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + SN_LOAD_CTX sLoadCtx; + BOOLEAN fMapped = FALSE; + + + SNLOG((W("StrongNameGetBlobFromImage(%08X, %08X, %08X, %08X)\n"), pbBase, dwLength, pbBlob, cbBlob)); + + SN_COMMON_PROLOG(); + + if (pbBase == NULL) + SN_ERROR(E_POINTER); + if (pbBlob == NULL) + SN_ERROR(E_POINTER); + if (cbBlob == NULL) + SN_ERROR(E_POINTER); + + // We don't need to map the image, it's already in memory. But we do need to + // set up a load context for some of the following routines. LoadAssembly + // copes with this case for us. + sLoadCtx.m_pbBase = pbBase; + sLoadCtx.m_dwLength = dwLength; + sLoadCtx.m_fReadOnly = TRUE; + if (!LoadAssembly(&sLoadCtx, NULL, 0, FALSE)) + goto Error; + fMapped = TRUE; + + // Go to common code to process the verification. + if (!CollectBlob(&sLoadCtx, pbBlob, cbBlob)) + goto Error; + + // Unmap the image. + fMapped = FALSE; + if (!UnloadAssembly(&sLoadCtx)) + goto Error; + + retVal = TRUE; + goto Exit; + + Error: + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + if (fMapped) + UnloadAssembly(&sLoadCtx); + +Exit: + END_ENTRYPOINT_VOIDRET; + + return retVal; +} + + +// Verify that two assemblies differ only by signature blob. +SNAPI StrongNameCompareAssemblies(LPCWSTR wszAssembly1, // [in] file name of first assembly + LPCWSTR wszAssembly2, // [in] file name of second assembly + DWORD *pdwResult) // [out] result of comparison +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + + SN_LOAD_CTX sLoadCtx1; + SN_LOAD_CTX sLoadCtx2; + size_t dwSkipOffsets[3]; + size_t dwSkipLengths[3]; + BOOLEAN bMappedAssem1 = FALSE; + BOOLEAN bMappedAssem2 = FALSE; + BOOLEAN bIdentical; + BOOLEAN bSkipping; + DWORD i, j; + + + + SNLOG((W("StrongNameCompareAssemblies(\"%s\", \"%s\", %08X)\n"), wszAssembly1, wszAssembly2, pdwResult)); + + SN_COMMON_PROLOG(); + + if (wszAssembly1 == NULL) + SN_ERROR(E_POINTER); + if (wszAssembly2 == NULL) + SN_ERROR(E_POINTER); + if (pdwResult == NULL) + SN_ERROR(E_POINTER); + + // Map each assembly. + sLoadCtx1.m_fReadOnly = TRUE; + if (!LoadAssembly(&sLoadCtx1, wszAssembly1)) + goto Error; + bMappedAssem1 = TRUE; + + sLoadCtx2.m_fReadOnly = TRUE; + if (!LoadAssembly(&sLoadCtx2, wszAssembly2)) + goto Error; + bMappedAssem2 = TRUE; + + // If the files aren't even the same length then they must be different. + if (sLoadCtx1.m_dwLength != sLoadCtx2.m_dwLength) + goto ImagesDiffer; + + // Check that the signatures are located at the same offset and are the same + // length in each assembly. + if (sLoadCtx1.m_pCorHeader->StrongNameSignature.VirtualAddress != + sLoadCtx2.m_pCorHeader->StrongNameSignature.VirtualAddress) + goto ImagesDiffer; + if (sLoadCtx1.m_pCorHeader->StrongNameSignature.Size != + sLoadCtx2.m_pCorHeader->StrongNameSignature.Size) + goto ImagesDiffer; + + // Set up list of image ranges to skip in the upcoming comparison. + // First there's the signature blob. + dwSkipOffsets[0] = sLoadCtx1.m_pbSignature - sLoadCtx1.m_pbBase; + dwSkipLengths[0] = sLoadCtx1.m_cbSignature; + + // Then there's the checksum. + if (sLoadCtx1.m_pNtHeaders->OptionalHeader.Magic != sLoadCtx2.m_pNtHeaders->OptionalHeader.Magic) + goto ImagesDiffer; + if (sLoadCtx1.m_pNtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)) + dwSkipOffsets[1] = (BYTE*)&((IMAGE_NT_HEADERS32*)sLoadCtx1.m_pNtHeaders)->OptionalHeader.CheckSum - sLoadCtx1.m_pbBase; + else if (sLoadCtx1.m_pNtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC)) + dwSkipOffsets[1] = (BYTE*)&((IMAGE_NT_HEADERS64*)sLoadCtx1.m_pNtHeaders)->OptionalHeader.CheckSum - sLoadCtx1.m_pbBase; + else { + SetLastError(CORSEC_E_INVALID_IMAGE_FORMAT); + goto Error; + } + dwSkipLengths[1] = sizeof(DWORD); + + // Skip the COM+ 2.0 PE header extension flags field. It's updated by the + // signing operation. + dwSkipOffsets[2] = (BYTE*)&sLoadCtx1.m_pCorHeader->Flags - sLoadCtx1.m_pbBase; + dwSkipLengths[2] = sizeof(DWORD); + + // Compare the two mapped images, skipping the ranges we defined above. + bIdentical = TRUE; + for (i = 0; i < sLoadCtx1.m_dwLength; i++) { + + // Determine if we're skipping the check on the current byte. + bSkipping = FALSE; + for (j = 0; j < (sizeof(dwSkipOffsets) / sizeof(dwSkipOffsets[0])); j++) + if ((i >= dwSkipOffsets[j]) && (i < (dwSkipOffsets[j] + dwSkipLengths[j]))) { + bSkipping = TRUE; + break; + } + + // Perform comparisons as desired. + if (sLoadCtx1.m_pbBase[i] != sLoadCtx2.m_pbBase[i]) + if (bSkipping) + bIdentical = FALSE; + else + goto ImagesDiffer; + } + + // The assemblies are the same. + *pdwResult = bIdentical ? SN_CMP_IDENTICAL : SN_CMP_SIGONLY; + + UnloadAssembly(&sLoadCtx1); + UnloadAssembly(&sLoadCtx2); + + retVal = TRUE; + goto Exit; + + Error: + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + if (bMappedAssem1) + UnloadAssembly(&sLoadCtx1); + if (bMappedAssem2) + UnloadAssembly(&sLoadCtx2); + goto Exit; + + ImagesDiffer: + if (bMappedAssem1) + UnloadAssembly(&sLoadCtx1); + if (bMappedAssem2) + UnloadAssembly(&sLoadCtx2); + *pdwResult = SN_CMP_DIFFERENT; + retVal = TRUE; + +Exit: + END_ENTRYPOINT_VOIDRET; + + return retVal; +} + + +// Compute the size of buffer needed to hold a hash for a given hash algorithm. +SNAPI StrongNameHashSize(ULONG ulHashAlg, // [in] hash algorithm + DWORD *pcbSize) // [out] size of the hash in bytes +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + DWORD dwSize; + + + SNLOG((W("StrongNameHashSize(%08X, %08X)\n"), ulHashAlg, pcbSize)); + + SN_COMMON_PROLOG(); + + if (pcbSize == NULL) + SN_ERROR(E_POINTER); + + // Default hashing algorithm ID if necessary. + if (ulHashAlg == 0) + ulHashAlg = CALG_SHA1; + + // Find a CSP supporting the required algorithm. + hProv = LocateCSP(NULL, SN_IGNORE_CONTAINER, ulHashAlg); + if (!hProv) + goto Error; + + // Create a hash object. + if (!CryptCreateHash(hProv, ulHashAlg, 0, 0, &hHash)) + goto Error; + + // And ask for the size of the hash. + dwSize = sizeof(DWORD); + if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)pcbSize, &dwSize, 0)) + goto Error; + + // Cleanup and exit. + CryptDestroyHash(hHash); + FreeCSP(hProv); + + retVal = TRUE; + goto Exit; + + Error: + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + if (hHash) + CryptDestroyHash(hHash); + if (hProv) + FreeCSP(hProv); + + Exit: + + END_ENTRYPOINT_VOIDRET; + + return retVal; +} + + +// Compute the size that needs to be allocated for a signature in an assembly. +SNAPI StrongNameSignatureSize(BYTE *pbPublicKeyBlob, // [in] public key blob + ULONG cbPublicKeyBlob, + DWORD *pcbSize) // [out] size of the signature in bytes +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + + PublicKeyBlob *pPublicKey = (PublicKeyBlob*)pbPublicKeyBlob; + ALG_ID uHashAlgId; + ALG_ID uSignAlgId; + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + HCRYPTKEY hKey = NULL; + LPCWSTR wszKeyContainer = NULL; + BOOLEAN bTempContainer = FALSE; + DWORD dwKeyLen; + DWORD dwBytes; + + SNLOG((W("StrongNameSignatureSize(%08X, %08X, %08X)\n"), pbPublicKeyBlob, cbPublicKeyBlob, pcbSize)); + + SN_COMMON_PROLOG(); + + if (pbPublicKeyBlob == NULL) + SN_ERROR(E_POINTER); + if (!StrongNameIsValidPublicKey(pbPublicKeyBlob, cbPublicKeyBlob, false)) + SN_ERROR(CORSEC_E_INVALID_PUBLICKEY); + if (pcbSize == NULL) + SN_ERROR(E_POINTER); + + // Special case neutral key. + if (SN_IS_NEUTRAL_KEY(pPublicKey)) + pPublicKey = SN_THE_KEY(); + + // Determine hashing/signing algorithms. + uHashAlgId = GET_UNALIGNED_VAL32(&pPublicKey->HashAlgID); + uSignAlgId = GET_UNALIGNED_VAL32(&pPublicKey->SigAlgID); + + // Default hashing and signing algorithm IDs if necessary. + if (uHashAlgId == 0) + uHashAlgId = CALG_SHA1; + if (uSignAlgId == 0) + uSignAlgId = CALG_RSA_SIGN; + + // Create a temporary key container name. + if (!GetKeyContainerName(&wszKeyContainer, &bTempContainer)) + goto Exit; + + // Find a CSP supporting the required algorithms and create a temporary key + // container. + hProv = LocateCSP(wszKeyContainer, SN_CREATE_CONTAINER, uHashAlgId, uSignAlgId); + if (!hProv) + goto Error; + + // Import the public key (we need to do this in order to determine the key + // length reliably). + if (!CryptImportKey(hProv, + pPublicKey->PublicKey, + GET_UNALIGNED_VAL32(&pPublicKey->cbPublicKey), + 0, 0, &hKey)) + goto Error; + + // Query the key attributes (it's the length we're interested in). + dwBytes = sizeof(dwKeyLen); + if (!CryptGetKeyParam(hKey, KP_KEYLEN, (BYTE*)&dwKeyLen, &dwBytes, 0)) + goto Error; + + // Delete the key container. + if (LocateCSP(wszKeyContainer, SN_DELETE_CONTAINER) == NULL) { + SetLastError(CORSEC_E_CONTAINER_NOT_FOUND); + goto Error; + } + + // Take shortcut for the typical case + if ((uSignAlgId == CALG_RSA_SIGN) && (dwKeyLen % 8 == 0)) { + // The signature size known for CALG_RSA_SIGN + *pcbSize = dwKeyLen / 8; + } + else { + // Recreate the container so we can create a temporary key pair. + hProv = LocateCSP(wszKeyContainer, SN_CREATE_CONTAINER, uHashAlgId, uSignAlgId); + if (!hProv) + goto Error; + + // Create the temporary key pair. + if (!CryptGenKey(hProv, g_uKeySpec, dwKeyLen << 16, &hKey)) + goto Error; + + // Create a hash. + if (!CryptCreateHash(hProv, uHashAlgId, 0, 0, &hHash)) + goto Error; + + // Compute size of the signature blob. + if (!CryptSignHashW(hHash, g_uKeySpec, NULL, 0, NULL, pcbSize)) + goto Error; + CryptDestroyHash(hHash); + + if (bTempContainer) + LocateCSP(wszKeyContainer, SN_DELETE_CONTAINER); + } + + SNLOG((W("Signature size for hashalg %08X, %08X key (%08X bits) is %08X bytes\n"), uHashAlgId, uSignAlgId, dwKeyLen, *pcbSize)); + + CryptDestroyKey(hKey); + FreeCSP(hProv); + FreeKeyContainerName(wszKeyContainer, bTempContainer); + + retVal = TRUE; + goto Exit; + + Error: + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); + if (hHash) + CryptDestroyHash(hHash); + if (hKey) + CryptDestroyKey(hKey); + if (hProv) + FreeCSP(hProv); + if (bTempContainer) + LocateCSP(wszKeyContainer, SN_DELETE_CONTAINER); + FreeKeyContainerName(wszKeyContainer, bTempContainer); + + Exit: + END_ENTRYPOINT_VOIDRET; + + return retVal; +} + +// Locate CSP based on criteria specified in the registry (CSP name etc). +// Optionally create or delete a named key container within that CSP. +HCRYPTPROV LocateCSP(LPCWSTR wszKeyContainer, + DWORD dwAction, + ALG_ID uHashAlgId, + ALG_ID uSignAlgId) +{ + DWORD i; + DWORD dwType; + WCHAR wszName[SN_MAX_CSP_NAME + 1]; + DWORD dwNameLength; + HCRYPTPROV hProv; + BOOLEAN bFirstAlg; + BOOLEAN bFoundHash; + BOOLEAN bFoundSign; + PROV_ENUMALGS rAlgs; + HCRYPTPROV hRetProv; + DWORD dwAlgsLen; + + DWORD dwProvType = g_uProvType; + + // If a CSP name has been provided (and we're not opening a CSP just to do a + // SHA1 hash or a verification), open the CSP directly. + if (g_bHasCSPName && + (dwAction != SN_HASH_SHA1_ONLY)) + { + if (StrongNameCryptAcquireContext(&hProv, + wszKeyContainer ? wszKeyContainer : NULL, + g_wszCSPName, + dwProvType, + SN_CAC_FLAGS(dwAction))) + return (dwAction == SN_DELETE_CONTAINER) ? (HCRYPTPROV)~0 : hProv; + else { + SNLOG((W("Failed to open CSP '%s': %08X\n"), g_wszCSPName, GetLastError())); + return NULL; + } + } + + // Set up hashing and signing algorithms to look for based upon input + // parameters. Or if these haven't been supplied use the configured defaults + // instead. + if (uHashAlgId == 0) + uHashAlgId = g_uHashAlgId; + if (uSignAlgId == 0) + uSignAlgId = g_uSignAlgId; + + // If default hashing and signing algorithms have been selected (SHA1 and + // RSA), we select the default CSP for the RSA_FULL type. + // For SHA2 and RSA, we select the default CSP For RSA_AES. + // Otherwise, you just get the first CSP that supports the algorithms + // you specified (with no guarantee that the selected CSP is a default of any type). + // This is because we have no way of forcing the enumeration to just give us default + // CSPs. + bool fUseDefaultCsp = false; + StrongNameCachedCsp cachedCspNumber = None; + + // We know what container to use for SHA1 algorithms with RSA + if (((uHashAlgId == CALG_SHA1) && (uSignAlgId == CALG_RSA_SIGN)) || + (dwAction == SN_HASH_SHA1_ONLY)) { + fUseDefaultCsp = true; + cachedCspNumber = Sha1CachedCsp; + dwProvType = PROV_RSA_FULL; + + SNLOG((W("Attempting to open default provider\n"))); + } + + // We know what container to use for SHA2 algorithms with RSA + if ((uHashAlgId == CALG_SHA_256 || uHashAlgId == CALG_SHA_384 || uHashAlgId == CALG_SHA_512) + && uSignAlgId == CALG_RSA_SIGN) { + fUseDefaultCsp = true; + cachedCspNumber = Sha2CachedCsp; + dwProvType = PROV_RSA_AES; + + SNLOG((W("Attempting to open default SHA2 provider\n"))); + } + + if (fUseDefaultCsp) + { + // If we're not trying to create/open/delete a key container, see if a + // CSP is cached. + if (wszKeyContainer == NULL && dwAction != SN_DELETE_CONTAINER) { + hProv = LookupCachedCSP(cachedCspNumber); + if (hProv) { + SNLOG((W("Found provider in cache\n"))); + return hProv; + } + } + if (StrongNameCryptAcquireContext(&hProv, + wszKeyContainer ? wszKeyContainer : NULL, + NULL, + dwProvType, + SN_CAC_FLAGS(dwAction))) { + // If we're not trying to create/open/delete a key container, cache + // the CSP returned. + if (wszKeyContainer == NULL && dwAction != SN_DELETE_CONTAINER) + CacheCSP(hProv, cachedCspNumber); + return (dwAction == SN_DELETE_CONTAINER) ? (HCRYPTPROV)~0 : hProv; + } else { + SNLOG((W("Failed to open: %08X\n"), GetLastError())); + return NULL; + } + } + + HRESULT hr = InitStrongNameCriticalSection(); + if (FAILED(hr)) { + SetLastError(hr); + return NULL; + } + + // Some crypto APIs are non thread safe (e.g. enumerating CSP + // hashing/signing algorithms). Use a mutex to serialize these operations. + // The following usage is GC-safe and exception-safe: + { + CRITSEC_Holder csh(g_rStrongNameMutex); + + for (i = 0; ; i++) { + + // Enumerate all CSPs. + dwNameLength = sizeof(wszName); + if (CryptEnumProvidersW(i, 0, 0, &dwType, wszName, &dwNameLength)) { + + // Open the currently selected CSP. + SNLOG((W("Considering CSP '%s'\n"), wszName)); + if (StrongNameCryptAcquireContext(&hProv, + NULL, + wszName, + dwType, + CRYPT_SILENT | + CRYPT_VERIFYCONTEXT | + (g_bUseMachineKeyset ? CRYPT_MACHINE_KEYSET : 0))) { + + // Enumerate all the algorithms the CSP supports. + bFirstAlg = TRUE; + bFoundHash = FALSE; + bFoundSign = FALSE; + for (;;) { + + dwAlgsLen = sizeof(rAlgs); + if (CryptGetProvParam(hProv, + PP_ENUMALGS, (BYTE*)&rAlgs, &dwAlgsLen, + bFirstAlg ? CRYPT_FIRST : 0)) { + + if (rAlgs.aiAlgid == uHashAlgId) + bFoundHash = TRUE; + else if (rAlgs.aiAlgid == uSignAlgId) + bFoundSign = TRUE; + + if (bFoundHash && bFoundSign) { + + // Found a CSP that supports the required + // algorithms. Re-open the context with access to + // the required key container. + + SNLOG((W("CSP matches\n"))); + + if (StrongNameCryptAcquireContext(&hRetProv, + wszKeyContainer ? wszKeyContainer : NULL, + wszName, + dwType, + CRYPT_SILENT | + SN_CAC_FLAGS(dwAction))) { + CryptReleaseContext(hProv, 0); + return (dwAction == SN_DELETE_CONTAINER) ? (HCRYPTPROV)~0 : hRetProv; + } else { + SNLOG((W("Failed to re-open for container: %08X\n"), GetLastError())); + break; + } + } + + bFirstAlg = FALSE; + + } else { + _ASSERTE(GetLastError() == ERROR_NO_MORE_ITEMS); + break; + } + + } + + CryptReleaseContext(hProv, 0); + + } else + SNLOG((W("Failed to open CSP: %08X\n"), GetLastError())); + + } else if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + + } + // csh for g_rStrongNameMutex goes out of scope here + } + + // No matching CSP found. + SetLastError(CORSEC_E_NO_SUITABLE_CSP); + return NULL; +} + + +// Release a CSP acquired through LocateCSP. +VOID FreeCSP(HCRYPTPROV hProv) +{ + // If the CSP is currently cached, don't release it yet. + if (!IsCachedCSP(hProv)) + CryptReleaseContext(hProv, 0); +} + +// Locate a cached CSP for this thread. +HCRYPTPROV LookupCachedCSP(StrongNameCachedCsp cspNumber) +{ + SN_THREAD_CTX *pThreadCtx = GetThreadContext(); + if (pThreadCtx == NULL) + return NULL; + return pThreadCtx->m_hProv[cspNumber]; +} + + +// Update the CSP cache for this thread (freeing any CSP displaced). +VOID CacheCSP(HCRYPTPROV hProv, StrongNameCachedCsp cspNumber) +{ + SN_THREAD_CTX *pThreadCtx = GetThreadContext(); + if (pThreadCtx == NULL) + return; + if (pThreadCtx->m_hProv[cspNumber]) + CryptReleaseContext(pThreadCtx->m_hProv[cspNumber], 0); + pThreadCtx->m_hProv[cspNumber] = hProv; +} + + +// Determine whether a given CSP is currently cached. +BOOLEAN IsCachedCSP(HCRYPTPROV hProv) +{ + SN_THREAD_CTX *pThreadCtx = GetThreadContext(); + if (pThreadCtx == NULL) + return FALSE; + for (ULONG i = 0; i < CachedCspCount; i++) + { + if(pThreadCtx->m_hProv[i] == hProv) + { + return TRUE; + } + } + return FALSE; +} + +// rehash all files in a multi-module assembly +BOOLEAN RehashModules (SN_LOAD_CTX *pLoadCtx, LPCWSTR wszFilePath) { + HRESULT hr; + ULONG ulHashAlg; + mdAssembly tkAssembly; + HENUMInternal hFileEnum; + mdFile tkFile; + LPCSTR pszFile; + BYTE *pbFileHash; + DWORD cbFileHash; + NewArrayHolder<BYTE> pbNewFileHash(NULL); + DWORD cbNewFileHash = 0; + DWORD cchDirectory; + DWORD cchFullFile; + CHAR szFullFile[MAX_PATH + 1]; + WCHAR wszFullFile[MAX_PATH + 1]; + LPCWSTR pszSlash; + DWORD cchFile; + IMDInternalImport *pMetaDataImport = NULL; + + // Determine the directory the assembly lives in (this is where we'll + // look for linked files). + if (((pszSlash = wcsrchr(wszFilePath, W('\\'))) != NULL) || ((pszSlash = wcsrchr(wszFilePath, W('/'))) != NULL)) { + cchDirectory = (DWORD) (pszSlash - wszFilePath + 1); + cchDirectory = WszWideCharToMultiByte(CP_UTF8, 0, wszFilePath, cchDirectory, szFullFile, MAX_PATH, NULL, NULL); + if (cchDirectory >= MAX_PATH) { + SNLOG((W("Assembly directory name too long\n"))); + hr = ERROR_BUFFER_OVERFLOW; + goto Error; + } + } else + cchDirectory = 0; + + // Open the scope on the mapped image. + if (FAILED(hr = GetMetadataImport(pLoadCtx, &tkAssembly, &pMetaDataImport))) + { + goto Error; + } + + // Determine the hash algorithm used for file references. + if (FAILED(hr = pMetaDataImport->GetAssemblyProps( + tkAssembly, // [IN] The Assembly for which to get the properties + NULL, // [OUT] Pointer to the Originator blob + NULL, // [OUT] Count of bytes in the Originator Blob + &ulHashAlg, // [OUT] Hash Algorithm + NULL, // [OUT] Buffer to fill with name + NULL, // [OUT] Assembly MetaData + NULL))) // [OUT] Flags + { + SNLOG((W("Failed to get assembly 0x%08X info, %08X\n"), tkAssembly, hr)); + goto Error; + } + + // Enumerate all file references. + if (FAILED(hr = pMetaDataImport->EnumInit(mdtFile, mdTokenNil, &hFileEnum))) + { + SNLOG((W("Failed to enumerate linked files, %08X\n"), hr)); + goto Error; + } + + for (; pMetaDataImport->EnumNext(&hFileEnum, &tkFile); ) { + + // Determine the file name and the location of the hash. + if (FAILED(hr = pMetaDataImport->GetFileProps( + tkFile, + &pszFile, + (const void **)&pbFileHash, + &cbFileHash, + NULL))) + { + SNLOG((W("Failed to get file 0x%08X info, %08X\n"), tkFile, hr)); + goto Error; + } + + // Build the full filename by appending to the assembly directory we + // calculated earlier. + cchFile = (DWORD) strlen(pszFile); + if ((cchFile + cchDirectory) >= COUNTOF(szFullFile)) { + pMetaDataImport->EnumClose(&hFileEnum); + SNLOG((W("Linked file name too long (%S)\n"), pszFile)); + hr = ERROR_BUFFER_OVERFLOW; + goto Error; + } + memcpy_s(&szFullFile[cchDirectory], COUNTOF(szFullFile) - cchDirectory, pszFile, cchFile + 1); + + // Allocate enough buffer for the new hash. + if (cbNewFileHash < cbFileHash) { + pbNewFileHash = new (nothrow) BYTE[cbFileHash]; + if (pbNewFileHash == NULL) { + hr = E_OUTOFMEMORY; + goto Error; + } + cbNewFileHash = cbFileHash; + } + + cchFullFile = WszMultiByteToWideChar(CP_UTF8, 0, szFullFile, -1, wszFullFile, MAX_PATH); + if (cchFullFile == 0 || cchFullFile >= MAX_PATH) { + pMetaDataImport->EnumClose(&hFileEnum); + SNLOG((W("Assembly directory name too long\n"))); + hr = ERROR_BUFFER_OVERFLOW; + goto Error; + } + + // Compute a new hash for the file. + if (FAILED(hr = GetHashFromFileW(wszFullFile, + (unsigned*)&ulHashAlg, + pbNewFileHash, + cbNewFileHash, + &cbNewFileHash))) { + pMetaDataImport->EnumClose(&hFileEnum); + SNLOG((W("Failed to get compute file hash, %08X\n"), hr)); + goto Error; + } + + // The new hash has to be the same size (since we used the same + // algorithm). + _ASSERTE(cbNewFileHash == cbFileHash); + + // We make the assumption here that the pointer to the file hash + // handed to us by the metadata is a direct pointer and not a + // buffered copy. If this changes, we'll need a new metadata API to + // support updates of this type. + memcpy_s(pbFileHash, cbFileHash, pbNewFileHash, cbFileHash); + } + + pMetaDataImport->EnumClose(&hFileEnum); + pMetaDataImport->Release(); + return TRUE; + +Error: + if (pMetaDataImport) + pMetaDataImport->Release(); + if (pbNewFileHash) + pbNewFileHash.Release(); + SetLastError(hr); + return FALSE; +} + +// +// Check that the public key portion of an assembly's identity matches the private key that it is being +// signed with. +// +// Arguments: +// pAssemblySignaturePublicKey - Assembly signature public key blob +// wszKeyContainer - Key container holding the key the assembly is signed with +// dwFlags - SN_ECMA_SIGN if the assembly is being ECMA signed, SN_TEST_SIGN if it is being test signed +// +// Return Value: +// true if the assembly's public key matches the private key in wszKeyContainer, otherwise false +// + +bool VerifyKeyMatchesAssembly(PublicKeyBlob * pAssemblySignaturePublicKey, __in_z LPCWSTR wszKeyContainer, BYTE *pbKeyBlob, ULONG cbKeyBlob, DWORD dwFlags) +{ + _ASSERTE(wszKeyContainer != NULL || pbKeyBlob != NULL); + + // If we're test signing, then the assembly's public key will not match the private key by design. + // Since there's nothing to check, we can quit early. + if ((dwFlags & SN_TEST_SIGN) == SN_TEST_SIGN) + { + return true; + } + + if (SN_IS_NEUTRAL_KEY(pAssemblySignaturePublicKey)) + { + // If we're ECMA signing an assembly with the ECMA public key, then by definition the key matches. + if ((dwFlags & SN_ECMA_SIGN) == SN_ECMA_SIGN) + { + return true; + } + + // Swap the real public key in for ECMA signing + pAssemblySignaturePublicKey = SN_THE_KEY(); + } + + // Otherwise, we need to check that the public key from the key container matches the public key from + // the assembly. + StrongNameBufferHolder<BYTE> pbSignaturePublicKey = NULL; + DWORD cbSignaturePublicKey; + if (!StrongNameGetPublicKeyEx(wszKeyContainer, pbKeyBlob, cbKeyBlob, &pbSignaturePublicKey, &cbSignaturePublicKey, GET_UNALIGNED_VAL32(&pAssemblySignaturePublicKey->HashAlgID), 0 /*Should be GET_UNALIGNED_VAL32(&pAssemblySignaturePublicKey->HashAlgID) once we support different signature algorithms*/)) + { + // We failed to get the public key for the key in the given key container. StrongNameGetPublicKey + // has already set the error information, so we can just return false here without resetting it. + return false; + } + _ASSERTE(!pbSignaturePublicKey.IsNull() && pAssemblySignaturePublicKey != NULL); + + // Do a raw compare on the public key blobs to see if they match + if (SN_SIZEOF_KEY(reinterpret_cast<PublicKeyBlob *>(pbSignaturePublicKey.GetValue())) == SN_SIZEOF_KEY(pAssemblySignaturePublicKey) && + memcmp(static_cast<void *>(pAssemblySignaturePublicKey), + static_cast<void *>(pbSignaturePublicKey.GetValue()), + cbSignaturePublicKey) == 0) + { + return true; + } + + SetStrongNameErrorInfo(SN_E_PUBLICKEY_MISMATCH); + return false; +} + +// Map an assembly into memory. +BOOLEAN LoadAssembly(SN_LOAD_CTX *pLoadCtx, LPCWSTR wszFilePath, DWORD inFlags, BOOLEAN fRequireSignature) +{ + DWORD dwError = S_OK; + + // If a filename is not supplied, the image has already been mapped (and the + // image base and length fields set up correctly). + if (wszFilePath == NULL) + { + pLoadCtx->m_fPreMapped = TRUE; + pLoadCtx->m_pedecoder = new (nothrow) PEDecoder(pLoadCtx->m_pbBase, static_cast<COUNT_T>(pLoadCtx->m_dwLength)); + if (pLoadCtx->m_pedecoder == NULL) { + dwError = E_OUTOFMEMORY; + goto Error; + } + } + else { + + pLoadCtx->m_hMap = INVALID_HANDLE_VALUE; + pLoadCtx->m_pbBase = NULL; + + // Open the file for reading or writing. + pLoadCtx->m_hFile = WszCreateFile(wszFilePath, + GENERIC_READ | (pLoadCtx->m_fReadOnly ? 0 : GENERIC_WRITE), + pLoadCtx->m_fReadOnly ? FILE_SHARE_READ : FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (pLoadCtx->m_hFile == INVALID_HANDLE_VALUE) { + dwError = HRESULT_FROM_GetLastError(); + goto Error; + } + + pLoadCtx->m_dwLength = SafeGetFileSize(pLoadCtx->m_hFile, NULL); + if (pLoadCtx->m_dwLength == 0xffffffff) { + dwError = HRESULT_FROM_GetLastError(); + goto Error; + } + + // Create a mapping handle for the file. + pLoadCtx->m_hMap = WszCreateFileMapping(pLoadCtx->m_hFile, NULL, pLoadCtx->m_fReadOnly ? PAGE_READONLY : PAGE_READWRITE, 0, 0, NULL); + if (pLoadCtx->m_hMap == NULL) { + dwError = HRESULT_FROM_GetLastError(); + goto Error; + } + + // And map it into memory. + pLoadCtx->m_pbBase = (BYTE*)CLRMapViewOfFile(pLoadCtx->m_hMap, pLoadCtx->m_fReadOnly ? FILE_MAP_READ : FILE_MAP_WRITE, 0, 0, 0); + if (pLoadCtx->m_pbBase == NULL) { + dwError = HRESULT_FROM_GetLastError(); + goto Error; + } + pLoadCtx->m_pedecoder = new (nothrow) PEDecoder(pLoadCtx->m_pbBase, static_cast<COUNT_T>(pLoadCtx->m_dwLength)); + if (pLoadCtx->m_pedecoder == NULL) { + dwError = E_OUTOFMEMORY; + goto Error; + } + } + + if (!pLoadCtx->m_pedecoder->HasContents() || !pLoadCtx->m_pedecoder->CheckCORFormat()) { + dwError = CORSEC_E_INVALID_IMAGE_FORMAT; + goto Error; + } + + // Locate standard NT image header. + pLoadCtx->m_pNtHeaders = pLoadCtx->m_pedecoder->GetNTHeaders32(); + + if (pLoadCtx->m_pNtHeaders == NULL) { + dwError = CORSEC_E_INVALID_IMAGE_FORMAT; + goto Error; + } + + pLoadCtx->m_pCorHeader = pLoadCtx->m_pedecoder->GetCorHeader(); + + if (pLoadCtx->m_pCorHeader == NULL) { + dwError = CORSEC_E_INVALID_IMAGE_FORMAT; + goto Error; + } + + // Set up signature pointer (if we require it). + if (fRequireSignature && pLoadCtx->m_pedecoder->HasStrongNameSignature()) + { + COUNT_T size = 0; + BYTE* pbSignature = (BYTE*)pLoadCtx->m_pedecoder->GetStrongNameSignature(&size); + + // Make sure the signature doesn't point back into the header + if (pbSignature <= reinterpret_cast<BYTE*>(pLoadCtx->m_pCorHeader) && + pbSignature > reinterpret_cast<BYTE*>(pLoadCtx->m_pCorHeader) - size) + { + dwError = CORSEC_E_INVALID_IMAGE_FORMAT; + goto Error; + } + if (pbSignature >= reinterpret_cast<BYTE*>(pLoadCtx->m_pCorHeader) && + pbSignature - sizeof(IMAGE_COR20_HEADER) < reinterpret_cast<BYTE*>(pLoadCtx->m_pCorHeader)) + { + dwError = CORSEC_E_INVALID_IMAGE_FORMAT; + goto Error; + } + + pLoadCtx->m_pbSignature = pbSignature; + pLoadCtx->m_cbSignature = static_cast<DWORD>(size); + } + + return TRUE; + + Error: + if (!pLoadCtx->m_fPreMapped) { + if (pLoadCtx->m_pbBase) + CLRUnmapViewOfFile(pLoadCtx->m_pbBase); + if (pLoadCtx->m_hMap != INVALID_HANDLE_VALUE) + CloseHandle(pLoadCtx->m_hMap); + if (pLoadCtx->m_hFile != INVALID_HANDLE_VALUE) + CloseHandle(pLoadCtx->m_hFile); + } + SetLastError(dwError); + return FALSE; +} + + +// Unload an assembly loaded with LoadAssembly (recomputing checksum if +// necessary). +BOOLEAN UnloadAssembly(SN_LOAD_CTX *pLoadCtx) +{ + BOOLEAN bResult = TRUE; + + if (!pLoadCtx->m_fReadOnly) { + + IMAGE_NT_HEADERS *pNtHeaders = NULL; + DWORD dwCheckSum = 0; + + // We late bind CheckSumMappedFile to avoid bringing in IMAGEHLP unless + // we need to. + HMODULE hLibrary = WszLoadLibrary(W("imagehlp.dll")); + if (hLibrary) { + IMAGE_NT_HEADERS *(*SN_CheckSumMappedFile)(BYTE*, DWORD, DWORD*, DWORD*); + + if ((*(FARPROC*)&SN_CheckSumMappedFile = GetProcAddress(hLibrary, "CheckSumMappedFile")) != NULL) { + DWORD dwOldCheckSum; + + pNtHeaders = SN_CheckSumMappedFile(pLoadCtx->m_pbBase, + pLoadCtx->m_dwLength, + &dwOldCheckSum, + &dwCheckSum); + } + + FreeLibrary(hLibrary); + + } + + if (pNtHeaders != NULL) { + if (pNtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)) + ((IMAGE_NT_HEADERS32*)pNtHeaders)->OptionalHeader.CheckSum = VAL32(dwCheckSum); + else + if (pNtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC)) + ((IMAGE_NT_HEADERS64*)pNtHeaders)->OptionalHeader.CheckSum = VAL32(dwCheckSum); + } else + bResult = FALSE; + + if (!pLoadCtx->m_fPreMapped && !FlushViewOfFile(pLoadCtx->m_pbBase, 0)) + bResult = FALSE; + } + + if (!pLoadCtx->m_fPreMapped) { + if (!CLRUnmapViewOfFile(pLoadCtx->m_pbBase)) + bResult = FALSE; + + if (!CloseHandle(pLoadCtx->m_hMap)) + bResult = FALSE; + + if (!CloseHandle(pLoadCtx->m_hFile)) + bResult = FALSE; + } + + if (pLoadCtx->m_pedecoder != NULL) + { + delete (pLoadCtx->m_pedecoder); + pLoadCtx->m_pedecoder = NULL; + } + + return bResult; +} + +template<class T> +LONG RegQueryValueT(HKEY hKey, LPCWSTR pValueName, T * pData) +{ + DWORD dwLength = sizeof(T); + + LONG status = WszRegQueryValueEx(hKey, pValueName, NULL, NULL, (BYTE*) pData, & dwLength); + + return status; +} + +// Reads CSP configuration info (name of CSP to use, IDs of hashing/signing +// algorithms) from the registry. +HRESULT ReadRegistryConfig() +{ + HKEY hKey; + DWORD dwLength; + + // Initialize all settings to their default values, in case they've not been + // specified in the registry. + g_bHasCSPName = FALSE; + g_bUseMachineKeyset = TRUE; + g_uKeySpec = AT_SIGNATURE; + g_uHashAlgId = CALG_SHA1; + g_uSignAlgId = CALG_RSA_SIGN; + g_uProvType = PROV_RSA_FULL; + +#ifdef FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED + g_pVerificationRecords = NULL; +#endif + + g_fCacheVerify = TRUE; + + // Open the configuration key in the registry. + if (WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, SN_CONFIG_KEY_W, 0, KEY_READ, &hKey) != ERROR_SUCCESS) + return S_OK; + + // Read the preferred CSP name. + { + // Working set optimization: avoid touching g_wszCSPName (2052 bytes in size) unless registry has value for it + WCHAR tempCSPName[_countof(g_wszCSPName)]; + dwLength = sizeof(tempCSPName); + + tempCSPName[0] = 0; + + // If the registry key value is too long, that means it is invalid. + VERIFY(WszRegQueryValueEx(hKey, SN_CONFIG_CSP_W, NULL, NULL, + (BYTE*) tempCSPName, &dwLength) != ERROR_MORE_DATA); + tempCSPName[COUNTOF(tempCSPName) - 1] = W('\0'); // make sure the string is NULL-terminated + SNLOG((W("Preferred CSP name: '%s'\n"), tempCSPName)); + + if (tempCSPName[0] != W('\0')) + { + memcpy(g_wszCSPName, tempCSPName, sizeof(g_wszCSPName)); + g_bHasCSPName = TRUE; + } + } + + // Read the machine vs user key container flag. + DWORD dwUseMachineKeyset = TRUE; + RegQueryValueT(hKey, SN_CONFIG_MACHINE_KEYSET_W, & dwUseMachineKeyset); + SNLOG((W("Use machine keyset: %s\n"), dwUseMachineKeyset ? W("TRUE") : W("FALSE"))); + g_bUseMachineKeyset = (BOOLEAN)dwUseMachineKeyset; + + // Read the key spec. + RegQueryValueT(hKey, SN_CONFIG_KEYSPEC_W, & g_uKeySpec); + SNLOG((W("Key spec: %08X\n"), g_uKeySpec)); + + // Read the provider type + RegQueryValueT(hKey, SN_CONFIG_PROV_TYPE_W, & g_uProvType); + SNLOG((W("Provider Type: %08X\n"), g_uProvType)); + + // Read the hashing algorithm ID. + RegQueryValueT(hKey, SN_CONFIG_HASH_ALG_W, & g_uHashAlgId); + SNLOG((W("Hashing algorithm: %08X\n"), g_uHashAlgId)); + + // Read the signing algorithm ID. + RegQueryValueT(hKey, SN_CONFIG_SIGN_ALG_W, & g_uSignAlgId); + SNLOG((W("Signing algorithm: %08X\n"), g_uSignAlgId)); + + // Read the OK to cache verifications flag. + DWORD dwCacheVerify = TRUE; + RegQueryValueT(hKey, SN_CONFIG_CACHE_VERIFY_W, & dwCacheVerify); + SNLOG((W("OK to cache verifications: %s\n"), dwCacheVerify ? W("TRUE") : W("FALSE"))); + g_fCacheVerify = (BOOLEAN)dwCacheVerify; + + RegCloseKey(hKey); + + HRESULT hr = S_OK; +#ifdef FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED + // Read verify disable records. + IfFailRet(ReadVerificationRecords()); +#endif // FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED + +#ifdef FEATURE_STRONGNAME_MIGRATION + IfFailRet(ReadRevocationRecords()); +#endif // FEATURE_STRONGNAME_MIGRATION + + return hr; +} + +#ifdef FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED +// Read verification records from the registry during startup. +HRESULT ReadVerificationRecords() +{ + HKEYHolder hKey; + WCHAR wszSubKey[MAX_PATH + 1]; + DWORD cchSubKey; + SN_VER_REC *pVerificationRecords = NULL; + HRESULT hr = S_OK; + + // Open the verification subkey in the registry. + if (WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, SN_CONFIG_KEY_W W("\\") SN_CONFIG_VERIFICATION_W, 0, KEY_READ, &hKey) != ERROR_SUCCESS) + return hr; + + // Assembly specific records are represented as subkeys of the key we've + // just opened. + for (DWORD i = 0; ; i++) { + // Get the name of the next subkey. + cchSubKey = MAX_PATH + 1; + FILETIME sFiletime; + if (WszRegEnumKeyEx(hKey, i, wszSubKey, &cchSubKey, NULL, NULL, NULL, &sFiletime) != ERROR_SUCCESS) + break; + + // Open the subkey. + HKEYHolder hSubKey; + if (WszRegOpenKeyEx(hKey, wszSubKey, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS) { + NewArrayHolder<WCHAR> mszUserList(NULL); + DWORD cbUserList; + NewArrayHolder<WCHAR> wszTestPublicKey(NULL); + DWORD cbTestPublicKey; + NewArrayHolder<WCHAR> wszAssembly(NULL); + SN_VER_REC *pVerRec; + + // Read a list of valid users, if supplied. + if ((WszRegQueryValueEx(hSubKey, SN_CONFIG_USERLIST_W, NULL, NULL, NULL, &cbUserList) == ERROR_SUCCESS) && + (cbUserList > 0)) { + mszUserList = new (nothrow) WCHAR[cbUserList / sizeof(WCHAR)]; + if (!mszUserList) { + hr = E_OUTOFMEMORY; + goto FreeListExit; + } + WszRegQueryValueEx(hSubKey, SN_CONFIG_USERLIST_W, NULL, NULL, (BYTE*)mszUserList.GetValue(), &cbUserList); + } + + // Read the test public key, if supplied + if ((WszRegQueryValueEx(hSubKey, SN_CONFIG_TESTPUBLICKEY_W, NULL, NULL, NULL, &cbTestPublicKey) == ERROR_SUCCESS) && + (cbTestPublicKey > 0)) { + wszTestPublicKey = new (nothrow) WCHAR[cbTestPublicKey / sizeof(WCHAR)]; + if (!wszTestPublicKey) { + hr = E_OUTOFMEMORY; + goto FreeListExit; + } + WszRegQueryValueEx(hSubKey, SN_CONFIG_TESTPUBLICKEY_W, NULL, NULL, (BYTE*)wszTestPublicKey.GetValue(), &cbTestPublicKey); + } + + size_t dwSubKeyLen = wcslen(wszSubKey); + wszAssembly = new (nothrow) WCHAR[dwSubKeyLen+1]; + if (!wszAssembly) { + hr = E_OUTOFMEMORY; + goto FreeListExit; + } + wcsncpy_s(wszAssembly, dwSubKeyLen+1, wszSubKey, _TRUNCATE); + wszAssembly[dwSubKeyLen] = W('\0'); + + // We've found a valid entry, add it to the local list. + pVerRec = new (nothrow) SN_VER_REC; + if (!pVerRec) { + hr = E_OUTOFMEMORY; + goto FreeListExit; + } + + pVerRec->m_mszUserList = mszUserList; + pVerRec->m_wszTestPublicKey = wszTestPublicKey; + pVerRec->m_wszAssembly = wszAssembly; + + mszUserList.SuppressRelease(); + wszTestPublicKey.SuppressRelease(); + wszAssembly.SuppressRelease(); + + pVerRec->m_pNext = pVerificationRecords; + pVerificationRecords = pVerRec; + SNLOG((W("Verification record for '%s' found in registry\n"), wszSubKey)); + } + } + + // Initialize the global list of verification records. + PVOID pv = InterlockedCompareExchangeT(&g_pVerificationRecords, pVerificationRecords, NULL); + if (pv == NULL) + return hr; + +FreeListExit: + // Iterate over local list of verification records and free allocated memory. + SN_VER_REC *pVerRec = pVerificationRecords; + while (pVerRec) { + delete [] pVerRec->m_mszUserList; + delete [] pVerRec->m_wszTestPublicKey; + delete [] pVerRec->m_wszAssembly; + SN_VER_REC *tmp = pVerRec->m_pNext; + delete pVerRec; + pVerRec = tmp; + } + return hr; +} +#endif // FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED + + +#ifdef FEATURE_STRONGNAME_MIGRATION + +#define SN_REVOCATION_KEY_NAME_W W("RevokedKeys") // Registry revocation key name +#define SN_REVOKEDKEY_VALUE_NAME_W W("RevokedKey") // Registry value name + +HRESULT ReadReplacementKeys(HKEY hKey, SN_REPLACEMENT_KEY_REC **ppReplacementRecords) +{ + HRESULT hr = S_OK; + + DWORD uValueCount; + DWORD cchMaxValueNameLen; + + NewArrayHolder<WCHAR> wszValueName(NULL); + + if(RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &uValueCount, &cchMaxValueNameLen, NULL, NULL, NULL) != ERROR_SUCCESS) + return hr; + + cchMaxValueNameLen++; // Add 1 for null character + + DWORD cchValueName; + wszValueName = new (nothrow) WCHAR[cchMaxValueNameLen]; + if (!wszValueName) { + return E_OUTOFMEMORY; + } + + for (DWORD j = 0; j < uValueCount; j++) { + cchValueName = cchMaxValueNameLen; + if (WszRegEnumValue(hKey, j, wszValueName, &cchValueName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) + break; + + if(SString::_wcsicmp(wszValueName, SN_REVOKEDKEY_VALUE_NAME_W) == 0) // Skip over the "RevokedKey" value + continue; + + NewArrayHolder<BYTE> pbReplacementKey(NULL); + DWORD cbReplacementKey; + DWORD dwValType; + if ((WszRegQueryValueEx(hKey, wszValueName, NULL, &dwValType, NULL, &cbReplacementKey) == ERROR_SUCCESS) && + (cbReplacementKey > 0) && (dwValType == REG_BINARY)) { + pbReplacementKey = new (nothrow) BYTE[cbReplacementKey]; + if (!pbReplacementKey) { + return E_OUTOFMEMORY; + } + if(WszRegQueryValueEx(hKey, wszValueName, NULL, NULL, (BYTE*)pbReplacementKey.GetValue(), &cbReplacementKey) == ERROR_SUCCESS) + { + NewHolder<SN_REPLACEMENT_KEY_REC> pReplacementRecord(new (nothrow) SN_REPLACEMENT_KEY_REC); + if (pReplacementRecord == NULL) { + return E_OUTOFMEMORY; + } + + pReplacementRecord->m_pbReplacementKey = pbReplacementKey.Extract(); + pReplacementRecord->m_cbReplacementKey = cbReplacementKey; + // Insert into list + pReplacementRecord->m_pNext = *ppReplacementRecords; + *ppReplacementRecords = pReplacementRecord.Extract(); + } + } + } + + return hr; +} + +// Read revocation records from the registry during startup. +HRESULT ReadRevocationRecordsFromKey(REGSAM samDesired, SN_REVOCATION_REC **ppRevocationRecords) +{ + HKEYHolder hKey; + WCHAR wszSubKey[MAX_PATH + 1]; + DWORD cchSubKey; + HRESULT hr = S_OK; + + // Open the revocation subkey in the registry. + if (WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, SN_CONFIG_KEY_W W("\\") SN_REVOCATION_KEY_NAME_W, 0, samDesired, &hKey) != ERROR_SUCCESS) + return hr; + + // Assembly specific records are represented as subkeys of the key we've + // just opened. + for (DWORD i = 0; ; i++) { + // Read the next subkey + cchSubKey = MAX_PATH + 1; // reset size of buffer, as the following call changes it + if (WszRegEnumKeyEx(hKey, i, wszSubKey, &cchSubKey, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) + break; + + // Open the subkey. + HKEYHolder hSubKey; + if (WszRegOpenKeyEx(hKey, wszSubKey, 0, samDesired, &hSubKey) == ERROR_SUCCESS) { + NewArrayHolder<BYTE> pbRevokedKey(NULL); + DWORD cbRevokedKey; + DWORD dwValType; + + // Read the "RevokedKey" value + if ((WszRegQueryValueEx(hSubKey, SN_REVOKEDKEY_VALUE_NAME_W, NULL, &dwValType, NULL, &cbRevokedKey) == ERROR_SUCCESS) && + (cbRevokedKey > 0) && (dwValType == REG_BINARY)) { + pbRevokedKey = new (nothrow) BYTE[cbRevokedKey]; + if (!pbRevokedKey) { + return E_OUTOFMEMORY; + } + + if(WszRegQueryValueEx(hSubKey, SN_REVOKEDKEY_VALUE_NAME_W, NULL, NULL, (BYTE*)pbRevokedKey.GetValue(), &cbRevokedKey) == ERROR_SUCCESS) + { + // We've found a valid entry, store it + NewHolder<SN_REVOCATION_REC> pRevocationRecord(new (nothrow) SN_REVOCATION_REC); + if (pRevocationRecord == NULL) { + return E_OUTOFMEMORY; + } + + pRevocationRecord->m_pbRevokedKey = pbRevokedKey.Extract(); + pRevocationRecord->m_cbRevokedKey = cbRevokedKey; + pRevocationRecord->m_pReplacementKeys = NULL; + + // Insert into list + pRevocationRecord->m_pNext = *ppRevocationRecords; + *ppRevocationRecords = pRevocationRecord.Extract(); + + IfFailRet(ReadReplacementKeys(hSubKey, &pRevocationRecord->m_pReplacementKeys)); + + SNLOG((W("Revocation record '%s' found in registry\n"), wszSubKey)); + } + } + } + } + + return hr; +} + +HRESULT ReadRevocationRecords() +{ + HRESULT hr = S_OK; + + SYSTEM_INFO systemInfo; + SN_REVOCATION_REC *pRevocationRecords = NULL; + + GetNativeSystemInfo(&systemInfo); + // Read both Software\ and Software\WOW6432Node\ on 64-bit systems + if(systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + { + IfFailGoto(ReadRevocationRecordsFromKey(KEY_READ | KEY_WOW64_64KEY, &pRevocationRecords), FreeListExit); + IfFailGoto(ReadRevocationRecordsFromKey(KEY_READ | KEY_WOW64_32KEY, &pRevocationRecords), FreeListExit); + } + else + { + IfFailGoto(ReadRevocationRecordsFromKey(KEY_READ, &pRevocationRecords), FreeListExit); + } + + // Initialize the global list of verification records. + PVOID pv = InterlockedCompareExchangeT(&g_pRevocationRecords, pRevocationRecords, NULL); + + if (pv == NULL) // Successfully inserted the list we just created + return hr; + +FreeListExit: + // Iterate over local list of verification records and free allocated memory. + SN_REVOCATION_REC *pRevRec = pRevocationRecords; + while (pRevRec) { + if(pRevRec->m_pbRevokedKey) + delete [] pRevRec->m_pbRevokedKey; + + SN_REPLACEMENT_KEY_REC *pKeyRec = pRevRec->m_pReplacementKeys; + while (pKeyRec) { + if(pKeyRec->m_pbReplacementKey) + delete [] pKeyRec->m_pbReplacementKey; + + SN_REPLACEMENT_KEY_REC *tmp = pKeyRec->m_pNext; + delete pKeyRec; + pKeyRec = tmp; + } + + SN_REVOCATION_REC *tmp2 = pRevRec->m_pNext; + delete pRevRec; + pRevRec = tmp2; + } + return hr; +} + +#endif // FEATURE_STRONGNAME_MIGRATION + +// Check current user name against a multi-string user name list. Return true if +// the name is found (or the list is empty). +BOOLEAN IsValidUser(__in_z WCHAR *mszUserList) +{ + HANDLE hToken; + DWORD dwRetLen; + TOKEN_USER *pUser; + WCHAR wszUser[1024]; + WCHAR wszDomain[1024]; + DWORD cchUser; + DWORD cchDomain; + SID_NAME_USE eSidUse; + WCHAR *wszUserEntry; + + // Empty list implies no user name checking. + if (mszUserList == NULL) + return TRUE; + + // Get current user name. Don't cache this to avoid threading/impersonation + // problems. + // First look to see if there's a security token on the current thread + // (maybe we're impersonating). If not, we'll get the token from the + // process. + if (!OpenThreadToken(GetCurrentThread(), TOKEN_READ, FALSE, &hToken)) + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) { + SNLOG((W("Failed to find a security token, error %08X\n"), GetLastError())); + return FALSE; + } + + // Get the user SID. (Calculate buffer size first). + if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwRetLen) && + GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + SNLOG((W("Failed to calculate token information buffer size, error %08X\n"), GetLastError())); + CloseHandle(hToken); + return FALSE; + } + + NewArrayHolder<BYTE> pvBuffer = new (nothrow) BYTE[dwRetLen]; + if (pvBuffer == NULL) + { + SetLastError(E_OUTOFMEMORY); + return FALSE; + } + + if (!GetTokenInformation(hToken, TokenUser, reinterpret_cast<LPVOID>((BYTE*)pvBuffer), dwRetLen, &dwRetLen)) { + SNLOG((W("Failed to acquire token information, error %08X\n"), GetLastError())); + CloseHandle(hToken); + return FALSE; + } + + pUser = reinterpret_cast<TOKEN_USER *>(pvBuffer.GetValue()); + + // Get the user and domain names. + cchUser = sizeof(wszUser) / sizeof(WCHAR); + cchDomain = sizeof(wszDomain) / sizeof(WCHAR); + if (!WszLookupAccountSid(NULL, pUser->User.Sid, + wszUser, &cchUser, + wszDomain, &cchDomain, + &eSidUse)) { + SNLOG((W("Failed to lookup account information, error %08X\n"), GetLastError())); + CloseHandle(hToken); + return FALSE; + } + + CloseHandle(hToken); + + // Concatenate user and domain name to get a fully qualified account name. + if (((wcslen(wszUser) + wcslen(wszDomain) + 2) * sizeof(WCHAR)) > sizeof(wszDomain)) { + SNLOG((W("Fully qualified account name was too long\n"))); + return FALSE; + } + wcscat_s(wszDomain, COUNTOF(wszDomain), W("\\")); + wcscat_s(wszDomain, COUNTOF(wszDomain), wszUser); + SNLOG((W("Current username is '%s'\n"), wszDomain)); + + // Check current user against each name in the multi-string (packed + // list of nul terminated strings terminated with an additional nul). + wszUserEntry = mszUserList; + while (*wszUserEntry) { + if (!SString::_wcsicmp(wszDomain, wszUserEntry)) + return TRUE; + wszUserEntry += wcslen(wszUserEntry) + 1; + } + + // No user name match, fail search. + SNLOG((W("No username match\n"))); + + return FALSE; +} + +#ifdef FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED +// See if there's a verification records for the given assembly. +SN_VER_REC *GetVerificationRecord(__in_z __deref LPWSTR wszAssemblyName, PublicKeyBlob *pPublicKey) +{ + SN_VER_REC *pVerRec; + SN_VER_REC *pWildcardVerRec = NULL; + LPWSTR pAssembly = NULL; + BYTE *pbToken; + DWORD cbToken; + WCHAR wszStrongName[(SN_SIZEOF_TOKEN * 2) + 1]; + DWORD i; + + // Compress the public key to make for a shorter assembly name. + if (!StrongNameTokenFromPublicKey((BYTE*)pPublicKey, + SN_SIZEOF_KEY(pPublicKey), + &pbToken, + &cbToken)) + return NULL; + + if (cbToken > SN_SIZEOF_TOKEN) + return NULL; + + // Turn the token into hex. + for (i = 0; i < cbToken; i++) { + static WCHAR *wszHex = W("0123456789ABCDEF"); + wszStrongName[(i * 2) + 0] = wszHex[(pbToken[i] >> 4)]; + wszStrongName[(i * 2) + 1] = wszHex[(pbToken[i] & 0x0F)]; + } + wszStrongName[i * 2] = W('\0'); + delete[] pbToken; + + // Build the full assembly name. + + size_t nLen = wcslen(wszAssemblyName) + wcslen(W(",")) + wcslen(wszStrongName); + pAssembly = new (nothrow) WCHAR[nLen +1]; // +1 for NULL + if (pAssembly == NULL) + return NULL; + wcscpy_s(pAssembly, nLen + 1, wszAssemblyName); + wcscat_s(pAssembly, nLen + 1, W(",")); + wcscat_s(pAssembly, nLen + 1, wszStrongName); + + // Iterate over global list of verification records. + for (pVerRec = g_pVerificationRecords; pVerRec; pVerRec = pVerRec->m_pNext) { + // Look for matching assembly name. + if (!SString::_wcsicmp(pAssembly, pVerRec->m_wszAssembly)) { + delete[] pAssembly; + // Check current user against allowed user name list. + if (IsValidUser(pVerRec->m_mszUserList)) + return pVerRec; + else + return NULL; + } else if (!wcscmp(W("*,*"), pVerRec->m_wszAssembly)) { + // Found a wildcard record, it'll do if we don't find something more + // specific. + if (pWildcardVerRec == NULL) + pWildcardVerRec = pVerRec; + } else if (!wcsncmp(W("*,"), pVerRec->m_wszAssembly, 2)) { + // Found a wildcard record (with a specific strong name). If the + // strong names match it'll do unless we find something more + // specific (it overrides "*,*" wildcards though). + if (!SString::_wcsicmp(wszStrongName, &pVerRec->m_wszAssembly[2])) + pWildcardVerRec = pVerRec; + } + } + + delete[] pAssembly; + + // No match on specific assembly name, see if there's a wildcard entry. + if (pWildcardVerRec) + // Check current user against allowed user name list. + if (IsValidUser(pWildcardVerRec->m_mszUserList)) + return pWildcardVerRec; + else + return NULL; + + return NULL; +} +#endif // FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED + +HRESULT +CallGetMetaDataInternalInterface( + LPVOID pData, + ULONG cbData, + DWORD flags, + REFIID riid, + LPVOID *ppInterface) +{ +#ifdef FEATURE_STRONGNAME_STANDALONE_WINRT + return E_NOTIMPL; +#elif STRONGNAME_IN_VM || !FEATURE_STANDALONE_SN + // We link the GetMetaDataInternalInterface, so just call it + return GetMetaDataInternalInterface( + pData, + cbData, + flags, + riid, + ppInterface); +#elif FEATURE_CORECLR + return E_NOTIMPL; +#else + + // We late bind the metadata function to avoid having a direct dependence on + // mscoree.dll unless we absolutely need to. + + HRESULT hr = S_OK; + ICLRMetaHost *pCLRMetaHost = NULL; + ICLRRuntimeInfo *pCLRRuntimeInfo = NULL; + ICLRRuntimeHostInternal *pCLRRuntimeHostInternal = NULL; + + HMODULE hLibrary = WszLoadLibrary(MSCOREE_SHIM_W); + if (hLibrary == NULL) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("WszLoadLibrary(\"") MSCOREE_SHIM_W W("\") failed with %08x\n"), hr)); + goto ErrExit; + } + + typedef HRESULT (__stdcall *PFNCLRCreateInstance)(REFCLSID clsid, REFIID riid, /*iid_is(riid)*/ LPVOID *ppInterface); + PFNCLRCreateInstance pfnCLRCreateInstance = reinterpret_cast<PFNCLRCreateInstance>(GetProcAddress( + hLibrary, + "CLRCreateInstance")); + if (pfnCLRCreateInstance == NULL) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("Couldn't find CLRCreateInstance() in ") MSCOREE_SHIM_W W(": %08x\n"), hr)); + goto ErrExit; + } + + if (FAILED(hr = pfnCLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID *)&pCLRMetaHost))) + { + SNLOG((W("Error calling CLRCreateInstance() in ") MSCOREE_SHIM_W W(": %08x\n"), hr)); + goto ErrExit; + } + + if (FAILED(hr = pCLRMetaHost->GetRuntime( + W("v") VER_PRODUCTVERSION_NO_QFE_STR_L, + IID_ICLRRuntimeInfo, + (LPVOID *)&pCLRRuntimeInfo))) + { + SNLOG((W("Error calling ICLRMetaHost::GetRuntime() in ") MSCOREE_SHIM_W W(": %08x\n"), hr)); + goto ErrExit; + } + + if (FAILED(hr = pCLRRuntimeInfo->GetInterface( + CLSID_CLRRuntimeHostInternal, + IID_ICLRRuntimeHostInternal, + (LPVOID *)&pCLRRuntimeHostInternal))) + { + SNLOG((W("Error calling ICLRRuntimeInfo::GetInterface() in ") MSCOREE_SHIM_W W(": %08x\n"), hr)); + goto ErrExit; + } + + hr = pCLRRuntimeHostInternal->GetMetaDataInternalInterface( + (BYTE *)pData, + cbData, + flags, + riid, + ppInterface); + +ErrExit: + if (pCLRMetaHost != NULL) + { + pCLRMetaHost->Release(); + } + if (pCLRRuntimeInfo != NULL) + { + pCLRRuntimeInfo->Release(); + } + if (pCLRRuntimeHostInternal != NULL) + { + pCLRRuntimeHostInternal->Release(); + } + + return hr; + +#endif +} // CallGetMetaDataInternalInterface + +// Load metadata engine and return an importer. +HRESULT +GetMetadataImport( + __in const SN_LOAD_CTX *pLoadCtx, + __in mdAssembly *ptkAssembly, + __out IMDInternalImport **ppMetaDataImport) +{ + HRESULT hr = E_FAIL; + BYTE *pMetaData = NULL; + + // Locate the COM+ meta data within the header. + if (pLoadCtx->m_pedecoder->CheckCorHeader()) + { + pMetaData = (BYTE *)pLoadCtx->m_pedecoder->GetMetadata(); + } + + if (pMetaData == NULL) + { + SNLOG((W("Couldn't locate the COM+ header\n"))); + return CORSEC_E_INVALID_IMAGE_FORMAT; + } + + // Open a metadata scope on the memory directly. + ReleaseHolder<IMDInternalImport> pMetaDataImportHolder; + if (FAILED(hr = CallGetMetaDataInternalInterface( + pMetaData, + VAL32(pLoadCtx->m_pCorHeader->MetaData.Size), + ofRead, + IID_IMDInternalImport, + &pMetaDataImportHolder))) + { + SNLOG((W("GetMetaDataInternalInterface() failed with %08x\n"), hr)); + return SubstituteErrorIfNotTransient(hr, CORSEC_E_INVALID_IMAGE_FORMAT); + } + + // Determine the metadata token for the assembly from the scope. + if (FAILED(hr = pMetaDataImportHolder->GetAssemblyFromScope(ptkAssembly))) + { + SNLOG((W("pMetaData->GetAssemblyFromScope() failed with %08x\n"), hr)); + return SubstituteErrorIfNotTransient(hr, CORSEC_E_INVALID_IMAGE_FORMAT); + } + + *ppMetaDataImport = pMetaDataImportHolder.Extract(); + return S_OK; +} +#if STRONGNAME_IN_VM +// Function to form the fully qualified assembly name from the load context +BOOL FormFullyQualifiedAssemblyName(SN_LOAD_CTX *pLoadCtx, SString &assemblyName) +{ + mdAssembly tkAssembly; + // Open a metadata scope on the image. + ReleaseHolder<IMDInternalImport> pMetaDataImport; + HRESULT hr; + if (FAILED(hr = GetMetadataImport(pLoadCtx, &tkAssembly, &pMetaDataImport))) + return FALSE; + + if (pMetaDataImport != NULL) + { + PEAssembly::GetFullyQualifiedAssemblyName(pMetaDataImport, tkAssembly, assemblyName); + return TRUE; + } + return FALSE; +} +#endif + + +// Locate the public key blob located within the metadata of an assembly file +// and return a copy (use delete to deallocate). Optionally get the assembly +// name as well. +HRESULT FindPublicKey(const SN_LOAD_CTX *pLoadCtx, + __out_ecount_opt(cchAssemblyName) LPWSTR wszAssemblyName, + DWORD cchAssemblyName, + __out PublicKeyBlob **ppPublicKey, + DWORD *pcbPublicKey) +{ + HRESULT hr = S_OK; + *ppPublicKey = NULL; + + // Open a metadata scope on the image. + mdAssembly tkAssembly; + ReleaseHolder<IMDInternalImport> pMetaDataImport; + if (FAILED(hr = GetMetadataImport(pLoadCtx, &tkAssembly, &pMetaDataImport))) + return hr; + + // Read the public key location from the assembly properties (it's known as + // the originator property). + PublicKeyBlob *pKey; + DWORD dwKeyLen; + LPCSTR szAssemblyName; + if (FAILED(hr = pMetaDataImport->GetAssemblyProps(tkAssembly, // [IN] The Assembly for which to get the properties + (const void **)&pKey, // [OUT] Pointer to the Originator blob + &dwKeyLen, // [OUT] Count of bytes in the Originator Blob + NULL, // [OUT] Hash Algorithm + &szAssemblyName, // [OUT] Buffer to fill with name + NULL, // [OUT] Assembly MetaData + NULL))) // [OUT] Flags + { + SNLOG((W("Did not get public key property: %08x\n"), hr)); + return SubstituteErrorIfNotTransient(hr, CORSEC_E_MISSING_STRONGNAME); + } + + if (dwKeyLen == 0) + { + SNLOG((W("No public key stored in metadata\n"))); + return CORSEC_E_MISSING_STRONGNAME; + } + + // Make a copy of the key blob (because we're going to close the metadata scope). + NewArrayHolder<BYTE> pKeyCopy(new (nothrow) BYTE[dwKeyLen]); + if (pKeyCopy == NULL) + return E_OUTOFMEMORY; + memcpy_s(pKeyCopy, dwKeyLen, pKey, dwKeyLen); + + // Copy the assembly name as well (if it was asked for). We also convert + // from UTF8 to UNICODE while we're at it. + if (wszAssemblyName) + WszMultiByteToWideChar(CP_UTF8, 0, szAssemblyName, -1, wszAssemblyName, cchAssemblyName); + + *ppPublicKey = reinterpret_cast<PublicKeyBlob *>(pKeyCopy.Extract()); + if(pcbPublicKey != NULL) + *pcbPublicKey = dwKeyLen; + + return S_OK; +} + +BYTE HexToByte (WCHAR wc) { + if (!iswxdigit(wc)) return (BYTE) 0xff; + if (iswdigit(wc)) return (BYTE) (wc - W('0')); + if (iswupper(wc)) return (BYTE) (wc - W('A') + 10); + return (BYTE) (wc - W('a') + 10); +} + +// Read the hex string into a PublicKeyBlob structure. +// Caller owns the blob. +PublicKeyBlob *GetPublicKeyFromHex(LPCWSTR wszPublicKeyHexString) { + size_t cchHex = wcslen(wszPublicKeyHexString); + size_t cbHex = cchHex / 2; + if (cchHex % 2 != 0) + return NULL; + + BYTE *pKey = new (nothrow) BYTE[cbHex]; + if (!pKey) + return NULL; + for (size_t i = 0; i < cbHex; i++) { + pKey[i] = (BYTE) ((HexToByte(*wszPublicKeyHexString) << 4) | HexToByte(*(wszPublicKeyHexString + 1))); + wszPublicKeyHexString += 2; + } + return (PublicKeyBlob*) pKey; +} + +// Create a temporary key container name likely to be unique to this process and +// thread. Any existing container with the same name is deleted. +BOOLEAN GetKeyContainerName(LPCWSTR *pwszKeyContainer, BOOLEAN *pbTempContainer) +{ + *pbTempContainer = FALSE; + + if (*pwszKeyContainer != NULL) + return TRUE; + + GUID guid; + HRESULT hr = CoCreateGuid(&guid); + if (FAILED(hr)) { + SetStrongNameErrorInfo(hr); + return FALSE; + } + + WCHAR wszGuid[64]; + if (GuidToLPWSTR(guid, wszGuid, sizeof(wszGuid) / sizeof(WCHAR)) == 0) { + SetStrongNameErrorInfo(E_UNEXPECTED); // this operation should never fail + return FALSE; + } + + // Name is of form '__MSCORSN__<guid>__' where <guid> is a GUID. + const size_t cchLengthOfKeyContainer = sizeof("__MSCORSN____") + (sizeof(wszGuid) / sizeof(WCHAR)) + 1 /* null */; + LPWSTR wszKeyContainer = new (nothrow) WCHAR[cchLengthOfKeyContainer]; + if (wszKeyContainer == NULL) { + SetStrongNameErrorInfo(E_OUTOFMEMORY); + return FALSE; + } + + _snwprintf_s(wszKeyContainer, cchLengthOfKeyContainer - 1 /* exclude null */, _TRUNCATE, + W("__MSCORSN__%s__"), + wszGuid); + + // Delete any stale container with the same name. + LocateCSP(wszKeyContainer, SN_DELETE_CONTAINER); + + SNLOG((W("Creating temporary key container name '%s'\n"), wszKeyContainer)); + + *pwszKeyContainer = wszKeyContainer; + *pbTempContainer = TRUE; + + return TRUE; +} + + +// Free resources allocated by GetKeyContainerName and delete the named +// container. +VOID FreeKeyContainerName(LPCWSTR wszKeyContainer, BOOLEAN bTempContainer) +{ + if (bTempContainer) { + // Free the name. + delete [] (WCHAR*)wszKeyContainer; + } +} + +static DWORD GetSpecialKeyFlags(PublicKeyBlob* pKey) +{ + if (SN_IS_THE_KEY(pKey)) + return SN_OUTFLAG_MICROSOFT_SIGNATURE; + + return 0; +} + +#ifdef FEATURE_STRONGNAME_MIGRATION + +HRESULT VerifyCounterSignature( + PublicKeyBlob *pSignaturePublicKey, + ULONG cbSignaturePublicKey, + PublicKeyBlob *pIdentityPublicKey, + BYTE *pCounterSignature, + ULONG cbCounterSignature) +{ + LIMITED_METHOD_CONTRACT; + + HRESULT hr = S_OK; + + HandleStrongNameCspHolder hProv(NULL); + HandleKeyHolder hKey(NULL); + HandleHashHolder hHash(NULL); + + hProv = LocateCSP(NULL, SN_IGNORE_CONTAINER, GET_UNALIGNED_VAL32(&pIdentityPublicKey->HashAlgID), GET_UNALIGNED_VAL32(&pIdentityPublicKey->SigAlgID)); + + if (!hProv) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("Failed to acquire a CSP: %08x"), hr)); + return hr; + } + + if(SN_IS_NEUTRAL_KEY(pIdentityPublicKey)) + { + pIdentityPublicKey = reinterpret_cast<PublicKeyBlob *>(const_cast<BYTE *>(g_rbTheKey)); + } + + BYTE *pbRealPublicKey = pIdentityPublicKey->PublicKey; + DWORD cbRealPublicKey = GET_UNALIGNED_VAL32(&pIdentityPublicKey->cbPublicKey); + + if (!CryptImportKey(hProv, pbRealPublicKey, cbRealPublicKey, 0, 0, &hKey)) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("Failed to import key: %08x"), hr)); + return hr; + } + + // Create a hash object. + if (!CryptCreateHash(hProv, GET_UNALIGNED_VAL32(&pIdentityPublicKey->HashAlgID), 0, 0, &hHash)) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("Failed to create hash: %08x"), hr)); + return hr; + } + + if (!CryptHashData(hHash, (BYTE*)pSignaturePublicKey, cbSignaturePublicKey, 0)) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("Failed to compute hash: %08x"), hr)); + return hr; + } + +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) + if (hHash != (HCRYPTHASH)INVALID_HANDLE_VALUE) { + DWORD cbHash; + DWORD dwRetLen = sizeof(cbHash); + if (CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&cbHash, &dwRetLen, 0)) + { + NewArrayHolder<BYTE> pbHash(new (nothrow) BYTE[cbHash]); + if (pbHash != NULL) + { + if (CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) + { + SNLOG((W("Computed Hash Value (%u bytes):\n"), cbHash)); + HexDump(pbHash, cbHash); + } + else + { + SNLOG((W("CryptGetHashParam() failed with %08X\n"), GetLastError())); + } + } + } + else + { + SNLOG((W("CryptGetHashParam() failed with %08X\n"), GetLastError())); + } + } +#endif // _DEBUG + + // Verify the hash against the signature. + //DbgCount(dwInFlags & SN_INFLAG_RUNTIME ? W("RuntimeVerify") : W("FusionVerify")); + if (pCounterSignature != NULL && cbCounterSignature != 0 && + CryptVerifySignatureW(hHash, pCounterSignature, cbCounterSignature, hKey, NULL, 0)) + { + SNLOG((W("Counter-signature verification succeeded\n"))); + } + else + { + SNLOG((W("Counter-signature verification failed\n"))); + hr = CORSEC_E_INVALID_COUNTERSIGNATURE; + } + + return hr; +} + +HRESULT ParseStringArgs( + CustomAttributeParser &ca, // The Custom Attribute blob. + CaArg* pArgs, // Array of argument descriptors. + ULONG cArgs) // Count of argument descriptors. +{ + LIMITED_METHOD_CONTRACT; + + HRESULT hr = S_OK; + + // For each expected arg... + for (ULONG ix=0; ix<cArgs; ++ix) + { + CaArg* pArg = &pArgs[ix]; + if(pArg->type.tag != SERIALIZATION_TYPE_STRING) + { + return E_UNEXPECTED; // The blob shouldn't have anything other than strings + } + IfFailGo(ca.GetString(&pArg->val.str.pStr, &pArg->val.str.cbStr)); + } + +ErrExit: + return hr; +} + +HRESULT GetVerifiedSignatureKey(__in SN_LOAD_CTX *pLoadCtx, __out PublicKeyBlob **ppPublicKey, __out_opt DWORD *pcbPublicKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + mdAssembly tkAssembly; + ReleaseHolder<IMDInternalImport> pMetaDataImport; + IfFailRet(GetMetadataImport(pLoadCtx, &tkAssembly, &pMetaDataImport)); + + HRESULT attributeHr; + void *pAttribute; + ULONG cbAttribute; + hr = pMetaDataImport->GetCustomAttributeByName(tkAssembly, g_AssemblySignatureKeyAttribute, const_cast<const void**>(&pAttribute), &cbAttribute); + + if (SUCCEEDED(hr) && hr != S_FALSE) + { + CustomAttributeParser parser(pAttribute, cbAttribute); + IfFailRet(parser.ValidateProlog()); + + CaType caTypeString; + caTypeString.Init(SERIALIZATION_TYPE_STRING); + + CaArg args[2]; + + CaArg* argPublicKey = &args[0]; + argPublicKey->Init(caTypeString); + + CaArg* argCounterSignature = &args[1]; + argCounterSignature->Init(caTypeString); + + IfFailRet(ParseStringArgs(parser, args, lengthof(args))); + + StrongNameBufferHolder<PublicKeyBlob> pSignaturePublicKey; + ULONG cbSignaturePublicKey; + if (argPublicKey->val.str.pStr == NULL || argPublicKey->val.str.cbStr == 0 || + (!GetBytesFromHex(argPublicKey->val.str.pStr, argPublicKey->val.str.cbStr, (BYTE**)(pSignaturePublicKey.GetAddr()), &cbSignaturePublicKey)) || + !StrongNameIsValidPublicKey((BYTE*)pSignaturePublicKey.GetValue(), cbSignaturePublicKey, false)) + { + return CORSEC_E_INVALID_SIGNATUREKEY; + } + + NewArrayHolder<BYTE> pCounterSignature; + ULONG cbCounterSignature; + if (argCounterSignature->val.str.pStr == NULL || argCounterSignature->val.str.cbStr == 0 || + (!GetBytesFromHex(argCounterSignature->val.str.pStr, argCounterSignature->val.str.cbStr, &pCounterSignature, &cbCounterSignature))) + { + return CORSEC_E_INVALID_COUNTERSIGNATURE; + } + + StrongNameBufferHolder<PublicKeyBlob> pIdentityPublicKey = NULL; + IfFailRet(FindPublicKey(pLoadCtx, NULL, 0, &pIdentityPublicKey)); + + IfFailRet(VerifyCounterSignature(pSignaturePublicKey, cbSignaturePublicKey, pIdentityPublicKey, pCounterSignature, cbCounterSignature)); + + *ppPublicKey = pSignaturePublicKey.Extract(); + if (pcbPublicKey != NULL) + *pcbPublicKey = cbSignaturePublicKey; + } + else + { + *ppPublicKey = NULL; + if (pcbPublicKey != NULL) + *pcbPublicKey = 0; + } + + return hr; +} + +// Checks revocation list against the assembly's public keys. +// If the identity key has been revoked, then the signature key must be non-null and +// must be in the replacement keys list to be allowed. +bool AreKeysAllowedByRevocationList(BYTE* pbAssemblyIdentityKey, DWORD cbAssemblyIdentityKey, BYTE* pbAssemblySignatureKey, DWORD cbAssemblySignatureKey) +{ + LIMITED_METHOD_CONTRACT; + + bool fRevoked = false; + + SN_REVOCATION_REC *pRevocationRec = g_pRevocationRecords; + while (pRevocationRec) + { + if (pRevocationRec->m_cbRevokedKey == cbAssemblyIdentityKey && + memcmp(pRevocationRec->m_pbRevokedKey, pbAssemblyIdentityKey, cbAssemblyIdentityKey) == 0) + { + fRevoked = true; // Identity key can't be trusted. + + if (pbAssemblySignatureKey != NULL) + { + SN_REPLACEMENT_KEY_REC *pReplacementKeyRec = pRevocationRec->m_pReplacementKeys; + + while (pReplacementKeyRec) + { + if (pReplacementKeyRec->m_cbReplacementKey == cbAssemblySignatureKey && + memcmp(pReplacementKeyRec->m_pbReplacementKey, pbAssemblySignatureKey, cbAssemblySignatureKey) == 0) + { + // Signature key was allowed as a replacement for the revoked identity key. + return true; + } + + pReplacementKeyRec = pReplacementKeyRec->m_pNext; + } + } + // We didn't find the signature key in the list of allowed replacement keys for this record. + // However, we don't return here, because another record might have the same identity key + // and allow the signature key as a replacement. + } + + pRevocationRec = pRevocationRec->m_pNext; + } + + return !fRevoked; +} + +#endif // FEATURE_STRONGNAME_MIGRATION + +// The common code used to verify a signature (taking into account whether skip +// verification is enabled for the given assembly). +HRESULT VerifySignature(__in SN_LOAD_CTX *pLoadCtx, DWORD dwInFlags, PublicKeyBlob *pRealEcmaPublicKey,__out_opt DWORD *pdwOutFlags) +{ + if (pdwOutFlags) + *pdwOutFlags = 0; + + // Read the public key used to sign the assembly from the assembly metadata. + // Also get the assembly name, we might need this if we fail the + // verification and need to look up a verification disablement entry. + WCHAR wszSimpleAssemblyName[MAX_PATH + 1]; + SString strFullyQualifiedAssemblyName; + BOOL bSuccess = FALSE; +#if STRONGNAME_IN_VM + BOOL bAssemblyNameFormed = FALSE; + BOOL bVerificationBegun = FALSE; +#endif + + HandleKeyHolder hKey(NULL); + HandleHashHolder hHash(NULL); + HandleStrongNameCspHolder hProv(NULL); + + StrongNameBufferHolder<PublicKeyBlob> pAssemblyIdentityKey; + DWORD cbAssemblyIdentityKey; + HRESULT hr = FindPublicKey(pLoadCtx, + wszSimpleAssemblyName, + sizeof(wszSimpleAssemblyName) / sizeof(WCHAR), + &pAssemblyIdentityKey, + &cbAssemblyIdentityKey); + if (FAILED(hr)) + return hr; + + BOOL isEcmaKey = SN_IS_NEUTRAL_KEY(pAssemblyIdentityKey); + // If we're handed the ECMA key, we translate it to the real key at this point. + // Note: gcc gets confused with the complexity of StrongNameBufferHolder<> and + // won't auto-convert pAssemblyIdentityKey to type PublicKeyBlob*, so cast it explicitly. + PublicKeyBlob *pRealPublicKey = isEcmaKey ? pRealEcmaPublicKey : static_cast<PublicKeyBlob*>(pAssemblyIdentityKey); + +// An assembly can specify a signature public key in an attribute. +// If one is present, we verify the signature using that public key. +#ifdef FEATURE_STRONGNAME_MIGRATION + StrongNameBufferHolder<PublicKeyBlob> pAssemblySignaturePublicKey; + DWORD cbAssemblySignaturePublicKey; + IfFailRet(GetVerifiedSignatureKey(pLoadCtx, &pAssemblySignaturePublicKey, &cbAssemblySignaturePublicKey)); + if(hr != S_FALSE) // Attribute was found + { + pRealPublicKey = pAssemblySignaturePublicKey; + } + +#endif // FEATURE_STRONGNAME_MIGRATION + + DWORD dwSpecialKeys = GetSpecialKeyFlags(pRealPublicKey); + + // If this isn't the first time we've been called for this assembly and we + // know it was fully signed and we're confident it couldn't have been + // tampered with in the meantime, we can just skip the verification. + if (!(dwInFlags & SN_INFLAG_FORCE_VER) && + !(dwInFlags & SN_INFLAG_INSTALL) && + (pLoadCtx->m_pCorHeader->Flags & VAL32(COMIMAGE_FLAGS_STRONGNAMESIGNED)) && + ((dwInFlags & SN_INFLAG_ADMIN_ACCESS) || g_fCacheVerify)) + { + SNLOG((W("Skipping verification due to cached result\n"))); + DbgCount(dwInFlags & SN_INFLAG_RUNTIME ? W("RuntimeSkipCache") : W("FusionSkipCache")); + return S_OK; + } + +#ifdef FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED + // If we're not forcing verification, let's see if there's a skip + // verification entry for this assembly. If there is we can skip all the + // hard work and just lie about the strong name now. The exception is if the + // assembly is marked as fully signed, in which case we have to force a + // verification to see if they're telling the truth. + StrongNameBufferHolder<PublicKeyBlob> pTestKey = NULL; + SN_VER_REC *pVerRec = GetVerificationRecord(wszSimpleAssemblyName, pAssemblyIdentityKey); + if (!(dwInFlags & SN_INFLAG_FORCE_VER) && !(pLoadCtx->m_pCorHeader->Flags & VAL32(COMIMAGE_FLAGS_STRONGNAMESIGNED))) + { + if (pVerRec != NULL) + { + if (pVerRec->m_wszTestPublicKey) + { + // substitute the public key with the test public key. + pTestKey = GetPublicKeyFromHex(pVerRec->m_wszTestPublicKey); + if (pTestKey != NULL) + { + + SNLOG((W("Using test public key for verification due to registry entry\n"))); + DbgCount(dwInFlags & SN_INFLAG_RUNTIME ? W("RuntimeSkipDelay") : W("FusionSkipDelay")); + + // If the assembly was not ECMA signed, then we need to update the key that it will be + // verified with as well. + if (!isEcmaKey) + { + // When test signing, there's no way to specify a hash algorithm. + // So instead of defaulting to SHA1, we pick the algorithm the assembly + // would've been signed with, if the test key wasn't present. + // Thus we use the same algorithm when verifying the signature. + SET_UNALIGNED_VAL32(&pTestKey->HashAlgID, GET_UNALIGNED_VAL32(&pRealPublicKey->HashAlgID)); + + pRealPublicKey = pTestKey; + } + } + } + else + { + SNLOG((W("Skipping verification due to registry entry\n"))); + DbgCount(dwInFlags & SN_INFLAG_RUNTIME ? W("RuntimeSkipDelay") : W("FusionSkipDelay")); + if (pdwOutFlags) + { + *pdwOutFlags |= dwSpecialKeys; + } + return S_OK; + } + } + } +#endif // FEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED + +#ifdef FEATURE_STRONGNAME_MIGRATION + if(!isEcmaKey) // We should never revoke the ecma key, as it is tied strongly to the runtime + { + if(!AreKeysAllowedByRevocationList((BYTE*)pAssemblyIdentityKey.GetValue(), cbAssemblyIdentityKey, (BYTE*)pAssemblySignaturePublicKey.GetValue(), cbAssemblySignaturePublicKey)) + { + if(pAssemblySignaturePublicKey == NULL) + { + SNLOG((W("Verification failed. Assembly public key has been revoked\n"))); + } + else + { + SNLOG((W("Verification failed. Assembly identity key has been revoked, an the assembly signature key isn't in the replacement key list\n"))); + } + + hr = CORSEC_E_INVALID_STRONGNAME; + goto Error; + } + } + +#endif // FEATURE_STRONGNAME_MIGRATION + +#ifdef FEATURE_CORECLR + // TritonTODO: check with security team on this + if (pLoadCtx->m_pbSignature == NULL) + { + hr = CORSEC_E_MISSING_STRONGNAME; + goto Error; + } +#endif //FEATURE_CORECLR + +#if STRONGNAME_IN_VM + bVerificationBegun = TRUE; + // SN verification start event + if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_SECURITY_KEYWORD)) + { + // form the fully qualified assembly name using the load context + bAssemblyNameFormed = FormFullyQualifiedAssemblyName(pLoadCtx, strFullyQualifiedAssemblyName); + if(bAssemblyNameFormed) + { + ETW::SecurityLog::StrongNameVerificationStart(dwInFlags,(LPWSTR)strFullyQualifiedAssemblyName.GetUnicode()); + } + } +#endif // STRONGNAME_IN_VM + + ALG_ID uHashAlgId = GET_UNALIGNED_VAL32(&pRealPublicKey->HashAlgID); + ALG_ID uSignAlgId = GET_UNALIGNED_VAL32(&pRealPublicKey->SigAlgID); + + // Default hashing and signing algorithm IDs if necessary. + if (uHashAlgId == 0) + uHashAlgId = CALG_SHA1; + if (uSignAlgId == 0) + uSignAlgId = CALG_RSA_SIGN; + + // Find a CSP supporting the required algorithms. + hProv = LocateCSP(NULL, SN_IGNORE_CONTAINER, uHashAlgId, uSignAlgId); + if (!hProv) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("Failed to acquire a CSP: %08x"), hr)); + goto Error; + } + + BYTE *pbRealPublicKey; + pbRealPublicKey = pRealPublicKey->PublicKey; + DWORD cbRealPublicKey; + cbRealPublicKey = GET_UNALIGNED_VAL32(&pRealPublicKey->cbPublicKey); + + if (!CryptImportKey(hProv, pbRealPublicKey, cbRealPublicKey, 0, 0, &hKey)) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("Failed to import key: %08x"), hr)); + goto Error; + } + + // Create a hash object. + + if (!CryptCreateHash(hProv, uHashAlgId, 0, 0, &hHash)) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("Failed to create hash: %08x"), hr)); + goto Error; + } + + // Compute a hash over the image. + if (!ComputeHash(pLoadCtx, hHash, CalcHash, NULL)) + { + hr = HRESULT_FROM_GetLastError(); + SNLOG((W("Failed to compute hash: %08x"), hr)); + goto Error; + } + + // Verify the hash against the signature. + DbgCount(dwInFlags & SN_INFLAG_RUNTIME ? W("RuntimeVerify") : W("FusionVerify")); + if (pLoadCtx->m_pbSignature != NULL && pLoadCtx->m_cbSignature != 0 && + CryptVerifySignatureW(hHash, pLoadCtx->m_pbSignature, pLoadCtx->m_cbSignature, hKey, NULL, 0)) + { + SNLOG((W("Verification succeeded (for real)\n"))); + if (pdwOutFlags) + { + *pdwOutFlags |= dwSpecialKeys | SN_OUTFLAG_WAS_VERIFIED; + } + bSuccess = TRUE; + } + else + { + SNLOG((W("Verification failed\n"))); + hr = CORSEC_E_INVALID_STRONGNAME; + } + +Error: + +#if STRONGNAME_IN_VM + // SN verification end event + if(bVerificationBegun && + ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_VERBOSE, CLR_SECURITY_KEYWORD)) + { + // form the fully qualified assembly name using the load context if it has not yet been formed + if(!bAssemblyNameFormed) + { + strFullyQualifiedAssemblyName.Clear(); + bAssemblyNameFormed = FormFullyQualifiedAssemblyName(pLoadCtx, strFullyQualifiedAssemblyName); + } + if(bAssemblyNameFormed) + { + ETW::SecurityLog::StrongNameVerificationStop(dwInFlags,(ULONG)hr, (LPWSTR)strFullyQualifiedAssemblyName.GetUnicode()); + } + } +#endif // STRONGNAME_IN_VM + + if (bSuccess) + return S_OK; + else + return hr; +} + +// Compute a hash over the elements of an assembly manifest file that should +// remain static (skip checksum, Authenticode signatures and strong name +// signature blob). +// This function can also be used to get the blob of bytes that would be +// hashed without actually hashing. +BOOLEAN ComputeHash(SN_LOAD_CTX *pLoadCtx, HCRYPTHASH hHash, HashFunc func, void* cookie) +{ + union { + IMAGE_NT_HEADERS32 m_32; + IMAGE_NT_HEADERS64 m_64; + } sHeaders; + IMAGE_SECTION_HEADER *pSections; + ULONG i; + BYTE *pbSig = pLoadCtx->m_pbSignature; + DWORD cbSig = pLoadCtx->m_cbSignature; + +#define LIMIT_CHECK(_start, _length, _fileStart, _fileLength) \ + do { if (((_start) < (_fileStart)) || \ + (((_start)+(_length)) < (_start)) || \ + (((_start)+(_length)) < (_fileStart)) || \ + (((_start)+(_length)) > ((_fileStart)+(_fileLength))) ) \ + { SetLastError(CORSEC_E_INVALID_IMAGE_FORMAT); return FALSE; } } while (false) + +#define FILE_LIMIT_CHECK(_start, _length) LIMIT_CHECK(_start, _length, pLoadCtx->m_pbBase, pLoadCtx->m_dwLength) + +#define SN_HASH(_start, _length) do { if (!func(hHash, (_start), (_length), 0, cookie)) return FALSE; } while (false) + +#define SN_CHECK_AND_HASH(_start, _length) do { FILE_LIMIT_CHECK(_start, _length); SN_HASH(_start, _length); } while (false) + + // Make sure the file size doesn't wrap around. + if (pLoadCtx->m_pbBase + pLoadCtx->m_dwLength <= pLoadCtx->m_pbBase) + { + SetLastError(CORSEC_E_INVALID_IMAGE_FORMAT); + return FALSE; + } + + // Make sure the signature is completely contained within the file. + FILE_LIMIT_CHECK(pbSig, cbSig); + + // Hash the DOS header if it exists. + if ((BYTE*)pLoadCtx->m_pNtHeaders != pLoadCtx->m_pbBase) + SN_CHECK_AND_HASH(pLoadCtx->m_pbBase, (DWORD)((BYTE*)pLoadCtx->m_pNtHeaders - pLoadCtx->m_pbBase)); + + // Add image headers minus the checksum and security data directory. + if (pLoadCtx->m_pNtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)) { + sHeaders.m_32 = *((IMAGE_NT_HEADERS32*)pLoadCtx->m_pNtHeaders); + sHeaders.m_32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0; + sHeaders.m_32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size = 0; + sHeaders.m_32.OptionalHeader.CheckSum = 0; + SN_HASH((BYTE*)&sHeaders.m_32, sizeof(sHeaders.m_32)); + } else if (pLoadCtx->m_pNtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC)) { + sHeaders.m_64 = *((IMAGE_NT_HEADERS64*)pLoadCtx->m_pNtHeaders); + sHeaders.m_64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0; + sHeaders.m_64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size = 0; + sHeaders.m_64.OptionalHeader.CheckSum = 0; + SN_HASH((BYTE*)&sHeaders.m_64, sizeof(sHeaders.m_64)); + } else { + SetLastError(CORSEC_E_INVALID_IMAGE_FORMAT); + return FALSE; + } + + // Then the section headers. + pSections = IMAGE_FIRST_SECTION(pLoadCtx->m_pNtHeaders); + SN_CHECK_AND_HASH((BYTE*)pSections, VAL16(pLoadCtx->m_pNtHeaders->FileHeader.NumberOfSections) * sizeof(IMAGE_SECTION_HEADER)); + + // Finally, add data from each section. + for (i = 0; i < VAL16(pLoadCtx->m_pNtHeaders->FileHeader.NumberOfSections); i++) { + BYTE *pbData = pLoadCtx->m_pbBase + VAL32(pSections[i].PointerToRawData); + DWORD cbData = VAL32(pSections[i].SizeOfRawData); + + // We need to exclude the strong name signature blob from the hash. The + // blob could intersect the section in a number of ways. + + if ((pbSig + cbSig) <= pbData || pbSig >= (pbData + cbData)) + // No intersection at all. Hash all data. + SN_CHECK_AND_HASH(pbData, cbData); + else if (pbSig == pbData && cbSig == cbData) + // Signature consumes entire block. Hash no data. + ; + else if (pbSig == pbData) + // Signature at start. Hash end. + SN_CHECK_AND_HASH(pbData + cbSig, cbData - cbSig); + else if ((pbSig + cbSig) == (pbData + cbData)) + // Signature at end. Hash start. + SN_CHECK_AND_HASH(pbData, cbData - cbSig); + else { + // Signature in the middle. Hash head and tail. + SN_CHECK_AND_HASH(pbData, (DWORD)(pbSig - pbData)); + SN_CHECK_AND_HASH(pbSig + cbSig, cbData - (DWORD)(pbSig + cbSig - pbData)); + } + } + +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) + if (hHash != (HCRYPTHASH)INVALID_HANDLE_VALUE) { + DWORD cbHash; + DWORD dwRetLen = sizeof(cbHash); + if (CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&cbHash, &dwRetLen, 0)) + { + NewArrayHolder<BYTE> pbHash(new (nothrow) BYTE[cbHash]); + if (pbHash != NULL) + { + if (CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) + { + SNLOG((W("Computed Hash Value (%u bytes):\n"), cbHash)); + HexDump(pbHash, cbHash); + } + else + { + SNLOG((W("CryptGetHashParam() failed with %08X\n"), GetLastError())); + } + } + } + else + { + SNLOG((W("CryptGetHashParam() failed with %08X\n"), GetLastError())); + } + } +#endif // _DEBUG + + return TRUE; + +#undef SN_CHECK_AND_HASH +#undef SN_HASH +#undef FILE_LIMIT_CHECK +#undef LIMIT_CHECK +} + + +SNAPI_(DWORD) GetHashFromAssemblyFile(LPCSTR szFilePath, // [IN] location of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash) // [OUT] length of hash byte array +{ + BOOL retVal = FALSE; + + BEGIN_ENTRYPOINT_NOTHROW; + // Convert filename to wide characters and call the W version of this + // function. + + MAKE_WIDEPTR_FROMANSI(wszFilePath, szFilePath); + retVal = GetHashFromAssemblyFileW(wszFilePath, piHashAlg, pbHash, cchHash, pchHash); + END_ENTRYPOINT_NOTHROW; + return retVal; +} + +SNAPI_(DWORD) GetHashFromAssemblyFileW(LPCWSTR wszFilePath, // [IN] location of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash) // [OUT] length of hash byte array +{ + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + SN_LOAD_CTX sLoadCtx; + BYTE *pbMetaData = NULL; + DWORD cbMetaData; + + sLoadCtx.m_fReadOnly = TRUE; + if (!LoadAssembly(&sLoadCtx, wszFilePath, 0, FALSE)) + IfFailGo(HRESULT_FROM_GetLastError()); + + if (sLoadCtx.m_pedecoder->CheckCorHeader()) + { + pbMetaData = (BYTE *)sLoadCtx.m_pedecoder->GetMetadata(); + } + if (pbMetaData == NULL) { + UnloadAssembly(&sLoadCtx); + IfFailGo(E_INVALIDARG); + } + cbMetaData = VAL32(sLoadCtx.m_pCorHeader->MetaData.Size); + + hr = GetHashFromBlob(pbMetaData, cbMetaData, piHashAlg, pbHash, cchHash, pchHash); + + UnloadAssembly(&sLoadCtx); +ErrExit: + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +SNAPI_(DWORD) GetHashFromFile(LPCSTR szFilePath, // [IN] location of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash) // [OUT] length of hash byte array +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HANDLE hFile = CreateFileA(szFilePath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_GetLastError(); + } + else + { + hr = GetHashFromHandle(hFile, piHashAlg, pbHash, cchHash, pchHash); + CloseHandle(hFile); + } + END_ENTRYPOINT_NOTHROW; + return hr; +} + +SNAPI_(DWORD) GetHashFromFileW(LPCWSTR wszFilePath, // [IN] location of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash) // [OUT] length of hash byte array +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + HANDLE hFile = WszCreateFile(wszFilePath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (hFile == INVALID_HANDLE_VALUE) + IfFailGo(HRESULT_FROM_GetLastError()); + + hr = GetHashFromHandle(hFile, piHashAlg, pbHash, cchHash, pchHash); + CloseHandle(hFile); +ErrExit: + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +SNAPI_(DWORD) GetHashFromHandle(HANDLE hFile, // [IN] handle of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash) // [OUT] length of hash byte array +{ + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + PBYTE pbBuffer = NULL; + DWORD dwFileLen = SafeGetFileSize(hFile, 0); + if (dwFileLen == 0xffffffff) + IfFailGo(HRESULT_FROM_GetLastError()); + + if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) + IfFailGo(HRESULT_FROM_GetLastError()); + + DWORD dwResultLen; + pbBuffer = new (nothrow) BYTE[dwFileLen]; + IfNullGo(pbBuffer); + + if (ReadFile(hFile, pbBuffer, dwFileLen, &dwResultLen, NULL)) + hr = GetHashFromBlob(pbBuffer, dwResultLen, piHashAlg, pbHash, cchHash, pchHash); + else + hr = HRESULT_FROM_GetLastError(); + + delete[] pbBuffer; + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +SNAPI_(DWORD) GetHashFromBlob(BYTE *pbBlob, // [IN] pointer to memory block to hash + DWORD cchBlob, // [IN] length of blob + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash) // [OUT] length of hash byte array +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HandleStrongNameCspHolder hProv(NULL); + CapiHashHolder hHash(NULL); + + if (!piHashAlg || !pbHash || !pchHash) + IfFailGo(E_INVALIDARG); + + if (!(*piHashAlg)) + *piHashAlg = CALG_SHA1; + + *pchHash = cchHash; + + hProv = LocateCSP(NULL, SN_IGNORE_CONTAINER, *piHashAlg); + + if (!hProv || + (!CryptCreateHash(hProv, *piHashAlg, 0, 0, &hHash)) || + (!CryptHashData(hHash, pbBlob, cchBlob, 0)) || + (!CryptGetHashParam(hHash, HP_HASHVAL, pbHash, pchHash, 0))) + hr = HRESULT_FROM_GetLastError(); + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +#endif // #ifndef DACCESS_COMPILE + +#else // !defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) + +#define InitStrongName() S_OK + +#endif // !defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) + + +// Free buffer allocated by routines below. +SNAPI_(VOID) StrongNameFreeBuffer(BYTE *pbMemory) // [in] address of memory to free +{ + BEGIN_ENTRYPOINT_VOIDRET; + + SNLOG((W("StrongNameFreeBuffer(%08X)\n"), pbMemory)); + + if (pbMemory != (BYTE*)SN_THE_KEY() && pbMemory != g_rbNeutralPublicKey) + delete [] pbMemory; + END_ENTRYPOINT_VOIDRET; + +} + +#ifndef DACCESS_COMPILE +// Retrieve per-thread context, lazily allocating it if necessary. +SN_THREAD_CTX *GetThreadContext() +{ + SN_THREAD_CTX *pThreadCtx = (SN_THREAD_CTX*)ClrFlsGetValue(TlsIdx_StrongName); + if (pThreadCtx == NULL) { + pThreadCtx = new (nothrow) SN_THREAD_CTX; + if (pThreadCtx == NULL) + return NULL; + pThreadCtx->m_dwLastError = S_OK; +#if !defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) + for (ULONG i = 0; i < CachedCspCount; i++) + { + pThreadCtx->m_hProv[i] = NULL; + } +#endif // !FEATURE_CORECLR || CROSSGEN_COMPILE + + EX_TRY { + ClrFlsSetValue(TlsIdx_StrongName, pThreadCtx); + } + EX_CATCH { + delete pThreadCtx; + pThreadCtx = NULL; + } + EX_END_CATCH (SwallowAllExceptions); + } + return pThreadCtx; +} + +// Set the per-thread last error code. +VOID SetStrongNameErrorInfo(DWORD dwStatus) +{ + SN_THREAD_CTX *pThreadCtx = GetThreadContext(); + if (pThreadCtx == NULL) + // We'll return E_OUTOFMEMORY when we attempt to get the error. + return; + pThreadCtx->m_dwLastError = dwStatus; +} + +#endif // !DACCESS_COMPILE + +// Return last error. +SNAPI_(DWORD) StrongNameErrorInfo(VOID) +{ + HRESULT hr = E_FAIL; + + BEGIN_ENTRYPOINT_NOTHROW; + +#ifndef DACCESS_COMPILE + SN_THREAD_CTX *pThreadCtx = GetThreadContext(); + if (pThreadCtx == NULL) + hr = E_OUTOFMEMORY; + else + hr = pThreadCtx->m_dwLastError; +#else + hr = E_FAIL; +#endif // #ifndef DACCESS_COMPILE + END_ENTRYPOINT_NOTHROW; + + return hr; +} + + +// Create a strong name token from a public key blob. +SNAPI StrongNameTokenFromPublicKey(BYTE *pbPublicKeyBlob, // [in] public key blob + ULONG cbPublicKeyBlob, + BYTE **ppbStrongNameToken, // [out] strong name token + ULONG *pcbStrongNameToken) +{ + BOOLEAN retVal = FALSE; + + BEGIN_ENTRYPOINT_VOIDRET; + +#ifndef DACCESS_COMPILE + +#ifndef FEATURE_CORECLR + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + HCRYPTKEY hKey = NULL; + DWORD dwHashLen; + DWORD dwRetLen; + NewArrayHolder<BYTE> pHash(NULL); +#else // !FEATURE_CORECLR + SHA1Hash sha1; + BYTE *pHash = NULL; +#endif // !FEATURE_CORECLR + + DWORD i; + DWORD cbKeyBlob; + PublicKeyBlob *pPublicKey = NULL; + DWORD dwHashLenMinusTokenSize = 0; + + SNLOG((W("StrongNameTokenFromPublicKey(%08X, %08X, %08X, %08X)\n"), pbPublicKeyBlob, cbPublicKeyBlob, ppbStrongNameToken, pcbStrongNameToken)); + +#if STRONGNAME_IN_VM + FireEtwSecurityCatchCall_V1(GetClrInstanceId()); +#endif // STRONGNAME_IN_VM + + SN_COMMON_PROLOG(); + + if (pbPublicKeyBlob == NULL) + SN_ERROR(E_POINTER); + if (!StrongNameIsValidPublicKey(pbPublicKeyBlob, cbPublicKeyBlob, false)) + SN_ERROR(CORSEC_E_INVALID_PUBLICKEY); + if (ppbStrongNameToken == NULL) + SN_ERROR(E_POINTER); + if (pcbStrongNameToken == NULL) + SN_ERROR(E_POINTER); + + // Allocate a buffer for the output token. + *ppbStrongNameToken = new (nothrow) BYTE[SN_SIZEOF_TOKEN]; + if (*ppbStrongNameToken == NULL) { + SetStrongNameErrorInfo(E_OUTOFMEMORY); + goto Exit; + } + *pcbStrongNameToken = SN_SIZEOF_TOKEN; + + // We cache a couple of common cases. + if (SN_IS_NEUTRAL_KEY(pbPublicKeyBlob)) { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, g_rbNeutralPublicKeyToken, SN_SIZEOF_TOKEN); + retVal = TRUE; + goto Exit; + } + if (cbPublicKeyBlob == SN_SIZEOF_THE_KEY() && + memcmp(pbPublicKeyBlob, SN_THE_KEY(), cbPublicKeyBlob) == 0) { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, SN_THE_KEYTOKEN(), SN_SIZEOF_TOKEN); + retVal = TRUE; + goto Exit; + } +#ifdef FEATURE_CORECLR + if (SN_IS_THE_SILVERLIGHT_PLATFORM_KEY(pbPublicKeyBlob)) + { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, SN_THE_SILVERLIGHT_PLATFORM_KEYTOKEN(), SN_SIZEOF_TOKEN); + retVal = TRUE; + goto Exit; + } + + if (SN_IS_THE_SILVERLIGHT_KEY(pbPublicKeyBlob)) + { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, SN_THE_SILVERLIGHT_KEYTOKEN(), SN_SIZEOF_TOKEN); + retVal = TRUE; + goto Exit; + } + +#ifdef FEATURE_WINDOWSPHONE + + if (SN_IS_THE_MICROSOFT_PHONE_KEY(pbPublicKeyBlob)) + { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, SN_THE_MICROSOFT_PHONE_KEYTOKEN(), SN_SIZEOF_TOKEN); + retVal = TRUE; + goto Exit; + } + + if (SN_IS_THE_MICROSOFT_XNA_KEY(pbPublicKeyBlob)) + { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, SN_THE_MICROSOFT_XNA_KEYTOKEN(), SN_SIZEOF_TOKEN); + retVal = TRUE; + goto Exit; + } + +#endif //FEATURE_WINDOWSPHONE +#endif //FEATURE_CORECLR + + // To compute the correct public key token, we need to make sure the public key blob + // was not padded with extra bytes that CAPI CryptImportKey would've ignored. + // Without this round trip, we would blindly compute the hash over the padded bytes + // which could make finding a public key token collision a significantly easier task + // since an attacker wouldn't need to work hard on generating valid key pairs before hashing. + if (cbPublicKeyBlob <= sizeof(PublicKeyBlob)) { + SetLastError(CORSEC_E_INVALID_PUBLICKEY); + goto Error; + } + + // Check that the blob type is PUBLICKEYBLOB. + pPublicKey = (PublicKeyBlob*) pbPublicKeyBlob; + + if (pPublicKey->PublicKey + GET_UNALIGNED_VAL32(&pPublicKey->cbPublicKey) < pPublicKey->PublicKey) { + SetLastError(CORSEC_E_INVALID_PUBLICKEY); + goto Error; + } + + if (cbPublicKeyBlob < SN_SIZEOF_KEY(pPublicKey)) { + SetLastError(CORSEC_E_INVALID_PUBLICKEY); + goto Error; + } + + if (*(BYTE*) pPublicKey->PublicKey /* PUBLICKEYSTRUC->bType */ != PUBLICKEYBLOB) { + SetLastError(CORSEC_E_INVALID_PUBLICKEY); + goto Error; + } + +#ifndef FEATURE_CORECLR + + // Look for a CSP to hash the public key. + hProv = LocateCSP(NULL, SN_HASH_SHA1_ONLY); + if (!hProv) + goto Error; + + if (!CryptImportKey(hProv, + pPublicKey->PublicKey, + GET_UNALIGNED_VAL32(&pPublicKey->cbPublicKey), + 0, + 0, + &hKey)) + goto Error; + + cbKeyBlob = sizeof(DWORD); + if (!CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, NULL, &cbKeyBlob)) + goto Error; + + if ((offsetof(PublicKeyBlob, PublicKey) + cbKeyBlob) != cbPublicKeyBlob) { + SetLastError(CORSEC_E_INVALID_PUBLICKEY); + goto Error; + } + + // Create a hash object. + if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) + goto Error; + + // Compute a hash over the public key. + if (!CryptHashData(hHash, pbPublicKeyBlob, cbPublicKeyBlob, 0)) + goto Error; + + // Get the length of the hash. + dwRetLen = sizeof(dwHashLen); + if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&dwHashLen, &dwRetLen, 0)) + goto Error; + + // Allocate a temporary block to hold the hash. + pHash = new (nothrow) BYTE[dwHashLen]; + if (pHash == NULL) + { + SetLastError(E_OUTOFMEMORY); + goto Error; + } + + // Read the hash value. + if (!CryptGetHashParam(hHash, HP_HASHVAL, pHash, &dwHashLen, 0)) + goto Error; + + // We no longer need the hash object or the provider. + CryptDestroyHash(hHash); + CryptDestroyKey(hKey); + FreeCSP(hProv); + + // Take the last few bytes of the hash value for our token. (These are the + // low order bytes from a network byte order point of view). Reverse the + // order of these bytes in the output buffer to get host byte order. + _ASSERTE(dwHashLen >= SN_SIZEOF_TOKEN); + if (!ClrSafeInt<DWORD>::subtraction(dwHashLen, SN_SIZEOF_TOKEN, dwHashLenMinusTokenSize)) + { + SetLastError(COR_E_OVERFLOW); + goto Error; + } + +#else // !FEATURE_CORECLR + + // Compute a hash over the public key. + sha1.AddData(pbPublicKeyBlob, cbPublicKeyBlob); + pHash = sha1.GetHash(); + static_assert(SHA1_HASH_SIZE >= SN_SIZEOF_TOKEN, "SN_SIZEOF_TOKEN must be smaller or equal to the SHA1_HASH_SIZE"); + dwHashLenMinusTokenSize = SHA1_HASH_SIZE - SN_SIZEOF_TOKEN; + +#endif // !FEATURE_CORECLR + + // Take the last few bytes of the hash value for our token. (These are the + // low order bytes from a network byte order point of view). Reverse the + // order of these bytes in the output buffer to get host byte order. + for (i = 0; i < SN_SIZEOF_TOKEN; i++) + (*ppbStrongNameToken)[SN_SIZEOF_TOKEN - (i + 1)] = pHash[i + dwHashLenMinusTokenSize]; + + retVal = TRUE; + goto Exit; + + Error: + SetStrongNameErrorInfo(HRESULT_FROM_GetLastError()); +#ifndef FEATURE_CORECLR + if (hHash) + CryptDestroyHash(hHash); + if (hKey) + CryptDestroyKey(hKey); + if (hProv) + FreeCSP(hProv); +#endif // !FEATURE_CORECLR + + if (*ppbStrongNameToken) { + delete [] *ppbStrongNameToken; + *ppbStrongNameToken = NULL; + } +Exit: +#else + DacNotImpl(); +#endif // #ifndef DACCESS_COMPILE + END_ENTRYPOINT_VOIDRET; + + return retVal; + +} diff --git a/src/strongname/api/strongnamecoreclr.cpp b/src/strongname/api/strongnamecoreclr.cpp new file mode 100644 index 0000000000..1430b42131 --- /dev/null +++ b/src/strongname/api/strongnamecoreclr.cpp @@ -0,0 +1,99 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// +// Several strong name tools are in a special scenario because they build in the CoreCLR build process, but +// are expected to run against the desktop VM. Because of this, we need to setup the callback structure that +// Utilcode asks for to point to callbacks that shim to the desktop. These methods provide that shimming functionality. +// + +#include "common.h" + +#if defined(FEATURE_CORECLR) + +CoreClrCallbacks *GetCoreClrCallbacks(); + +// +// Get a pointer to an API out of the shim +// +// Arguments: +// szApiName - name of the API to get a pointer to +// +// + +template<typename FunctionPointer> +FunctionPointer ApiShim(LPCSTR szApiName) +{ + static FunctionPointer pfnApi = NULL; + + if (pfnApi == NULL) + { + CoreClrCallbacks *pCallbacks = GetCoreClrCallbacks(); + pfnApi = reinterpret_cast<FunctionPointer>(GetProcAddress(pCallbacks->m_hmodCoreCLR, szApiName)); + _ASSERTE(pfnApi != NULL); + } + + return pfnApi; +} + +// +// Shim APIs, passing off into the desktop VM +// + +IExecutionEngine * __stdcall SnIEE() +{ + typedef IExecutionEngine * ( __stdcall *IEEFn_t)(); + return ApiShim<IEEFn_t>("IEE")(); +} + +STDAPI SnGetCorSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* dwLength) +{ + typedef HRESULT (__stdcall *GetCorSystemDirectoryFn_t)(LPWSTR, DWORD, DWORD *); + return ApiShim<GetCorSystemDirectoryFn_t>("GetCORSystemDirectory")(pbuffer, cchBuffer, dwLength); +} + +// +// Initialize a set of CoreCLR callbacks for utilcode to call into the VM with +// +// Return Value: +// CoreClrCallbacks for UtilCode +// +// Notes: +// Will not return NULL +// + +CoreClrCallbacks *GetCoreClrCallbacks() +{ + static CoreClrCallbacks coreClrCallbacks = { 0 }; + if (coreClrCallbacks.m_hmodCoreCLR == NULL) + { + // Run against the desktop CLR + coreClrCallbacks.m_hmodCoreCLR = WszLoadLibrary(W("mscoree.dll")); + coreClrCallbacks.m_pfnIEE = SnIEE; + coreClrCallbacks.m_pfnGetCORSystemDirectory = SnGetCorSystemDirectory; + coreClrCallbacks.m_pfnGetCLRFunction = NULL; + } + + return &coreClrCallbacks; +} + +// Initialize Utilcode +// +// Notes: +// Should only be called once +// + +void InitUtilcode() +{ +#ifdef _DEBUG + static bool fAlreadyInitialized = false; + _ASSERTE(!fAlreadyInitialized); + fAlreadyInitialized = true; +#endif + + InitUtilcode(*GetCoreClrCallbacks()); +} + +#endif // FEATURE_CORECLR && !STRONGNAME_IN_VM diff --git a/src/strongname/api/strongnameinternal.cpp b/src/strongname/api/strongnameinternal.cpp new file mode 100644 index 0000000000..10a08fa18d --- /dev/null +++ b/src/strongname/api/strongnameinternal.cpp @@ -0,0 +1,448 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// Strong name APIs which are not exposed publicly but are used by CLR code +// + +#include "common.h" +#include "strongnameinternal.h" +#include "strongnameholders.h" +#include "thekey.h" +#include "ecmakey.h" + +#ifdef FEATURE_STRONGNAME_TESTKEY_ALLOWED +#include "thetestkey.h" + +BYTE g_rbTestKeyBuffer[] = { TEST_KEY_HEADER TEST_KEY_BUFFER }; +#endif // FEATURE_STRONGNAME_TESTKEY_ALLOWED + +//--------------------------------------------------------------------------------------- +// +// Check to see if a public key blob is the ECMA public key blob +// +// Arguments: +// pbKey - public key blob to check +// cbKey - size in bytes of pbKey +// + +bool StrongNameIsEcmaKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The key should be the same size as the ECMA key + if (cbKey != sizeof(g_rbNeutralPublicKey)) + { + return false; + } + + const PublicKeyBlob *pKeyBlob = reinterpret_cast<const PublicKeyBlob *>(pbKey); + return StrongNameIsEcmaKey(*pKeyBlob); +} + +//--------------------------------------------------------------------------------------- +// +// Check to see if a public key blob is the ECMA public key blob +// +// Arguments: +// keyPublicKey - Key to check to see if it matches the ECMA key +// + +bool StrongNameIsEcmaKey(const PublicKeyBlob &keyPublicKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return StrongNameSizeOfPublicKey(keyPublicKey) == sizeof(g_rbNeutralPublicKey) && + memcmp(reinterpret_cast<const BYTE *>(&keyPublicKey), g_rbNeutralPublicKey, sizeof(g_rbNeutralPublicKey)) == 0; +} + +//--------------------------------------------------------------------------------------- +// +// Check to see if a public key blob is the TheKey public key blob +// +// Arguments: +// pbKey - public key blob to check +// cbKey - size in bytes of pbKey +// +bool StrongNameIsTheKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The key should be the same size as the TheKey key + if (cbKey != sizeof(g_rbTheKey)) + { + return false; + } + + return (memcmp(pbKey, g_rbTheKey, sizeof(g_rbTheKey)) == 0); +} + +#ifdef FEATURE_CORECLR +//--------------------------------------------------------------------------------------- +// +// Check to see if a public key blob is the Silverlight Platform public key blob +// +// Arguments: +// pbKey - public key blob to check +// cbKey - size in bytes of pbKey +// + +bool StrongNameIsSilverlightPlatformKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The key should be the same size as the ECMA key + if (cbKey != sizeof(g_rbTheSilverlightPlatformKey)) + { + return false; + } + + const PublicKeyBlob *pKeyBlob = reinterpret_cast<const PublicKeyBlob *>(pbKey); + return StrongNameIsSilverlightPlatformKey(*pKeyBlob); +} + +//--------------------------------------------------------------------------------------- +// +// Check to see if a public key blob is the Silverlight Platform public key blob +// +// Arguments: +// keyPublicKey - Key to check to see if it matches the ECMA key +// + +bool StrongNameIsSilverlightPlatformKey(const PublicKeyBlob &keyPublicKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return StrongNameSizeOfPublicKey(keyPublicKey) == sizeof(g_rbTheSilverlightPlatformKey) && + memcmp(reinterpret_cast<const BYTE *>(&keyPublicKey), g_rbTheSilverlightPlatformKey, sizeof(g_rbTheSilverlightPlatformKey)) == 0; +} +#endif //FEATURE_CORECLR + +#ifdef FEATURE_STRONGNAME_TESTKEY_ALLOWED + +//--------------------------------------------------------------------------------------- +// +// Check to see if a public key blob is the Silverlight Platform public key blob +// +// See code:g_rbTestKeyBuffer#TestKeyStamping +// +// Arguments: +// pbKey - public key blob to check +// cbKey - size in bytes of pbKey +// + +bool StrongNameIsTestKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The key should be the same size as the ECMA key + if (cbKey != sizeof(g_rbTestKeyBuffer) - 2 * sizeof(GUID)) + { + return false; + } + + const PublicKeyBlob *pKeyBlob = reinterpret_cast<const PublicKeyBlob *>(pbKey); + return StrongNameIsTestKey(*pKeyBlob); +} + +//--------------------------------------------------------------------------------------- +// +// Determine if the public key blob is the test public key stamped into the VM. +// +// See code:g_rbTestKeyBuffer#TestKeyStamping +// +// Arguments: +// keyPublicKey - public key blob to check for emptyness +// + +bool StrongNameIsTestKey(const PublicKeyBlob &keyPublicKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // Find the blob in the VM by looking past the two header GUIDs in the buffer + _ASSERTE(sizeof(g_rbTestKeyBuffer) > 2 * sizeof(GUID) + sizeof(PublicKeyBlob)); + const PublicKeyBlob *pbTestPublicKey = reinterpret_cast<const PublicKeyBlob *>(g_rbTestKeyBuffer + 2 * sizeof(GUID)); + + DWORD cbTestPublicKey = StrongNameSizeOfPublicKey(*pbTestPublicKey); + DWORD cbCheckPublicKey = StrongNameSizeOfPublicKey(keyPublicKey); + + // Check whether valid test key was stamped in + if (cbTestPublicKey == 0) + return false; + + // This is the test public key if it is the same size as the public key in the buffer, and is identical + // to the test key as well. + return cbTestPublicKey == cbCheckPublicKey && + memcmp(reinterpret_cast<const void *>(pbTestPublicKey), reinterpret_cast<const void *>(&keyPublicKey), cbTestPublicKey) == 0; +} + +#endif // FEATURE_STRONGNAME_TESTKEY_ALLOWED + +//--------------------------------------------------------------------------------------- +// +// Verify that a public key blob looks like a reasonable public key +// +// Arguments: +// pbBuffer - buffer to verify the format of +// cbBuffer - size of pbBuffer +// fImportKeys - do a more extensive check by attempting to import the keys +// + +bool StrongNameIsValidPublicKey(__in_ecount(cbBuffer) const BYTE *pbBuffer, DWORD cbBuffer, bool fImportKeys) +{ + CONTRACTL + { + PRECONDITION(CheckPointer(pbBuffer)); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The buffer must be at least as large as the public key structure + if (cbBuffer < sizeof(PublicKeyBlob)) + { + return false; + } + + // The buffer must be the same size as the structure header plus the trailing key data + const PublicKeyBlob *pkeyPublicKey = reinterpret_cast<const PublicKeyBlob *>(pbBuffer); + if (GET_UNALIGNED_VAL32(&pkeyPublicKey->cbPublicKey) != cbBuffer - offsetof(PublicKeyBlob, PublicKey)) + { + return false; + } + + // The buffer itself looks reasonable, but the public key structure needs to be validated as well + return StrongNameIsValidPublicKey(*pkeyPublicKey, fImportKeys); +} + +//--------------------------------------------------------------------------------------- +// +// Verify that a public key blob looks like a reasonable public key. +// +// Arguments: +// keyPublicKey - key blob to verify +// fImportKeys - do a more extensive check by verifying that the key data imports into CAPI +// +// Notes: +// This can be a very expensive operation, since it involves importing keys. +// + +bool StrongNameIsValidPublicKey(const PublicKeyBlob &keyPublicKey, bool fImportKeys) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The ECMA key doesn't look like a valid key so it will fail the below checks. If we were passed that + // key, then we can skip them + if (StrongNameIsEcmaKey(keyPublicKey)) + { + return true; + } + + // If a hash algorithm is specified, it must be a sensible value + bool fHashAlgorithmValid = GET_ALG_CLASS(GET_UNALIGNED_VAL32(&keyPublicKey.HashAlgID)) == ALG_CLASS_HASH && + GET_ALG_SID(GET_UNALIGNED_VAL32(&keyPublicKey.HashAlgID)) >= ALG_SID_SHA1; + if (keyPublicKey.HashAlgID != 0 && !fHashAlgorithmValid) + { + return false; + } + + // If a signature algorithm is specified, it must be a sensible value + bool fSignatureAlgorithmValid = GET_ALG_CLASS(GET_UNALIGNED_VAL32(&keyPublicKey.SigAlgID)) == ALG_CLASS_SIGNATURE; + if (keyPublicKey.SigAlgID != 0 && !fSignatureAlgorithmValid) + { + return false; + } + + // The key blob must indicate that it is a PUBLICKEYBLOB + if (keyPublicKey.PublicKey[0] != PUBLICKEYBLOB) + { + return false; + } + +#if !defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) + // Make sure the public key blob imports properly + if (fImportKeys) + { + CapiProviderHolder hProv; + if (!StrongNameCryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { + return false; + } + + CapiKeyHolder hKey; + if (!CryptImportKey(hProv, keyPublicKey.PublicKey, GET_UNALIGNED_VAL32(&keyPublicKey.cbPublicKey), NULL, 0, &hKey)) + { + return false; + } + } +#else // !FEATURE_CORECLR || CROSSGEN_COMPILE + _ASSERTE(!fImportKeys); +#endif // !FEATURE_CORECLR || CROSSGEN_COMPILE + + return true; +} + + +//--------------------------------------------------------------------------------------- +// +// Determine the number of bytes that a public key blob occupies, including the key portion +// +// Arguments: +// keyPublicKey - key blob to calculate the size of +// + +DWORD StrongNameSizeOfPublicKey(const PublicKeyBlob &keyPublicKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return offsetof(PublicKeyBlob, PublicKey) + // Size of the blob header plus + GET_UNALIGNED_VAL32(&keyPublicKey.cbPublicKey); // the number of bytes in the key +} + +#if !defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) + +//--------------------------------------------------------------------------------------- +// +// Check to see if the value held in a buffer is a full strong name key pair +// +// Arguments: +// pbBuffer - Blob to check +// cbBuffer - Size of the buffer in bytes +// +// Return Value: +// true if the buffer represents a full strong name key pair, false otherwise +// + +bool StrongNameIsValidKeyPair(__in_ecount(cbKeyPair) const BYTE *pbKeyPair, DWORD cbKeyPair) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pbKeyPair)); + } + CONTRACTL_END; + + // Key pairs are just CAPI PRIVATEKEYBLOBs, so see if CAPI can import the blob + CapiProviderHolder hProv; + if (!StrongNameCryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { + return false; + } + + CapiKeyHolder hKey; + if (!CryptImportKey(hProv, pbKeyPair, cbKeyPair, NULL, 0, &hKey)) + { + return false; + } + + return true; +} + + +BYTE HexToByteA (char c) { + LIMITED_METHOD_CONTRACT; + + if (!isxdigit(c)) return (BYTE) 0xff; + if (isdigit(c)) return (BYTE) (c - '0'); + if (isupper(c)) return (BYTE) (c - 'A' + 10); + return (BYTE) (c - 'a' + 10); +} + +// Read the hex string into a buffer +// Caller owns the buffer. +// Returns NULL if the string contains non-hex characters, or doesn't contain a multiple of 2 characters. +bool GetBytesFromHex(LPCUTF8 szHexString, ULONG cchHexString, BYTE** buffer, ULONG *cbBufferSize) { + LIMITED_METHOD_CONTRACT; + + ULONG cchHex = cchHexString; + if (cchHex % 2 != 0) + return false; + *cbBufferSize = cchHex / 2; + NewArrayHolder<BYTE> tempBuffer(new (nothrow) BYTE[*cbBufferSize]); + if (tempBuffer == NULL) + return false; + + for (ULONG i = 0; i < *cbBufferSize; i++) { + BYTE msn = HexToByteA(*szHexString); + BYTE lsn = HexToByteA(*(szHexString + 1)); + if(msn == 0xFF || lsn == 0xFF) + { + return false; + } + + tempBuffer[i] = (BYTE) ( (msn << 4) | lsn ); + szHexString += 2; + } + + *buffer = tempBuffer.Extract(); + return true; +} + +// Helper method to call CryptAcquireContext, making sure we have a valid set of flags +bool StrongNameCryptAcquireContext(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider, DWORD dwProvType, DWORD dwFlags) +{ + LIMITED_METHOD_CONTRACT; + +#if defined(CRYPT_VERIFYCONTEXT) && defined(CRYPT_MACHINE_KEYSET) + // Specifying both verify context (for an ephemeral key) and machine keyset (for a persisted machine key) + // does not make sense. Additionally, Widows is beginning to lock down against uses of MACHINE_KEYSET + // (for instance in the app container), even if verify context is present. Therefore, if we're using + // an ephemeral key, strip out MACHINE_KEYSET from the flags. + if ((dwFlags & CRYPT_VERIFYCONTEXT) && (dwFlags & CRYPT_MACHINE_KEYSET)) + { + dwFlags &= ~CRYPT_MACHINE_KEYSET; + } +#endif // FEATURE_CRYPTO + + return !!WszCryptAcquireContext(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags); +} + +#endif // !FEATURE_CORECLR || CROSSGEN_COMPILE + diff --git a/src/strongname/api/wks/.gitmirror b/src/strongname/api/wks/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/strongname/api/wks/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/strongname/api/wks/CMakeLists.txt b/src/strongname/api/wks/CMakeLists.txt new file mode 100644 index 0000000000..581fee7717 --- /dev/null +++ b/src/strongname/api/wks/CMakeLists.txt @@ -0,0 +1 @@ +add_library(strongname_wks ${STRONGNAME_SOURCES}) diff --git a/src/strongname/api/wks/strongname_wks.nativeproj b/src/strongname/api/wks/strongname_wks.nativeproj new file mode 100644 index 0000000000..fcfa71ae6c --- /dev/null +++ b/src/strongname/api/wks/strongname_wks.nativeproj @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <PropertyGroup> + <StrongnameInVm>true</StrongnameInVm> + </PropertyGroup> + <Import Project="$(ClrBase)\src\strongname\api\api.props" /> + <PropertyGroup Label="Globals"> + <SccProjectName>SAK</SccProjectName> + <SccAuxPath>SAK</SccAuxPath> + <SccLocalPath>SAK</SccLocalPath> + <SccProvider>SAK</SccProvider> + </PropertyGroup> + <!--Leaf project Properties--> + <PropertyGroup> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + <OutputName>strongname_wks</OutputName> + </PropertyGroup> + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/strongname/dirs.proj b/src/strongname/dirs.proj new file mode 100644 index 0000000000..9cb3cf6724 --- /dev/null +++ b/src/strongname/dirs.proj @@ -0,0 +1,29 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <ItemDefinitionGroup> + <ProjectFile> + <ProductGroups>FX</ProductGroups> + </ProjectFile> + </ItemDefinitionGroup> + + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="api\dirs.proj" /> + <ProjectFile Include="inc\StrongName_inc.nativeproj" /> + <ProjectFile Include="tools\dirs.proj" > + <ProductGroups>VS;FX</ProductGroups> + </ProjectFile> + </ItemGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/strongname/inc/.gitmirror b/src/strongname/inc/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/strongname/inc/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/strongname/inc/StrongName_inc.nativeproj b/src/strongname/inc/StrongName_inc.nativeproj new file mode 100644 index 0000000000..c72249f207 --- /dev/null +++ b/src/strongname/inc/StrongName_inc.nativeproj @@ -0,0 +1,31 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <!--Leaf project Properties--> + <PropertyGroup> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + <OutputPath>$(ClrLibDest)</OutputPath> + <TargetType>PUBLISH</TargetType> + <OutputName>StrongName</OutputName> + </PropertyGroup> + + <!--Leaf Project Items--> + <ItemGroup> + <PublishPartLinked Include="StrongName.h"> + <Visibility>Inter</Visibility> + <FileType>Include</FileType> + </PublishPartLinked> + </ItemGroup> + + <ItemGroup> + <CopyFile Include="StrongName.h"> + <DestFolder>$(BinariesDirectory)</DestFolder> + </CopyFile> + </ItemGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/strongname/inc/ecmakey.h b/src/strongname/inc/ecmakey.h new file mode 100644 index 0000000000..1948b8b3b9 --- /dev/null +++ b/src/strongname/inc/ecmakey.h @@ -0,0 +1,10 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +#pragma once + +// The byte values of the ECMA pseudo public key and its token. +const BYTE g_rbNeutralPublicKey[] = { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0 }; +const BYTE g_rbNeutralPublicKeyToken[] = { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }; + diff --git a/src/strongname/inc/sncoreclr.h b/src/strongname/inc/sncoreclr.h new file mode 100644 index 0000000000..3d6020f40f --- /dev/null +++ b/src/strongname/inc/sncoreclr.h @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#ifndef _SNCORECLR_H +#define _SNCORECLR_H + +#if !defined(FEATURE_CORECLR) +#error sncoreclr.h should only be used on CoreCLR builds +#endif // !FEATURE_CORECLR + +void InitUtilcode(); + +#endif // _SNCORECLR_H diff --git a/src/strongname/inc/strongname.h b/src/strongname/inc/strongname.h new file mode 100644 index 0000000000..5c93a1ba8b --- /dev/null +++ b/src/strongname/inc/strongname.h @@ -0,0 +1,307 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +#ifndef __STRONG_NAME_H +#define __STRONG_NAME_H + +// =========================================================================== +// File: StrongName.h +// +// Wrappers for signing and hashing functions needed to implement strong names +// =========================================================================== + + +#include <windows.h> +#include <wincrypt.h> +#include <ole2.h> + +#include <corerror.h> +#include <winapifamily.h> + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +#ifdef __cplusplus +extern "C"{ +#endif + + +// Public key blob binary format. +typedef struct { + unsigned int SigAlgID; // (ALG_ID) signature algorithm used to create the signature + unsigned int HashAlgID; // (ALG_ID) hash algorithm used to create the signature + ULONG cbPublicKey; // length of the key in bytes + BYTE PublicKey[1]; // variable length byte array containing the key value in format output by CryptoAPI +} PublicKeyBlob; + + +// Location in the registry (under HKLM) that strong name configuration info is +// stored. +#define SN_CONFIG_KEY "Software\\Microsoft\\StrongName" +#define SN_CONFIG_CSP "CSP" // REG_SZ +#define SN_CONFIG_MACHINE_KEYSET "MachineKeyset" // REG_DWORD +#define SN_CONFIG_KEYSPEC "KeySpec" // REG_DWORD +#define SN_CONFIG_HASH_ALG "HashAlgorithm" // REG_DWORD +#define SN_CONFIG_SIGN_ALG "SignAlgorithm" // REG_DWORD +#define SN_CONFIG_VERIFICATION "Verification" // Registry subkey +#define SN_CONFIG_USERLIST "UserList" // REG_MULTI_SZ +#define SN_CONFIG_CACHE_VERIFY "CacheVerify" // REG_DWORD + +#define SN_CONFIG_KEY_W L"Software\\Microsoft\\StrongName" +#define SN_CONFIG_CSP_W L"CSP" // REG_SZ +#define SN_CONFIG_PROV_TYPE_W L"ProvType" // REG_DWORD +#define SN_CONFIG_MACHINE_KEYSET_W L"MachineKeyset" // REG_DWORD +#define SN_CONFIG_KEYSPEC_W L"KeySpec" // REG_DWORD +#define SN_CONFIG_HASH_ALG_W L"HashAlgorithm" // REG_DWORD +#define SN_CONFIG_SIGN_ALG_W L"SignAlgorithm" // REG_DWORD +#define SN_CONFIG_VERIFICATION_W L"Verification" // Registry subkey +#define SN_CONFIG_USERLIST_W L"UserList" // REG_MULTI_SZ +#define SN_CONFIG_TESTPUBLICKEY_W L"TestPublicKey" // REG_SZ +#define SN_CONFIG_CACHE_VERIFY_W L"CacheVerify" // REG_DWORD + +// VM related registry locations (Note, these values are under HKLM\Software\Microsoft\.NETFramework, rather +// than SN_CONFIG_KEY +#define SN_CONFIG_BYPASS_POLICY "AllowStrongNameBypass" // REG_DWORD +#define SN_CONFIG_BYPASS_POLICY_W L"AllowStrongNameBypass" // REG_DWORD + +#if defined(_MSC_VER) && !defined(USE_DEPRECATED_CLR_API_WITHOUT_WARNING) +#define DEPRECATED_CLR_API_MESG "This API has been deprecated. Refer to http://go.microsoft.com/fwlink/?LinkId=143720 for more details." +#define DECLARE_DEPRECATED __declspec(deprecated(DEPRECATED_CLR_API_MESG)) +#else // _MSC_VER && !USE_DEPRECATED_CLR_API_WITHOUT_WARNING +#define DECLARE_DEPRECATED +#endif // _MSC_VER && !USE_DEPRECATED_CLR_API_WITHOUT_WARNING + +#define SNAPI DECLARE_DEPRECATED BOOLEAN __stdcall +#define SNAPI_(_type) DECLARE_DEPRECATED _type __stdcall + +// Return last error. +SNAPI_(DWORD) StrongNameErrorInfo(VOID); + + +// Free buffer allocated by routines below. +SNAPI_(VOID) StrongNameFreeBuffer(BYTE *pbMemory); // [in] address of memory to free + + +// Generate a new key pair for strong name use. +SNAPI StrongNameKeyGen(LPCWSTR wszKeyContainer, // [in] desired key container name + DWORD dwFlags, // [in] flags (see below) + BYTE **ppbKeyBlob, // [out] public/private key blob + ULONG *pcbKeyBlob); + +// Generate a new key pair with the specified key size for strong name use. +SNAPI StrongNameKeyGenEx(LPCWSTR wszKeyContainer, // [in] desired key container name, must be a non-empty string + DWORD dwFlags, // [in] flags (see below) + DWORD dwKeySize, // [in] desired key size. + BYTE **ppbKeyBlob, // [out] public/private key blob + ULONG *pcbKeyBlob); + +// Flags for StrongNameKeyGen. +#define SN_LEAVE_KEY 0x00000001 // Leave key pair registered with CSP + + +// Import key pair into a key container. +SNAPI StrongNameKeyInstall(LPCWSTR wszKeyContainer,// [in] desired key container name, must be a non-empty string + BYTE *pbKeyBlob, // [in] public/private key pair blob + ULONG cbKeyBlob); + + +// Delete a key pair. +SNAPI StrongNameKeyDelete(LPCWSTR wszKeyContainer); // [in] desired key container name + +// Retrieve the public portion of a key pair. +SNAPI StrongNameGetPublicKey (LPCWSTR wszKeyContainer, // [in] desired key container name + BYTE *pbKeyBlob, // [in] public/private key blob (optional) + ULONG cbKeyBlob, + BYTE **ppbPublicKeyBlob, // [out] public key blob + ULONG *pcbPublicKeyBlob); + +// Retrieve the public portion of a key pair. +SNAPI StrongNameGetPublicKeyEx (LPCWSTR wszKeyContainer, // [in] desired key container name + BYTE *pbKeyBlob, // [in] public/private key blob (optional) + ULONG cbKeyBlob, + BYTE **ppbPublicKeyBlob, // [out] public key blob + ULONG *pcbPublicKeyBlob, + ULONG uHashAlgId, + ULONG uReserved); // reserved for future use + +// Hash and sign a manifest. +SNAPI StrongNameSignatureGeneration(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + LPCWSTR wszKeyContainer, // [in] desired key container name + BYTE *pbKeyBlob, // [in] public/private key blob (optional) + ULONG cbKeyBlob, + BYTE **ppbSignatureBlob, // [out] signature blob + ULONG *pcbSignatureBlob); + +SNAPI StrongNameSignatureGenerationEx(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + LPCWSTR wszKeyContainer, // [in] desired key container name + BYTE *pbKeyBlob, // [in] public/private key blob (optional) + ULONG cbKeyBlob, + BYTE **ppbSignatureBlob, // [out] signature blob + ULONG *pcbSignatureBlob, + DWORD dwFlags); // [in] modifer flags; see below + +#define SN_SIGN_ALL_FILES 0x00000001 // Rehash all linked modules as well as resigning the manifest +#define SN_TEST_SIGN 0x00000002 // Test sign the assembly +#define SN_ECMA_SIGN 0x00000004 // Sign the assembly treating the input key as the real ECMA key + +// Digest signing support + +// Generate the digest of an input assembly, which can be signed with StrongNameDigestSign +SNAPI StrongNameDigestGenerate(_In_z_ LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + _Outptr_result_bytebuffer_(*pcbDigestBlob) BYTE** ppbDigestBlob, // [out] digest of the assembly, free with StrongNameFreeBuffer + _Out_ ULONG* pcbDigestBlob, // [out] number of bytes in the assembly digest + DWORD dwFlags); // [in] modifier flags (see StrongNameSignatureGenerationEx) + +// Sign an the digest of an assembly calculated by StrongNameDigestGenerate +SNAPI StrongNameDigestSign(_In_opt_z_ LPCWSTR wszKeyContainer, // [in] desired key container name (optional) + _In_reads_bytes_opt_(cbKeyBlob) BYTE* pbKeyBlob, // [in] public/private key blob (optional) + ULONG cbKeyBlob, + _In_reads_bytes_(cbDigestBlob) BYTE* pbDigestBlob, // [in] digest blob, from StrongNameDigestGenerate + ULONG cbDigestBlob, + DWORD hashAlgId, // [in] algorithm id of the hash algorithm used with pbDigestBlob + _Outptr_result_bytebuffer_(*pcbSignatureBlob) BYTE** ppbSignatureBlob, // [out] signature blob, freed with StrongNameFreeBuffer + _Out_ ULONG* pcbSignatureBlob, + DWORD dwFlags); // [in] modifier flags (see StrongNameSignatureGenerationEx) + +// Embed a digest signature generated with StrongNameDigestSign into an assembly +SNAPI StrongNameDigestEmbed(_In_z_ LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly to update + _In_reads_bytes_(cbSignatureBlob) BYTE* pbSignatureBlob, // [in] signature blob for the assembly + ULONG cbSignatureBlob); + +// Create a strong name token from an assembly file. +SNAPI StrongNameTokenFromAssembly(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + BYTE **ppbStrongNameToken, // [out] strong name token + ULONG *pcbStrongNameToken); + +// Create a strong name token from an assembly file and additionally return the full public key. +SNAPI StrongNameTokenFromAssemblyEx(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + BYTE **ppbStrongNameToken, // [out] strong name token + ULONG *pcbStrongNameToken, + BYTE **ppbPublicKeyBlob, // [out] public key blob + ULONG *pcbPublicKeyBlob); + +// Create a strong name token from a public key blob. +SNAPI StrongNameTokenFromPublicKey(BYTE *pbPublicKeyBlob, // [in] public key blob + ULONG cbPublicKeyBlob, + BYTE **ppbStrongNameToken, // [out] strong name token + ULONG *pcbStrongNameToken); + + +// Verify a strong name/manifest against a public key blob. +SNAPI StrongNameSignatureVerification(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + DWORD dwInFlags, // [in] flags modifying behaviour (see below) + DWORD *pdwOutFlags); // [out] additional output info (see below) + + +// Verify a strong name/manifest against a public key blob. +SNAPI StrongNameSignatureVerificationEx(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + BOOLEAN fForceVerification, // [in] verify even if settings in the registry disable it + BOOLEAN *pfWasVerified); // [out] set to false if verify succeeded due to registry settings + +// Verify a strong name/manifest against a public key blob. +SNAPI StrongNameSignatureVerificationEx2(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + BOOLEAN fForceVerification, // [in] verify even if settings in the registry disable it + BYTE *pbEcmaPublicKey, // [in] mapping from the ECMA public key to the real key used for verification + DWORD cbEcmaPublicKey, // [in] length of the real ECMA public key + BOOLEAN *pfWasVerified); // [out] set to false if verify succeeded due to registry settings + +// Verify a strong name/manifest against a public key blob when the assembly is +// already memory mapped. +SNAPI StrongNameSignatureVerificationFromImage(BYTE *pbBase, // [in] base address of mapped manifest file + DWORD dwLength, // [in] length of mapped image in bytes + DWORD dwInFlags, // [in] flags modifying behaviour (see below) + DWORD *pdwOutFlags); // [out] additional output info (see below) + +// Flags for use with the verify routines. +#define SN_INFLAG_FORCE_VER 0x00000001 // verify even if settings in the registry disable it +#define SN_INFLAG_INSTALL 0x00000002 // verification is the first (on entry to the cache) +#define SN_INFLAG_ADMIN_ACCESS 0x00000004 // cache protects assembly from all but admin access +#define SN_INFLAG_USER_ACCESS 0x00000008 // cache protects user's assembly from other users +#define SN_INFLAG_ALL_ACCESS 0x00000010 // cache provides no access restriction guarantees + +#define SN_INFLAG_RUNTIME 0x80000000 // internal debugging use only + +#define SN_OUTFLAG_WAS_VERIFIED 0x00000001 // set to false if verify succeeded due to registry settings +#define SN_OUTFLAG_MICROSOFT_SIGNATURE 0x00000002 // set if the public key corresponds to SN_THE_KEY + +// Verify that two assemblies differ only by signature blob. +SNAPI StrongNameCompareAssemblies(LPCWSTR wszAssembly1, // [in] file name of first assembly + LPCWSTR wszAssembly2, // [in] file name of second assembly + DWORD *pdwResult); // [out] result of comparison (see codes below) + +#define SN_CMP_DIFFERENT 0 // Assemblies contain different data +#define SN_CMP_IDENTICAL 1 // Assemblies are exactly the same, even signatures +#define SN_CMP_SIGONLY 2 // Assemblies differ only by signature (and checksum etc.) + + +// Compute the size of buffer needed to hold a hash for a given hash algorithm. +SNAPI StrongNameHashSize(ULONG ulHashAlg, // [in] hash algorithm + DWORD *pcbSize); // [out] size of the hash in bytes + + +// Compute the size that needs to be allocated for a signature in an assembly. +SNAPI StrongNameSignatureSize(BYTE *pbPublicKeyBlob, // [in] public key blob + ULONG cbPublicKeyBlob, + DWORD *pcbSize); // [out] size of the signature in bytes + + +SNAPI_(DWORD) GetHashFromAssemblyFile(LPCSTR szFilePath, // [IN] location of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash); // [OUT] length of hash byte array + +SNAPI_(DWORD) GetHashFromAssemblyFileW(LPCWSTR wszFilePath, // [IN] location of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash); // [OUT] length of hash byte array + +SNAPI_(DWORD) GetHashFromFile(LPCSTR szFilePath, // [IN] location of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash); // [OUT] length of hash byte array + +SNAPI_(DWORD) GetHashFromFileW(LPCWSTR wszFilePath, // [IN] location of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash); // [OUT] length of hash byte array + +SNAPI_(DWORD) GetHashFromHandle(HANDLE hFile, // [IN] handle of file to be hashed + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash); // [OUT] length of hash byte array + +SNAPI_(DWORD) GetHashFromBlob(BYTE *pbBlob, // [IN] pointer to memory block to hash + DWORD cchBlob, // [IN] length of blob + unsigned int *piHashAlg, // [IN/OUT] constant specifying the hash algorithm (set to 0 if you want the default) + BYTE *pbHash, // [OUT] hash buffer + DWORD cchHash, // [IN] max size of buffer + DWORD *pchHash); // [OUT] length of hash byte array + +SNAPI StrongNameGetBlob(LPCWSTR wszFilePath, // [in] valid path to the PE file for the assembly + BYTE *pbBlob, // [in] buffer to fill with blob + DWORD *pcbBlob); // [in/out] size of buffer/number of bytes put into buffer + +SNAPI StrongNameGetBlobFromImage(BYTE *pbBase, // [in] base address of mapped manifest file + DWORD dwLength, // [in] length of mapped image in bytes + BYTE *pbBlob, // [in] buffer to fill with blob + DWORD *pcbBlob); // [in/out] size of buffer/number of bytes put into buffer + +#undef DECLARE_DEPRECATED +#undef DEPRECATED_CLR_API_MESG +#undef SNAPI +#undef SNAPI_ +#define SNAPI BOOLEAN __stdcall +#define SNAPI_(_type) _type __stdcall + +#ifdef __cplusplus +} +#endif + +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +#endif diff --git a/src/strongname/inc/strongnameholders.h b/src/strongname/inc/strongnameholders.h new file mode 100644 index 0000000000..663fd5eaa6 --- /dev/null +++ b/src/strongname/inc/strongnameholders.h @@ -0,0 +1,104 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#ifndef __STRONGNAME_HOLDERS_H__ +#define __STRONGNAME_HOLDERS_H__ + +#include <holder.h> +#include <strongname.h> +#include <wincrypt.h> + +// +// Holder classes for types returned from and used in strong name APIs +// + +// Holder for any memory allocated by the strong name APIs +template<class T> +void VoidStrongNameFreeBuffer(__in T *pBuffer) +{ + StrongNameFreeBuffer(reinterpret_cast<BYTE *>(pBuffer)); +} +NEW_WRAPPER_TEMPLATE1(StrongNameBufferHolder, VoidStrongNameFreeBuffer<_TYPE>); + +#if !defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) +// Holder for HCRYPTPROV handles directly allocated from CAPI +inline void ReleaseCapiProvider(HCRYPTPROV hProv) +{ + CryptReleaseContext(hProv, 0); +} +typedef Wrapper<HCRYPTPROV, DoNothing, ReleaseCapiProvider, 0> CapiProviderHolder; + +inline void ReleaseCapiKey(HCRYPTKEY hKey) +{ + CryptDestroyKey(hKey); +} +typedef Wrapper<HCRYPTKEY, DoNothing, ReleaseCapiKey, 0> CapiKeyHolder; + +inline void ReleaseCapiHash(HCRYPTHASH hHash) +{ + CryptDestroyHash(hHash); +} +typedef Wrapper<HCRYPTHASH, DoNothing, ReleaseCapiHash, 0> CapiHashHolder; +#endif // !FEATURE_CORECLR || CROSSGEN_COMPILE + +#if SNAPI_INTERNAL + +// Context structure tracking information for a loaded assembly. +struct SN_LOAD_CTX +{ + HANDLE m_hFile; // Open file handle + HANDLE m_hMap; // Mapping file handle + BYTE *m_pbBase; // Base address of mapped file + DWORD m_dwLength; // Length of file in bytes + IMAGE_NT_HEADERS32 *m_pNtHeaders; // Address of NT headers + IMAGE_COR20_HEADER *m_pCorHeader; // Address of COM+ 2.0 header + BYTE *m_pbSignature; // Address of signature blob + DWORD m_cbSignature; // Size of signature blob + BOOLEAN m_fReadOnly; // File mapped for read-only access + BOOLEAN m_fPreMapped; // File was already mapped for us + PEDecoder *m_pedecoder; // PEDecoder corresponding to this file + SN_LOAD_CTX() { ZeroMemory(this, sizeof(*this)); } +}; + +BOOLEAN LoadAssembly(SN_LOAD_CTX *pLoadCtx, LPCWSTR szFilePath, DWORD inFlags = 0, BOOLEAN fRequireSignature = TRUE); +BOOLEAN UnloadAssembly(SN_LOAD_CTX *pLoadCtx); + +// Holder for loading an assembly into an SN_LOAD_CTX +class StrongNameAssemblyLoadHolder +{ +private: + SN_LOAD_CTX m_snLoadCtx; + bool m_fLoaded; + +public: + StrongNameAssemblyLoadHolder(LPCWSTR wszAssembly, bool fReadOnly) + { + m_snLoadCtx.m_fReadOnly = !!fReadOnly; + m_fLoaded = !!LoadAssembly(&m_snLoadCtx, wszAssembly); + } + + ~StrongNameAssemblyLoadHolder() + { + if (m_fLoaded) + { + UnloadAssembly(&m_snLoadCtx); + } + } + +public: + SN_LOAD_CTX *GetLoadContext() + { + return &m_snLoadCtx; + } + + bool IsLoaded() + { + return m_fLoaded; + } +}; + +#endif // SNAPI_INTERNAL + +#endif // !__STRONGNAME_HOLDERS_H__ diff --git a/src/strongname/inc/strongnameinternal.h b/src/strongname/inc/strongnameinternal.h new file mode 100644 index 0000000000..ec5fb5da90 --- /dev/null +++ b/src/strongname/inc/strongnameinternal.h @@ -0,0 +1,59 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// Strong name APIs which are not exposed publicly, but are built into StrongName.lib +// + +#ifndef _STRONGNAME_INTERNAL_H +#define _STRONGNAME_INTERNAL_H + +#include <strongname.h> + +#include <wincrypt.h> + +// NTDDI_VERSION is currently defined as XP SP2. +// Strongname api's that use this are supported on XP SP3 and later, so we can use them. +#ifndef ALG_SID_SHA_256 +#define ALG_SID_SHA_256 12 +#define ALG_SID_SHA_384 13 +#define ALG_SID_SHA_512 14 +#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) +#define CALG_SHA_384 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384) +#define CALG_SHA_512 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) +#endif //ALG_SID_SHA_256 + +#ifdef FEATURE_STRONGNAME_TESTKEY_ALLOWED +bool StrongNameIsTestKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey); +bool StrongNameIsTestKey(const PublicKeyBlob &keyPublicKey); +#endif // FEATURE_STRONGNAME_TESTKEY_ALLOWED + +// Determine the number of bytes in a public key +DWORD StrongNameSizeOfPublicKey(const PublicKeyBlob &keyPublicKey); + +bool StrongNameIsValidPublicKey(__in_ecount(cbPublicKeyBlob) const BYTE *pbPublicKeyBlob, DWORD cbPublicKeyBlob, bool fImporKey); +bool StrongNameIsValidPublicKey(const PublicKeyBlob &keyPublicKey, bool fImportKey); + +// Determine if a public key is the ECMA key +bool StrongNameIsEcmaKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey); +bool StrongNameIsEcmaKey(const PublicKeyBlob &keyPublicKey); + +bool StrongNameIsTheKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey); + +#if !defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) + +// Verify the format of a public key blob +bool StrongNameIsValidKeyPair(__in_ecount(cbKeyPair) const BYTE *pbKeyPair, DWORD cbKeyPair); + +bool GetBytesFromHex(LPCUTF8 szHexString, ULONG cchHexString, BYTE** buffer, ULONG *cbBufferSize); + +bool StrongNameCryptAcquireContext(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider, DWORD dwProvType, DWORD dwFlags); +#endif // !FEATURE_CORECLR || CROSSGEN_COMPILE + +#ifdef FEATURE_CORECLR +bool StrongNameIsSilverlightPlatformKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey); +bool StrongNameIsSilverlightPlatformKey(const PublicKeyBlob &keyPublicKey); +#endif // FEATURE_CORECLR + +#endif // !_STRONGNAME_INTERNAL_H diff --git a/src/strongname/inc/thekey.h b/src/strongname/inc/thekey.h new file mode 100644 index 0000000000..1c447b2067 --- /dev/null +++ b/src/strongname/inc/thekey.h @@ -0,0 +1,105 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +#pragma once +// This file allows customization of the strongname key used to replace the ECMA key + +static const BYTE g_rbTheKey[] = +{ +0x00,0x24,0x00,0x00,0x04,0x80,0x00,0x00,0x94,0x00,0x00,0x00,0x06,0x02,0x00,0x00, +0x00,0x24,0x00,0x00,0x52,0x53,0x41,0x31,0x00,0x04,0x00,0x00,0x01,0x00,0x01,0x00, +0x07,0xd1,0xfa,0x57,0xc4,0xae,0xd9,0xf0,0xa3,0x2e,0x84,0xaa,0x0f,0xae,0xfd,0x0d, +0xe9,0xe8,0xfd,0x6a,0xec,0x8f,0x87,0xfb,0x03,0x76,0x6c,0x83,0x4c,0x99,0x92,0x1e, +0xb2,0x3b,0xe7,0x9a,0xd9,0xd5,0xdc,0xc1,0xdd,0x9a,0xd2,0x36,0x13,0x21,0x02,0x90, +0x0b,0x72,0x3c,0xf9,0x80,0x95,0x7f,0xc4,0xe1,0x77,0x10,0x8f,0xc6,0x07,0x77,0x4f, +0x29,0xe8,0x32,0x0e,0x92,0xea,0x05,0xec,0xe4,0xe8,0x21,0xc0,0xa5,0xef,0xe8,0xf1, +0x64,0x5c,0x4c,0x0c,0x93,0xc1,0xab,0x99,0x28,0x5d,0x62,0x2c,0xaa,0x65,0x2c,0x1d, +0xfa,0xd6,0x3d,0x74,0x5d,0x6f,0x2d,0xe5,0xf1,0x7e,0x5e,0xaf,0x0f,0xc4,0x96,0x3d, +0x26,0x1c,0x8a,0x12,0x43,0x65,0x18,0x20,0x6d,0xc0,0x93,0x34,0x4d,0x5a,0xd2,0x93 +}; + +static const BYTE g_rbTheKeyToken[] = {0xb0,0x3f,0x5f,0x7f,0x11,0xd5,0x0a,0x3a}; + + +#ifdef FEATURE_CORECLR +static const BYTE g_rbTheSilverlightPlatformKey[] = +{ +0x00,0x24,0x00,0x00,0x04,0x80,0x00,0x00,0x94,0x00,0x00,0x00,0x06,0x02,0x00,0x00, +0x00,0x24,0x00,0x00,0x52,0x53,0x41,0x31,0x00,0x04,0x00,0x00,0x01,0x00,0x01,0x00, +0x8d,0x56,0xc7,0x6f,0x9e,0x86,0x49,0x38,0x30,0x49,0xf3,0x83,0xc4,0x4b,0xe0,0xec, +0x20,0x41,0x81,0x82,0x2a,0x6c,0x31,0xcf,0x5e,0xb7,0xef,0x48,0x69,0x44,0xd0,0x32, +0x18,0x8e,0xa1,0xd3,0x92,0x07,0x63,0x71,0x2c,0xcb,0x12,0xd7,0x5f,0xb7,0x7e,0x98, +0x11,0x14,0x9e,0x61,0x48,0xe5,0xd3,0x2f,0xba,0xab,0x37,0x61,0x1c,0x18,0x78,0xdd, +0xc1,0x9e,0x20,0xef,0x13,0x5d,0x0c,0xb2,0xcf,0xf2,0xbf,0xec,0x3d,0x11,0x58,0x10, +0xc3,0xd9,0x06,0x96,0x38,0xfe,0x4b,0xe2,0x15,0xdb,0xf7,0x95,0x86,0x19,0x20,0xe5, +0xab,0x6f,0x7d,0xb2,0xe2,0xce,0xef,0x13,0x6a,0xc2,0x3d,0x5d,0xd2,0xbf,0x03,0x17, +0x00,0xae,0xc2,0x32,0xf6,0xc6,0xb1,0xc7,0x85,0xb4,0x30,0x5c,0x12,0x3b,0x37,0xab +}; + +static const BYTE g_rbTheSilverlightPlatformKeyToken[] = {0x7c,0xec,0x85,0xd7,0xbe,0xa7,0x79,0x8e}; + +static const BYTE g_rbTheSilverlightKey[] = +{ +0x00,0x24,0x00,0x00,0x04,0x80,0x00,0x00,0x94,0x00,0x00,0x00,0x06,0x02,0x00,0x00, +0x00,0x24,0x00,0x00,0x52,0x53,0x41,0x31,0x00,0x04,0x00,0x00,0x01,0x00,0x01,0x00, +0xb5,0xfc,0x90,0xe7,0x02,0x7f,0x67,0x87,0x1e,0x77,0x3a,0x8f,0xde,0x89,0x38,0xc8, +0x1d,0xd4,0x02,0xba,0x65,0xb9,0x20,0x1d,0x60,0x59,0x3e,0x96,0xc4,0x92,0x65,0x1e, +0x88,0x9c,0xc1,0x3f,0x14,0x15,0xeb,0xb5,0x3f,0xac,0x11,0x31,0xae,0x0b,0xd3,0x33, +0xc5,0xee,0x60,0x21,0x67,0x2d,0x97,0x18,0xea,0x31,0xa8,0xae,0xbd,0x0d,0xa0,0x07, +0x2f,0x25,0xd8,0x7d,0xba,0x6f,0xc9,0x0f,0xfd,0x59,0x8e,0xd4,0xda,0x35,0xe4,0x4c, +0x39,0x8c,0x45,0x43,0x07,0xe8,0xe3,0x3b,0x84,0x26,0x14,0x3d,0xae,0xc9,0xf5,0x96, +0x83,0x6f,0x97,0xc8,0xf7,0x47,0x50,0xe5,0x97,0x5c,0x64,0xe2,0x18,0x9f,0x45,0xde, +0xf4,0x6b,0x2a,0x2b,0x12,0x47,0xad,0xc3,0x65,0x2b,0xf5,0xc3,0x08,0x05,0x5d,0xa9 +}; + +static const BYTE g_rbTheSilverlightKeyToken[] = {0x31,0xBF,0x38,0x56,0xAD,0x36,0x4E,0x35}; + +#ifdef FEATURE_WINDOWSPHONE + +static const BYTE g_rbTheMicrosoftPhoneKey[] = +{ +0x00,0x24,0x00,0x00,0x04,0x80,0x00,0x00,0x14,0x01,0x00,0x00,0x06,0x02,0x00,0x00, +0x00,0x24,0x00,0x00,0x52,0x53,0x41,0x31,0x00,0x08,0x00,0x00,0x01,0x00,0x01,0x00, +0xCB,0x8F,0x76,0xDA,0xE0,0x5D,0x35,0x2C,0x11,0x7A,0x84,0x67,0xEF,0x32,0x60,0xE0, +0x02,0x10,0x8A,0xA9,0xE7,0x09,0x56,0xE7,0x05,0xF6,0x43,0x0F,0x6C,0xBC,0xFE,0x35, +0x9D,0x02,0x11,0x75,0x63,0x40,0x00,0x17,0xD1,0x00,0x4D,0x68,0xD1,0x3F,0xE5,0x74, +0xA2,0x56,0x49,0x58,0x0E,0xD1,0xEE,0x22,0x5A,0xA4,0xBB,0xC0,0x93,0x06,0x2D,0xA0, +0xB1,0x68,0xBB,0xEA,0x3F,0x57,0x95,0x50,0x8F,0xCB,0xD7,0x53,0x13,0x74,0xC7,0x45, +0xCE,0x63,0x27,0xA9,0x1A,0xC7,0x24,0x35,0x71,0xF2,0xB4,0xE8,0xE4,0xFC,0x24,0x8C, +0xA1,0xA2,0xF3,0xC8,0x73,0xE1,0x49,0xB3,0x43,0x0B,0x37,0xBD,0x51,0xA6,0xA3,0xAD, +0xC9,0x5F,0x68,0x5C,0x91,0x74,0x57,0x69,0x13,0x0F,0xA1,0x0F,0xBF,0xC7,0xB2,0x28, +0x2F,0x25,0xDF,0xFD,0x4B,0xA2,0xC0,0xBD,0x7C,0x82,0x88,0xCD,0xE4,0x80,0x6C,0x8B, +0xDC,0x81,0x7D,0x12,0x6C,0x7E,0x54,0x36,0x10,0xD3,0x47,0x6A,0x63,0x17,0xB5,0xD5, +0x48,0x76,0xA0,0xE3,0xB3,0xAD,0x42,0xB1,0x2C,0x51,0x38,0xDB,0x2F,0xFB,0x66,0x9F, +0x1B,0xE6,0x31,0x4B,0xC9,0x58,0x7A,0xC1,0xAC,0xE4,0x75,0x1D,0x6C,0x58,0x84,0xDB, +0x04,0x81,0x15,0x90,0x7C,0x09,0x0D,0xBB,0xDF,0x98,0xC6,0x5E,0xD4,0xC0,0x42,0xC3, +0x77,0x5A,0xCA,0xA7,0xD6,0x04,0x23,0x07,0xFA,0xE2,0xAA,0xFA,0xE7,0x12,0x8E,0x17, +0x65,0x81,0x92,0x44,0x6C,0x07,0x40,0x0D,0x22,0xC5,0xBA,0x43,0xF3,0xA1,0xE7,0xA3, +0xE5,0xAF,0x81,0xBE,0x0E,0x65,0xA5,0x01,0x6C,0x5F,0x0C,0x62,0xA3,0xC0,0xDE,0xB3 +}; + +static const BYTE g_rbTheMicrosoftPhoneKeyToken[] = {0x24,0xEE,0xC0,0xD8,0xC8,0x6C,0xDA,0x1E}; + + +static const BYTE g_rbTheMicrosoftXNAKey[] = +{ +0x00,0x24,0x00,0x00,0x04,0x80,0x00,0x00,0x94,0x00,0x00,0x00,0x06,0x02,0x00,0x00, +0x00,0x24,0x00,0x00,0x52,0x53,0x41,0x31,0x00,0x04,0x00,0x00,0x01,0x00,0x01,0x00, +0x0F,0xC5,0x99,0x3E,0x0F,0x51,0x1A,0xD5,0xE1,0x6E,0x8B,0x22,0x65,0x53,0x49,0x3E, +0x09,0x06,0x7A,0xFC,0x41,0x03,0x9F,0x70,0xDA,0xEB,0x94,0xA9,0x68,0xD6,0x64,0xF4, +0x0E,0x69,0xA4,0x6B,0x61,0x7D,0x15,0xD3,0xD5,0x32,0x8B,0xE7,0xDB,0xED,0xD0,0x59, +0xEB,0x98,0x49,0x5A,0x3B,0x03,0xCB,0x4E,0xA4,0xBA,0x12,0x74,0x44,0x67,0x1C,0x3C, +0x84,0xCB,0xC1,0xFD,0xC3,0x93,0xD7,0xE1,0x0B,0x5E,0xE3,0xF3,0x1F,0x5A,0x29,0xF0, +0x05,0xE5,0xEE,0xD7,0xE3,0xC9,0xC8,0xAF,0x74,0xF4,0x13,0xF0,0x00,0x4F,0x0C,0x2C, +0xAB,0xB2,0x2F,0x9D,0xD4,0xF7,0x5A,0x6F,0x59,0x97,0x84,0xE1,0xBA,0xB7,0x09,0x85, +0xEF,0x81,0x74,0xCA,0x6C,0x68,0x42,0x78,0xBE,0x82,0xCE,0x05,0x5A,0x03,0xEB,0xAF +}; + +static const BYTE g_rbTheMicrosoftXNAKeyToken[] = {0xCC,0xAC,0x92,0xED,0x87,0x3B,0x18,0x5C}; + +#endif +// for FEATURE_WINDOWSPHONE, we can add the Microsoft.Phone key and the Xna key to the list of blessed keys... + +#endif // FEATURE_CORECLR + diff --git a/src/strongname/inc/thetestkey.h b/src/strongname/inc/thetestkey.h new file mode 100644 index 0000000000..f644467d5f --- /dev/null +++ b/src/strongname/inc/thetestkey.h @@ -0,0 +1,87 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// This file allows customization of the test strongname key used in place of the real MS key + +#ifdef FEATURE_STRONGNAME_TESTKEY_ALLOWED + +// +// #TestKeyStamping +// +// On CoreCLR, we have a requirement that only keys signed with the Microsoft public key can be considered +// part of the platform and therefore contain critical code. This leads to an issue with testing the +// product, since any tests which need to test platform only feautres would need to be signed by the +// Microsoft public key, and for a variety of reasons it is not feasable to sign all of our test assemblies +// with that key. +// +// Instead, we provide an extension where any assembly signed with the public key contained in the +// g_rbTestKeyBuffer will also be considered part of the platform. The buffer is filled with a zero key, +// which means by default non-Microsoft signatures will not be accepted. However, the slstampkey tool looks +// for the two GUIDs in the beginning of the buffer, and can replace the key with a real one of the test +// team's choosing. Once this modification is in place, test assemblies can be considered part of the +// platform and can access critical APIs. +// +// Since this buffer is searched for in the VM using external tools, it's very important that it only appear +// once in the final image of the runtime. If it appears multiple times, tools will be unable to determine +// which test key buffer is the real buffer, and therefore tests which rely on containing platform code will +// be unable to run on the build. +// + +#define TEST_KEY_HEADER \ + /* Test key buffer header 1: { cd517db8-a1b1-44bf-aa64-bf66fefa3831 } */ \ + 0xb8, 0x7d, 0x51, 0xcd, 0xb1, 0xa1, 0xbf, 0x44, 0x64, 0xaa, 0xbf, 0x66, 0xfe, 0xfa, 0x38, 0x31, \ + \ + /* Test key buffer header 2: { 5f363032-eaaf-4103-b312-ab2c8e35cf58 } */ \ + 0x32, 0x30, 0x36, 0x5f, 0xaf, 0xea, 0x03, 0x41, 0x12, 0xb3, 0xab, 0x2c, 0x8e, 0x35, 0xcf, 0x58, + + +#define TEST_KEY_VALUE \ + /* SigAlgId = */\ + 0x00, 0x24, 0x00, 0x00, \ + \ + /* HashAlgId = */\ + 0x04, 0x80, 0x00, 0x00, \ + \ + /* cbPublicKey = 0x94 */ \ + 0x94, 0x00, 0x00, 0x00, \ + \ + /* 1024 bit public key */ \ + 0x06, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, \ + 0x0f, 0xc5, 0x99, 0x3e, 0x0f, 0x51, 0x1a, 0xd5, 0xe1, 0x6e, 0x8b, 0x22, 0x65, 0x53, 0x49, 0x3e, 0x09, 0x06, 0x7a, 0xfc, \ + 0x41, 0x03, 0x9f, 0x70, 0xda, 0xeb, 0x94, 0xa9, 0x68, 0xd6, 0x64, 0xf4, 0x0e, 0x69, 0xa4, 0x6b, 0x61, 0x7d, 0x15, 0xd3, \ + 0xd5, 0x32, 0x8b, 0xe7, 0xdb, 0xed, 0xd0, 0x59, 0xeb, 0x98, 0x49, 0x5a, 0x3b, 0x03, 0xcb, 0x4e, 0xa4, 0xba, 0x12, 0x74, \ + 0x44, 0x67, 0x1c, 0x3c, 0x84, 0xcb, 0xc1, 0xfd, 0xc3, 0x93, 0xd7, 0xe1, 0x0b, 0x5e, 0xe3, 0xf3, 0x1f, 0x5a, 0x29, 0xf0, \ + 0x05, 0xe5, 0xee, 0xd7, 0xe3, 0xc9, 0xc8, 0xaf, 0x74, 0xf4, 0x13, 0xf0, 0x00, 0x4f, 0x0c, 0x2c, 0xab, 0xb2, 0x2f, 0x9d, \ + 0xd4, 0xf7, 0x5a, 0x6f, 0x59, 0x97, 0x84, 0xe1, 0xba, 0xb7, 0x09, 0x85, 0xef, 0x81, 0x74, 0xca, 0x6c, 0x68, 0x42, 0x78, \ + 0xbe, 0x82, 0xce, 0x05, 0x5a, 0x03, 0xeb, 0xaf + + // + // Test Public key blob + // + + // + // No prestamped test key - create an empty key buffer + // +#define TEST_KEY_BUFFER \ + /* SigAlgId */ \ + 0x00, 0x00, 0x00, 0x00, \ + \ + /* HashAlgId */ \ + 0x00, 0x00, 0x00, 0x00, \ + \ + /* cbPublicKey = 0x94 - this needs to match the size of the public key section below */ \ + 0x94, 0x00, 0x00, 0x00, \ + \ + /* 1024 bit public key - 0x94 bytes */ \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +#endif // FEATURE_STRONGNAME_TESTKEY_ALLOWED diff --git a/src/strongname/strongname.vcxproj b/src/strongname/strongname.vcxproj new file mode 100644 index 0000000000..66f370883a --- /dev/null +++ b/src/strongname/strongname.vcxproj @@ -0,0 +1,396 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="amd64chk|Win32"> + <Configuration>amd64chk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64corechk|Win32"> + <Configuration>amd64corechk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64coredbg|Win32"> + <Configuration>amd64coredbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64coreret|Win32"> + <Configuration>amd64coreret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64dbg|Win32"> + <Configuration>amd64dbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="amd64ret|Win32"> + <Configuration>amd64ret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="armcorechk|Win32"> + <Configuration>armcorechk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="armcoredbg|Win32"> + <Configuration>armcoredbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="armcoreret|Win32"> + <Configuration>armcoreret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Mac.inteldbg|Win32"> + <Configuration>Mac.inteldbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86chk|Win32"> + <Configuration>x86chk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86corechk|Win32"> + <Configuration>x86corechk</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86coredbg|Win32"> + <Configuration>x86coredbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86coreret|Win32"> + <Configuration>x86coreret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86dbg|Win32"> + <Configuration>x86dbg</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="x86ret|Win32"> + <Configuration>x86ret</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{6AA7CBD3-6CAA-4159-B5B6-DEFE6065D41B}</ProjectGuid> + <RootNamespace>StrongName</RootNamespace> + <SccProjectName>SAK</SccProjectName> + <SccAuxPath>SAK</SccAuxPath> + <SccLocalPath>SAK</SccLocalPath> + <SccProvider>SAK</SccProvider> + <Keyword>MakeFileProj</Keyword> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoreret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcorechk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoredbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coreret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coredbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64corechk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Mac.inteldbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coreret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coredbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86corechk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64ret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64dbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64chk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86ret|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86chk|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86dbg|Win32'" Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='armcoreret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='armcorechk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='armcoredbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64coreret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64coredbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64corechk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Mac.inteldbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86coreret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86coredbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86corechk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64ret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64dbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64chk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86ret|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86chk|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86dbg|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>12.0.20617.1</_ProjectFileVersion> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86dbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86chk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86ret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64chk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64dbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64ret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86corechk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coredbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coreret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Mac.inteldbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(SolutionDir)\..\..\..\rotor\palrt\inc;$(SolutionDir)\..\..\..\rotor\pal\inc;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64corechk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coredbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coreret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoredbg|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcorechk|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoreret|Win32'"> + <OutDir>$(Configuration)\</OutDir> + <IntDir>$(Configuration)\</IntDir> + <NMakeBuildCommandLine /> + <NMakeReBuildCommandLine /> + <NMakeCleanCommandLine /> + <NMakeOutput /> + <NMakeIncludeSearchPath>.\inc;..\inc;..\md\inc;..\vm;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes> + </PropertyGroup> + <ItemDefinitionGroup> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="api\common.cpp" /> + <ClCompile Include="api\StrongName.cpp" /> + <ClCompile Include="api\StrongNameCoreClr.cpp" /> + <ClCompile Include="api\StrongNameInternal.cpp" /> + <ClCompile Include="tools\slstampkey\slstampkey.cpp" /> + <ClCompile Include="tools\sn\sn.cpp" /> + <ClCompile Include="tools\snhdr\snhdr.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api\common.h" /> + <ClInclude Include="api\CryptApis.h" /> + <ClInclude Include="inc\EcmaKey.h" /> + <ClInclude Include="inc\sncoreclr.h" /> + <ClInclude Include="inc\StrongName.h" /> + <ClInclude Include="inc\StrongNameHolders.h" /> + <ClInclude Include="inc\StrongNameInternal.h" /> + <ClInclude Include="inc\TheKey.h" /> + <ClInclude Include="inc\TheTestKey.h" /> + <ClInclude Include="inc\WellKnownImages.h" /> + <ClInclude Include="tools\sn\resources.h" /> + </ItemGroup> + <ItemGroup> + <None Include="inc\sources" /> + <None Include="tools\slstampkey\sources" /> + <None Include="tools\sn\sources" /> + <None Include="tools\snhdr\sources" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="tools\slstampkey\slstampkey.rc" /> + <ResourceCompile Include="tools\sn\sn.rc" /> + <ResourceCompile Include="tools\snhdr\snhdr.rc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/src/strongname/strongname.vcxproj.filters b/src/strongname/strongname.vcxproj.filters new file mode 100644 index 0000000000..77aa40e76c --- /dev/null +++ b/src/strongname/strongname.vcxproj.filters @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="api"> + <UniqueIdentifier>{7388d6b8-789c-47c9-80a2-66ee17a5958c}</UniqueIdentifier> + <Extensions>cpp</Extensions> + </Filter> + <Filter Include="inc"> + <UniqueIdentifier>{26401483-02d0-4bef-8fb5-30e94e81f89d}</UniqueIdentifier> + <Extensions>h</Extensions> + </Filter> + <Filter Include="slstampkey"> + <UniqueIdentifier>{e15d16b5-68c0-45bf-9836-33b5790e6da5}</UniqueIdentifier> + <Extensions>cpp</Extensions> + </Filter> + <Filter Include="sn"> + <UniqueIdentifier>{c014e6a5-fc76-4b23-8f0b-bd4c275b8d6b}</UniqueIdentifier> + <Extensions>cpp</Extensions> + </Filter> + <Filter Include="snhdr"> + <UniqueIdentifier>{ec414528-cbc8-41ca-95bb-34b0babd892d}</UniqueIdentifier> + <Extensions>cpp</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="api\common.cpp"> + <Filter>api</Filter> + </ClCompile> + <ClCompile Include="api\StrongName.cpp"> + <Filter>api</Filter> + </ClCompile> + <ClCompile Include="api\StrongNameCoreClr.cpp"> + <Filter>api</Filter> + </ClCompile> + <ClCompile Include="api\StrongNameInternal.cpp"> + <Filter>api</Filter> + </ClCompile> + <ClCompile Include="tools\slstampkey\slstampkey.cpp"> + <Filter>slstampkey</Filter> + </ClCompile> + <ClCompile Include="tools\sn\sn.cpp"> + <Filter>sn</Filter> + </ClCompile> + <ClCompile Include="tools\snhdr\snhdr.cpp"> + <Filter>snhdr</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api\common.h"> + <Filter>api</Filter> + </ClInclude> + <ClInclude Include="api\CryptApis.h"> + <Filter>api</Filter> + </ClInclude> + <ClInclude Include="inc\EcmaKey.h"> + <Filter>inc</Filter> + </ClInclude> + <ClInclude Include="inc\sncoreclr.h"> + <Filter>inc</Filter> + </ClInclude> + <ClInclude Include="inc\StrongName.h"> + <Filter>inc</Filter> + </ClInclude> + <ClInclude Include="inc\StrongNameHolders.h"> + <Filter>inc</Filter> + </ClInclude> + <ClInclude Include="inc\StrongNameInternal.h"> + <Filter>inc</Filter> + </ClInclude> + <ClInclude Include="inc\TheKey.h"> + <Filter>inc</Filter> + </ClInclude> + <ClInclude Include="inc\TheTestKey.h"> + <Filter>inc</Filter> + </ClInclude> + <ClInclude Include="inc\WellKnownImages.h"> + <Filter>inc</Filter> + </ClInclude> + <ClInclude Include="tools\sn\resources.h"> + <Filter>sn</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <None Include="inc\sources"> + <Filter>inc</Filter> + </None> + <None Include="tools\slstampkey\sources"> + <Filter>slstampkey</Filter> + </None> + <None Include="tools\sn\sources"> + <Filter>sn</Filter> + </None> + <None Include="tools\snhdr\sources"> + <Filter>snhdr</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="tools\slstampkey\slstampkey.rc"> + <Filter>slstampkey</Filter> + </ResourceCompile> + <ResourceCompile Include="tools\sn\sn.rc"> + <Filter>sn</Filter> + </ResourceCompile> + <ResourceCompile Include="tools\snhdr\snhdr.rc"> + <Filter>snhdr</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/strongname/strongname.vcxproj.vspscc b/src/strongname/strongname.vcxproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/src/strongname/strongname.vcxproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} |