Skip to content

Commit ebaad8d

Browse files
committed
feat(pkey) get and set DH parameters
1 parent f4661c6 commit ebaad8d

File tree

7 files changed

+356
-75
lines changed

7 files changed

+356
-75
lines changed

README.md

+24-9
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ Each key type may only support part of operations:
399399
Key Type | Load existing key | Key generation | Encrypt/Decrypt | Sign/Verify | Key Exchange |
400400
---------|----------|----------------|-----------------|-------------|---------- |
401401
RSA| Y | Y | Y | Y | |
402+
DH | Y | Y | | | Y |
402403
EC | Y | Y | | Y (ECDSA) | Y (ECDH) |
403404
Ed25519 | Y | Y | | Y (PureEdDSA) | |
404405
X25519 | Y | Y | | | Y (ECDH) |
@@ -417,9 +418,9 @@ X448 | Y | Y | | | Y (ECDH) |
417418

418419
**syntax**: *pk, err = pkey.new()*
419420

420-
Creates a new pkey instance. The first argument can be:
421+
Function to generate a key pair, or load existing key in PEM or DER format.
421422

422-
1. A `config` table to create a new PKEY pair. Which defaults to:
423+
1. Pass a `config` table to create a new PKEY pair. Which defaults to:
423424

424425
```lua
425426
pkey.new({
@@ -435,7 +436,7 @@ To generate EC or DH key, please refer to [pkey.paramgen](#pkeyparamgen) for pos
435436
Other possible `type`s are `Ed25519`, `X25519`, `Ed448` and `X448`. No additional parameters
436437
can be set during key generation for those keys.
437438

438-
2. A `string` of private or public key in PEM, DER or JWK format text; optionally accpet a table
439+
2. Pass a `string` of private or public key in PEM, DER or JWK format text; optionally accpet a table
439440
`opts` to explictly load `format` and key `type`. When loading a key in PEM format,
440441
`passphrase` or `passphrase_cb` may be provided to decrypt the key.
441442

@@ -459,8 +460,8 @@ pkey.new(pem_or_der_text, {
459460
- Public key part for `OKP` keys
460461
(the `x` parameter) is always not honored and derived from private key part (the `d` parameter) if it's specified.
461462

462-
3. `nil` to create a 2048 bits RSA key.
463-
4. A `EVP_PKEY*` pointer, to return a wrapped `pkey` instance. Normally user won't use this
463+
3. Pass `nil` to create a 2048 bits RSA key.
464+
4. Pass a `EVP_PKEY*` pointer, to return a wrapped `pkey` instance. Normally user won't use this
464465
approach. User shouldn't free the pointer on their own, since the pointer is not copied.
465466

466467
[Back to TOC](#table-of-contents)
@@ -479,16 +480,14 @@ Returns `true` if table is an instance of `pkey`. Returns `false` otherwise.
479480

480481
Generate parameters for EC or DH key and output as PEM-encoded text.
481482

482-
For EC private key:
483+
For EC key:
483484

484485
Parameter | Description
485486
-----------|-------------
486487
type | `"EC"`
487488
curve | EC curves. If omitted, default to `"prime192v1"`. To see list of supported EC curves, use `openssl ecparam -list_curves`.
488489

489-
To see list of supported EC curves, use `openssl ecparam -list_curves`.
490-
491-
For DH private key:
490+
For DH key:
492491

493492
Parameter | Description
494493
-----------|-------------
@@ -508,6 +507,11 @@ local pem, err = pkey.paramgen({
508507
type = 'EC',
509508
curve = 'prime192v1',
510509
})
510+
511+
local pem, err = pkey.paramgen({
512+
type = 'DH',
513+
group = 'ffdhe4096',
514+
})
511515
```
512516

