1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
|
/** @file
PKCS7 Decryption implementation over OpenSSL
Copyright (c) 2026, Microsoft Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "InternalCryptLib.h"
#include <openssl/evp.h>
#include <openssl/pkcs7.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <Library/MemoryAllocationLib.h>
/**
Decrypts a DER-encoded PKCS#7 ContentInfo containing an envelopedData structure
(such as one produced by Pkcs7Encrypt) and recovers the original content.
The private key is supplied in PEM form so that this interface is not tied to any
particular key algorithm (currently supports RSA).
The correct recipient within the envelopedData is identified automatically. The
envelopedData carries, for each recipient, an issuer name and serial number that
identify the recipient certificate. If RecipientCert is supplied, its issuer name
and serial number are used to select the matching recipient. If RecipientCert is
NULL, the supplied private key is tried against every recipient in the envelope.
If this interface is not supported, return FALSE.
@param[in] PemData Pointer to the PEM-encoded private key of a recipient
of the message. May be an RSA or other supported key
type.
@param[in] PemSize Size of the PEM key data in bytes.
@param[in] Password [Optional] NULL-terminated passphrase used to decrypt an
encrypted PEM key. Pass NULL if the PEM key is not
password protected.
@param[in] RecipientCert [Optional] Pointer to the DER-encoded X.509 certificate
of the recipient whose private key is supplied in
PemData. If provided, it is used to select the matching
recipient in the envelope by issuer name and serial
number. Pass NULL to try the private key against every
recipient.
@param[in] RecipientCertSize Size of the DER-encoded certificate in bytes. Ignored
(and may be 0) when RecipientCert is NULL.
@param[in] ContentInfo Pointer to the PKCS#7 DER-encoded ContentInfo that wraps
an envelopedData to be decrypted.
@param[in] ContentInfoSize Size of the ContentInfo in bytes.
@param[in] Flags Flags for the decryption operation. Currently only
CRYPTO_PKCS7_DEFAULT is supported, which indicates that
the decrypted content is treated as binary data.
@param[out] OutData Receives a pointer to the newly allocated buffer
containing the decrypted content. The caller must free
the returned buffer with FreePool().
@param[out] OutDataSize Receives the size of the decrypted content in bytes.
@retval TRUE PKCS#7 data decryption succeeded.
@retval FALSE PKCS#7 data decryption failed.
@retval FALSE This interface is not supported.
**/
BOOLEAN
EFIAPI
Pkcs7Decrypt (
IN CONST UINT8 *PemData,
IN UINTN PemSize,
IN CONST CHAR8 *Password OPTIONAL,
IN CONST UINT8 *RecipientCert OPTIONAL,
IN UINTN RecipientCertSize OPTIONAL,
IN CONST UINT8 *ContentInfo,
IN UINTN ContentInfoSize,
IN UINT32 Flags,
OUT UINT8 **OutData,
OUT UINTN *OutDataSize
)
{
BOOLEAN Succeeded;
UINT8 *ReturnData;
UINTN ReturnSize;
VOID *Rsa;
BIO *OutBio;
EVP_PKEY *Key;
X509 *Cert;
PKCS7 *Pkcs7;
CONST UINT8 *DerCursor;
long OutLength;
char *OutBuffer;
ReturnData = NULL;
ReturnSize = 0;
Rsa = NULL;
OutBio = NULL;
Key = NULL;
Cert = NULL;
Pkcs7 = NULL;
if ((PemData == NULL) ||
(PemSize == 0) ||
(PemSize > INT_MAX) ||
(ContentInfo == NULL) ||
(ContentInfoSize == 0) ||
(ContentInfoSize > INT_MAX) ||
(Flags != CRYPTO_PKCS7_DEFAULT) ||
(OutData == NULL) ||
(OutDataSize == NULL))
{
Succeeded = FALSE; // Invalid argument.
goto Done;
}
if ((RecipientCert != NULL) &&
((RecipientCertSize == 0) || (RecipientCertSize > INT_MAX)))
{
Succeeded = FALSE; // Invalid certificate argument.
goto Done;
}
//
// Retrieve the recipient's private key from the PEM data and wrap it in an
// EVP_PKEY for PKCS7_decrypt.
//
if (!RsaGetPrivateKeyFromPem (PemData, PemSize, Password, &Rsa)) {
Succeeded = FALSE; // Invalid PEM key data or incorrect password.
goto Done;
}
Key = EVP_PKEY_new ();
if (Key == NULL) {
Succeeded = FALSE; // Memory allocation failed.
goto Done;
}
if (EVP_PKEY_assign_RSA (Key, (RSA *)Rsa) == 0) {
Succeeded = FALSE; // Key conversion failed.
goto Done;
}
//
// Key now owns Rsa and frees it via EVP_PKEY_free.
//
Rsa = NULL;
//
// Optionally parse the recipient certificate. When supplied, OpenSSL uses its
// issuer name and serial number to select the matching recipient in the envelope.
//
if (RecipientCert != NULL) {
DerCursor = RecipientCert;
Cert = d2i_X509 (NULL, &DerCursor, (long)RecipientCertSize);
if (Cert == NULL) {
Succeeded = FALSE; // Invalid certificate.
goto Done;
}
}
//
// Parse the DER-encoded PKCS#7 ContentInfo.
//
DerCursor = ContentInfo;
Pkcs7 = d2i_PKCS7 (NULL, &DerCursor, (long)ContentInfoSize);
if (Pkcs7 == NULL) {
Succeeded = FALSE; // Invalid PKCS#7 data.
goto Done;
}
if (!PKCS7_type_is_enveloped (Pkcs7)) {
Succeeded = FALSE; // Not an envelopedData structure.
goto Done;
}
//
// Decrypt into a memory BIO.
//
OutBio = BIO_new (BIO_s_mem ());
if (OutBio == NULL) {
Succeeded = FALSE; // BIO creation failed.
goto Done;
}
if (PKCS7_decrypt (Pkcs7, Key, Cert, OutBio, PKCS7_BINARY) != 1) {
Succeeded = FALSE; // Decryption failed (wrong key, no matching recipient, etc.).
goto Done;
}
OutLength = BIO_get_mem_data (OutBio, &OutBuffer);
if ((OutLength < 0) || ((UINTN)OutLength > INT_MAX)) {
Succeeded = FALSE; // Unexpected decrypted length.
goto Done;
}
ReturnSize = (UINTN)OutLength;
//
// Always allocate at least one byte so that a successful decryption of empty
// content still yields a non-NULL, freeable buffer.
//
ReturnData = (UINT8 *)AllocateZeroPool (ReturnSize == 0 ? 1 : ReturnSize);
if (ReturnData == NULL) {
ReturnSize = 0;
Succeeded = FALSE; // Memory allocation failed.
goto Done;
}
if (ReturnSize != 0) {
CopyMem (ReturnData, OutBuffer, ReturnSize);
}
Succeeded = TRUE; // Success, result in ReturnData/ReturnSize.
Done:
if (Pkcs7 != NULL) {
PKCS7_free (Pkcs7);
Pkcs7 = NULL;
}
if (Cert != NULL) {
X509_free (Cert);
Cert = NULL;
}
if (Key != NULL) {
EVP_PKEY_free (Key);
Key = NULL;
}
if (Rsa != NULL) {
RsaFree (Rsa);
Rsa = NULL;
}
if (OutBio != NULL) {
BIO_free (OutBio);
OutBio = NULL;
}
if (OutData != NULL) {
*OutData = ReturnData;
}
if (OutDataSize != NULL) {
*OutDataSize = ReturnSize;
}
return Succeeded;
}
|