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