• 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

91.35
/src/ec.c
1
/***
2
ec module to create EC keys and do EC key processes.
3

4
@module ec
5
@usage
6
  ec = require('openssl').ec
7
*/
8
#include <openssl/engine.h>
9

10
#include "openssl.h"
11
#include "private.h"
12

13
#if !defined(OPENSSL_NO_EC)
14

15
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
16
EVP_PKEY* openssl_new_pkey_ec_with(const EC_GROUP *group,
17
                                   const BIGNUM *x,
18
                                   const BIGNUM *y,
19
                                   const BIGNUM *d)
20
{
21
  EVP_PKEY *pkey = NULL;
22
  OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
23

24
  if (param_bld) {
25
    EVP_PKEY_CTX *ctx = NULL;
26
    OSSL_PARAM *params = NULL;
27
    const char *point_form = NULL;
28
    const char *asn1_encoding = NULL;
29
    unsigned char *point_buf = NULL;
30
    int nid = EC_GROUP_get_curve_name(group);
31
    int form = EC_GROUP_get_point_conversion_form(group);
32
    int flags = EC_GROUP_get_asn1_flag(group);
33

34
    const char *curve_name = OBJ_nid2sn(nid);
35
    if (curve_name == NULL) {
36
      goto cleanup;
37
    }
38

39
    if (d == NULL && (x == NULL || y == NULL)) {
40
      goto cleanup;
41
    }
42

43
    if (!OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME, curve_name, 0)) {
44
      goto cleanup;
45
    }
46

47
    if (form == POINT_CONVERSION_COMPRESSED)
48
      point_form = "compressed";
49
    else if (form == POINT_CONVERSION_UNCOMPRESSED)
50
      point_form = "uncompressed";
51
    else if (form == POINT_CONVERSION_HYBRID)
52
      point_form = "hybrid";
53
    else
54
      goto cleanup;
55

56
    if (!OSSL_PARAM_BLD_push_utf8_string(param_bld,
57
                                         OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
58
                                         point_form, 0))
59
      goto cleanup;
60

61
    if (flags == OPENSSL_EC_NAMED_CURVE)
62
      asn1_encoding = "named_curve";
63
    else if (flags == OPENSSL_EC_EXPLICIT_CURVE)
64
      asn1_encoding = "explicit";
65
    else
66
      goto cleanup;
67
    if (!OSSL_PARAM_BLD_push_utf8_string(param_bld,
68
                                         OSSL_PKEY_PARAM_EC_ENCODING,
69
                                         asn1_encoding, 0))
70
      goto cleanup;
71

72
    if (x && y)
73
    {
74
      size_t degree = EC_GROUP_get_degree(group);
75
      size_t field_len = (degree + 7) / 8;
76
      size_t point_len = 0;
77

78
      switch(form) {
79
        case POINT_CONVERSION_COMPRESSED:
80
          point_len = 1 + field_len;
81
          point_buf = malloc(point_len);
82
          if (point_buf) {
83
            point_buf[0] = 0x02 + (BN_is_odd(y) ? 1 : 0);
84
            BN_bn2binpad(x, point_buf + 1, field_len);
85
          }
86
          break;
87

88
        case POINT_CONVERSION_UNCOMPRESSED:
89
          point_len = 1 + 2 * field_len;
90
          point_buf = malloc(point_len);
91
          if (point_buf) {
92
            point_buf[0] = 0x04;
93
            BN_bn2binpad(x, point_buf + 1, field_len);
94
            BN_bn2binpad(y, point_buf + 1 + field_len, field_len);
95
          }
96
          break;
97

98
        case POINT_CONVERSION_HYBRID:
99
          point_len = 1 + 2 * field_len;
100
          point_buf = malloc(point_len);
101
          if (point_buf) {
102
            point_buf[0] = 0x06 + (BN_is_odd(y) ? 1 : 0);
103
            BN_bn2binpad(x, point_buf + 1, field_len);
104
            BN_bn2binpad(y, point_buf + 1 + field_len, field_len);
105
          }
106
          break;
107

108
        default:
109
          break;
110
      }
111

112
      if (!point_buf || !OSSL_PARAM_BLD_push_octet_string(param_bld,
113
                                         OSSL_PKEY_PARAM_PUB_KEY,
114
                                         point_buf, point_len)) {
115
        goto cleanup;
116
      }
117
    }
118

119
    if (d && !OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, d)) {
120
      goto cleanup;
121
    }
122

123
    params = OSSL_PARAM_BLD_to_param(param_bld);
124
    if (!params) goto cleanup;
125

126
    ctx = EVP_PKEY_CTX_new_from_name(NULL,
127
                                     nid == NID_sm2 ? "SM2" : "EC",
128
                                     NULL);
129
    if (!ctx) goto cleanup;
130

131
    if (EVP_PKEY_fromdata_init(ctx) <= 0) goto cleanup;
132

133
    int selection = (d != NULL) ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY;
134
    if (EVP_PKEY_fromdata(ctx, &pkey, selection, params) <= 0) {
135
      pkey = NULL;
136
    }
137

138
  cleanup:
