• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zhaozg / lua-openssl / 25776463823

13 May 2026 03:28AM UTC coverage: 91.231% (-2.6%) from 93.832%
25776463823

Pull #408

travis-ci

zhaozg
feat(pqc): Phase 2.4 - Provider Management for PQC

Add PQC provider management capabilities to the provider module:

- Add `provider.query_pqc_algorithms()` to probe and list available PQC
  algorithms by attempting key generation for known PQC algorithm names
- Add `provider.load_pqc_providers()` to auto-detect and load common
  PQC providers (oqsprovider, liboqs, oqs, oqs-provider)
- Auto-load common PQC providers on module initialization (best-effort)
- Support both old OQS names (DILITHIUM2, KYBER768, etc.) and
  standardized NIST names (ML-DSA-44, ML-KEM-768, SLH-DSA-SHA2-*, etc.)
- Add comprehensive LDoc documentation for all new functions
- Add test suite covering query, load, and combined scenarios

This completes Phase 2.4 of the PQC implementation roadmap.
Pull Request #408: Feat/pqc

913 of 1124 new or added lines in 10 files covered. (81.23%)

45 existing lines in 10 files now uncovered.

9519 of 10434 relevant lines covered (91.23%)

1598.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.05
/src/pkey/core.c
1
/***
2
 * pkey core module
3
 * Core key management, type mapping, and module registration
4
 *
5
 * @module pkey
6
 */
7
#include "pkey.h"
8

9
/* Suppress deprecation warnings for low-level key APIs in OpenSSL 3.0+ */
10
#ifndef OSSL_NELEM
11
#define OSSL_NELEM(ary) (sizeof(ary) / sizeof(ary[0]))
12
#endif
13

14
#if defined(__GNUC__) || defined(__clang__)
15
#pragma GCC diagnostic push
16
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
17
#endif
18

19
/* ========================================================================
20
 * Type mapping: standard_name2type and helpers
21
 * ======================================================================== */
22

23
static const OSSL_ITEM standard_name2type[] = {
24
#ifdef EVP_PKEY_RSA
25
  { EVP_PKEY_RSA,     "RSA"      },
26
#endif
27
#ifdef EVP_PKEY_RSA_PSS
28
  { EVP_PKEY_RSA_PSS, "RSA-PSS"  },
29
#endif
30
#ifdef EVP_PKEY_EC
31
  { EVP_PKEY_EC,      "EC"       },
32
#endif
33
#ifdef EVP_PKEY_ED25519
34
  { EVP_PKEY_ED25519, "ED25519"  },
35
#endif
36
#ifdef EVP_PKEY_ED448
37
  { EVP_PKEY_ED448,   "ED448"    },
38
#endif
39
#ifdef EVP_PKEY_X25519
40
  { EVP_PKEY_X25519,  "X25519"   },
41
#endif
42
#ifdef EVP_PKEY_X448
43
  { EVP_PKEY_X448,    "X448"     },
44
#endif
45
#ifdef EVP_PKEY_SM2
46
  { EVP_PKEY_SM2,     "SM2"      },
47
#endif
48
#ifdef EVP_PKEY_DH
49
  { EVP_PKEY_DH,      "DH"       },
50
#endif
51
#ifdef EVP_PKEY_DHX
52
  { EVP_PKEY_DHX,     "X9.42 DH" },
53
#endif
54
#ifdef EVP_PKEY_DHX
55
  { EVP_PKEY_DHX,     "DHX"      },
56
#endif
57
#ifdef EVP_PKEY_DSA
58
  { EVP_PKEY_DSA,     "DSA"      },
59
#endif
60

61
/* PQC (Post-Quantum Cryptography) algorithm type mappings.
62
 * These are conditionally compiled based on OpenSSL provider support.
63
 * When an OQS provider or similar is loaded, these NIDs become available.
64
 *
65
 * We support both the old OQS names (DILITHIUM, KYBER, SPHINCS) and the
66
 * standardized NIST names (ML-DSA, ML-KEM, SLH-DSA) via compatibility
67
 * macros defined in private.h. */
68

69
/* ML-DSA (FIPS 204) - Dilithium */
70
#ifdef EVP_PKEY_DILITHIUM
71
  { EVP_PKEY_DILITHIUM, "DILITHIUM"   },
72
  { EVP_PKEY_DILITHIUM, "ML-DSA"      },
73
#endif
74
#ifdef EVP_PKEY_DILITHIUM2
75
  { EVP_PKEY_DILITHIUM2, "DILITHIUM2"  },
76
  { EVP_PKEY_DILITHIUM2, "ML-DSA-44"   },
77
#endif
78
#ifdef EVP_PKEY_DILITHIUM3
79
  { EVP_PKEY_DILITHIUM3, "DILITHIUM3"  },
80
  { EVP_PKEY_DILITHIUM3, "ML-DSA-65"   },
81
#endif
82
#ifdef EVP_PKEY_DILITHIUM5
83
  { EVP_PKEY_DILITHIUM5, "DILITHIUM5"  },
84
  { EVP_PKEY_DILITHIUM5, "ML-DSA-87"   },
85
#endif
86

87
/* ML-KEM (FIPS 203) - Kyber */
88
#ifdef EVP_PKEY_KYBER
89
  { EVP_PKEY_KYBER,     "KYBER"       },
90
  { EVP_PKEY_KYBER,     "ML-KEM"      },
91
#endif
92
#ifdef EVP_PKEY_KYBER512
93
  { EVP_PKEY_KYBER512,  "KYBER512"    },
94
  { EVP_PKEY_KYBER512,  "ML-KEM-512"  },
95
#endif
96
#ifdef EVP_PKEY_KYBER768
97
  { EVP_PKEY_KYBER768,  "KYBER768"    },
98
  { EVP_PKEY_KYBER768,  "ML-KEM-768"  },
99
#endif
100
#ifdef EVP_PKEY_KYBER1024
101
  { EVP_PKEY_KYBER1024, "KYBER1024"   },
102
  { EVP_PKEY_KYBER1024, "ML-KEM-1024" },
103
#endif
104

105
/* Falcon */
106
#ifdef EVP_PKEY_FALCON
107
  { EVP_PKEY_FALCON,    "FALCON"      },
108
#endif
109
#ifdef EVP_PKEY_FALCON512
110
  { EVP_PKEY_FALCON512, "FALCON512"   },
111
#endif
112
#ifdef EVP_PKEY_FALCON1024
113
  { EVP_PKEY_FALCON1024,"FALCON1024"  },
114
#endif
115

116
/* SLH-DSA (FIPS 205) - SPHINCS+ */
117
#ifdef EVP_PKEY_SPHINCS
118
  { EVP_PKEY_SPHINCS,   "SPHINCS"     },
119
  { EVP_PKEY_SPHINCS,   "SLH-DSA"     },
120
#endif
121
#ifdef EVP_PKEY_SPHINCSSHA256
122
  { EVP_PKEY_SPHINCSSHA256, "SPHINCS-SHA256" },
123
#endif
124
#ifdef EVP_PKEY_SPHINCSSHAKE256
125
  { EVP_PKEY_SPHINCSSHAKE256, "SPHINCS-SHAKE256" },
126
#endif
127

128
/* SLH-DSA standardized NIST names (OpenSSL 3.5+) */
129
#ifdef EVP_PKEY_SLH_DSA_SHA2_128S
130
  { EVP_PKEY_SLH_DSA_SHA2_128S, "SLH-DSA-SHA2-128S" },
131
#endif
132
#ifdef EVP_PKEY_SLH_DSA_SHA2_128F
133
  { EVP_PKEY_SLH_DSA_SHA2_128F, "SLH-DSA-SHA2-128F" },
134
#endif
135
#ifdef EVP_PKEY_SLH_DSA_SHA2_192S
136
  { EVP_PKEY_SLH_DSA_SHA2_192S, "SLH-DSA-SHA2-192S" },
137
#endif
138
#ifdef EVP_PKEY_SLH_DSA_SHA2_192F
139
  { EVP_PKEY_SLH_DSA_SHA2_192F, "SLH-DSA-SHA2-192F" },
140
#endif
141
#ifdef EVP_PKEY_SLH_DSA_SHA2_256S
142
  { EVP_PKEY_SLH_DSA_SHA2_256S, "SLH-DSA-SHA2-256S" },
143
#endif
144
#ifdef EVP_PKEY_SLH_DSA_SHA2_256F
145
  { EVP_PKEY_SLH_DSA_SHA2_256F, "SLH-DSA-SHA2-256F" },
146
#endif
147
#ifdef EVP_PKEY_SLH_DSA_SHAKE_128S
148
  { EVP_PKEY_SLH_DSA_SHAKE_128S, "SLH-DSA-SHAKE-128S" },
149
#endif
150
#ifdef EVP_PKEY_SLH_DSA_SHAKE_128F
151
  { EVP_PKEY_SLH_DSA_SHAKE_128F, "SLH-DSA-SHAKE-128F" },
152
#endif
153
#ifdef EVP_PKEY_SLH_DSA_SHAKE_192S
154
  { EVP_PKEY_SLH_DSA_SHAKE_192S, "SLH-DSA-SHAKE-192S" },
155
#endif
156
#ifdef EVP_PKEY_SLH_DSA_SHAKE_192F
157
  { EVP_PKEY_SLH_DSA_SHAKE_192F, "SLH-DSA-SHAKE-192F" },
158
#endif
159
#ifdef EVP_PKEY_SLH_DSA_SHAKE_256S
160
  { EVP_PKEY_SLH_DSA_SHAKE_256S, "SLH-DSA-SHAKE-256S" },
161
#endif
162
#ifdef EVP_PKEY_SLH_DSA_SHAKE_256F
163
  { EVP_PKEY_SLH_DSA_SHAKE_256F, "SLH-DSA-SHAKE-256F" },
164
#endif
165

166
/* ML-DSA standardized NIST names (OpenSSL 3.5+ built-in) */
167
#ifdef EVP_PKEY_ML_DSA_44
168
  { EVP_PKEY_ML_DSA_44, "ML-DSA-44" },
169
#endif
170
#ifdef EVP_PKEY_ML_DSA_65
171
  { EVP_PKEY_ML_DSA_65, "ML-DSA-65" },
172
#endif
173
#ifdef EVP_PKEY_ML_DSA_87
174
  { EVP_PKEY_ML_DSA_87, "ML-DSA-87" },
175
#endif
176

177
/* ML-KEM standardized NIST names (OpenSSL 3.5+ built-in) */
178
#ifdef EVP_PKEY_ML_KEM_512
179
  { EVP_PKEY_ML_KEM_512, "ML-KEM-512" },
180
#endif
181
#ifdef EVP_PKEY_ML_KEM_768
182
  { EVP_PKEY_ML_KEM_768, "ML-KEM-768" },
183
#endif
184
#ifdef EVP_PKEY_ML_KEM_1024
185
  { EVP_PKEY_ML_KEM_1024, "ML-KEM-1024" },
186
#endif
187
};
188

