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 | |
23 | bool 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 | |
50 | bool 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 | // |
71 | bool 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 | |
98 | bool 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 | |
125 | bool 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 | |
148 | bool 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 | |
187 | bool 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 | |
256 | DWORD 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 | |
283 | bool 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 | |
310 | BYTE 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. |
322 | bool 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 |
350 | bool 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 | |