139
    OSSL_PARAM_free(params);
140
    OSSL_PARAM_BLD_free(param_bld);
141
    EVP_PKEY_CTX_free(ctx);
142
    free(point_buf);
143
  }
144
  return pkey;
145
}
146
#endif
147

148
/* Suppress deprecation warnings for EC_KEY accessor functions that are part of the module's API.
149
 * The core cryptographic operations (ECDSA sign/verify, ECDH) have been migrated to EVP APIs.
150
 * These accessors are needed for the Lua API and object lifecycle management. */
151
#if defined(__GNUC__) || defined(__clang__)
152
#pragma GCC diagnostic push
153
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
154
#endif
155

156
#include "ec_util.c"
157
/* Include EC_GROUP and EC_POINT modules */
158
#include "group.c"
159
#include "point.c"
160

161
#if defined(__GNUC__) || defined(__clang__)
162
#pragma GCC diagnostic pop
163
#endif
164

165
/* Helper function to convert EC_KEY to EVP_PKEY for cryptographic operations (OpenSSL 3.0+) */
166
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
167
static EVP_PKEY*
168
ec_key_to_evp_pkey(EC_KEY *ec)
169
{
170
  EVP_PKEY *pkey = EVP_PKEY_new();
171
  if (pkey == NULL)
172
    return NULL;
173

174
  /* Suppress deprecation warning for EVP_PKEY_set1_EC_KEY (deprecated in OpenSSL 3.0).
175
   * This function is necessary for compatibility with EC_KEY-based operations. */
176
#if defined(__GNUC__) || defined(__clang__)
177
#pragma GCC diagnostic push
178
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
179
#endif
180
  if (EVP_PKEY_set1_EC_KEY(pkey, ec) != 1) {
181
    EVP_PKEY_free(pkey);
182
    return NULL;
183
  }
184
#if defined(__GNUC__) || defined(__clang__)
185
#pragma GCC diagnostic pop
186
#endif
187

188
  return pkey;
189
}
190
#endif
191

192
static int
193
openssl_ecdsa_do_sign(lua_State *L)
36✔
194
{
195
  EC_KEY              *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
36✔
196
  size_t               l;
197
  const unsigned char *sdata = (const unsigned char *)luaL_checklstring(L, 2, &l);
36✔
198
  int                  der = lua_isnone(L, 3) ? 1 : lua_toboolean(L, 3);
36✔
199
  int                  ret = 0;
36✔
200

201
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
202
  /* OpenSSL 3.0+: Use EVP API */
203
  EVP_PKEY            *pkey = NULL;
204
  EVP_MD_CTX          *ctx = NULL;
205
  unsigned char       *sigbuf = NULL;
206
  size_t               siglen = 0;
207

208
  pkey = ec_key_to_evp_pkey(ec);
209
  if (pkey == NULL)
210
    return openssl_pushresult(L, 0);
211

212
  ctx = EVP_MD_CTX_new();
213
  if (ctx == NULL) {
214
    EVP_PKEY_free(pkey);
215
    return openssl_pushresult(L, 0);
216
  }
217

218
  /* Initialize with NULL digest to sign pre-hashed data */
219
  ret = EVP_DigestSignInit(ctx, NULL, NULL, NULL, pkey);
220
  if (ret == 1) {
221
    /* Get signature length */
222
    ret = EVP_DigestSign(ctx, NULL, &siglen, sdata, l);
223
    if (ret == 1) {
224
      sigbuf = OPENSSL_malloc(siglen);
225
      if (sigbuf) {
226
        ret = EVP_DigestSign(ctx, sigbuf, &siglen, sdata, l);
227
        if (ret == 1) {
228
          if (der) {
229
            /* Return DER-encoded signature */
230
            lua_pushlstring(L, (const char *)sigbuf, siglen);
231
            ret = 1;
232
          } else {
233
            /* Parse DER signature and return r, s components */
234
            const unsigned char *p = sigbuf;
235
            ECDSA_SIG           *sig = d2i_ECDSA_SIG(NULL, &p, siglen);
236
            if (sig) {
237
              const BIGNUM *r = NULL, *s = NULL;
238
              ECDSA_SIG_get0(sig, &r, &s);
239

240
              r = BN_dup(r);
241
              s = BN_dup(s);
242

243
              PUSH_OBJECT(r, "openssl.bn");
244
              PUSH_OBJECT(s, "openssl.bn");
245
              ret = 2;
246
              ECDSA_SIG_free(sig);
247
            } else {
248
              ret = 0;
249
            }
250
          }
251
        }
252
        OPENSSL_free(sigbuf);
253
      } else {
254
        ret = 0;
255
      }
256
    }
257
  }
258

259
  EVP_MD_CTX_free(ctx);
260
  EVP_PKEY_free(pkey);
261

262
  return ret > 0 ? ret : openssl_pushresult(L, 0);
263
#else
264
  /* OpenSSL 1.1 and LibreSSL: Use legacy API */
265
  ECDSA_SIG *sig = ECDSA_do_sign(sdata, l, ec);
36✔
266

267
  if (der) {
36✔
268
    unsigned char *p = NULL;
24✔
269
    l = i2d_ECDSA_SIG(sig, &p);
24✔
270
    if (l > 0) {
24✔
271
      lua_pushlstring(L, (const char *)p, l);
24✔
272
      OPENSSL_free(p);
24✔
273
      ret = 1;
24✔
274
    }
275
  } else {
276
    const BIGNUM *r = NULL, *s = NULL;
12✔
277
    ECDSA_SIG_get0(sig, &r, &s);
12✔
278

279
    r = BN_dup(r);
12✔
280
    s = BN_dup(s);
12✔
281

282
    PUSH_OBJECT(r, "openssl.bn");
12✔
283
    PUSH_OBJECT(s, "openssl.bn");
12✔
284
    ret = 2;
12✔
285
  }
286
  ECDSA_SIG_free(sig);
36✔
287
  return ret;
36✔
288
#endif
289
}
290