189
#if (OPENSSL_VERSION_NUMBER < 0x30000000L || defined(LIBRESSL_VERSION_NUMBER))
190
int
191
evp_pkey_name2type(const char *name)
48✔
192
{
193
  size_t i;
194

195
  for (i = 0; i < OSSL_NELEM(standard_name2type); i++) {
369✔
196
    if (strcasecmp(name, standard_name2type[i].ptr) == 0) return (int)standard_name2type[i].id;
339✔
197
  }
198

199
  return -1;
30✔
200
}
201

202
const char *
203
evp_pkey_type2name(int type)
42✔
204
{
205
  size_t      i;
206
  const char *ret = NULL;
42✔
207

208
  for (i = 0; i < OSSL_NELEM(standard_name2type); i++) {
175✔
209
    if (type == (int)standard_name2type[i].id) {
175✔
210
      ret = standard_name2type[i].ptr;
42✔
211
      break;
42✔
212
    }
213
  }
214

215
  return ret;
42✔
216
}
217
#endif
218

219
/* ========================================================================
220
 * PQC signature algorithm NID table
221
 *
222
 * This table lists all known PQC signature algorithm NIDs that use
223
 * internal hashing (no external digest needed). It is used by
224
 * evp_pkey_needs_null_digest() to detect PQC keys.
225
 *
226
 * The table is conditionally compiled: each entry is guarded by
227
 * #ifdef for the corresponding EVP_PKEY_* macro, so it automatically
228
 * adapts to the available PQC algorithms in the current OpenSSL build.
229
 *
230
 * Both old OQS provider names and standardized NIST names are included.
231
 * ======================================================================== */
232

233
/* Helper macro: add a PQC signature NID to the table if available */
234
#define PQC_SIG_NID(nid) { nid, #nid }
235

236
static const struct {
237
  int   nid;
238
  const char *name;
239
} pqc_sig_nids[] = {
240
  /* Old OQS provider names */
241
#ifdef EVP_PKEY_DILITHIUM
242
  PQC_SIG_NID(EVP_PKEY_DILITHIUM),
243
#endif
244
#ifdef EVP_PKEY_DILITHIUM2
245
  PQC_SIG_NID(EVP_PKEY_DILITHIUM2),
246
#endif
247
#ifdef EVP_PKEY_DILITHIUM3
248
  PQC_SIG_NID(EVP_PKEY_DILITHIUM3),
249
#endif
250
#ifdef EVP_PKEY_DILITHIUM5
251
  PQC_SIG_NID(EVP_PKEY_DILITHIUM5),
252
#endif
253
#ifdef EVP_PKEY_FALCON
254
  PQC_SIG_NID(EVP_PKEY_FALCON),
255
#endif
256
#ifdef EVP_PKEY_FALCON512
257
  PQC_SIG_NID(EVP_PKEY_FALCON512),
258
#endif
259
#ifdef EVP_PKEY_FALCON1024
260
  PQC_SIG_NID(EVP_PKEY_FALCON1024),
261
#endif
262
#ifdef EVP_PKEY_SPHINCS
263
  PQC_SIG_NID(EVP_PKEY_SPHINCS),
264
#endif
265
#ifdef EVP_PKEY_SPHINCSSHA256
266
  PQC_SIG_NID(EVP_PKEY_SPHINCSSHA256),
267
#endif
268
#ifdef EVP_PKEY_SPHINCSSHAKE256
269
  PQC_SIG_NID(EVP_PKEY_SPHINCSSHAKE256),
270
#endif
271

272
  /* Standardized NIST names (OpenSSL 3.5+) */
273
#ifdef EVP_PKEY_ML_DSA_44
274
  PQC_SIG_NID(EVP_PKEY_ML_DSA_44),
275
#endif
276
#ifdef EVP_PKEY_ML_DSA_65
277
  PQC_SIG_NID(EVP_PKEY_ML_DSA_65),
278
#endif
279
#ifdef EVP_PKEY_ML_DSA_87
280
  PQC_SIG_NID(EVP_PKEY_ML_DSA_87),
281
#endif
282
#ifdef EVP_PKEY_SLH_DSA_SHA2_128S
283
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHA2_128S),
284
#endif
285
#ifdef EVP_PKEY_SLH_DSA_SHA2_128F
286
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHA2_128F),
287
#endif
288
#ifdef EVP_PKEY_SLH_DSA_SHA2_192S
289
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHA2_192S),
290
#endif
291
#ifdef EVP_PKEY_SLH_DSA_SHA2_192F
292
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHA2_192F),
293
#endif
294
#ifdef EVP_PKEY_SLH_DSA_SHA2_256S
295
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHA2_256S),
296
#endif
297
#ifdef EVP_PKEY_SLH_DSA_SHA2_256F
298
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHA2_256F),
299
#endif
300
#ifdef EVP_PKEY_SLH_DSA_SHAKE_128S
301
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHAKE_128S),
302
#endif
303
#ifdef EVP_PKEY_SLH_DSA_SHAKE_128F
304
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHAKE_128F),
305
#endif
306
#ifdef EVP_PKEY_SLH_DSA_SHAKE_192S
307
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHAKE_192S),
308
#endif
309
#ifdef EVP_PKEY_SLH_DSA_SHAKE_192F
310
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHAKE_192F),
311
#endif
312
#ifdef EVP_PKEY_SLH_DSA_SHAKE_256S
313
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHAKE_256S),
314
#endif
315
#ifdef EVP_PKEY_SLH_DSA_SHAKE_256F
316
  PQC_SIG_NID(EVP_PKEY_SLH_DSA_SHAKE_256F),
