summaryrefslogtreecommitdiff
path: root/src/strongname/api/strongnameinternal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/strongname/api/strongnameinternal.cpp')
-rw-r--r--src/strongname/api/strongnameinternal.cpp448
1 files changed, 448 insertions, 0 deletions
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
+