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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
|
/** @file
PEM (Privacy Enhanced Mail) Format Handler Wrapper Implementation over OpenSSL.
Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "InternalCryptLib.h"
#include "KeyContext.h"
#include <openssl/pem.h>
/**
Callback function for password phrase conversion used for retrieving the encrypted PEM.
@param[out] Buf Pointer to the buffer to write the passphrase to.
@param[in] Size Maximum length of the passphrase (i.e. the size of Buf).
@param[in] Flag A flag which is set to 0 when reading and 1 when writing.
@param[in] Key Key data to be passed to the callback routine.
@retval The number of characters in the passphrase or 0 if an error occurred.
**/
INTN
PasswordCallback (
OUT CHAR8 *Buf,
IN INTN Size,
IN INTN Flag,
IN VOID *Key
)
{
INTN KeyLength;
ZeroMem ((VOID *)Buf, (UINTN)Size);
if (Key != NULL) {
//
// Duplicate key phrase directly.
//
KeyLength = (INTN)AsciiStrLen ((CHAR8 *)Key);
KeyLength = (KeyLength > Size) ? Size : KeyLength;
CopyMem (Buf, Key, (UINTN)KeyLength);
return KeyLength;
} else {
return 0;
}
}
/**
Retrieve a private key from PEM-encoded data using OpenSSL BIO.
This helper function creates a memory BIO, writes the PEM data to it, and reads
the private key using OpenSSL's PEM_read_bio_PrivateKey function. It supports
password-protected PEM data.
@param[in] PemData Pointer to the PEM-encoded key data.
@param[in] PemSize Size of the PEM key data in bytes.
@param[in] Password NULL-terminated passphrase used for encrypted PEM key data.
@param[out] Pkey Pointer to receive the EVP_PKEY structure containing the private key.
@retval TRUE Private key was retrieved successfully.
@retval FALSE Failed to create BIO, write data, or read private key.
**/
STATIC
BOOLEAN
GetPrivateKeyFromPem (
IN CONST UINT8 *PemData,
IN UINTN PemSize,
IN CONST CHAR8 *Password,
OUT EVP_PKEY **Pkey
)
{
BIO *PemBio;
BOOLEAN Result;
// Create a memory BIO and write PEM data to it
PemBio = BIO_new (BIO_s_mem ());
if (PemBio == NULL) {
return FALSE;
}
if (BIO_write (PemBio, PemData, (int)PemSize) <= 0) {
BIO_free (PemBio);
return FALSE;
}
Result = FALSE;
// Read Private Key from encrypted PEM data
*Pkey = PEM_read_bio_PrivateKey (PemBio, NULL, (pem_password_cb *)&PasswordCallback, (void *)Password);
if (*Pkey != NULL) {
Result = TRUE;
}
// Always free the BIO before returning
BIO_free (PemBio);
return Result;
}
/**
Allocate and initialize a KEY_CONTEXT structure wrapping an EVP_PKEY.
This helper function allocates a KEY_CONTEXT structure and wraps the provided
EVP_PKEY pointer within it.
@param[in] Pkey Pointer to an EVP_PKEY structure to be wrapped.
@param[in] Nid The NID representing the type of key (e.g., EVP_PKEY_ED448).
@param[out] Context Pointer to receive the allocated KEY_CONTEXT structure.
@retval TRUE KEY_CONTEXT was allocated and initialized successfully.
@retval FALSE Memory allocation failed.
**/
STATIC
BOOLEAN
AllocateKeyContext (
IN EVP_PKEY *Pkey,
IN INT32 Nid,
OUT VOID **Context
)
{
KEY_CONTEXT *Ctx;
Ctx = (KEY_CONTEXT *)AllocateZeroPool (sizeof (KEY_CONTEXT));
if (Ctx == NULL) {
return FALSE;
}
Ctx->EvpPkey = Pkey;
Ctx->Nid = Nid;
*Context = (VOID *)Ctx;
return TRUE;
}
/**
Convert an ML-DSA type name string to an OpenSSL NID.
This helper function translates ML-DSA type name strings (e.g., "ML-DSA-87")
to their corresponding OpenSSL EVP_PKEY NIDs (e.g., EVP_PKEY_ML_DSA_87).
If the type name is not recognized, EVP_PKEY_NONE is returned.
@param[in] TypeName ML-DSA type name string (e.g., "ML-DSA-87").
@retval OpenSSL NID (e.g., EVP_PKEY_ML_DSA_87) if recognized.
@retval EVP_PKEY_NONE if the type name is not recognized.
**/
STATIC
INT32
MlDsaTypeNameToNid (
IN CONST CHAR8 *TypeName
)
{
INT32 Nid;
if (AsciiStrCmp (TypeName, "ML-DSA-87") == 0) {
Nid = EVP_PKEY_ML_DSA_87;
} else {
Nid = EVP_PKEY_NONE;
}
return Nid;
}
/**
Check if the given NID is supported for ML-DSA.
This helper function checks if the provided NID corresponds to a supported
ML-DSA type. Currently, only EVP_PKEY_ML_DSA_87 is supported.
@param[in] Nid The NID to check.
@retval TRUE The NID is supported for ML-DSA.
@retval FALSE The NID is not supported for ML-DSA.
**/
STATIC
BOOLEAN
IsMlDsaNidSupported (
IN INT32 Nid
)
{
switch (Nid) {
case EVP_PKEY_ML_DSA_87:
return TRUE;
default:
return FALSE;
}
}
/**
Convert an SLH-DSA type name string to an OpenSSL NID.
This helper function translates SLH-DSA type name strings (e.g., "SLH-DSA-SHAKE-256s")
to their corresponding OpenSSL EVP_PKEY NIDs (e.g., EVP_PKEY_SLH_DSA_SHAKE_256S).
If the type name is not recognized, EVP_PKEY_NONE is returned.
@param[in] TypeName SLH-DSA type name string (e.g., "SLH-DSA-SHAKE-256s").
@retval OpenSSL NID (e.g., EVP_PKEY_SLH_DSA_SHAKE_256S) if recognized.
@retval EVP_PKEY_NONE if the type name is not recognized.
**/
STATIC
INT32
SlhDsaTypeNameToNid (
IN CONST CHAR8 *TypeName
)
{
INT32 Nid;
if (AsciiStrCmp (TypeName, "SLH-DSA-SHAKE-256s") == 0) {
Nid = EVP_PKEY_SLH_DSA_SHAKE_256S;
} else {
Nid = EVP_PKEY_NONE;
}
return Nid;
}
/**
Check if the given NID is supported for SLH-DSA.
This helper function checks if the provided NID corresponds to a supported
SLH-DSA type. Currently, only EVP_PKEY_SLH_DSA_SHAKE_256S is supported.
@param[in] Nid The NID to check.
@retval TRUE The NID is supported for SLH-DSA.
@retval FALSE The NID is not supported for SLH-DSA.
**/
STATIC
BOOLEAN
IsSlhDsaNidSupported (
IN INT32 Nid
)
{
switch (Nid) {
case EVP_PKEY_SLH_DSA_SHAKE_256S:
return TRUE;
default:
return FALSE;
}
}
/**
Retrieve the RSA Private Key from the password-protected PEM key data.
@param[in] PemData Pointer to the PEM-encoded key data to be retrieved.
@param[in] PemSize Size of the PEM key data in bytes.
@param[in] Password NULL-terminated passphrase used for encrypted PEM key data.
@param[out] RsaContext Pointer to new-generated RSA context which contain the retrieved
RSA private key component. Use RsaFree() function to free the
resource.
If PemData is NULL, then return FALSE.
If RsaContext is NULL, then return FALSE.
@retval TRUE RSA Private Key was retrieved successfully.
@retval FALSE Invalid PEM key data or incorrect password.
**/
BOOLEAN
EFIAPI
RsaGetPrivateKeyFromPem (
IN CONST UINT8 *PemData,
IN UINTN PemSize,
IN CONST CHAR8 *Password,
OUT VOID **RsaContext
)
{
BOOLEAN Status;
BIO *PemBio;
//
// Check input parameters.
//
if ((PemData == NULL) || (RsaContext == NULL) || (PemSize > INT_MAX)) {
return FALSE;
}
//
// Add possible block-cipher descriptor for PEM data decryption.
// NOTE: Only support most popular ciphers AES for the encrypted PEM.
//
if (EVP_add_cipher (EVP_aes_128_cbc ()) == 0) {
return FALSE;
}
if (EVP_add_cipher (EVP_aes_192_cbc ()) == 0) {
return FALSE;
}
if (EVP_add_cipher (EVP_aes_256_cbc ()) == 0) {
return FALSE;
}
Status = FALSE;
//
// Read encrypted PEM Data.
//
PemBio = BIO_new (BIO_s_mem ());
if (PemBio == NULL) {
goto _Exit;
}
if (BIO_write (PemBio, PemData, (int)PemSize) <= 0) {
goto _Exit;
}
//
// Retrieve RSA Private Key from encrypted PEM data.
//
*RsaContext = PEM_read_bio_RSAPrivateKey (PemBio, NULL, (pem_password_cb *)&PasswordCallback, (void *)Password);
if (*RsaContext != NULL) {
Status = TRUE;
}
_Exit:
//
// Release Resources.
//
BIO_free (PemBio);
return Status;
}
/**
Retrieve the EC Private Key from the password-protected PEM key data.
@param[in] PemData Pointer to the PEM-encoded key data to be retrieved.
@param[in] PemSize Size of the PEM key data in bytes.
@param[in] Password NULL-terminated passphrase used for encrypted PEM key data.
@param[out] EcContext Pointer to new-generated EC DSA context which contain the retrieved
EC private key component. Use EcFree() function to free the
resource.
If PemData is NULL, then return FALSE.
If EcContext is NULL, then return FALSE.
@retval TRUE EC Private Key was retrieved successfully.
@retval FALSE Invalid PEM key data or incorrect password.
**/
BOOLEAN
EFIAPI
EcGetPrivateKeyFromPem (
IN CONST UINT8 *PemData,
IN UINTN PemSize,
IN CONST CHAR8 *Password,
OUT VOID **EcContext
)
{
BOOLEAN Status;
BIO *PemBio;
//
// Check input parameters.
//
if ((PemData == NULL) || (EcContext == NULL) || (PemSize > INT_MAX)) {
return FALSE;
}
//
// Add possible block-cipher descriptor for PEM data decryption.
// NOTE: Only support most popular ciphers AES for the encrypted PEM.
//
if (EVP_add_cipher (EVP_aes_128_cbc ()) == 0) {
return FALSE;
}
if (EVP_add_cipher (EVP_aes_192_cbc ()) == 0) {
return FALSE;
}
if (EVP_add_cipher (EVP_aes_256_cbc ()) == 0) {
return FALSE;
}
Status = FALSE;
//
// Read encrypted PEM Data.
//
PemBio = BIO_new (BIO_s_mem ());
if (PemBio == NULL) {
goto _Exit;
}
if (BIO_write (PemBio, PemData, (int)PemSize) <= 0) {
goto _Exit;
}
//
// Retrieve EC Private Key from encrypted PEM data.
//
*EcContext = PEM_read_bio_ECPrivateKey (PemBio, NULL, (pem_password_cb *)&PasswordCallback, (void *)Password);
if (*EcContext != NULL) {
Status = TRUE;
}
_Exit:
//
// Release Resources.
//
BIO_free (PemBio);
return Status;
}
/**
Retrieve the EdDSA Private Key from the password-protected PEM key data.
@param[in] PemData Pointer to the PEM-encoded key data to be retrieved.
@param[in] PemSize Size of the PEM key data in bytes.
@param[in] Password NULL-terminated passphrase used for encrypted PEM key data.
@param[out] EdDsaContext Pointer to new-generated EdDSA context which contains the retrieved
EdDSA private key component. Use EdDsaFree() function to free the
resource.
If PemData is NULL, then return FALSE.
If EdDsaContext is NULL, then return FALSE.
@retval TRUE EdDSA Private Key was retrieved successfully.
@retval FALSE Invalid PEM key data or incorrect password.
**/
BOOLEAN
EFIAPI
EdDsaGetPrivateKeyFromPem (
IN CONST UINT8 *PemData,
IN UINTN PemSize,
IN CONST CHAR8 *Password,
OUT VOID **EdDsaContext
)
{
EVP_PKEY *Pkey;
INT32 Nid;
// Check input parameters
if ((PemData == NULL) || (EdDsaContext == NULL) || (PemSize > INT_MAX)) {
return FALSE;
}
// Read PEM data
if (!GetPrivateKeyFromPem (PemData, PemSize, Password, &Pkey)) {
return FALSE;
}
Nid = EVP_PKEY_id (Pkey);
if (Nid != EVP_PKEY_ED448) {
EVP_PKEY_free (Pkey);
return FALSE;
}
// Allocate wrapper structure (now consistent with other key types)
if (!AllocateKeyContext (Pkey, Nid, EdDsaContext)) {
EVP_PKEY_free (Pkey);
return FALSE;
}
return TRUE;
}
/**
Retrieve the ML-DSA Private Key from the password-protected PEM key data.
If PemData is NULL, then return FALSE.
If MlDsaContext is NULL, then return FALSE.
@param[in] PemData Pointer to the PEM-encoded key data to be retrieved.
@param[in] PemSize Size of the PEM key data in bytes.
@param[in] Password NULL-terminated passphrase used for encrypted PEM key data.
@param[out] MlDsaContext Pointer to new-generated ML-DSA context which contains
the retrieved ML-DSA private key. Use MlDsaFree() to free.
@retval TRUE ML-DSA Private Key was retrieved successfully.
@retval FALSE Invalid PEM key data or incorrect password.
**/
BOOLEAN
EFIAPI
MlDsaGetPrivateKeyFromPem (
IN CONST UINT8 *PemData,
IN UINTN PemSize,
IN CONST CHAR8 *Password,
OUT VOID **MlDsaContext
)
{
EVP_PKEY *Pkey;
INT32 Nid;
//
// Check input parameters.
//
if ((PemData == NULL) || (MlDsaContext == NULL) || (PemSize > INT_MAX)) {
return FALSE;
}
// Read PEM data
if (!GetPrivateKeyFromPem (PemData, PemSize, Password, &Pkey)) {
return FALSE;
}
Nid = MlDsaTypeNameToNid (EVP_PKEY_get0_type_name (Pkey));
if (!IsMlDsaNidSupported (Nid)) {
EVP_PKEY_free (Pkey);
return FALSE;
}
// Allocate wrapper structure (now consistent with other key types)
if (!AllocateKeyContext (Pkey, Nid, MlDsaContext)) {
EVP_PKEY_free (Pkey);
return FALSE;
}
return TRUE;
}
/**
Retrieve the SLH-DSA Private Key from the password-protected PEM key data.
If PemData is NULL, then return FALSE.
If SlhDsaContext is NULL, then return FALSE.
@param[in] PemData Pointer to the PEM-encoded key data to be retrieved.
@param[in] PemSize Size of the PEM key data in bytes.
@param[in] Password NULL-terminated passphrase used for encrypted PEM key data.
@param[out] SlhDsaContext Pointer to new-generated SLH-DSA context which contains
the retrieved SLH-DSA private key. Use SlhDsaFree() to free.
@retval TRUE SLH-DSA Private Key was retrieved successfully.
@retval FALSE Invalid PEM key data or incorrect password.
**/
BOOLEAN
EFIAPI
SlhDsaGetPrivateKeyFromPem (
IN CONST UINT8 *PemData,
IN UINTN PemSize,
IN CONST CHAR8 *Password,
OUT VOID **SlhDsaContext
)
{
EVP_PKEY *Pkey;
INT32 Nid;
//
// Check input parameters.
//
if ((PemData == NULL) || (SlhDsaContext == NULL) || (PemSize > INT_MAX)) {
return FALSE;
}
// Read PEM data
if (!GetPrivateKeyFromPem (PemData, PemSize, Password, &Pkey)) {
return FALSE;
}
Nid = SlhDsaTypeNameToNid (EVP_PKEY_get0_type_name (Pkey));
if (!IsSlhDsaNidSupported (Nid)) {
EVP_PKEY_free (Pkey);
return FALSE;
}
// Allocate wrapper structure (now consistent with other key types)
if (!AllocateKeyContext (Pkey, Nid, SlhDsaContext)) {
EVP_PKEY_free (Pkey);
return FALSE;
}
return TRUE;
}
|