317
#endif
318
};
319

320
#undef PQC_SIG_NID
321

322
/* ========================================================================
323
 * evp_pkey_is_pqc_sig - Check if a key is a PQC signature algorithm
324
 *
325
 * Returns 1 if the key is a PQC signature algorithm (ML-DSA, Falcon,
326
 * SLH-DSA) that uses internal hashing. Returns 0 otherwise.
327
 *
328
 * Uses the pqc_sig_nids[] table for O(1) lookup by NID.
329
 * For OpenSSL 3.0+, also tries EVP_PKEY_is_a() for keymgmt-based keys
330
 * where EVP_PKEY_id() may return -1.
331
 *
332
 * @tparam evp_pkey pkey the key to check
333
 * @treturn boolean 1 if PQC signature algorithm, 0 otherwise
334
 * ======================================================================== */
335
int
336
evp_pkey_is_pqc_sig(EVP_PKEY *pkey)
28✔
337
{
338
  int pkey_id = EVP_PKEY_id(pkey);
28✔
339
  size_t i;
340

341
  /* Check against the PQC signature NID table */
342
  for (i = 0; i < OSSL_NELEM(pqc_sig_nids); i++) {
28✔
343
    if (pkey_id == pqc_sig_nids[i].nid)
344
      return 1;
345
  }
346

347
  /* For keymgmt-based keys (EVP_PKEY_id == -1), such as PQC public keys
348
   * obtained via EVP_PKEY_dup on OpenSSL 3.5+, try EVP_PKEY_is_a() */
349
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
350
  if (pkey_id == -1) {
351
    /* Check against known PQC signature algorithm names */
352
    for (i = 0; i < OSSL_NELEM(pqc_sig_nids); i++) {
353
      if (EVP_PKEY_is_a(pkey, pqc_sig_nids[i].name)) {
354
        return 1;
355
      }
356
    }
357
  }
358
#else
359
  (void)pkey_id;
360
  /* On older OpenSSL, keymgmt-based keys don't exist, so nothing to do */
361
#endif
362

363
  return 0;
28✔
364
}
365

366
/* ========================================================================
367
 * evp_pkey_needs_null_digest - Check if a key uses internal hashing
368
 *
369
 * Check if a key type uses internal hashing and does not need an external
370
 * digest algorithm. This applies to:
371
 *   - EdDSA (Ed25519, Ed448)
372
 *   - PQC signature algorithms: ML-DSA (Dilithium), Falcon, SLH-DSA (SPHINCS+)
373
 *   - Keymgmt-based keys (EVP_PKEY_id == -1) as fallback
374
 *
375
 * @tparam evp_pkey pkey the key to check
376
 * @treturn boolean true if the key uses internal hashing
377
 * ======================================================================== */
378
int
379
evp_pkey_needs_null_digest(EVP_PKEY *pkey)
28✔
380
{
381
  int pkey_type = EVP_PKEY_type(EVP_PKEY_id(pkey));
28✔
382

383
  /* EdDSA keys (Ed25519, Ed448) use internal hashing */
384
#ifdef EVP_PKEY_ED25519
385
  if (pkey_type == EVP_PKEY_ED25519
14✔
386
#ifdef EVP_PKEY_ED448
387
      || pkey_type == EVP_PKEY_ED448
14✔
388
#endif
389
  ) {
390
    return 1;
391
  }
392
#endif
393

394
  /* PQC signature algorithms use internal hashing */
395
  if (evp_pkey_is_pqc_sig(pkey))
28✔
NEW
396
    return 1;
×
397

398
  /* Fallback: for keymgmt-based keys (EVP_PKEY_id == -1), such as
399
   * PQC public keys obtained via EVP_PKEY_dup, try NULL digest.
400
   * These algorithms (ML-DSA, SLH-DSA) use internal hashing. */
401
  if (EVP_PKEY_id(pkey) == -1)
28✔
NEW
402
    return 1;
×
403

404
  return 0;
28✔
405
}
406

407
/* ========================================================================
408
 * openssl_pkey_free - __gc metamethod
409
 * ======================================================================== */
410

411
/***
412
 * free EVP_PKEY object
413
 * @function __gc
414
 */
415
int
416
openssl_pkey_free(lua_State *L)
662✔
417
{
418
  EVP_PKEY *pkey = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
662✔
419
  EVP_PKEY_free(pkey);
662✔
420
  return 0;
662✔
421
}
422

423
/* ========================================================================
424
 * openssl_pkey_bits
425
 * ======================================================================== */
426

427
/***
428
 * get key bits
429
 * @function bits
430
 * @treturn integer key size in bits
431
 */
432
int
433
openssl_pkey_bits(lua_State *L)
20✔
434
{
435
  EVP_PKEY   *pkey = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
20✔
436
  lua_Integer ret = EVP_PKEY_bits(pkey);
20✔
437
  lua_pushinteger(L, ret);
20✔
438
  return 1;
20✔
439
}
440

441
/* ========================================================================
442
 * openssl_pkey_mssing_parameters
443
 * ======================================================================== */
444

445
/***
446
 * check if key parameters are missing
447
 * @function missing_paramaters
448
 * @treturn boolean true if key is missing parameters
449
 */
450
int
451
openssl_pkey_mssing_parameters(lua_State *L)
10✔
452
{
453
  EVP_PKEY *pkey = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
10✔
454
  int       ret = EVP_PKEY_missing_parameters(pkey);
10✔
455
  lua_pushboolean(L, ret == 1);
10✔
456
  return 1;
10✔
457
}
458

459
/* ========================================================================
460
 * openssl_pkey_is_private1
461
 * ======================================================================== */
462

463
/***
464
 * return key is private or not
465
 * @function is_private
466
 * @treturn boolean ture is private or public key
467
 */
468
int
469
openssl_pkey_is_private1(lua_State *L)
70✔
470
{
471
  EVP_PKEY *pkey = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
70✔
472
  int private = openssl_pkey_is_private(pkey);
70✔
473
  luaL_argcheck(L, private == 0 || private == 1, 1, "not support");
70✔
474

475
  lua_pushboolean(L, private);
70✔
476
  return 1;
70✔
477
}
478

479
/* ========================================================================
480
 * openssl_pkey_get_public
481
 * ======================================================================== */
482

483
/***
484
 * return public key
485
 * @function get_public
486
 * @tparam openssl.evp_pkey pkey private key (or key with public key data)
487
 * @treturn openssl.evp_pkey pub public key
488
 * @treturn[2] nil
489
 * @treturn[2] string error message
490
 *
491
 * For traditional algorithms (RSA, EC, Ed25519, etc.), uses standard
492
 * i2d_PUBKEY/d2i_PUBKEY round-trip.
493
 *
494
 * For PQC algorithms (ML-DSA, ML-KEM, SLH-DSA, etc.) where the
495
 * SubjectPublicKeyInfo round-trip may fail, falls back to EVP_PKEY_dup().
496
 * PQC private keys contain the full public key data internally.
497
 *
498
 * @see openssl/evp.h:EVP_PKEY_dup
499
 * @usage
500
 * local key = pkey.new("ML-DSA-44")
501
 * local pub = pkey.get_public(key)
502
 * assert(not pub:is_private())
503
 */