291
static int
292
openssl_ecdsa_do_verify(lua_State *L)
36✔
293
{
294
  size_t               l, sigl;
295
  int                  ret;
296
  EC_KEY              *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
36✔
297
  const unsigned char *dgst = (const unsigned char *)luaL_checklstring(L, 2, &l);
36✔
298
  int                  top = lua_gettop(L);
36✔
299

300
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
301
  /* OpenSSL 3.0+: Use EVP API */
302
  EVP_PKEY            *pkey = NULL;
303
  EVP_MD_CTX          *ctx = NULL;
304
  unsigned char       *sigbuf = NULL;
305
  size_t               siglen = 0;
306

307
  pkey = ec_key_to_evp_pkey(ec);
308
  if (pkey == NULL)
309
    return openssl_pushresult(L, 0);
310

311
  ctx = EVP_MD_CTX_new();
312
  if (ctx == NULL) {
313
    EVP_PKEY_free(pkey);
314
    return openssl_pushresult(L, 0);
315
  }
316

317
  /* Convert signature to DER format if needed */
318
  if (top == 3) {
319
    /* Signature is already in DER format */
320
    const unsigned char *s = (const unsigned char *)luaL_checklstring(L, 3, &sigl);
321
    sigbuf = OPENSSL_malloc(sigl);
322
    if (sigbuf == NULL) {
323
      EVP_MD_CTX_free(ctx);
324
      EVP_PKEY_free(pkey);
325
      return openssl_pushresult(L, 0);
326
    }
327
    memcpy(sigbuf, s, sigl);
328
    siglen = sigl;
329
  } else {
330
    /* Signature is provided as r, s components */
331
    BIGNUM    *r = BN_get(L, 3);
332
    BIGNUM    *s = BN_get(L, 4);
333
    ECDSA_SIG *sig = ECDSA_SIG_new();
334
    ECDSA_SIG_set0(sig, r, s);
335

336
    /* Convert to DER */
337
    unsigned char *p = NULL;
338
    int            derlen = i2d_ECDSA_SIG(sig, &p);
339
    if (derlen > 0) {
340
      sigbuf = p;
341
      siglen = derlen;
342
    }
343
    ECDSA_SIG_free(sig);
344

345
    if (derlen <= 0) {
346
      EVP_MD_CTX_free(ctx);
347
      EVP_PKEY_free(pkey);
348
      return openssl_pushresult(L, 0);
349
    }
350
  }
351

352
  /* Initialize with NULL digest to verify pre-hashed data */
353
  ret = EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey);
354
  if (ret == 1) {
355
    ret = EVP_DigestVerify(ctx, sigbuf, siglen, dgst, l);
356
  }
357

358
  OPENSSL_free(sigbuf);
359
  EVP_MD_CTX_free(ctx);
360
  EVP_PKEY_free(pkey);
361

362
  if (ret == -1)
363
    return openssl_pushresult(L, ret);
364

365
  lua_pushboolean(L, ret == 1);
366
  return 1;
367
#else
368
  /* OpenSSL 1.1 and LibreSSL: Use legacy API */
369
  if (top == 3) {
36✔
370
    const unsigned char *s = (const unsigned char *)luaL_checklstring(L, 3, &sigl);
24✔
371
    ECDSA_SIG  *sig = d2i_ECDSA_SIG(NULL, &s, sigl);
24✔
372
    ret = ECDSA_do_verify(dgst, l, sig, ec);
24✔
373
    ECDSA_SIG_free(sig);
24✔
374
  } else {
375
    BIGNUM    *r = BN_get(L, 3);
12✔
376
    BIGNUM    *s = BN_get(L, 4);
12✔
377
    ECDSA_SIG *sig = ECDSA_SIG_new();
12✔
378
    ECDSA_SIG_set0(sig, r, s);
12✔
379
    ret = ECDSA_do_verify(dgst, l, sig, ec);
12✔
380
    ECDSA_SIG_free(sig);
12✔
381
  }
382
  if (ret == -1) return openssl_pushresult(L, ret);
36✔
383
  lua_pushboolean(L, ret);
36✔
384
  return 1;
36✔
385
#endif
386
}
387