513517
[Back to TOC](#table-of-contents)
@@ -568,6 +572,17 @@ group | the named curve group | [NID] as a number, when passed in as `set_parame
568572
It's not possible to set `x`, `y` with `public` at same time as `x` and `y` is basically another representation
569573
of `public`. Also currently it's only possible to set `x` and `y` at same time.
570574

575+
Parameters for DH key:
576+
577+
Parameter | Description | Type
578+
-----------|-------------|-----
579+
private | private key | [bn](#restyopensslbn)
580+
public | public key | [bn](#restyopensslbn)
581+
p | prime modulus | [bn](#restyopensslbn)
582+
q | reference position | [bn](#restyopensslbn)
583+
p | base generator | [bn](#restyopensslbn)
584+
585+
571586
Parameters for Curve25519 and Curve448 keys:
572587

573588
Parameter | Description | Type

lib/resty/openssl/dh.lua

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
local ffi = require "ffi"
2+
local C = ffi.C
3+
local ffi_gc = ffi.gc
4+
5+
require "resty.openssl.include.dh"
6+
local bn_lib = require "resty.openssl.bn"
7+
local objects_lib = require "resty.openssl.objects"
8+
9+
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
10+
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
11+
local format_error = require("resty.openssl.err").format_error
12+
13+
local _M = {}
14+
15+
_M.params = {"public", "private", "p", "q", "g"}
16+
17+
local empty_table = {}
18+
local bn_ptrptr_ct = ffi.typeof("const BIGNUM *[1]")
19+
function _M.get_parameters(dh_st)
20+
return setmetatable(empty_table, {
21+
__index = function(_, k)
22+
local ptr, ret
23+
if OPENSSL_11_OR_LATER then
24+
ptr = bn_ptrptr_ct()
25+
end
26+
27+
if OPENSSL_11_OR_LATER then
28+
ptr = bn_ptrptr_ct()
29+
end
30+
31+
if k == 'p' then
32+
if OPENSSL_11_OR_LATER then
33+
C.DH_get0_pqg(dh_st, ptr, nil, nil)
34+
end
35+
elseif k == 'q' then
36+
if OPENSSL_11_OR_LATER then
37+
C.DH_get0_pqg(dh_st, nil, ptr, nil)
38+
end
39+
elseif k == 'g' then
40+
if OPENSSL_11_OR_LATER then
41+
C.DH_get0_pqg(dh_st, nil, nil, ptr)
42+
end
43+
elseif k == 'public' then
44+
if OPENSSL_11_OR_LATER then
45+
C.DH_get0_key(dh_st, ptr, nil)
46+
end
47+
k = "pub_key"
48+
elseif k == 'private' then
49+
if OPENSSL_11_OR_LATER then
50+
C.DH_get0_key(dh_st, nil, ptr)
51+
end
52+
k = "priv_key"
53+
else
54+
return nil, "rsa.get_parameters: unknown parameter \"" .. k .. "\" for RSA key"
55+
end
56+
57+
if OPENSSL_11_OR_LATER then
58+
ret = ptr[0]
59+
elseif OPENSSL_10 then
60+
ret = dh_st[k]
61+
end
62+
63+
if ret == nil then
64+
return nil
65+
end
66+
return bn_lib.dup(ret)
67+
end
68+
}), nil
69+
end
70+
71+
local function dup_bn_value(v)
72+
if not bn_lib.istype(v) then
73+
return nil, "expect value to be a bn instance"
74+
end
75+
local bn = C.BN_dup(v.ctx)
76+
if bn == nil then
77+
return nil, "BN_dup() failed"
78+
end
79+
return bn
80+
end
81+
82+
function _M.set_parameters(dh_st, opts)
83+
local err
84+
local opts_bn = {}
85+
-- remember which parts of BNs has been added to dh_st, they should be freed
86+
-- by DH_free and we don't cleanup them on failure
87+
local cleanup_from_idx = 1
88+
-- dup input
89+
local do_set_key, do_set_pqg
90+
for k, v in pairs(opts) do
91+
opts_bn[k], err = dup_bn_value(v)
92+
if err then
93+
err = "dh.set_parameters: cannot process parameter \"" .. k .. "\":" .. err
94+
goto cleanup_with_error
95+
end
96+
if k == "private" or k == "public" then
97+
do_set_key = true
98+
elseif k == "p" or k == "q" or k == "g" then
99+
do_set_pqg = true
100+
end
101+
end
102+
if OPENSSL_11_OR_LATER then
103+
local code
104+
if do_set_key then
105+
code = C.DH_set0_key(dh_st, opts_bn["public"], opts_bn["private"])
106+
if code == 0 then
107+
err = format_error("dh.set_parameters: DH_set0_key")
108+
goto cleanup_with_error
109+
end
110+
end
111+
cleanup_from_idx = cleanup_from_idx + 2
112+
if do_set_pqg then
113+
code = C.DH_set0_pqg(dh_st, opts_bn["p"], opts_bn["q"], opts_bn["g"])
114+
if code == 0 then
115+
err = format_error("dh.set_parameters: DH_set0_pqg")
116+
goto cleanup_with_error
117+
end
118+
end
119+
return true
120+
elseif OPENSSL_10 then
121+
for k, v in pairs(opts_bn) do
122+
if k == "public" then
123+
k = "pub_key"
124+
elseif k == "private" then
125+
k = "priv_key"
126+
end
127+
dh_st[k]= v
128+
end
129+
return true
130+
end
131+
132+
::cleanup_with_error::
133+
for i, k in pairs(_M.params) do
134+
if i >= cleanup_from_idx then
135+
C.BN_free(opts_bn[k])
136+
end
137+
end
138+
return false, err
139+
end
140+
141+
return _M

lib/resty/openssl/include/dh.lua

+44-1
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,56 @@ local C = ffi.C
33

44
require "resty.openssl.include.ossl_typ"
55
require "resty.openssl.include.objects"
6+
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
7+
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
8+
9+
if OPENSSL_11_OR_LATER then
10+
ffi.cdef [[
11+
void DH_get0_pqg(const DH *dh,
12+
const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
13+
int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g);
14+
void DH_get0_key(const DH *dh,
15+
const BIGNUM **pub_key, const BIGNUM **priv_key);
16+
int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key);
17+
]]
18+
elseif OPENSSL_10 then
19+
ffi.cdef [[
20+
struct dh_st {
21+
/*
22+
* This first argument is used to pick up errors when a DH is passed
23+
* instead of a EVP_PKEY
24+
*/
25+
int pad;
26+
int version;
27+
BIGNUM *p;
28+
BIGNUM *g;
29+
long length; /* optional */
30+
BIGNUM *pub_key; /* g^x */
31+
BIGNUM *priv_key; /* x */
32+
int flags;
33+
/*BN_MONT_CTX*/ void *method_mont_p;
34+
/* Place holders if we want to do X9.42 DH */
35+
BIGNUM *q;
36+
BIGNUM *j;
37+
unsigned char *seed;
38+
int seedlen;
39+
BIGNUM *counter;
40+
int references;
41+
/* trimmer */
42+
// CRYPTO_EX_DATA ex_data;
43+
// const DH_METHOD *meth;
44+
// ENGINE *engine;
45+
};
46+
]]
47+
end
648

749
ffi.cdef [[
850
DH *DH_get_1024_160(void);
951
DH *DH_get_2048_224(void);
1052
DH *DH_get_2048_256(void);
1153
DH *DH_new_by_nid(int nid);
12-
]]
54+
]];
55+
1356

1457
local dh_groups = {
1558
-- per https://tools.ietf.org/html/rfc5114

lib/resty/openssl/pkey.lua

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ local evp_macro = require "resty.openssl.include.evp"
1616
local util = require "resty.openssl.util"
1717
local digest_lib = require "resty.openssl.digest"
1818
local rsa_lib = require "resty.openssl.rsa"
19+
local dh_lib = require "resty.openssl.dh"
1920
local ec_lib = require "resty.openssl.ec"
2021
local ecx_lib = require "resty.openssl.ecx"
2122
local objects_lib = require "resty.openssl.objects"
@@ -442,6 +443,8 @@ function _M:get_parameters()
442443
return rsa_lib.get_parameters(key)
443444
elseif self.key_type == evp_macro.EVP_PKEY_EC then
444445
return ec_lib.get_parameters(key)
446+
elseif self.key_type == evp_macro.EVP_PKEY_DH then
447+
return dh_lib.get_parameters(key)
445448
end
446449
else
447450
return ecx_lib.get_parameters(self.ctx)
@@ -463,6 +466,8 @@ function _M:set_parameters(opts)
463466
return rsa_lib.set_parameters(key, opts)
464467
elseif self.key_type == evp_macro.EVP_PKEY_EC then
465468
return ec_lib.set_parameters(key, opts)
469+
elseif self.key_type == evp_macro.EVP_PKEY_DH then
470+
return dh_lib.set_parameters(key, opts)
466471
end
467472
else
468473
-- for ecx keys we always create a new EVP_PKEY and release the old one

lib/resty/openssl/rsa.lua

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ function _M.get_parameters(rsa_st)
6464
ret = rsa_st[k]
6565
end
6666

67+
if ret == nil then
68+
return nil
69+
end
6770
return bn_lib.dup(ret)
6871
end
6972
}), nil

lua-resty-openssl-0.6.7-1.rockspec

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ build = {
1818
["resty.openssl.aux.jwk"] = "lib/resty/openssl/aux/jwk.lua",
1919
["resty.openssl.bn"] = "lib/resty/openssl/bn.lua",
2020
["resty.openssl.cipher"] = "lib/resty/openssl/cipher.lua",
21+
["resty.openssl.dh"] = "lib/resty/openssl/dh.lua",
2122
["resty.openssl.digest"] = "lib/resty/openssl/digest.lua",
2223
["resty.openssl.ec"] = "lib/resty/openssl/ec.lua",
2324
["resty.openssl.ecx"] = "lib/resty/openssl/ecx.lua",

0 commit comments

Comments
 (0)