504
int
505
openssl_pkey_get_public(lua_State *L)
46✔
506
{
507
  EVP_PKEY *pkey = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
46✔
508
  int       ret = 0;
46✔
509
  size_t    len;
510

511
#if OPENSSL_VERSION_NUMBER > 0x30100000L && !defined(LIBRESSL_VERSION_NUMBER)
512
  if (EVP_PKEY_id(pkey) == EVP_PKEY_SM2) {
513
    /* NOTES: bugs in openssl3 for SM2, ugly hack */
514
    EC_KEY *ec = (EC_KEY*)EVP_PKEY_get0_EC_KEY(pkey);
515
    ec = EC_KEY_dup(ec);
516
    EC_KEY_set_private_key(ec, NULL);
517
    pkey = EVP_PKEY_new();
518
    EVP_PKEY_assign_EC_KEY(pkey, ec);
519
    PUSH_OBJECT(pkey, "openssl.evp_pkey");
520
    return 1;
521
  }
522
#endif
523

524
  /* Try standard i2d_PUBKEY/d2i_PUBKEY round-trip first.
525
   * This works for traditional algorithms (RSA, EC, Ed25519, etc.) */
526
  len = i2d_PUBKEY(pkey, NULL);
46✔
527

528
  if (len > 0) {
46✔
529
    unsigned char *buf = OPENSSL_malloc(len);
46✔
530

531
    if (buf != NULL) {
46✔
532
      unsigned char *p = buf;
46✔
533
      EVP_PKEY      *pub = EVP_PKEY_new();
46✔
534
#if OPENSSL_VERSION_NUMBER > 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
535
      EVP_PKEY_copy_parameters(pub, pkey);
536
#endif
537
      len = i2d_PUBKEY(pkey, &p);
46✔
538
      p = buf;
46✔
539
      pub = d2i_PUBKEY(&pub, (const unsigned char **)&p, len);
46✔
540
      if (pub) {
46✔
541
        /* Check if the reconstructed key has a valid type.
542
         * For PQC keys on OpenSSL 3.5+, d2i_PUBKEY may return id=-1
543
         * (EVP_PKEY_KEYMGMT). In that case, fall through to the dup path. */
544
        int pub_id = EVP_PKEY_id(pub);
46✔
545
        if (pub_id != NID_undef) {
46✔
546
          PUSH_OBJECT(pub, "openssl.evp_pkey");
46✔
547
          ret = 1;
46✔
548
        } else {
NEW
549
          EVP_PKEY_free(pub);
×
550
        }
551
      }
552
      OPENSSL_free(buf);
46✔
553
    }
554
  }
555

556
  /* Fallback: for PQC keys (ML-DSA, ML-KEM, SLH-DSA, etc.) where
557
   * the SubjectPublicKeyInfo round-trip returns id=-1, use EVP_PKEY_dup.
558
   * PQC signature algorithm private keys contain the full public key data,
559
   * so a dup'd key can be used for verification. */
560
  if (!ret) {
46✔
NEW
561
    EVP_PKEY *pub = EVP_PKEY_dup(pkey);
×
NEW
562
    if (pub) {
×
NEW
563
      PUSH_OBJECT(pub, "openssl.evp_pkey");
×
NEW
564
      ret = 1;
×
565
    }
566
  }
567

568
  return ret;
46✔
569
}
570

571
/* ========================================================================
572
 * openssl_pkey_parse
573
 * ======================================================================== */
574

575
/***
576
 * get key details as table
577
 * @function parse
578
 * @treturn table infos with key bits, size, type, and algorithm-specific fields.
579
 *  For traditional algorithms (RSA, DSA, DH, EC), includes the raw key object
580
 *  (rsa, dsa, dh, ec) with factor hex encoded bignums.
581
 *  For PQC algorithms (ML-DSA, ML-KEM, SLH-DSA, etc.), includes:
582
 *    - algorithm: the algorithm identifier string
583
 *    - pub_key_raw: raw public key bytes (if available)
584
 *    - security_bits: estimated security strength in bits
585
 * @usage
586
 * local info = key:parse()
587
 * print(info.type, info.bits, info.size)
588
 * -- For PQC keys:
589
 * -- print(info.algorithm, info.security_bits)
590
 */
591
int
592
openssl_pkey_parse(lua_State *L)
42✔
593
{
594
  EVP_PKEY *pkey = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
42✔
595
  int       typ = EVP_PKEY_id(pkey);
42✔
596

597
  lua_newtable(L);
42✔
598

599
  AUXILIAR_SET(L, -1, "bits", EVP_PKEY_bits(pkey), integer);
42✔
600
  AUXILIAR_SET(L, -1, "size", EVP_PKEY_size(pkey), integer);
42✔
601

602
  /* Get type name: first try our lookup table, then fall back to OBJ_nid2sn */
603
  {
604
    const char *tname = evp_pkey_type2name(typ);
42✔
605
    if (tname == NULL) {
42✔
606
      /* For unknown types (e.g., PQC algorithms on OpenSSL 3.5+),
607
       * try to get the short name from the NID */
NEW
608
      tname = OBJ_nid2sn(typ);
×
609
    }
610
    if (tname == NULL) {
42✔
611
      /* Last resort: use the long name */
NEW
612
      tname = OBJ_nid2ln(typ);
×
613
    }
614
    AUXILIAR_SET(L, -1, "type", tname, string);
42✔
615
  }
616

617
  switch (typ) {
42✔
618
#ifndef OPENSSL_NO_RSA
619
  case EVP_PKEY_RSA: {
6✔
620
    RSA *rsa = EVP_PKEY_get1_RSA(pkey);
6✔
621
    PUSH_OBJECT(rsa, "openssl.rsa");
6✔
622
    lua_setfield(L, -2, "rsa");
6✔
623
  } break;
6✔
624
#endif
625
#ifndef OPENSSL_NO_DSA
626
  case EVP_PKEY_DSA: {
10✔
627
    DSA *dsa = EVP_PKEY_get1_DSA(pkey);
10✔
628
    PUSH_OBJECT(dsa, "openssl.dsa");
10✔
629
    lua_setfield(L, -2, "dsa");
10✔
630
  } break;
10✔
631
#endif
632
#ifndef OPENSSL_NO_DH
633
  case EVP_PKEY_DH: {
4✔
634
    DH *dh = EVP_PKEY_get1_DH(pkey);
4✔
635
    PUSH_OBJECT(dh, "openssl.dh");
4✔
636
    lua_setfield(L, -2, "dh");
4✔
637
  } break;
4✔
638
#endif
639
#if OPENSSL_VERSION_NUMBER > 0x30000000
640
#ifndef OPENSSL_NO_SM2
641
  case EVP_PKEY_SM2: {
642
    const EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
643
    PUSH_OBJECT(ec, "openssl.ec_key");
644
    lua_setfield(L, -2, "sm2");
645
  } break;
646
#endif
647
#endif
648
#ifndef OPENSSL_NO_EC
649
  case EVP_PKEY_EC:
22✔
650
#if OPENSSL_VERSION_NUMBER < 0x30000000
651
#ifdef EVP_PKEY_SM2
652
  case EVP_PKEY_SM2:
653
#endif
654
#endif
655
  {
656
    const EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
22✔
657
    PUSH_OBJECT(ec, "openssl.ec_key");
22✔
658
    lua_setfield(L, -2, "ec");
22✔
659
  } break;
22✔
660
#endif
661

NEW
662
  default:
×
663
  {
664
    /* For unknown key types (including PQC algorithms), try to extract
665
     * generic parameters via OpenSSL 3.x PARAM API */
666
#if defined(OSSL_PKEY_PARAM_ALGORITHM_ID)
667
    {
668
      /* Try to get the algorithm ID (works for PQC keys with OQS provider) */
669
      char alg_name[256];
670
      size_t alg_len = sizeof(alg_name);
671
      if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_ALGORITHM_ID,
672
                                          alg_name, sizeof(alg_name), &alg_len)) {
673
        AUXILIAR_SET(L, -1, "algorithm", alg_name, string);
674
        /* Also update the "type" field with the actual algorithm name */
675
        AUXILIAR_SET(L, -1, "type", alg_name, string);
676
      }
677

678
      /* Try to get public key raw bytes for display */
679
      unsigned char *pubkey_buf = NULL;
680
      size_t pubkey_len = 0;
681
      if (EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
682
                                           NULL, 0, &pubkey_len) && pubkey_len > 0) {
683
        pubkey_buf = OPENSSL_malloc(pubkey_len);
684
        if (pubkey_buf) {
685
          if (EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
686
                                               pubkey_buf, pubkey_len, &pubkey_len)) {
687
            lua_pushlstring(L, (const char *)pubkey_buf, pubkey_len);
688
            lua_setfield(L, -2, "pub_key_raw");
689
          }
690
          OPENSSL_free(pubkey_buf);
691
        }
692
      }