388
/***
389
do EC sign
390

391
@function sign
392
@tparam openssl.ec_key eckey
393
@tparam string digest result of digest to be signed
394
@tparam evp_md|string|nid md digest alg identity, default is sm3
395
@treturn string signature
396
*/
397
static int openssl_ecdsa_sign(lua_State *L)
14✔
398
{
399
  int                  ret;
400
  EC_KEY              *eckey = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
14✔
401
  size_t               dgstlen = 0;
14✔
402
  const unsigned char *dgst = (const unsigned char *)luaL_checklstring(L, 2, &dgstlen);
14✔
403
  const EVP_MD        *md = get_digest(L, 3, NULL);
14✔
404

405
  luaL_argcheck(L, dgstlen == (size_t)EVP_MD_size(md), 4, "invalid digest");
14✔
406

407
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
408
  /* OpenSSL 3.0+: Use EVP API */
409
  EVP_PKEY            *pkey = NULL;
410
  EVP_MD_CTX          *ctx = NULL;
411
  unsigned char       *sig = NULL;
412
  size_t               siglen = 0;
413

414
  pkey = ec_key_to_evp_pkey(eckey);
415
  if (pkey == NULL)
416
    return openssl_pushresult(L, 0);
417

418
  ctx = EVP_MD_CTX_new();
419
  if (ctx == NULL) {
420
    EVP_PKEY_free(pkey);
421
    return openssl_pushresult(L, 0);
422
  }
423

424
  /* Initialize with NULL digest to sign pre-hashed data */
425
  ret = EVP_DigestSignInit(ctx, NULL, NULL, NULL, pkey);
426
  if (ret == 1) {
427
    /* Get signature length */
428
    ret = EVP_DigestSign(ctx, NULL, &siglen, dgst, dgstlen);
429
    if (ret == 1) {
430
      sig = OPENSSL_malloc(siglen);
431
      if (sig) {
432
        ret = EVP_DigestSign(ctx, sig, &siglen, dgst, dgstlen);
433
        if (ret == 1) {
434
          lua_pushlstring(L, (const char *)sig, siglen);
435
        }
436
        OPENSSL_free(sig);
437
      } else {
438
        ret = 0;
439
      }
440
    }
441
  }
442

443
  EVP_MD_CTX_free(ctx);
444
  EVP_PKEY_free(pkey);
445

446
  return ret == 1 ? 1 : openssl_pushresult(L, ret);
447
#else
448
  /* OpenSSL 1.1 and LibreSSL: Use legacy API */
449
  unsigned int         siglen = ECDSA_size(eckey);
14✔
450
  unsigned char       *sig = OPENSSL_malloc(siglen);
14✔
451

452
  ret = ECDSA_sign(EVP_MD_type(md), dgst, dgstlen, sig, &siglen, eckey);
14✔
453
  if (ret == 1) {
14✔
454
    lua_pushlstring(L, (const char *)sig, siglen);
14✔
455
  } else
UNCOV
456
    ret = openssl_pushresult(L, ret);
×
457
  OPENSSL_free(sig);
14✔
458
  return ret;
14✔
459
#endif
460
}
461

462
/***
463
do EC verify, input msg is digest result
464

465
@function verify
466
@tparam openssl.ec_key eckey
467
@tparam string digest result of digest to be signed
468
@tparam string signature
469
@tparam evp_md|string|nid md digest alg identity
470
@treturn boolean true for verified, false for invalid signature
471
@return nil for error, and followed by error message
472
*/
473
static int openssl_ecdsa_verify(lua_State *L)
20✔
474
{
475
  int                  ret;
476
  EC_KEY              *eckey = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
20✔
477
  size_t               dgstlen = 0;
20✔
478
  const unsigned char *dgst = (const unsigned char *)luaL_checklstring(L, 2, &dgstlen);
20✔
479
  size_t               siglen = 0;
20✔
480
  const unsigned char *sig = (const unsigned char *)luaL_checklstring(L, 3, &siglen);
20✔
481
  const EVP_MD        *md = get_digest(L, 4, NULL);
20✔
482

483
  luaL_argcheck(L, dgstlen == (size_t)EVP_MD_size(md), 4, "invalid digest");
20✔
484

485
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
486
  /* OpenSSL 3.0+: Use EVP API */
487
  EVP_PKEY            *pkey = NULL;
488
  EVP_MD_CTX          *ctx = NULL;
489

490
  pkey = ec_key_to_evp_pkey(eckey);
491
  if (pkey == NULL)
492
    return openssl_pushresult(L, 0);
493

494
  ctx = EVP_MD_CTX_new();
495
  if (ctx == NULL) {
496
    EVP_PKEY_free(pkey);
497
    return openssl_pushresult(L, 0);
498
  }
499

500
  /* Initialize with NULL digest to verify pre-hashed data */
501
  ret = EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey);
502
  if (ret == 1) {
503
    ret = EVP_DigestVerify(ctx, sig, siglen, dgst, dgstlen);
504
  }
505

506
  EVP_MD_CTX_free(ctx);
507
  EVP_PKEY_free(pkey);
508

509
  if (ret == -1)
510
    return openssl_pushresult(L, ret);
511

512
  lua_pushboolean(L, ret == 1);
