/src/edk2/SecurityPkg/Library/FmpAuthenticationLibRsa2048Sha256/FmpAuthenticationLibRsa2048Sha256.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** @file |
2 | | FMP Authentication RSA2048SHA256 handler. |
3 | | Provide generic FMP authentication functions for DXE/PEI post memory phase. |
4 | | |
5 | | Caution: This module requires additional review when modified. |
6 | | This module will have external input - capsule image. |
7 | | This external input must be validated carefully to avoid security issue like |
8 | | buffer overflow, integer overflow. |
9 | | |
10 | | FmpAuthenticatedHandlerRsa2048Sha256(), AuthenticateFmpImage() will receive |
11 | | untrusted input and do basic validation. |
12 | | |
13 | | Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> |
14 | | SPDX-License-Identifier: BSD-2-Clause-Patent |
15 | | |
16 | | **/ |
17 | | |
18 | | #include <Uefi.h> |
19 | | |
20 | | #include <Guid/SystemResourceTable.h> |
21 | | #include <Guid/FirmwareContentsSigned.h> |
22 | | #include <Guid/WinCertificate.h> |
23 | | |
24 | | #include <Library/BaseLib.h> |
25 | | #include <Library/BaseMemoryLib.h> |
26 | | #include <Library/DebugLib.h> |
27 | | #include <Library/MemoryAllocationLib.h> |
28 | | #include <Library/BaseCryptLib.h> |
29 | | #include <Library/FmpAuthenticationLib.h> |
30 | | #include <Library/PcdLib.h> |
31 | | #include <Protocol/FirmwareManagement.h> |
32 | | #include <Guid/SystemResourceTable.h> |
33 | | |
34 | | /// |
35 | | /// Public Exponent of RSA Key. |
36 | | /// |
37 | | STATIC CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 }; |
38 | | |
39 | | /** |
40 | | The handler is used to do the authentication for FMP capsule based upon |
41 | | EFI_FIRMWARE_IMAGE_AUTHENTICATION. |
42 | | |
43 | | Caution: This function may receive untrusted input. |
44 | | |
45 | | This function assumes the caller AuthenticateFmpImage() |
46 | | already did basic validation for EFI_FIRMWARE_IMAGE_AUTHENTICATION. |
47 | | |
48 | | @param[in] Image Points to an FMP authentication image, started from EFI_FIRMWARE_IMAGE_AUTHENTICATION. |
49 | | @param[in] ImageSize Size of the authentication image in bytes. |
50 | | @param[in] PublicKeyData The public key data used to validate the signature. |
51 | | @param[in] PublicKeyDataLength The length of the public key data. |
52 | | |
53 | | @retval RETURN_SUCCESS Authentication pass. |
54 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_SUCCESS. |
55 | | @retval RETURN_SECURITY_VIOLATION Authentication fail. |
56 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR. |
57 | | @retval RETURN_INVALID_PARAMETER The image is in an invalid format. |
58 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT. |
59 | | @retval RETURN_OUT_OF_RESOURCES No Authentication handler associated with CertType. |
60 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES. |
61 | | **/ |
62 | | RETURN_STATUS |
63 | | FmpAuthenticatedHandlerRsa2048Sha256 ( |
64 | | IN EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image, |
65 | | IN UINTN ImageSize, |
66 | | IN CONST UINT8 *PublicKeyData, |
67 | | IN UINTN PublicKeyDataLength |
68 | | ) |
69 | 35 | { |
70 | 35 | RETURN_STATUS Status; |
71 | 35 | EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlockRsa2048Sha256; |
72 | 35 | BOOLEAN CryptoStatus; |
73 | 35 | UINT8 Digest[SHA256_DIGEST_SIZE]; |
74 | 35 | UINT8 *PublicKey; |
75 | 35 | UINTN PublicKeyBufferSize; |
76 | 35 | VOID *HashContext; |
77 | 35 | VOID *Rsa; |
78 | | |
79 | 35 | DEBUG ((DEBUG_INFO, "FmpAuthenticatedHandlerRsa2048Sha256 - Image: 0x%08x - 0x%08x\n", (UINTN)Image, (UINTN)ImageSize)); |
80 | | |
81 | 35 | if (Image->AuthInfo.Hdr.dwLength != OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData) + sizeof (EFI_CERT_BLOCK_RSA_2048_SHA256)) { |
82 | 33 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256 - dwLength: 0x%04x, dwLength - 0x%04x\n", (UINTN)Image->AuthInfo.Hdr.dwLength, (UINTN)OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData) + sizeof (EFI_CERT_BLOCK_RSA_2048_SHA256))); |
83 | 33 | return RETURN_INVALID_PARAMETER; |
84 | 33 | } |
85 | | |
86 | 2 | CertBlockRsa2048Sha256 = (EFI_CERT_BLOCK_RSA_2048_SHA256 *)Image->AuthInfo.CertData; |
87 | 2 | if (!CompareGuid (&CertBlockRsa2048Sha256->HashType, &gEfiHashAlgorithmSha256Guid)) { |
88 | 1 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256 - HashType: %g, expect - %g\n", &CertBlockRsa2048Sha256->HashType, &gEfiHashAlgorithmSha256Guid)); |
89 | 1 | return RETURN_INVALID_PARAMETER; |
90 | 1 | } |
91 | | |
92 | 1 | HashContext = NULL; |
93 | 1 | Rsa = NULL; |
94 | | |
95 | | // |
96 | | // Allocate hash context buffer required for SHA 256 |
97 | | // |
98 | 1 | HashContext = AllocatePool (Sha256GetContextSize ()); |
99 | 1 | if (HashContext == NULL) { |
100 | 0 | CryptoStatus = FALSE; |
101 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Can not allocate hash context\n")); |
102 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
103 | 0 | goto Done; |
104 | 0 | } |
105 | | |
106 | | // |
107 | | // Hash public key from data payload with SHA256. |
108 | | // |
109 | 1 | ZeroMem (Digest, SHA256_DIGEST_SIZE); |
110 | 1 | CryptoStatus = Sha256Init (HashContext); |
111 | 1 | if (!CryptoStatus) { |
112 | 1 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Init() failed\n")); |
113 | 1 | Status = RETURN_OUT_OF_RESOURCES; |
114 | 1 | goto Done; |
115 | 1 | } |
116 | | |
117 | 0 | CryptoStatus = Sha256Update (HashContext, &CertBlockRsa2048Sha256->PublicKey, sizeof (CertBlockRsa2048Sha256->PublicKey)); |
118 | 0 | if (!CryptoStatus) { |
119 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Update() failed\n")); |
120 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
121 | 0 | goto Done; |
122 | 0 | } |
123 | | |
124 | 0 | CryptoStatus = Sha256Final (HashContext, Digest); |
125 | 0 | if (!CryptoStatus) { |
126 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Final() failed\n")); |
127 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
128 | 0 | goto Done; |
129 | 0 | } |
130 | | |
131 | | // |
132 | | // Fail if the PublicKey is not one of the public keys in the input PublicKeyData. |
133 | | // |
134 | 0 | PublicKey = (VOID *)PublicKeyData; |
135 | 0 | PublicKeyBufferSize = PublicKeyDataLength; |
136 | 0 | CryptoStatus = FALSE; |
137 | 0 | while (PublicKeyBufferSize != 0) { |
138 | 0 | if (CompareMem (Digest, PublicKey, SHA256_DIGEST_SIZE) == 0) { |
139 | 0 | CryptoStatus = TRUE; |
140 | 0 | break; |
141 | 0 | } |
142 | | |
143 | 0 | PublicKey = PublicKey + SHA256_DIGEST_SIZE; |
144 | 0 | PublicKeyBufferSize = PublicKeyBufferSize - SHA256_DIGEST_SIZE; |
145 | 0 | } |
146 | |
|
147 | 0 | if (!CryptoStatus) { |
148 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Public key in section is not supported\n")); |
149 | 0 | Status = RETURN_SECURITY_VIOLATION; |
150 | 0 | goto Done; |
151 | 0 | } |
152 | | |
153 | | // |
154 | | // Generate & Initialize RSA Context. |
155 | | // |
156 | 0 | Rsa = RsaNew (); |
157 | 0 | if (Rsa == NULL) { |
158 | 0 | CryptoStatus = FALSE; |
159 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: RsaNew() failed\n")); |
160 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
161 | 0 | goto Done; |
162 | 0 | } |
163 | | |
164 | | // |
165 | | // Set RSA Key Components. |
166 | | // NOTE: Only N and E are needed to be set as RSA public key for signature verification. |
167 | | // |
168 | 0 | CryptoStatus = RsaSetKey (Rsa, RsaKeyN, CertBlockRsa2048Sha256->PublicKey, sizeof (CertBlockRsa2048Sha256->PublicKey)); |
169 | 0 | if (!CryptoStatus) { |
170 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: RsaSetKey(RsaKeyN) failed\n")); |
171 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
172 | 0 | goto Done; |
173 | 0 | } |
174 | | |
175 | 0 | CryptoStatus = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE)); |
176 | 0 | if (!CryptoStatus) { |
177 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: RsaSetKey(RsaKeyE) failed\n")); |
178 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
179 | 0 | goto Done; |
180 | 0 | } |
181 | | |
182 | | // |
183 | | // Hash data payload with SHA256. |
184 | | // |
185 | 0 | ZeroMem (Digest, SHA256_DIGEST_SIZE); |
186 | 0 | CryptoStatus = Sha256Init (HashContext); |
187 | 0 | if (!CryptoStatus) { |
188 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Init() failed\n")); |
189 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
190 | 0 | goto Done; |
191 | 0 | } |
192 | | |
193 | | // It is a signature across the variable data and the Monotonic Count value. |
194 | 0 | CryptoStatus = Sha256Update ( |
195 | 0 | HashContext, |
196 | 0 | (UINT8 *)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength, |
197 | 0 | ImageSize - sizeof (Image->MonotonicCount) - Image->AuthInfo.Hdr.dwLength |
198 | 0 | ); |
199 | 0 | if (!CryptoStatus) { |
200 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Update() failed\n")); |
201 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
202 | 0 | goto Done; |
203 | 0 | } |
204 | | |
205 | 0 | CryptoStatus = Sha256Update ( |
206 | 0 | HashContext, |
207 | 0 | (UINT8 *)&Image->MonotonicCount, |
208 | 0 | sizeof (Image->MonotonicCount) |
209 | 0 | ); |
210 | 0 | if (!CryptoStatus) { |
211 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Update() failed\n")); |
212 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
213 | 0 | goto Done; |
214 | 0 | } |
215 | | |
216 | 0 | CryptoStatus = Sha256Final (HashContext, Digest); |
217 | 0 | if (!CryptoStatus) { |
218 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: Sha256Final() failed\n")); |
219 | 0 | Status = RETURN_OUT_OF_RESOURCES; |
220 | 0 | goto Done; |
221 | 0 | } |
222 | | |
223 | | // |
224 | | // Verify the RSA 2048 SHA 256 signature. |
225 | | // |
226 | 0 | CryptoStatus = RsaPkcs1Verify ( |
227 | 0 | Rsa, |
228 | 0 | Digest, |
229 | 0 | SHA256_DIGEST_SIZE, |
230 | 0 | CertBlockRsa2048Sha256->Signature, |
231 | 0 | sizeof (CertBlockRsa2048Sha256->Signature) |
232 | 0 | ); |
233 | 0 | if (!CryptoStatus) { |
234 | | // |
235 | | // If RSA 2048 SHA 256 signature verification fails, AUTH tested failed bit is set. |
236 | | // |
237 | 0 | DEBUG ((DEBUG_ERROR, "FmpAuthenticatedHandlerRsa2048Sha256: RsaPkcs1Verify() failed\n")); |
238 | 0 | Status = RETURN_SECURITY_VIOLATION; |
239 | 0 | goto Done; |
240 | 0 | } |
241 | | |
242 | 0 | DEBUG ((DEBUG_INFO, "FmpAuthenticatedHandlerRsa2048Sha256: PASS verification\n")); |
243 | |
|
244 | 0 | Status = RETURN_SUCCESS; |
245 | |
|
246 | 1 | Done: |
247 | | // |
248 | | // Free allocated resources used to perform RSA 2048 SHA 256 signature verification |
249 | | // |
250 | 1 | if (Rsa != NULL) { |
251 | 0 | RsaFree (Rsa); |
252 | 0 | } |
253 | | |
254 | 1 | if (HashContext != NULL) { |
255 | 1 | FreePool (HashContext); |
256 | 1 | } |
257 | | |
258 | 1 | return Status; |
259 | 0 | } |
260 | | |
261 | | /** |
262 | | The function is used to do the authentication for FMP capsule based upon |
263 | | EFI_FIRMWARE_IMAGE_AUTHENTICATION. |
264 | | |
265 | | The FMP capsule image should start with EFI_FIRMWARE_IMAGE_AUTHENTICATION, |
266 | | followed by the payload. |
267 | | |
268 | | If the return status is RETURN_SUCCESS, the caller may continue the rest |
269 | | FMP update process. |
270 | | If the return status is NOT RETURN_SUCCESS, the caller should stop the FMP |
271 | | update process and convert the return status to LastAttemptStatus |
272 | | to indicate that FMP update fails. |
273 | | The LastAttemptStatus can be got from ESRT table or via |
274 | | EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo(). |
275 | | |
276 | | Caution: This function may receive untrusted input. |
277 | | |
278 | | @param[in] Image Points to an FMP authentication image, started from EFI_FIRMWARE_IMAGE_AUTHENTICATION. |
279 | | @param[in] ImageSize Size of the authentication image in bytes. |
280 | | @param[in] PublicKeyData The public key data used to validate the signature. |
281 | | @param[in] PublicKeyDataLength The length of the public key data. |
282 | | |
283 | | @retval RETURN_SUCCESS Authentication pass. |
284 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_SUCCESS. |
285 | | @retval RETURN_SECURITY_VIOLATION Authentication fail. |
286 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR. |
287 | | @retval RETURN_INVALID_PARAMETER The image is in an invalid format. |
288 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT. |
289 | | @retval RETURN_UNSUPPORTED No Authentication handler associated with CertType. |
290 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT. |
291 | | @retval RETURN_UNSUPPORTED Image or ImageSize is invalid. |
292 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT. |
293 | | @retval RETURN_OUT_OF_RESOURCES No Authentication handler associated with CertType. |
294 | | The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES. |
295 | | **/ |
296 | | RETURN_STATUS |
297 | | EFIAPI |
298 | | AuthenticateFmpImage ( |
299 | | IN EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image, |
300 | | IN UINTN ImageSize, |
301 | | IN CONST UINT8 *PublicKeyData, |
302 | | IN UINTN PublicKeyDataLength |
303 | | ) |
304 | 339 | { |
305 | 339 | GUID *CertType; |
306 | 339 | EFI_STATUS Status; |
307 | | |
308 | 339 | if ((Image == NULL) || (ImageSize == 0)) { |
309 | 0 | return RETURN_UNSUPPORTED; |
310 | 0 | } |
311 | | |
312 | 339 | if ((PublicKeyDataLength % SHA256_DIGEST_SIZE) != 0) { |
313 | 0 | DEBUG ((DEBUG_ERROR, "PublicKeyDataLength is not multiple SHA256 size\n")); |
314 | 0 | return RETURN_UNSUPPORTED; |
315 | 0 | } |
316 | | |
317 | 339 | if (ImageSize < sizeof (EFI_FIRMWARE_IMAGE_AUTHENTICATION)) { |
318 | 12 | DEBUG ((DEBUG_ERROR, "AuthenticateFmpImage - ImageSize too small\n")); |
319 | 12 | return RETURN_INVALID_PARAMETER; |
320 | 12 | } |
321 | | |
322 | 327 | if (Image->AuthInfo.Hdr.dwLength <= OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) { |
323 | 9 | DEBUG ((DEBUG_ERROR, "AuthenticateFmpImage - dwLength too small\n")); |
324 | 9 | return RETURN_INVALID_PARAMETER; |
325 | 9 | } |
326 | | |
327 | 318 | if ((UINTN)Image->AuthInfo.Hdr.dwLength > MAX_UINTN - sizeof (UINT64)) { |
328 | 0 | DEBUG ((DEBUG_ERROR, "AuthenticateFmpImage - dwLength too big\n")); |
329 | 0 | return RETURN_INVALID_PARAMETER; |
330 | 0 | } |
331 | | |
332 | 318 | if (ImageSize <= sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) { |
333 | 97 | DEBUG ((DEBUG_ERROR, "AuthenticateFmpImage - ImageSize too small\n")); |
334 | 97 | return RETURN_INVALID_PARAMETER; |
335 | 97 | } |
336 | | |
337 | 221 | if (Image->AuthInfo.Hdr.wRevision != 0x0200) { |
338 | 23 | DEBUG ((DEBUG_ERROR, "AuthenticateFmpImage - wRevision: 0x%02x, expect - 0x%02x\n", (UINTN)Image->AuthInfo.Hdr.wRevision, (UINTN)0x0200)); |
339 | 23 | return RETURN_INVALID_PARAMETER; |
340 | 23 | } |
341 | | |
342 | 198 | if (Image->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) { |
343 | 24 | DEBUG ((DEBUG_ERROR, "AuthenticateFmpImage - wCertificateType: 0x%02x, expect - 0x%02x\n", (UINTN)Image->AuthInfo.Hdr.wCertificateType, (UINTN)WIN_CERT_TYPE_EFI_GUID)); |
344 | 24 | return RETURN_INVALID_PARAMETER; |
345 | 24 | } |
346 | | |
347 | 174 | CertType = &Image->AuthInfo.CertType; |
348 | 174 | DEBUG ((DEBUG_INFO, "AuthenticateFmpImage - CertType: %g\n", CertType)); |
349 | | |
350 | 174 | if (CompareGuid (&gEfiCertTypeRsa2048Sha256Guid, CertType)) { |
351 | | // |
352 | | // Call the match handler to extract raw data for the input section data. |
353 | | // |
354 | 35 | Status = FmpAuthenticatedHandlerRsa2048Sha256 ( |
355 | 35 | Image, |
356 | 35 | ImageSize, |
357 | 35 | PublicKeyData, |
358 | 35 | PublicKeyDataLength |
359 | 35 | ); |
360 | 35 | return Status; |
361 | 35 | } |
362 | | |
363 | | // |
364 | | // Not found, the input guided section is not supported. |
365 | | // |
366 | 139 | return RETURN_UNSUPPORTED; |
367 | 174 | } |