693

694
      /* Try to get security bits for PQC keys */
695
      {
696
        int sec_bits = 0;
697
        if (EVP_PKEY_get_int_param(pkey, OSSL_PKEY_PARAM_SECURITY_BITS, &sec_bits)) {
698
          AUXILIAR_SET(L, -1, "security_bits", sec_bits, integer);
699
        }
700
      }
701
    }
702
#else
703
    {
704
      /* On older OpenSSL, just record the type name if we can */
NEW
705
      const char *tname = evp_pkey_type2name(typ);
×
NEW
706
      if (tname) {
×
NEW
707
        AUXILIAR_SET(L, -1, "key_type_name", tname, string);
×
708
        /* Also update the "type" field */
NEW
709
        AUXILIAR_SET(L, -1, "type", tname, string);
×
710
      }
711
    }
712
#endif
NEW
713
    break;
×
714
  }
715
  }
716
  return 1;
42✔
717
}
718

719
/* ========================================================================
720
 * openssl_pkey_export
721
 * ======================================================================== */
722

723
/***
724
 * openssl.evp_pkey object
725
 * @type evp_pkey
726
 */
727

728
/***
729
export evp_pkey as pem/der string
730
@function export
731
@tparam[opt='pem'] string support export as 'pem' or 'der' format, default is 'pem'
732
@tparam[opt=false] boolean raw true for export low layer key just rsa,dsa,ec
733
@tparam[opt] string passphrase if given, export key will encrypt with aes-128-cbc,
734
only need when export private key
735
@treturn string
736
*/
737
int openssl_pkey_export(lua_State *L)
520✔
738
{
739
  EVP_PKEY         *key;
740
  int               ispriv = 0;
520✔
741
  int               exraw = 0;
520✔
742
  int               fmt = FORMAT_AUTO;
520✔
743
  size_t            passphrase_len = 0;
520✔
744
  BIO              *bio_out = NULL;
520✔
745
  int               ret = 0;
520✔
746
  const EVP_CIPHER *cipher;
747
  const char       *passphrase = NULL;
520✔
748

749
  key = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
520✔
750
  ispriv = openssl_pkey_is_private(key);
520✔
751

752
  fmt = lua_type(L, 2);
520✔
753
  luaL_argcheck(L, fmt == LUA_TSTRING || fmt == LUA_TNONE, 2, "only accept 'pem','der' or none");
520✔
754
  fmt = luaL_checkoption(L, 2, "pem", format);
520✔
755
  luaL_argcheck(
520✔
756
    L, fmt == FORMAT_PEM || fmt == FORMAT_DER, 2, "only accept pem or der, default is pem");
757

758
  if (!lua_isnone(L, 3)) exraw = lua_toboolean(L, 3);
520✔
759
  passphrase = luaL_optlstring(L, 4, NULL, &passphrase_len);
520✔
760

761
  if (passphrase) {
520✔
762
    cipher = (EVP_CIPHER *)EVP_aes_128_cbc();
58✔
763
  } else {
764
    cipher = NULL;
462✔
765
  }
766

767
  bio_out = BIO_new(BIO_s_mem());
520✔
768
  if (fmt == FORMAT_PEM) {
520✔
769
    if (exraw == 0) {
420✔
770
      ret = ispriv
382✔
771
              ? PEM_write_bio_PrivateKey(
260✔
772
                  bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)
773
              : PEM_write_bio_PUBKEY(bio_out, key);
382✔
774
    } else {
775
      /* export raw key format */
776
      switch (EVP_PKEY_type(EVP_PKEY_id(key))) {
38✔
777
#ifndef OPENSSL_NO_RSA
778
      case EVP_PKEY_RSA:
18✔
779
        ret = ispriv ? PEM_write_bio_RSAPrivateKey(bio_out,
16✔
780
                                                   EVP_PKEY_get0_RSA(key),
8✔
781
                                                   cipher,
782
                                                   (unsigned char *)passphrase,
783
                                                   passphrase_len,
784
                                                   NULL,
785
                                                   NULL)
786
                     : PEM_write_bio_RSAPublicKey(bio_out, EVP_PKEY_get0_RSA(key));
18✔
787
        break;
18✔
788
#endif
789
#ifndef OPENSSL_NO_DSA
790
      case EVP_PKEY_DSA: {
10✔
791
        ret = ispriv ? PEM_write_bio_DSAPrivateKey(bio_out,
8✔
792
                                                   EVP_PKEY_get0_DSA(key),
4✔
793
                                                   cipher,
794
                                                   (unsigned char *)passphrase,
795
                                                   passphrase_len,
796
                                                   NULL,
797
                                                   NULL)
798
                     : PEM_write_bio_DSA_PUBKEY(bio_out, EVP_PKEY_get0_DSA(key));
10✔
799
      } break;
10✔
800
#endif
801
#ifndef OPENSSL_NO_EC
802
      case EVP_PKEY_EC:
10✔
803
        ret = ispriv ? PEM_write_bio_ECPrivateKey(bio_out,
8✔
804
                                                  EVP_PKEY_get0_EC_KEY(key),
4✔
805
                                                  cipher,
806
                                                  (unsigned char *)passphrase,
807
                                                  passphrase_len,
808
                                                  NULL,
809
                                                  NULL)
810
                     : PEM_write_bio_EC_PUBKEY(bio_out, EVP_PKEY_get0_EC_KEY(key));
10✔
811
        break;
10✔
812
#endif
NEW
813
      default:
×
NEW
814
        break;
×
815
      }
816
    }
817
  } else {
818
    /* out put der */
819
    if (exraw == 0) {
100✔
820
      ret = ispriv ? (passphrase == NULL
76✔
821
                        ? i2d_PrivateKey_bio(bio_out, key)
34✔
822
                        : i2d_PKCS8PrivateKey_bio(
60✔
823
                            bio_out, key, cipher, (char *)passphrase, passphrase_len, NULL, NULL))
824
                   : i2d_PUBKEY_bio(bio_out, key);
136✔
825
    } else {
826
      /* output raw key, rsa, ec, dh, dsa */
827
      switch (EVP_PKEY_type(EVP_PKEY_id(key))) {
24✔
828
#ifndef OPENSSL_NO_RSA
829
      case EVP_PKEY_RSA:
6✔
830
        ret = ispriv ? i2d_RSAPrivateKey_bio(bio_out, EVP_PKEY_get0_RSA(key))
2✔
831
                     : i2d_RSAPublicKey_bio(bio_out, EVP_PKEY_get0_RSA(key));
6✔
832
        break;
6✔
833
#endif
834
#ifndef OPENSSL_NO_DSA
835
      case EVP_PKEY_DSA: {
6✔
836
        ret = ispriv ? i2d_DSAPrivateKey_bio(bio_out, EVP_PKEY_get0_DSA(key))
2✔
837
                     : i2d_DSA_PUBKEY_bio(bio_out, EVP_PKEY_get0_DSA(key));
6✔
838
      } break;
6✔
839
#endif
840
#ifndef OPENSSL_NO_EC
841
      case EVP_PKEY_EC:
6✔
842
        ret = ispriv ? i2d_ECPrivateKey_bio(bio_out, EVP_PKEY_get0_EC_KEY(key))
2✔
843
                     : i2d_EC_PUBKEY_bio(bio_out, EVP_PKEY_get0_EC_KEY(key));
6✔
844
        break;
6✔
845
#endif
846
      default:
6✔
847
        ret = ispriv ? i2d_PrivateKey_bio(bio_out, key) : i2d_PUBKEY_bio(bio_out, key);
6✔
848
      }
849
    }
850
  }
851

852
  if (ret) {
520✔
853
    char *bio_mem_ptr;
854
    long  bio_mem_len;
855

856
    bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
520✔
857

858
    lua_pushlstring(L, bio_mem_ptr, bio_mem_len);
520✔
859
    ret = 1;
520✔
860
  }
861

862
  if (bio_out) BIO_free(bio_out);
520✔
863

864
  return ret;
520✔
865
}
866