513
  return 1;
514
#else
515
  /* OpenSSL 1.1 and LibreSSL: Use legacy API */
516
  int type = EVP_MD_type(md);
16✔
517

518
  ret = ECDSA_verify(type, dgst, (int)dgstlen, sig, (int)siglen, eckey);
16✔
519
  if (ret == -1) return openssl_pushresult(L, ret);
16✔
520
  lua_pushboolean(L, ret);
14✔
521
  return 1;
14✔
522
#endif
523
}
524

525
static int
526
openssl_key_free(lua_State *L)
50✔
527
{
528
  EC_KEY *p = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
50✔
529
#if defined(__GNUC__) || defined(__clang__)
530
#pragma GCC diagnostic push
531
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
532
#endif
533
  EC_KEY_free(p);
50✔
534
#if defined(__GNUC__) || defined(__clang__)
535
#pragma GCC diagnostic pop
536
#endif
537
  return 0;
50✔
538
}
539

540
/***
541
parse EC key components and parameters
542
@function parse
543
@tparam[opt=false] boolean basic true for basic information only
544
@treturn table EC key information including encoding flags, conversion form, group, and key components
545
*/
546
static int
547
openssl_key_parse(lua_State *L)
26✔
548
{
549
  EC_KEY         *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
26✔
550
  int             basic = luaL_opt(L, lua_toboolean, 2, 0);
26✔
551
  const EC_POINT *point;
552
  const EC_GROUP *group;
553
  const BIGNUM   *priv;
554

555
#if defined(__GNUC__) || defined(__clang__)
556
#pragma GCC diagnostic push
557
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
558
#endif
559

560
  point = EC_KEY_get0_public_key(ec);
26✔
561
  group = EC_KEY_get0_group(ec);
26✔
562
  priv = EC_KEY_get0_private_key(ec);
26✔
563
  lua_newtable(L);
26✔
564

565
  AUXILIAR_SET(L, -1, "enc_flag", EC_KEY_get_enc_flags(ec), integer);
26✔
566
  AUXILIAR_SET(L, -1, "conv_form", EC_KEY_get_conv_form(ec), integer);
26✔
567
  AUXILIAR_SET(L, -1, "curve_name", EC_GROUP_get_curve_name(group), integer);
26✔
568

569
#if defined(__GNUC__) || defined(__clang__)
570
#pragma GCC diagnostic pop
571
#endif
572

573
  if (basic) {
26✔
574
    BIGNUM *x = BN_new();
6✔
575
    BIGNUM *y = BN_new();
6✔
576

577
    if (priv != NULL) {
6✔
578
      priv = BN_dup(priv);
4✔
579
      AUXILIAR_SETOBJECT(L, priv, "openssl.bn", -1, "d");
4✔
580
    }
581

582
    if (point && EC_POINT_get_affine_coordinates(group, point, x, y, NULL) == 1) {
6✔
583
      AUXILIAR_SETOBJECT(L, x, "openssl.bn", -1, "x");
6✔
584
      AUXILIAR_SETOBJECT(L, y, "openssl.bn", -1, "y");
6✔
585
    };
586
  } else {
587
    point = EC_POINT_dup(point, group);
20✔
588
    AUXILIAR_SETOBJECT(L, point, "openssl.ec_point", -1, "pub_key");
20✔
589
    group = EC_GROUP_dup(group);
20✔
590
    AUXILIAR_SETOBJECT(L, group, "openssl.ec_group", -1, "group");
20✔
591

592
    OPENSSL_PKEY_GET_BN(priv, priv_key);
20✔
593
  }
594
  return 1;
26✔
595
};
596

597
#define MAX_ECDH_SIZE 256
598

599
#if OPENSSL_VERSION_NUMBER < 0x30000000L || defined(LIBRESSL_VERSION_NUMBER)
600
/* KDF1_SHA1 for legacy ECDH implementation (OpenSSL < 3.0 and LibreSSL) */
601
#ifndef OPENSSL_NO_SHA
602
static const int KDF1_SHA1_len = 20;
603
static void *
UNCOV
604
KDF1_SHA1(const void *in, size_t inlen, void *out, size_t *outlen)
×
605
{
UNCOV
606
  if (*outlen < SHA_DIGEST_LENGTH)
×
UNCOV
607
    return NULL;
×
608
  else
UNCOV
609
    *outlen = SHA_DIGEST_LENGTH;
×
UNCOV
610
  return SHA1(in, inlen, out);
×
611
}
612
#endif
613
#endif
614

