Skip to content

Commit dc8da3a

Browse files
qmuntalkariannadagood
authored
Add support for crypto/dsa (#135)
* implement crypto/dsa * add dsa.h include * fix TestNewPublicKeyDSAWithBadPublicKey * update dsa test names * fix TestDSANewPublicKeyWithBadPublicKey * fix dsa memory leaks * remove unused functions * port necessary dsa functions to openssl 1.0.2 * fix legacy macros * port support openssl 3 * fix newDSA3 * fix openssl3 in fips mode * free context in newDSA3 * add some comments * Apply suggestions from code review Co-authored-by: Martijn Verburg <[email protected]> * improve port_dsa.c description * implement checkMajorVersion * use lowercase for parameters * Apply suggestions from code review Co-authored-by: Davis Goodin <[email protected]> * use OpenSSL naming convention for FFC params * Apply suggestions from code review Co-authored-by: Davis Goodin <[email protected]> * call DSA_free in a deferred function --------- Co-authored-by: Martijn Verburg <[email protected]> Co-authored-by: Davis Goodin <[email protected]>
1 parent 97b3bd9 commit dc8da3a

File tree

8 files changed

+664
-14
lines changed

8 files changed

+664
-14
lines changed

dsa.go

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
//go:build !cmd_go_bootstrap
2+
3+
package openssl
4+
5+
// #include "goopenssl.h"
6+
import "C"
7+
import (
8+
"runtime"
9+
"unsafe"
10+
)
11+
12+
var (
13+
OSSL_PKEY_PARAM_FFC_PBITS = C.CString("pbits")
14+
OSSL_PKEY_PARAM_FFC_QBITS = C.CString("qbits")
15+
OSSL_PKEY_PARAM_FFC_P = C.CString("p")
16+
OSSL_PKEY_PARAM_FFC_Q = C.CString("q")
17+
OSSL_PKEY_PARAM_FFC_G = C.CString("g")
18+
)
19+
20+
// DSAParameters contains the DSA parameters.
21+
type DSAParameters struct {
22+
P, Q, G BigInt
23+
}
24+
25+
// PrivateKeyDSA represents a DSA private key.
26+
type PrivateKeyDSA struct {
27+
DSAParameters
28+
X, Y BigInt
29+
30+
// _pkey MUST NOT be accessed directly. Instead, use the withKey method.
31+
_pkey C.GO_EVP_PKEY_PTR
32+
}
33+
34+
func (k *PrivateKeyDSA) finalize() {
35+
C.go_openssl_EVP_PKEY_free(k._pkey)
36+
}
37+
38+
func (k *PrivateKeyDSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int {
39+
defer runtime.KeepAlive(k)
40+
return f(k._pkey)
41+
}
42+
43+
// PublicKeyDSA represents a DSA public key.
44+
type PublicKeyDSA struct {
45+
DSAParameters
46+
Y BigInt
47+
48+
// _pkey MUST NOT be accessed directly. Instead, use the withKey method.
49+
_pkey C.GO_EVP_PKEY_PTR
50+
}
51+
52+
func (k *PublicKeyDSA) finalize() {
53+
C.go_openssl_EVP_PKEY_free(k._pkey)
54+
}
55+
56+
func (k *PublicKeyDSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int {
57+
defer runtime.KeepAlive(k)
58+
return f(k._pkey)
59+
}
60+
61+
// GenerateDSAParameters generates a set of DSA parameters.
62+
func GenerateDSAParameters(l, n int) (DSAParameters, error) {
63+
// The DSA parameters are generated by creating a new DSA key and
64+
// extracting the domain parameters from it.
65+
66+
// Generate a new DSA key context and set the known parameters.
67+
ctx := C.go_openssl_EVP_PKEY_CTX_new_id(C.GO_EVP_PKEY_DSA, nil)
68+
if ctx == nil {
69+
return DSAParameters{}, newOpenSSLError("EVP_PKEY_CTX_new_id failed")
70+
}
71+
defer C.go_openssl_EVP_PKEY_CTX_free(ctx)
72+
if C.go_openssl_EVP_PKEY_paramgen_init(ctx) != 1 {
73+
return DSAParameters{}, newOpenSSLError("EVP_PKEY_paramgen_init failed")
74+
}
75+
if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_DSA, -1, C.GO_EVP_PKEY_CTRL_DSA_PARAMGEN_BITS, C.int(l), nil) != 1 {
76+
return DSAParameters{}, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
77+
}
78+
if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_DSA, -1, C.GO_EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, C.int(n), nil) != 1 {
79+
return DSAParameters{}, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
80+
}
81+
var pkey C.GO_EVP_PKEY_PTR
82+
if C.go_openssl_EVP_PKEY_paramgen(ctx, &pkey) != 1 {
83+
return DSAParameters{}, newOpenSSLError("EVP_PKEY_paramgen failed")
84+
}
85+
defer C.go_openssl_EVP_PKEY_free(pkey)
86+
87+
// Extract the domain parameters from the generated key.
88+
var p, q, g C.GO_BIGNUM_PTR
89+
switch vMajor {
90+
case 1:
91+
dsa := getDSA(pkey)
92+
if vMinor == 0 {
93+
C.go_openssl_DSA_get0_pqg_backport(dsa, &p, &q, &g)
94+
} else {
95+
C.go_openssl_DSA_get0_pqg(dsa, &p, &q, &g)
96+
}
97+
case 3:
98+
defer func() {
99+
C.go_openssl_BN_free(p)
100+
C.go_openssl_BN_free(q)
101+
C.go_openssl_BN_free(g)
102+
}()
103+
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_P, &p) != 1 ||
104+
C.go_openssl_EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_Q, &q) != 1 ||
105+
C.go_openssl_EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_G, &g) != 1 {
106+
return DSAParameters{}, newOpenSSLError("EVP_PKEY_get_bn_param")
107+
}
108+
default:
109+
panic(errUnsupportedVersion())
110+
}
111+
112+
return DSAParameters{
113+
P: bnToBig(p),
114+
Q: bnToBig(q),
115+
G: bnToBig(g),
116+
}, nil
117+
}
118+
119+
// NewPrivateKeyDSA creates a new DSA private key from the given parameters.
120+
func NewPrivateKeyDSA(params DSAParameters, x, y BigInt) (*PrivateKeyDSA, error) {
121+
if x == nil || y == nil {
122+
panic("x and y must not be nil")
123+
}
124+
pkey, err := newDSA(params, x, y)
125+
if err != nil {
126+
return nil, err
127+
}
128+
k := &PrivateKeyDSA{params, x, y, pkey}
129+
runtime.SetFinalizer(k, (*PrivateKeyDSA).finalize)
130+
return k, nil
131+
}
132+
133+
// NewPublicKeyDSA creates a new DSA public key from the given parameters.
134+
func NewPublicKeyDSA(params DSAParameters, y BigInt) (*PublicKeyDSA, error) {
135+
if y == nil {
136+
panic("y must not be nil")
137+
}
138+
pkey, err := newDSA(params, nil, y)
139+
if err != nil {
140+
return nil, err
141+
}
142+
k := &PublicKeyDSA{params, y, pkey}
143+
runtime.SetFinalizer(k, (*PublicKeyDSA).finalize)
144+
return k, nil
145+
}
146+
147+
// GenerateKeyDSA generates a new private DSA key using the given parameters.
148+
func GenerateKeyDSA(params DSAParameters) (*PrivateKeyDSA, error) {
149+
pkey, err := newDSA(params, nil, nil)
150+
if err != nil {
151+
return nil, err
152+
}
153+
var x, y C.GO_BIGNUM_PTR
154+
switch vMajor {
155+
case 1:
156+
dsa := getDSA(pkey)
157+
if vMinor == 0 {
158+
C.go_openssl_DSA_get0_key_backport(dsa, &y, &x)
159+
} else {
160+
C.go_openssl_DSA_get0_key(dsa, &y, &x)
161+
}
162+
case 3:
163+
defer func() {
164+
C.go_openssl_BN_clear_free(x)
165+
C.go_openssl_BN_free(y)
166+
}()
167+
if C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramPubKey, &y) != 1 ||
168+
C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramPrivKey, &x) != 1 {
169+
return nil, newOpenSSLError("EVP_PKEY_get_bn_param")
170+
}
171+
default:
172+
panic(errUnsupportedVersion())
173+
}
174+
k := &PrivateKeyDSA{params, bnToBig(x), bnToBig(y), pkey}
175+
runtime.SetFinalizer(k, (*PrivateKeyDSA).finalize)
176+
return k, nil
177+
}
178+
179+
// SignDSA signs a hash (which should be the result of hashing a larger message).
180+
func SignDSA(priv *PrivateKeyDSA, hash []byte) ([]byte, error) {
181+
return evpSign(priv.withKey, 0, 0, 0, hash)
182+
}
183+
184+
// VerifyDSA verifiessig using the public key, pub.
185+
func VerifyDSA(pub *PublicKeyDSA, hash []byte, sig []byte) bool {
186+
return evpVerify(pub.withKey, 0, 0, 0, sig, hash) == nil
187+
}
188+
189+
func newDSA(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) {
190+
switch vMajor {
191+
case 1:
192+
return newDSA1(params, x, y)
193+
case 3:
194+
return newDSA3(params, x, y)
195+
default:
196+
panic(errUnsupportedVersion())
197+
}
198+
}
199+
200+
func newDSA1(params DSAParameters, x, y BigInt) (pkey C.GO_EVP_PKEY_PTR, err error) {
201+
checkMajorVersion(1)
202+
203+
dsa := C.go_openssl_DSA_new()
204+
if dsa == nil {
205+
return nil, newOpenSSLError("DSA_new failed")
206+
}
207+
defer func() {
208+
if pkey == nil {
209+
C.go_openssl_DSA_free(dsa)
210+
}
211+
}()
212+
213+
p, q, g := bigToBN(params.P), bigToBN(params.Q), bigToBN(params.G)
214+
var ret C.int
215+
if vMinor == 0 {
216+
ret = C.go_openssl_DSA_set0_pqg_backport(dsa, p, q, g)
217+
} else {
218+
ret = C.go_openssl_DSA_set0_pqg(dsa, p, q, g)
219+
}
220+
if ret != 1 {
221+
C.go_openssl_BN_free(p)
222+
C.go_openssl_BN_free(q)
223+
C.go_openssl_BN_free(g)
224+
return nil, newOpenSSLError("DSA_set0_pqg failed")
225+
}
226+
if y != nil {
227+
pub, priv := bigToBN(y), bigToBN(x)
228+
if vMinor == 0 {
229+
ret = C.go_openssl_DSA_set0_key_backport(dsa, pub, priv)
230+
} else {
231+
ret = C.go_openssl_DSA_set0_key(dsa, pub, priv)
232+
}
233+
if ret != 1 {
234+
C.go_openssl_BN_free(pub)
235+
C.go_openssl_BN_clear_free(priv)
236+
return nil, newOpenSSLError("DSA_set0_key failed")
237+
}
238+
} else {
239+
if C.go_openssl_DSA_generate_key(dsa) != 1 {
240+
return nil, newOpenSSLError("DSA_generate_key failed")
241+
}
242+
}
243+
pkey = C.go_openssl_EVP_PKEY_new()
244+
if pkey == nil {
245+
return nil, newOpenSSLError("EVP_PKEY_new failed")
246+
}
247+
if C.go_openssl_EVP_PKEY_assign(pkey, C.GO_EVP_PKEY_DSA, unsafe.Pointer(dsa)) != 1 {
248+
C.go_openssl_EVP_PKEY_free(pkey)
249+
return nil, newOpenSSLError("EVP_PKEY_assign failed")
250+
}
251+
return pkey, nil
252+
}
253+
254+
func newDSA3(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) {
255+
checkMajorVersion(3)
256+
257+
bld := C.go_openssl_OSSL_PARAM_BLD_new()
258+
if bld == nil {
259+
return nil, newOpenSSLError("OSSL_PARAM_BLD_new")
260+
}
261+
defer C.go_openssl_OSSL_PARAM_BLD_free(bld)
262+
p, q, g := bigToBN(params.P), bigToBN(params.Q), bigToBN(params.G)
263+
defer func() {
264+
C.go_openssl_BN_free(p)
265+
C.go_openssl_BN_free(q)
266+
C.go_openssl_BN_free(g)
267+
}()
268+
if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) != 1 ||
269+
C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q) != 1 ||
270+
C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) != 1 {
271+
272+
return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN")
273+
}
274+
selection := C.int(C.GO_EVP_PKEY_KEYPAIR)
275+
if y != nil {
276+
pub := bigToBN(y)
277+
defer C.go_openssl_BN_free(pub)
278+
if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPubKey, pub) != 1 {
279+
return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN")
280+
}
281+
if x == nil {
282+
selection = C.int(C.GO_EVP_PKEY_PUBLIC_KEY)
283+
}
284+
}
285+
if x != nil {
286+
priv := bigToBN(x)
287+
defer C.go_openssl_BN_clear_free(priv)
288+
if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPrivKey, priv) != 1 {
289+
return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN")
290+
}
291+
}
292+
bldparams := C.go_openssl_OSSL_PARAM_BLD_to_param(bld)
293+
if bldparams == nil {
294+
return nil, newOpenSSLError("OSSL_PARAM_BLD_to_param")
295+
}
296+
defer C.go_openssl_OSSL_PARAM_free(bldparams)
297+
pkey, err := newEvpFromParams(C.GO_EVP_PKEY_DSA, selection, bldparams)
298+
if err != nil {
299+
return nil, err
300+
}
301+
if y != nil {
302+
return pkey, nil
303+
}
304+
// pkey doesn't contain the public component, but the crypto/dsa package
305+
// expects it to be always there. Generate a new key using pkey as domain
306+
// parameters placeholder.
307+
defer C.go_openssl_EVP_PKEY_free(pkey)
308+
ctx := C.go_openssl_EVP_PKEY_CTX_new_from_pkey(nil, pkey, nil)
309+
if ctx == nil {
310+
return nil, newOpenSSLError("EVP_PKEY_CTX_new_from_pkey")
311+
}
312+
defer C.go_openssl_EVP_PKEY_CTX_free(ctx)
313+
if C.go_openssl_EVP_PKEY_keygen_init(ctx) != 1 {
314+
return nil, newOpenSSLError("EVP_PKEY_keygen_init")
315+
}
316+
var gkey C.GO_EVP_PKEY_PTR
317+
if C.go_openssl_EVP_PKEY_keygen(ctx, &gkey) != 1 {
318+
return nil, newOpenSSLError("EVP_PKEY_keygen")
319+
}
320+
return gkey, nil
321+
}
322+
323+
// getDSA returns the DSA from pkey.
324+
// If pkey does not contain an DSA it panics.
325+
// The returned key should not be freed.
326+
func getDSA(pkey C.GO_EVP_PKEY_PTR) (key C.GO_DSA_PTR) {
327+
if vMajor == 1 && vMinor == 0 {
328+
if key0 := C.go_openssl_EVP_PKEY_get0(pkey); key0 != nil {
329+
key = C.GO_DSA_PTR(key0)
330+
}
331+
} else {
332+
key = C.go_openssl_EVP_PKEY_get0_DSA(pkey)
333+
}
334+
if key == nil {
335+
panic("pkey does not contain an DSA")
336+
}
337+
return key
338+
}

0 commit comments

Comments
 (0)