867
/* ========================================================================
868
 * openssl_pkey_encrypt / decrypt (RSA only)
869
 * ======================================================================== */
870

871
/***
872
 * encrypt message with public key
873
 * encrypt length of message must not longer than key size, if shorter will do padding,currently
874
 * supports 6 padding modes. They are: pkcs1, sslv23, no, oaep, x931, pss.
875
 * @function encrypt
876
 * @tparam string data data to be encrypted
877
 * @tparam string[opt='pkcs1'] string padding padding mode
878
 * @treturn string encrypted message
879
 */
880
int
881
openssl_pkey_encrypt(lua_State *L)
8✔
882
{
883
  size_t        dlen = 0;
8✔
884
  EVP_PKEY     *pkey = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
8✔
885
  const char   *data = luaL_checklstring(L, 2, &dlen);
8✔
886
  int           padding = openssl_get_padding(L, 3, "pkcs1");
8✔
887
  ENGINE       *engine = lua_isnoneornil(L, 4) ? NULL : CHECK_OBJECT(4, ENGINE, "openssl.engine");
8✔
888
  size_t        clen = EVP_PKEY_size(pkey);
8✔
889
  EVP_PKEY_CTX *ctx = NULL;
8✔
890
  int           ret = 0;
8✔
891
  int           typ = EVP_PKEY_type(EVP_PKEY_id(pkey));
8✔
892

893
  luaL_argcheck(
8✔
894
    L, typ == EVP_PKEY_RSA || typ == EVP_PKEY_RSA2, 1, "EVP_PKEY must be of type RSA or RSA2");
895

896
  ctx = EVP_PKEY_CTX_new(pkey, engine);
8✔
897
  if (EVP_PKEY_encrypt_init(ctx) == 1) {
8✔
898
    if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) == 1) {
8✔
899
      byte *buf = malloc(clen);
8✔
900
      if (EVP_PKEY_encrypt(ctx, buf, &clen, (const unsigned char *)data, dlen) == 1) {
8✔
901
        lua_pushlstring(L, (const char *)buf, clen);
8✔
902
        ret = 1;
8✔
903
      }
904
      free(buf);
8✔
905
    }
906
  }
907
  EVP_PKEY_CTX_free(ctx);
8✔
908

909
  return ret;
8✔
910
}
911

912
/***
913
 * decrypt message with private key
914
 * pair with encrypt
915
 * @function decrypt
916
 * @tparam string data data to be decrypted
917
 * @tparam string[opt='pkcs1'] string padding padding mode
918
 * @treturn[1] string result
919
 * @treturn[2] nil
920
 */
921
int
922
openssl_pkey_decrypt(lua_State *L)
8✔
923
{
924
  size_t        dlen = 0;
8✔
925
  EVP_PKEY     *pkey = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
8✔
926
  const char   *data = luaL_checklstring(L, 2, &dlen);
8✔
927
  int           padding = openssl_get_padding(L, 3, "pkcs1");
8✔
928
  ENGINE       *engine = lua_isnoneornil(L, 4) ? NULL : CHECK_OBJECT(4, ENGINE, "openssl.engine");
8✔
929
  size_t        clen = EVP_PKEY_size(pkey);
8✔
930
  EVP_PKEY_CTX *ctx = NULL;
8✔
931
  int           ret = 0;
8✔
932
  int           type = EVP_PKEY_type(EVP_PKEY_id(pkey));
8✔
933

934
  luaL_argcheck(
8✔
935
    L, type == EVP_PKEY_RSA || type == EVP_PKEY_RSA2, 1, "EVP_PKEY must be of type RSA or RSA2");
936

937
  ctx = EVP_PKEY_CTX_new(pkey, engine);
8✔
938
  if (EVP_PKEY_decrypt_init(ctx) == 1) {
8✔
939
    if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) == 1) {
8✔
940
      byte *buf = malloc(clen);
8✔
941

942
      if (EVP_PKEY_decrypt(ctx, buf, &clen, (const unsigned char *)data, dlen) == 1) {
8✔
943
        lua_pushlstring(L, (const char *)buf, clen);
8✔
944
        ret = 1;
8✔
945
      }
946
      free(buf);
8✔
947
    }
948
  }
949
  EVP_PKEY_CTX_free(ctx);
8✔
950

951
  return ret;
8✔
952
}
953

954
/* ========================================================================
955
 * openssl_pkey_ctx - create EVP_PKEY_CTX context
956
 * ======================================================================== */
957

958
/***
959
 * create EVP_PKEY_CTX context for public key operations
960
 * @function ctx
961
 * @tparam[opt] openssl.engine engine optional engine for hardware acceleration
962
 * @treturn evp_pkey_ctx public key context object for RSA operations
963
 */
964
int
965
openssl_pkey_ctx(lua_State *L)
20✔
966
{
967
  EVP_PKEY     *pkey = CHECK_OBJECT(1, EVP_PKEY, "openssl.evp_pkey");
20✔
968
  ENGINE       *engine = lua_isnoneornil(L, 2) ? NULL : CHECK_OBJECT(2, ENGINE, "openssl.engine");
20✔
969
  EVP_PKEY_CTX *ctx = NULL;
20✔
970
  int           typ = EVP_PKEY_type(EVP_PKEY_id(pkey));
20✔
971

972
  luaL_argcheck(
20✔
973
    L, typ == EVP_PKEY_RSA || typ == EVP_PKEY_RSA2, 1, "EVP_PKEY must be of type RSA or RSA2");
974

975
  ctx = EVP_PKEY_CTX_new(pkey, engine);
20✔
976
  if (ctx) {
20✔
977
    PUSH_OBJECT(ctx, "openssl.evp_pkey_ctx");
20✔
978
    return 1;
20✔
979
  }
980

NEW
981
  return openssl_pushresult(L, 0);
×
982
}
983

984
/* ========================================================================
985
 * EVP_PKEY_CTX operations
986
 * ======================================================================== */
987

988
/***
989
 * create new EVP_PKEY_CTX context
990
 * @function ctx_new
991
 * @tparam string alg algorithm name
992
 * @tparam[opt] openssl.engine engine optional engine for hardware acceleration
993
 * @treturn evp_pkey_ctx public key context object
994
 */
995
int
996
openssl_pkey_ctx_new(lua_State *L)
2✔
997
{
998
  int     nid = lua_isnumber(L, 1) ? lua_tointeger(L, 1) : OBJ_txt2nid(luaL_checkstring(L, 1));
2✔
999
  ENGINE *eng = lua_isnoneornil(L, 2) ? NULL : CHECK_OBJECT(2, ENGINE, "openssl.engine");
2✔
1000
  EVP_PKEY_CTX *pctx;
1001

1002
  luaL_argcheck(L, nid > 0, 1, "invalid public key algorithm");
2✔
1003

1004
  pctx = EVP_PKEY_CTX_new_id(nid, eng);
2✔
1005
  if (pctx) {
2✔
1006
    PUSH_OBJECT(pctx, "openssl.evp_pkey_ctx");
2✔
1007
    return 1;
2✔
1008
  }
NEW
1009
  return openssl_pushresult(L, 0);
×
1010
}
1011