615
/***
616
compute ECDH shared key
617
@function compute_key
618
@tparam openssl.ec_key peer peer EC key for key exchange
619
@tparam[opt] function kdf key derivation function
620
@treturn string shared secret or nil if failed
621
*/
622
static int
623
openssl_ecdh_compute_key(lua_State *L)
8✔
624
{
625
  EC_KEY        *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
8✔
626
  EC_KEY        *peer = CHECK_OBJECT(2, EC_KEY, "openssl.ec_key");
8✔
627

628
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
629
  /* OpenSSL 3.0+: Use EVP API */
630
  EVP_PKEY      *pkey = NULL;
631
  EVP_PKEY      *peerkey = NULL;
632
  EVP_PKEY_CTX  *ctx = NULL;
633
  unsigned char *secret = NULL;
634
  size_t         secretlen = 0;
635
  int            ret = 0;
636

637
  pkey = ec_key_to_evp_pkey(ec);
638
  if (pkey == NULL)
639
    return openssl_pushresult(L, 0);
640

641
  peerkey = ec_key_to_evp_pkey(peer);
642
  if (peerkey == NULL) {
643
    EVP_PKEY_free(pkey);
644
    return openssl_pushresult(L, 0);
645
  }
646

647
  ctx = EVP_PKEY_CTX_new(pkey, NULL);
648
  if (ctx == NULL) {
649
    EVP_PKEY_free(pkey);
650
    EVP_PKEY_free(peerkey);
651
    return openssl_pushresult(L, 0);
652
  }
653

654
  ret = EVP_PKEY_derive_init(ctx);
655
  if (ret == 1) {
656
    ret = EVP_PKEY_derive_set_peer(ctx, peerkey);
657
    if (ret == 1) {
658
      /* Get the length of the shared secret */
659
      ret = EVP_PKEY_derive(ctx, NULL, &secretlen);
660
      if (ret == 1) {
661
        secret = OPENSSL_malloc(secretlen);
662
        if (secret) {
663
          ret = EVP_PKEY_derive(ctx, secret, &secretlen);
664
          if (ret == 1) {
665
            lua_pushlstring(L, (const char *)secret, secretlen);
666
          }
667
          OPENSSL_free(secret);
668
        } else {
669
          ret = 0;
670
        }
671
      }
672
    }
673
  }
674

675
  EVP_PKEY_CTX_free(ctx);
676
  EVP_PKEY_free(peerkey);
677
  EVP_PKEY_free(pkey);
678

679
  return ret == 1 ? 1 : openssl_pushresult(L, ret);
680
#else
681
  /* OpenSSL 1.1 and LibreSSL: Use legacy API */
682
  int           field_size, outlen, secret_size_a;
683
  unsigned char secret_a[MAX_ECDH_SIZE];
684
  void *(*kdf)(const void *in, size_t inlen, void *out, size_t *xoutlen);
685

686
  field_size = EC_GROUP_get_degree(EC_KEY_get0_group(ec));
8✔
687
  if (field_size <= 24 * 8) {
8✔
688
#ifndef OPENSSL_NO_SHA
UNCOV
689
    outlen = KDF1_SHA1_len;
×
UNCOV
690
    kdf = KDF1_SHA1;
×
691
#else
692
    outlen = (field_size + 7) / 8;
693
    kdf = NULL;
694
#endif
695
  } else {
696
    outlen = (field_size + 7) / 8;
8✔
697
    kdf = NULL;
8✔
698
  }
699
  secret_size_a = ECDH_compute_key(secret_a, outlen, EC_KEY_get0_public_key(peer), ec, kdf);
8✔
700
  lua_pushlstring(L, (const char *)secret_a, secret_size_a);
8✔
701
  return 1;
8✔
702
#endif
703
}
704

705
/***
706
set ECDSA signing method for EC key
707
@function set_method
708
@tparam openssl.engine engine engine providing the ECDSA method
709
@treturn boolean result true for success
710
*/
711
static int
712
openssl_ecdsa_set_method(lua_State *L)
12✔
713
{
714
#ifndef OPENSSL_NO_ENGINE
715
  EC_KEY *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
12✔
716
  ENGINE *e = CHECK_OBJECT(2, ENGINE, "openssl.engine");
12✔
717
#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L
718
  const ECDSA_METHOD *m = ENGINE_get_ECDSA(e);
6✔
719
  if (m) {
6✔
720
    int r = ECDSA_set_method(ec, m);
6✔
721
    return openssl_pushresult(L, r);
6✔
722
  }
723
#else
724
#if defined(__GNUC__) || defined(__clang__)
725
#pragma GCC diagnostic push
726
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
727
#endif
728

729
  const EC_KEY_METHOD *m = ENGINE_get_EC(e);
6✔
730
  if (m) {
6✔
731
    int r = EC_KEY_set_method(ec, m);
6✔
732

733
#if defined(__GNUC__) || defined(__clang__)
734
#pragma GCC diagnostic pop
735
#endif
736

737
    return openssl_pushresult(L, r);
6✔
738
  }
739

740

741
#endif
742
#endif
743
  return 0;
×
744
}
745

746
/***
747
check if EC key is valid
748
@function check
749
@treturn boolean true if key is valid, false otherwise
750
*/
751
static int
752
openssl_key_check_key(lua_State *L)
12✔
753
{
754
  EC_KEY *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
12✔
755
#if defined(__GNUC__) || defined(__clang__)
756
#pragma GCC diagnostic push
757
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
758
#endif
759
  lua_pushboolean(L, EC_KEY_check_key(ec));
12✔
760
#if defined(__GNUC__) || defined(__clang__)
761
#pragma GCC diagnostic pop
762
#endif
763
  return 1;
12✔
764
}
765

