1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5// Strong name APIs which are not exposed publicly but are used by CLR code
6//
7
8#include "common.h"
9#include "strongnameinternal.h"
10#include "strongnameholders.h"
11#include "thekey.h"
12#include "ecmakey.h"
13
14//---------------------------------------------------------------------------------------
15//
16// Check to see if a public key blob is the ECMA public key blob
17//
18// Arguments:
19// pbKey - public key blob to check
20// cbKey - size in bytes of pbKey
21//
22
23bool StrongNameIsEcmaKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey)
24{
25 CONTRACTL
26 {
27 NOTHROW;
28 GC_NOTRIGGER;
29 }
30 CONTRACTL_END;
31
32 // The key should be the same size as the ECMA key
33 if (cbKey != sizeof(g_rbNeutralPublicKey))
34 {
35 return false;
36 }
37
38 const PublicKeyBlob *pKeyBlob = reinterpret_cast<const PublicKeyBlob *>(pbKey);
39 return StrongNameIsEcmaKey(*pKeyBlob);
40}
41
42//---------------------------------------------------------------------------------------
43//
44// Check to see if a public key blob is the ECMA public key blob
45//
46// Arguments:
47// keyPublicKey - Key to check to see if it matches the ECMA key
48//
49
50bool StrongNameIsEcmaKey(const PublicKeyBlob &keyPublicKey)
51{
52 CONTRACTL
53 {
54 NOTHROW;
55 GC_NOTRIGGER;
56 }
57 CONTRACTL_END;
58
59 return StrongNameSizeOfPublicKey(keyPublicKey) == sizeof(g_rbNeutralPublicKey) &&
60 memcmp(reinterpret_cast<const BYTE *>(&keyPublicKey), g_rbNeutralPublicKey, sizeof(g_rbNeutralPublicKey)) == 0;
61}
62
63//---------------------------------------------------------------------------------------
64//
65// Check to see if a public key blob is the TheKey public key blob
66//
67// Arguments:
68// pbKey - public key blob to check
69// cbKey - size in bytes of pbKey
70//
71bool StrongNameIsTheKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey)
72{
73 CONTRACTL
74 {
75 NOTHROW;
76 GC_NOTRIGGER;
77 }
78 CONTRACTL_END;
79
80 // The key should be the same size as the TheKey key
81 if (cbKey != sizeof(g_rbTheKey))
82 {
83 return false;
84 }
85
86 return (memcmp(pbKey, g_rbTheKey, sizeof(g_rbTheKey)) == 0);
87}
88
89//---------------------------------------------------------------------------------------
90//
91// Check to see if a public key blob is the Silverlight Platform public key blob
92//
93// Arguments:
94// pbKey - public key blob to check
95// cbKey - size in bytes of pbKey
96//
97
98bool StrongNameIsSilverlightPlatformKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey)
99{
100 CONTRACTL
101 {
102 NOTHROW;
103 GC_NOTRIGGER;
104 }
105 CONTRACTL_END;
106
107 // The key should be the same size as the ECMA key
108 if (cbKey != sizeof(g_rbTheSilverlightPlatformKey))
109 {
110 return false;
111 }
112
113 const PublicKeyBlob *pKeyBlob = reinterpret_cast<const PublicKeyBlob *>(pbKey);
114 return StrongNameIsSilverlightPlatformKey(*pKeyBlob);
115}
116
117//---------------------------------------------------------------------------------------
118//
119// Check to see if a public key blob is the Silverlight Platform public key blob
120//
121// Arguments:
122// keyPublicKey - Key to check to see if it matches the ECMA key
123//
124
125bool StrongNameIsSilverlightPlatformKey(const PublicKeyBlob &keyPublicKey)
126{
127 CONTRACTL
128 {
129 NOTHROW;
130 GC_NOTRIGGER;
131 }
132 CONTRACTL_END;
133
134 return StrongNameSizeOfPublicKey(keyPublicKey) == sizeof(g_rbTheSilverlightPlatformKey) &&
135 memcmp(reinterpret_cast<const BYTE *>(&keyPublicKey), g_rbTheSilverlightPlatformKey, sizeof(g_rbTheSilverlightPlatformKey)) == 0;
136}
137
138//---------------------------------------------------------------------------------------
139//
140// Verify that a public key blob looks like a reasonable public key
141//
142// Arguments:
143// pbBuffer - buffer to verify the format of
144// cbBuffer - size of pbBuffer
145// fImportKeys - do a more extensive check by attempting to import the keys
146//
147
148bool StrongNameIsValidPublicKey(__in_ecount(cbBuffer) const BYTE *pbBuffer, DWORD cbBuffer, bool fImportKeys)
149{
150 CONTRACTL
151 {
152 PRECONDITION(CheckPointer(pbBuffer));
153 NOTHROW;
154 GC_NOTRIGGER;
155 }
156 CONTRACTL_END;
157
158 // The buffer must be at least as large as the public key structure
159 if (cbBuffer < sizeof(PublicKeyBlob))
160 {
161 return false;
162 }
163
164 // The buffer must be the same size as the structure header plus the trailing key data
165 const PublicKeyBlob *pkeyPublicKey = reinterpret_cast<const PublicKeyBlob *>(pbBuffer);
166 if (GET_UNALIGNED_VAL32(&pkeyPublicKey->cbPublicKey) != cbBuffer - offsetof(PublicKeyBlob, PublicKey))
167 {
168 return false;
169 }
170
171 // The buffer itself looks reasonable, but the public key structure needs to be validated as well
172 return StrongNameIsValidPublicKey(*pkeyPublicKey, fImportKeys);
173}
174
175//---------------------------------------------------------------------------------------
176//
177// Verify that a public key blob looks like a reasonable public key.
178//
179// Arguments:
180// keyPublicKey - key blob to verify
181// fImportKeys - do a more extensive check by verifying that the key data imports into CAPI
182//
183// Notes:
184// This can be a very expensive operation, since it involves importing keys.
185//
186
187bool StrongNameIsValidPublicKey(const PublicKeyBlob &keyPublicKey, bool fImportKeys)
188{
189 CONTRACTL
190 {
191 NOTHROW;
192 GC_NOTRIGGER;
193 }
194 CONTRACTL_END;
195
196 // The ECMA key doesn't look like a valid key so it will fail the below checks. If we were passed that
197 // key, then we can skip them
198 if (StrongNameIsEcmaKey(keyPublicKey))
199 {
200 return true;
201 }
202
203 // If a hash algorithm is specified, it must be a sensible value
204 bool fHashAlgorithmValid = GET_ALG_CLASS(GET_UNALIGNED_VAL32(&keyPublicKey.HashAlgID)) == ALG_CLASS_HASH &&
205 GET_ALG_SID(GET_UNALIGNED_VAL32(&keyPublicKey.HashAlgID)) >= ALG_SID_SHA1;
206 if (keyPublicKey.HashAlgID != 0 && !fHashAlgorithmValid)
207 {
208 return false;
209 }
210
211 // If a signature algorithm is specified, it must be a sensible value
212 bool fSignatureAlgorithmValid = GET_ALG_CLASS(GET_UNALIGNED_VAL32(&keyPublicKey.SigAlgID)) == ALG_CLASS_SIGNATURE;
213 if (keyPublicKey.SigAlgID != 0 && !fSignatureAlgorithmValid)
214 {
215 return false;
216 }
217
218 // The key blob must indicate that it is a PUBLICKEYBLOB
219 if (keyPublicKey.PublicKey[0] != PUBLICKEYBLOB)
220 {
221 return false;
222 }
223
224#if (defined(CROSSGEN_COMPILE) && !defined(PLATFORM_UNIX))
225 // Make sure the public key blob imports properly
226 if (fImportKeys)
227 {
228 CapiProviderHolder hProv;
229 if (!StrongNameCryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
230 {
231 return false;
232 }
233
234 CapiKeyHolder hKey;
235 if (!CryptImportKey(hProv, keyPublicKey.PublicKey, GET_UNALIGNED_VAL32(&keyPublicKey.cbPublicKey), NULL, 0, &hKey))
236 {
237 return false;
238 }
239 }
240#else // (CROSSGEN_COMPILE && !PLATFORM_UNIX)
241 _ASSERTE(!fImportKeys);
242#endif // (CROSSGEN_COMPILE && !PLATFORM_UNIX)
243
244 return true;
245}
246
247
248//---------------------------------------------------------------------------------------
249//
250// Determine the number of bytes that a public key blob occupies, including the key portion
251//
252// Arguments:
253// keyPublicKey - key blob to calculate the size of
254//
255
256DWORD StrongNameSizeOfPublicKey(const PublicKeyBlob &keyPublicKey)
257{
258 CONTRACTL
259 {
260 NOTHROW;
261 GC_NOTRIGGER;
262 }
263 CONTRACTL_END;
264
265 return offsetof(PublicKeyBlob, PublicKey) + // Size of the blob header plus
266 GET_UNALIGNED_VAL32(&keyPublicKey.cbPublicKey); // the number of bytes in the key
267}
268
269#if (defined(CROSSGEN_COMPILE) && !defined(PLATFORM_UNIX))
270
271//---------------------------------------------------------------------------------------
272//
273// Check to see if the value held in a buffer is a full strong name key pair
274//
275// Arguments:
276// pbBuffer - Blob to check
277// cbBuffer - Size of the buffer in bytes
278//
279// Return Value:
280// true if the buffer represents a full strong name key pair, false otherwise
281//
282
283bool StrongNameIsValidKeyPair(__in_ecount(cbKeyPair) const BYTE *pbKeyPair, DWORD cbKeyPair)
284{
285 CONTRACTL
286 {
287 NOTHROW;
288 GC_NOTRIGGER;
289 PRECONDITION(CheckPointer(pbKeyPair));
290 }
291 CONTRACTL_END;
292
293 // Key pairs are just CAPI PRIVATEKEYBLOBs, so see if CAPI can import the blob
294 CapiProviderHolder hProv;
295 if (!StrongNameCryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
296 {
297 return false;
298 }
299
300 CapiKeyHolder hKey;
301 if (!CryptImportKey(hProv, pbKeyPair, cbKeyPair, NULL, 0, &hKey))
302 {
303 return false;
304 }
305
306 return true;
307}
308
309
310BYTE HexToByteA (char c) {
311 LIMITED_METHOD_CONTRACT;
312
313 if (!isxdigit(c)) return (BYTE) 0xff;
314 if (isdigit(c)) return (BYTE) (c - '0');
315 if (isupper(c)) return (BYTE) (c - 'A' + 10);
316 return (BYTE) (c - 'a' + 10);
317}
318
319// Read the hex string into a buffer
320// Caller owns the buffer.
321// Returns NULL if the string contains non-hex characters, or doesn't contain a multiple of 2 characters.
322bool GetBytesFromHex(LPCUTF8 szHexString, ULONG cchHexString, BYTE** buffer, ULONG *cbBufferSize) {
323 LIMITED_METHOD_CONTRACT;
324
325 ULONG cchHex = cchHexString;
326 if (cchHex % 2 != 0)
327 return false;
328 *cbBufferSize = cchHex / 2;
329 NewArrayHolder<BYTE> tempBuffer(new (nothrow) BYTE[*cbBufferSize]);
330 if (tempBuffer == NULL)
331 return false;
332
333 for (ULONG i = 0; i < *cbBufferSize; i++) {
334 BYTE msn = HexToByteA(*szHexString);
335 BYTE lsn = HexToByteA(*(szHexString + 1));
336 if(msn == 0xFF || lsn == 0xFF)
337 {
338 return false;
339 }
340
341 tempBuffer[i] = (BYTE) ( (msn << 4) | lsn );
342 szHexString += 2;
343 }
344
345 *buffer = tempBuffer.Extract();
346 return true;
347}
348
349// Helper method to call CryptAcquireContext, making sure we have a valid set of flags
350bool StrongNameCryptAcquireContext(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider, DWORD dwProvType, DWORD dwFlags)
351{
352 LIMITED_METHOD_CONTRACT;
353
354#if defined(CRYPT_VERIFYCONTEXT) && defined(CRYPT_MACHINE_KEYSET)
355 // Specifying both verify context (for an ephemeral key) and machine keyset (for a persisted machine key)
356 // does not make sense. Additionally, Widows is beginning to lock down against uses of MACHINE_KEYSET
357 // (for instance in the app container), even if verify context is present. Therefore, if we're using
358 // an ephemeral key, strip out MACHINE_KEYSET from the flags.
359 if ((dwFlags & CRYPT_VERIFYCONTEXT) && (dwFlags & CRYPT_MACHINE_KEYSET))
360 {
361 dwFlags &= ~CRYPT_MACHINE_KEYSET;
362 }
363#endif // defined(CRYPT_VERIFYCONTEXT) && defined(CRYPT_MACHINE_KEYSET)
364
365 return !!WszCryptAcquireContext(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
366}
367
368#endif // (CROSSGEN_COMPILE && !PLATFORM_UNIX)
369
370