1012
/***
1013
 * free EVP_PKEY_CTX context
1014
 * @function __gc
1015
 */
1016
int
1017
openssl_pkey_ctx_free(lua_State *L)
22✔
1018
{
1019
  EVP_PKEY_CTX *ctx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
22✔
1020
  EVP_PKEY_CTX_free(ctx);
22✔
1021
  return 0;
22✔
1022
}
1023

1024
/***
1025
 * generate key
1026
 * @function keygen
1027
 * @treturn openssl.evp_pkey generated key
1028
 */
1029
int
1030
openssl_pkey_ctx_keygen(lua_State *L)
2✔
1031
{
1032
  EVP_PKEY_CTX *ctx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
2✔
1033
  int           bits = luaL_optinteger(L, 2, 0);
2✔
1034
  EVP_PKEY     *pkey = NULL;
2✔
1035

1036
  int ret = EVP_PKEY_keygen_init(ctx);
2✔
1037
  if (ret == 1) {
2✔
1038
    ret = EVP_PKEY_keygen(ctx, &pkey);
2✔
1039
  }
1040
  if (ret == 1) {
2✔
1041
    PUSH_OBJECT(pkey, "openssl.evp_pkey");
2✔
NEW
1042
  } else if (ret == -2) {
×
NEW
1043
    lua_pushnil(L);
×
NEW
1044
    lua_pushstring(L, "NOT_SUPPORT");
×
NEW
1045
    ret = 2;
×
1046
  } else
NEW
1047
    ret = openssl_pushresult(L, ret);
×
1048

1049
  (void)bits;
1050
  return ret;
2✔
1051
}
1052

1053
/***
1054
 * ctrl
1055
 * @function ctrl
1056
 */
1057
int
1058
openssl_pkey_ctx_ctrl(lua_State *L)
44✔
1059
{
1060
  EVP_PKEY_CTX *ctx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
44✔
1061
  int           ret = 0;
44✔
1062

1063
  if (lua_isnumber(L, 2)) {
44✔
NEW
1064
    int type = lua_tointeger(L, 2);
×
NEW
1065
    int p1 = luaL_checkint(L, 3);
×
NEW
1066
    long p2 = luaL_optlong(L, 4, 0);
×
NEW
1067
    ret = EVP_PKEY_CTX_ctrl(ctx, -1, type, p1, p2, NULL);
×
1068
  } else if (lua_isstring(L, 2)) {
44✔
1069
    const char *name = lua_tostring(L, 2);
44✔
1070
    const char *value = luaL_checkstring(L, 3);
44✔
1071
    ret = EVP_PKEY_CTX_ctrl_str(ctx, name, value);
44✔
1072
  }
1073

1074
  lua_pushboolean(L, ret > 0);
44✔
1075
  return 1;
44✔
1076
}
1077

1078
/***
1079
 * decrypt_init
1080
 * @function decrypt_init
1081
 * @tparam[opt] string|env_digest md_alg digest algorithm
1082
 * @treturn boolean true for success
1083
 */
1084
int
1085
openssl_pkey_ctx_decrypt_init(lua_State *L)
4✔
1086
{
1087
  EVP_PKEY_CTX *ctx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
4✔
1088

1089
  if (EVP_PKEY_decrypt_init(ctx) <= 0) return openssl_pushresult(L, 0);
4✔
1090

1091
  lua_pushvalue(L, 1);
4✔
1092
  return 1;
4✔
1093
}
1094

1095
/***
1096
 * encrypt_init
1097
 * @function encrypt_init
1098
 * @tparam[opt] string|env_digest md_alg digest algorithm
1099
 * @treturn boolean true for success
1100
 */
1101
int
1102
openssl_pkey_ctx_encrypt_init(lua_State *L)
4✔
1103
{
1104
  EVP_PKEY_CTX *ctx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
4✔
1105

1106
  if (EVP_PKEY_encrypt_init(ctx) <= 0) return openssl_pushresult(L, 0);
4✔
1107

1108
  lua_pushvalue(L, 1);
4✔
1109
  return 1;
4✔
1110
}
1111

1112
/***
1113
 * verify_init
1114
 * @function verify_init
1115
 * @tparam[opt] string|env_digest md_alg digest algorithm
1116
 * @treturn boolean true for success
1117
 */
1118
int
1119
openssl_pkey_ctx_verify_init(lua_State *L)
6✔
1120
{
1121
  EVP_PKEY_CTX *ctx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
6✔
1122

1123
  if (EVP_PKEY_verify_init(ctx) <= 0) return openssl_pushresult(L, 0);
6✔
1124

1125
  lua_pushvalue(L, 1);
6✔
1126
  return 1;
6✔
1127
}
1128

1129
/***
1130
 * sign_init
1131
 * @function sign_init
1132
 * @tparam[opt] string|env_digest md_alg digest algorithm
1133
 * @treturn boolean true for success
1134
 */
1135
int
1136
openssl_pkey_ctx_sign_init(lua_State *L)
6✔
1137
{
1138
  EVP_PKEY_CTX *ctx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
6✔
1139

1140
  if (EVP_PKEY_sign_init(ctx) <= 0) return openssl_pushresult(L, 0);
6✔
1141

1142
  lua_pushvalue(L, 1);
6✔
1143
  return 1;
6✔
1144
}
1145

1146
/***
1147
 * decrypt
1148
 * @function decrypt
1149
 * @tparam string data data to decrypt
1150
 * @treturn string decrypted data
1151
 */
1152
int
1153
openssl_pkey_ctx_decrypt(lua_State *L)
4✔
1154
{
1155
  EVP_PKEY_CTX *ctx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
4✔
1156
  size_t        dlen = 0;
4✔
1157
  const char   *data = luaL_checklstring(L, 2, &dlen);
4✔
1158
  int           ret = 0;
4✔
1159

1160
  size_t clen = dlen;
4✔
1161
  byte  *buf = malloc(clen);
4✔
1162
  if (EVP_PKEY_decrypt(ctx, buf, &clen, (const unsigned char *)data, dlen) == 1) {
4✔
1163
    lua_pushlstring(L, (const char *)buf, clen);
4✔
1164
    ret = 1;
4✔
1165
  }
1166
  free(buf);
4✔
1167

1168
  return ret;
4✔
1169
}
1170

1171
/***
1172
 * encrypt
1173
 * @function encrypt
1174
 * @tparam string data data to encrypt
1175
 * @treturn string encrypted data
1176
 */
1177
int
1178
openssl_pkey_ctx_encrypt(lua_State *L)
4✔
1179
{
1180
  EVP_PKEY_CTX *ctx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
4✔
1181
  size_t        in_len = 0;
4✔
1182
  const char   *in = luaL_checklstring(L, 2, &in_len);
4✔
1183
  int           ret = 0;
4✔
1184
  size_t        buf_len = 0;
4✔
1185
  byte         *buf = NULL;
4✔
1186

1187
  if (EVP_PKEY_encrypt(ctx, NULL, &buf_len, (const unsigned char *)in, in_len) > 0) {
4✔
1188
    buf = malloc(buf_len);
4✔
1189
    if (EVP_PKEY_encrypt(ctx, buf, &buf_len, (const unsigned char *)in, in_len) > 0) {
4✔
1190
      lua_pushlstring(L, (const char *)buf, buf_len);
4✔
1191
      ret = 1;
4✔
1192
    }
1193
    free(buf);
4✔
1194
  }
1195

1196
  return ret;
4✔
1197
}
1198