766
/***
767
export EC key to DER format
768
@function export
769
@treturn string DER encoded EC private key
770
*/
771
static int
772
openssl_key_export(lua_State *L)
24✔
773
{
774
  EC_KEY        *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
24✔
775
  unsigned char *der = NULL;
24✔
776
  int            len;
777

778
#if defined(__GNUC__) || defined(__clang__)
779
#pragma GCC diagnostic push
780
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
781
#endif
782

783
  len = i2d_ECPrivateKey(ec, &der);
24✔
784

785
#if defined(__GNUC__) || defined(__clang__)
786
#pragma GCC diagnostic pop
787
#endif
788

789
  if (len > 0)
24✔
790
    lua_pushlstring(L, (const char *)der, len);
24✔
791
  else
792
    lua_pushnil(L);
×
793
  if (der) OPENSSL_free(der);
24✔
794
  return 1;
24✔
795
}
796

797
/***
798
get or set EC group for EC key
799
@function group
800
@tparam[opt] openssl.ec_group group optional EC group to set
801
@treturn openssl.ec_group current EC group when called without parameters
802
@treturn boolean true when setting group successfully
803
*/
804
static int
805
openssl_key_group(lua_State *L)
12✔
806
{
807
  EC_KEY *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
12✔
808

809
#if defined(__GNUC__) || defined(__clang__)
810
#pragma GCC diagnostic push
811
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
812
#endif
813

814
  if (!lua_isnone(L, 2)) {
12✔
815
    EC_GROUP *g = CHECK_OBJECT(2, EC_GROUP, "openssl.ec_group");
×
816
    int       ret = EC_KEY_set_group(ec, g);
×
817

818
    return openssl_pushresult(L, ret);
×
819
  } else {
820
    const EC_GROUP *g = EC_KEY_get0_group(ec);
12✔
821
    g = EC_GROUP_dup(g);
12✔
822

823
    PUSH_OBJECT(g, "openssl.ec_group");
12✔
824

825
#if defined(__GNUC__) || defined(__clang__)
826
#pragma GCC diagnostic pop
827
#endif
828

829
    return 1;
12✔
830
  }
831
}
832

833
/***
834
read EC key from DER encoded data
835
@function read
836
@tparam string der DER encoded EC private key data
837
@treturn openssl.ec_key|nil parsed EC key or nil on failure
838
*/
839
static int
840
openssl_key_read(lua_State *L)
12✔
841
{
842
  size_t               len = 0;
12✔
843
  const unsigned char *der = (const unsigned char *)luaL_checklstring(L, 1, &len);
12✔
844
  EC_KEY              *ec;
845

846
#if defined(__GNUC__) || defined(__clang__)
847
#pragma GCC diagnostic push
848
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
849
#endif
850

851
  ec = d2i_ECPrivateKey(NULL, &der, len);
12✔
852

853
#if defined(__GNUC__) || defined(__clang__)
854
#pragma GCC diagnostic pop
855
#endif
856

857
  if (ec)
12✔
858
    PUSH_OBJECT(ec, "openssl.ec_key");
12✔
859
  else
860
    lua_pushnil(L);
×
861
  return 1;
12✔
862
}
863

864
/***
865
get or set point conversion form for EC key
866
@function conv_form
867
@tparam[opt] string|number form point conversion form to set
868
@treturn[1] string point conversion form name if getting
869
@treturn[1] number point conversion form value if getting
870
@treturn[2] boolean result true for success if setting
871
*/
872
static int
873
openssl_key_conv_form(lua_State *L)
24✔
874
{
875
  EC_KEY                 *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
24✔
876
  point_conversion_form_t cform;
877

878
#if defined(__GNUC__) || defined(__clang__)
879
#pragma GCC diagnostic push
880
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
881
#endif
882

883
  if (lua_isnone(L, 2)) {
24✔
884
    cform = EC_KEY_get_conv_form(ec);
12✔
885

886
#if defined(__GNUC__) || defined(__clang__)
887
#pragma GCC diagnostic pop
888
#endif
889

890
    openssl_push_point_conversion_form(L, cform);
12✔
891
    lua_pushinteger(L, cform);
12✔
892
    return 2;
12✔
893
  } else if (lua_isnumber(L, 2))
12✔
894
    cform = lua_tointeger(L, 2);
×
895
  else
896
    cform = openssl_to_point_conversion_form(L, 2, NULL);
12✔
897

898
#if defined(__GNUC__) || defined(__clang__)
899
#pragma GCC diagnostic push
900
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
901
#endif
902

903
  EC_KEY_set_conv_form(ec, cform);
12✔
904

905
#if defined(__GNUC__) || defined(__clang__)
906
#pragma GCC diagnostic pop
907
#endif
908

909
  lua_pushvalue(L, 1);
12✔
910
  return 1;
12✔
911
}
912

913
/***
914
get or set encoding flags for EC key
915
@function enc_flags
916
@tparam[opt] string|number flags encoding flags to set
917
@treturn[1] string encoding flags name if getting
918
@treturn[1] number encoding flags value if getting
919
@treturn[2] boolean result true for success if setting
920
*/
921
static int
922
openssl_key_enc_flags(lua_State *L)
24✔
923
{
924
  EC_KEY      *ec = CHECK_OBJECT(1, EC_KEY, "openssl.ec_key");
24✔
925
  unsigned int flags;
926

927
#if defined(__GNUC__) || defined(__clang__)
928
#pragma GCC diagnostic push
929
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
930
#endif
931

932
  if (lua_isnone(L, 2)) {
24✔
933
    flags = EC_KEY_get_enc_flags(ec);
12✔
934

935
#if defined(__GNUC__) || defined(__clang__)
936
#pragma GCC diagnostic pop
937
#endif
938

939
    openssl_push_group_asn1_flag(L, flags);
12✔
940
    lua_pushinteger(L, flags);
12✔
941
    return 2;
12✔
942
  } else if (lua_isnumber(L, 2))
12✔
943
    flags = luaL_checkint(L, 2);
×
944
  else
945
    flags = openssl_to_group_asn1_flag(L, 2, NULL);
12✔
946

947
#if defined(__GNUC__) || defined(__clang__)
948
#pragma GCC diagnostic push
949
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
950
#endif
951

952
  EC_KEY_set_enc_flags(ec, flags);
12✔
953

954
#if defined(__GNUC__) || defined(__clang__)
955
#pragma GCC diagnostic pop
956
#endif
957

958
  lua_pushvalue(L, 1);
12✔
959
  return 1;
12✔
960
}
961

962
#ifdef EC_EXT
963
EC_EXT_DEFINE
964
#endif
965

966
static luaL_Reg ec_key_funs[] = {
967
  { "check",       openssl_key_check_key },
968
  { "export",      openssl_key_export    },
969
  { "parse",       openssl_key_parse     },
970
  { "group",       openssl_key_group     },
971
  { "do_sign",     openssl_ecdsa_do_sign    },
972
  { "do_verify",   openssl_ecdsa_do_verify  },
973
  { "sign",        openssl_ecdsa_sign       },
974
  { "verify",      openssl_ecdsa_verify     },
975
  { "compute_key", openssl_ecdh_compute_key },
976
  { "set_method",  openssl_ecdsa_set_method },
977
  { "conv_form",   openssl_key_conv_form },
978
  { "enc_flags",   openssl_key_enc_flags },
979

980
#ifdef EC_EXT
981
  EC_EXT
982
#endif
983

984
  { "__gc",        openssl_key_free      },
985
  { "__tostring",  auxiliar_tostring        },
986

987
  { NULL,          NULL                     }
988
};
989

990
/***
991
list all available elliptic curve names
992
@function list
993
@treturn table array of curve names and descriptions
994
*/
995
static int openssl_list_curve_name(lua_State *L)
2✔
996
{
997
  size_t            i = 0;
2✔
998
  size_t            crv_len = EC_get_builtin_curves(NULL, 0);
2✔
999
  EC_builtin_curve *curves = OPENSSL_malloc((int)(sizeof(EC_builtin_curve) * crv_len));
2✔
1000

1001
  if (curves == NULL) return 0;
2✔
1002

1003
  if (!EC_get_builtin_curves(curves, crv_len)) {
2✔
1004
    OPENSSL_free(curves);
×
1005
    return 0;
×
1006
  }
1007

1008
  lua_newtable(L);
2✔
1009
  for (i = 0; i < crv_len; i++) {
165✔
1010
    const char *comment;
1011
    const char *sname;
1012
    comment = curves[i].comment;
163✔
1013
    sname = OBJ_nid2sn(curves[i].nid);
163✔
1014
    if (comment == NULL) comment = "CURVE DESCRIPTION NOT AVAILABLE";
163✔
1015
    if (sname == NULL) sname = "";
163✔
1016

1017
    AUXILIAR_SET(L, -1, sname, comment, string);
163✔
1018
  }
1019

1020
  OPENSSL_free(curves);
2✔
1021
  return 1;
2✔
1022
};
1023

1024
static luaL_Reg R[] = {
1025
  { "read",      openssl_key_read        },
1026
  { "list",      openssl_list_curve_name },
1027

1028
  { "do_sign",   openssl_ecdsa_do_sign      },
1029
  { "do_verify", openssl_ecdsa_do_verify    },
1030
  { "sign",      openssl_ecdsa_sign         },
1031
  { "verify",    openssl_ecdsa_verify       },
1032

1033
  { NULL,        NULL                       }
1034
};
1035

1036
int
1037
luaopen_ec(lua_State *L)
15✔
1038
{
1039
  auxiliar_newclass(L, "openssl.ec_key", ec_key_funs);
15✔
1040

1041
  lua_newtable(L);
15✔
1042
  luaL_setfuncs(L, R, 0);
15✔
1043

1044
  /* Register group sub-module */
1045
  luaopen_ec_group(L);
15✔
1046
  lua_setfield(L, -2, "group");
15✔
1047

1048
  /* Register point sub-module */
1049
  luaopen_ec_point(L);
15✔
1050
  lua_setfield(L, -2, "point");
15✔
1051

1052
  return 1;
15✔
1053
}
1054

1055
#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