1199
/***
1200
 * verify
1201
 * @function verify
1202
 * @tparam string sig signature
1203
 * @tparam string data data to verify
1204
 * @treturn boolean true if signature is valid
1205
 */
1206
int
1207
openssl_pkey_ctx_verify(lua_State *L)
6✔
1208
{
1209
  EVP_PKEY_CTX *pCtx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
6✔
1210
  size_t        dlen = 0;
6✔
1211
  const char   *data = luaL_checklstring(L, 2, &dlen);
6✔
1212
  size_t        slen = 0;
6✔
1213
  const char   *sign = luaL_checklstring(L, 3, &slen);
6✔
1214

1215
  int ret
1216
    = EVP_PKEY_verify(pCtx, (const unsigned char *)data, dlen, (const unsigned char *)sign, slen);
6✔
1217
  lua_pushboolean(L, ret > 0);
6✔
1218

1219
  return 1;
6✔
1220
}
1221

1222
/***
1223
 * sign
1224
 * @function sign
1225
 * @tparam string data data to sign
1226
 * @treturn string signature
1227
 */
1228
int
1229
openssl_pkey_ctx_sign(lua_State *L)
6✔
1230
{
1231
  EVP_PKEY_CTX  *pCtx = CHECK_OBJECT(1, EVP_PKEY_CTX, "openssl.evp_pkey_ctx");
6✔
1232
  size_t         digest_len = 0;
6✔
1233
  const char    *digest = luaL_checklstring(L, 2, &digest_len);
6✔
1234
  size_t         sig_len = 0;
6✔
1235
  unsigned char *sig = NULL;
6✔
1236
  int            ret = 0;
6✔
1237

1238
  if (EVP_PKEY_sign(pCtx, NULL, &sig_len, (const unsigned char *)digest, digest_len) > 0) {
6✔
1239
    sig = malloc(sig_len);
6✔
1240
    if (EVP_PKEY_sign(pCtx, sig, &sig_len, (const unsigned char *)digest, digest_len) > 0) {
6✔
1241
      lua_pushlstring(L, (const char *)sig, sig_len);
6✔
1242
      ret = 1;
6✔
1243
    }
1244
    free(sig);
6✔
1245
  }
1246

1247
  return ret;
6✔
1248
}
1249

1250
/* ========================================================================
1251
 * luaL_Reg arrays
1252
 * ======================================================================== */
1253

1254
luaL_Reg pkey_funcs[] = {
1255
  { "is_private",         openssl_pkey_is_private1       },
1256
  { "get_public",         openssl_pkey_get_public        },
1257
#ifndef OPENSSL_NO_ENGINE
1258
  { "set_engine",         openssl_pkey_set_engine        },
1259
#endif
1260

1261
  { "export",             openssl_pkey_export            },
1262
  { "parse",              openssl_pkey_parse             },
1263
  { "bits",               openssl_pkey_bits              },
1264

1265
  { "ctx",                openssl_pkey_ctx               },
1266
  { "encrypt",            openssl_pkey_encrypt           },
1267
  { "decrypt",            openssl_pkey_decrypt           },
1268
  { "sign",               openssl_sign                   },
1269
  { "verify",             openssl_verify                 },
1270

1271
  { "seal",               openssl_seal                   },
1272
  { "open",               openssl_open                   },
1273

1274
  { "derive",             openssl_derive                 },
1275

1276
  { "encapsulate",        openssl_encapsulate            },
1277
  { "decapsulate",        openssl_decapsulate            },
1278

1279
#if defined(OPENSSL_SUPPORT_SM2) && OPENSSL_VERSION_NUMBER < 0x30000000
1280
  { "as_sm2",             openssl_pkey_as_sm2            },
1281
#endif
1282
  { "missing_paramaters", openssl_pkey_mssing_parameters },
1283

1284
  { "__gc",               openssl_pkey_free              },
1285
  { "__tostring",         auxiliar_tostring              },
1286

1287
  { NULL,                 NULL                           },
1288
};
1289

1290
luaL_Reg pkey_ctx_funcs[] = {
1291
  { "encrypt_init", openssl_pkey_ctx_encrypt_init },
1292
  { "decrypt_init", openssl_pkey_ctx_decrypt_init },
1293
  { "verify_init",  openssl_pkey_ctx_verify_init  },
1294
  { "sign_init",    openssl_pkey_ctx_sign_init    },
1295

1296
  { "ctrl",         openssl_pkey_ctx_ctrl         },
1297

1298
  { "keygen",       openssl_pkey_ctx_keygen       },
1299

1300
  { "decrypt",      openssl_pkey_ctx_decrypt      },
1301
  { "encrypt",      openssl_pkey_ctx_encrypt      },
1302

1303
  { "verify",       openssl_pkey_ctx_verify       },
1304
  { "sign",         openssl_pkey_ctx_sign         },
1305

1306
  { "__gc",         openssl_pkey_ctx_free         },
1307
  { "__tostring",   auxiliar_tostring             },
1308

1309
  { NULL,           NULL                          },
1310
};
1311

1312
static const luaL_Reg R[] = {
1313
  { "read",        openssl_pkey_read        },
1314
  { "new",         openssl_pkey_new         },
1315
  { "ctx_new",     openssl_pkey_ctx_new     },
1316

1317
  { "seal",        openssl_seal             },
1318
  { "seal_init",   openssl_seal_init        },
1319
  { "seal_update", openssl_seal_update      },
1320
  { "seal_final",  openssl_seal_final       },
1321
  { "open",        openssl_open             },
1322
  { "open_init",   openssl_open_init        },
1323
  { "open_update", openssl_open_update      },
1324
  { "open_final",  openssl_open_final       },
1325

1326
  { "get_public",  openssl_pkey_get_public  },
1327
#ifndef OPENSSL_NO_ENGINE
1328
  { "set_engine",  openssl_pkey_set_engine  },
1329
#endif
1330
  { "is_private",  openssl_pkey_is_private1 },
1331
  { "export",      openssl_pkey_export      },
1332
  { "parse",       openssl_pkey_parse       },
1333
  { "bits",        openssl_pkey_bits        },
1334

1335
  { "encrypt",     openssl_pkey_encrypt     },
1336
  { "decrypt",     openssl_pkey_decrypt     },
1337
  { "sign",        openssl_sign             },
1338
  { "verify",      openssl_verify           },
1339
  { "derive",      openssl_derive           },
1340

1341
  { "encapsulate", openssl_encapsulate      },
1342
  { "decapsulate", openssl_decapsulate      },
1343

1344
#if defined(OPENSSL_SUPPORT_SM2) && OPENSSL_VERSION_NUMBER < 0x30000000
1345
  { "as_sm2",      openssl_pkey_as_sm2      },
1346
#endif
1347

1348
  { NULL,          NULL                     }
1349
};
1350

1351
/* ========================================================================
1352
 * luaopen_pkey - module entry point
1353
 * ======================================================================== */
1354

1355
int
1356
luaopen_pkey(lua_State *L)
15✔
1357
{
1358
  size_t i;
1359

1360
  auxiliar_newclass(L, "openssl.evp_pkey", pkey_funcs);
15✔
1361
  auxiliar_newclass(L, "openssl.evp_pkey_ctx", pkey_ctx_funcs);
15✔
1362

1363
  lua_newtable(L);
15✔
1364
  luaL_setfuncs(L, R, 0);
15✔
1365

1366
  for (i = 0; i < OSSL_NELEM(standard_name2type); i++) {
189✔
1367
    lua_pushstring(L, standard_name2type[i].ptr);
174✔
1368
    lua_pushinteger(L, standard_name2type[i].id);
174✔
1369
    lua_rawset(L, -3);
174✔
1370
  }
1371

1372
  return 1;
15✔
1373
}
1374

1375
#if defined(__GNUC__) || defined(__clang__)
1376
#pragma GCC diagnostic pop
1377
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc