mirror of
https://github.com/libp2p/go-openssl.git
synced 2025-02-07 06:40:08 +08:00
Fix `staticcheck` issues: - S1028 use `fmt.Errorf` to construct formatted errors - ST1017 yoda conditions - ST1005 error message capitalization - ST1006 avoid `self` as receiver name - S1030 use `buf.String` - S1011 avoid redundant loop when `append` suffices - SA4006 unused value - S1019 remove redundant capacity on `make` call - SA2002 `t.Fatal` called outside of test Exported error violates ST1012, which is ignored by this PR since rename may cause breaking changes. Remove redundant parentheses wrapping, and use CamelCase naming while at it.
523 lines
14 KiB
Go
523 lines
14 KiB
Go
// Copyright (C) 2017. See AUTHORS.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package openssl
|
|
|
|
// #include "shim.h"
|
|
import "C"
|
|
|
|
import (
|
|
"errors"
|
|
"io/ioutil"
|
|
"runtime"
|
|
"unsafe"
|
|
)
|
|
|
|
var ( // some (effectively) constants for tests to refer to
|
|
ed25519_support = C.X_ED25519_SUPPORT != 0
|
|
)
|
|
|
|
type Method *C.EVP_MD
|
|
|
|
var (
|
|
SHA1_Method Method = C.X_EVP_sha1()
|
|
SHA256_Method Method = C.X_EVP_sha256()
|
|
SHA512_Method Method = C.X_EVP_sha512()
|
|
)
|
|
|
|
// Constants for the various key types.
|
|
// Mapping of name -> NID taken from openssl/evp.h
|
|
const (
|
|
KeyTypeNone = NID_undef
|
|
KeyTypeRSA = NID_rsaEncryption
|
|
KeyTypeRSA2 = NID_rsa
|
|
KeyTypeDSA = NID_dsa
|
|
KeyTypeDSA1 = NID_dsa_2
|
|
KeyTypeDSA2 = NID_dsaWithSHA
|
|
KeyTypeDSA3 = NID_dsaWithSHA1
|
|
KeyTypeDSA4 = NID_dsaWithSHA1_2
|
|
KeyTypeDH = NID_dhKeyAgreement
|
|
KeyTypeDHX = NID_dhpublicnumber
|
|
KeyTypeEC = NID_X9_62_id_ecPublicKey
|
|
KeyTypeHMAC = NID_hmac
|
|
KeyTypeCMAC = NID_cmac
|
|
KeyTypeTLS1PRF = NID_tls1_prf
|
|
KeyTypeHKDF = NID_hkdf
|
|
KeyTypeX25519 = NID_X25519
|
|
KeyTypeX448 = NID_X448
|
|
KeyTypeED25519 = NID_ED25519
|
|
KeyTypeED448 = NID_ED448
|
|
)
|
|
|
|
type PublicKey interface {
|
|
// Verifies the data signature using PKCS1.15
|
|
VerifyPKCS1v15(method Method, data, sig []byte) error
|
|
|
|
// MarshalPKIXPublicKeyPEM converts the public key to PEM-encoded PKIX
|
|
// format
|
|
MarshalPKIXPublicKeyPEM() (pem_block []byte, err error)
|
|
|
|
// MarshalPKIXPublicKeyDER converts the public key to DER-encoded PKIX
|
|
// format
|
|
MarshalPKIXPublicKeyDER() (der_block []byte, err error)
|
|
|
|
// KeyType returns an identifier for what kind of key is represented by this
|
|
// object.
|
|
KeyType() NID
|
|
|
|
// BaseType returns an identifier for what kind of key is represented
|
|
// by this object.
|
|
// Keys that share same algorithm but use different legacy formats
|
|
// will have the same BaseType.
|
|
//
|
|
// For example, a key with a `KeyType() == KeyTypeRSA` and a key with a
|
|
// `KeyType() == KeyTypeRSA2` would both have `BaseType() == KeyTypeRSA`.
|
|
BaseType() NID
|
|
|
|
// Equal compares the key with the passed in key.
|
|
Equal(key PublicKey) bool
|
|
|
|
// Size returns the size (in bytes) of signatures created with this key.
|
|
Size() int
|
|
|
|
evpPKey() *C.EVP_PKEY
|
|
}
|
|
|
|
type PrivateKey interface {
|
|
PublicKey
|
|
|
|
// Signs the data using PKCS1.15
|
|
SignPKCS1v15(Method, []byte) ([]byte, error)
|
|
|
|
// MarshalPKCS1PrivateKeyPEM converts the private key to PEM-encoded PKCS1
|
|
// format
|
|
MarshalPKCS1PrivateKeyPEM() (pem_block []byte, err error)
|
|
|
|
// MarshalPKCS1PrivateKeyDER converts the private key to DER-encoded PKCS1
|
|
// format
|
|
MarshalPKCS1PrivateKeyDER() (der_block []byte, err error)
|
|
}
|
|
|
|
type pKey struct {
|
|
key *C.EVP_PKEY
|
|
}
|
|
|
|
func (key *pKey) evpPKey() *C.EVP_PKEY { return key.key }
|
|
|
|
func (key *pKey) Equal(other PublicKey) bool {
|
|
return C.EVP_PKEY_cmp(key.key, other.evpPKey()) == 1
|
|
}
|
|
|
|
func (key *pKey) KeyType() NID {
|
|
return NID(C.EVP_PKEY_id(key.key))
|
|
}
|
|
|
|
func (key *pKey) Size() int {
|
|
return int(C.EVP_PKEY_size(key.key))
|
|
}
|
|
|
|
func (key *pKey) BaseType() NID {
|
|
return NID(C.EVP_PKEY_base_id(key.key))
|
|
}
|
|
|
|
func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) {
|
|
|
|
ctx := C.X_EVP_MD_CTX_new()
|
|
defer C.X_EVP_MD_CTX_free(ctx)
|
|
|
|
if key.KeyType() == KeyTypeED25519 {
|
|
// do ED specific one-shot sign
|
|
|
|
if method != nil || len(data) == 0 {
|
|
return nil, errors.New("signpkcs1v15: 0-length data or non-null digest")
|
|
}
|
|
|
|
if C.X_EVP_DigestSignInit(ctx, nil, nil, nil, key.key) != 1 {
|
|
return nil, errors.New("signpkcs1v15: failed to init signature")
|
|
}
|
|
|
|
// evp signatures are 64 bytes
|
|
sig := make([]byte, 64)
|
|
var sigblen C.size_t = 64
|
|
if C.X_EVP_DigestSign(ctx,
|
|
(*C.uchar)(unsafe.Pointer(&sig[0])),
|
|
&sigblen,
|
|
(*C.uchar)(unsafe.Pointer(&data[0])),
|
|
C.size_t(len(data))) != 1 {
|
|
return nil, errors.New("signpkcs1v15: failed to do one-shot signature")
|
|
}
|
|
|
|
return sig[:sigblen], nil
|
|
} else {
|
|
if C.X_EVP_SignInit(ctx, method) != 1 {
|
|
return nil, errors.New("signpkcs1v15: failed to init signature")
|
|
}
|
|
if len(data) > 0 {
|
|
if C.X_EVP_SignUpdate(
|
|
ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) != 1 {
|
|
return nil, errors.New("signpkcs1v15: failed to update signature")
|
|
}
|
|
}
|
|
sig := make([]byte, C.X_EVP_PKEY_size(key.key))
|
|
var sigblen C.uint
|
|
if C.X_EVP_SignFinal(ctx,
|
|
(*C.uchar)(unsafe.Pointer(&sig[0])), &sigblen, key.key) != 1 {
|
|
return nil, errors.New("signpkcs1v15: failed to finalize signature")
|
|
}
|
|
return sig[:sigblen], nil
|
|
}
|
|
}
|
|
|
|
func (key *pKey) VerifyPKCS1v15(method Method, data, sig []byte) error {
|
|
ctx := C.X_EVP_MD_CTX_new()
|
|
defer C.X_EVP_MD_CTX_free(ctx)
|
|
|
|
if len(sig) == 0 {
|
|
return errors.New("verifypkcs1v15: 0-length sig")
|
|
}
|
|
|
|
if key.KeyType() == KeyTypeED25519 {
|
|
// do ED specific one-shot sign
|
|
|
|
if method != nil || len(data) == 0 {
|
|
return errors.New("verifypkcs1v15: 0-length data or non-null digest")
|
|
}
|
|
|
|
if C.X_EVP_DigestVerifyInit(ctx, nil, nil, nil, key.key) != 1 {
|
|
return errors.New("verifypkcs1v15: failed to init verify")
|
|
}
|
|
|
|
if C.X_EVP_DigestVerify(ctx,
|
|
(*C.uchar)(unsafe.Pointer(&sig[0])),
|
|
C.size_t(len(sig)),
|
|
(*C.uchar)(unsafe.Pointer(&data[0])),
|
|
C.size_t(len(data))) != 1 {
|
|
return errors.New("verifypkcs1v15: failed to do one-shot verify")
|
|
}
|
|
|
|
return nil
|
|
|
|
} else {
|
|
if C.X_EVP_VerifyInit(ctx, method) != 1 {
|
|
return errors.New("verifypkcs1v15: failed to init verify")
|
|
}
|
|
if len(data) > 0 {
|
|
if C.X_EVP_VerifyUpdate(
|
|
ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) != 1 {
|
|
return errors.New("verifypkcs1v15: failed to update verify")
|
|
}
|
|
}
|
|
if C.X_EVP_VerifyFinal(ctx,
|
|
(*C.uchar)(unsafe.Pointer(&sig[0])), C.uint(len(sig)), key.key) != 1 {
|
|
return errors.New("verifypkcs1v15: failed to finalize verify")
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (key *pKey) MarshalPKCS1PrivateKeyPEM() (pem_block []byte,
|
|
err error) {
|
|
bio := C.BIO_new(C.BIO_s_mem())
|
|
if bio == nil {
|
|
return nil, errors.New("failed to allocate memory BIO")
|
|
}
|
|
defer C.BIO_free(bio)
|
|
|
|
// PEM_write_bio_PrivateKey_traditional will use the key-specific PKCS1
|
|
// format if one is available for that key type, otherwise it will encode
|
|
// to a PKCS8 key.
|
|
if int(C.X_PEM_write_bio_PrivateKey_traditional(bio, key.key, nil, nil,
|
|
C.int(0), nil, nil)) != 1 {
|
|
return nil, errors.New("failed dumping private key")
|
|
}
|
|
|
|
return ioutil.ReadAll(asAnyBio(bio))
|
|
}
|
|
|
|
func (key *pKey) MarshalPKCS1PrivateKeyDER() (der_block []byte,
|
|
err error) {
|
|
bio := C.BIO_new(C.BIO_s_mem())
|
|
if bio == nil {
|
|
return nil, errors.New("failed to allocate memory BIO")
|
|
}
|
|
defer C.BIO_free(bio)
|
|
|
|
if int(C.i2d_PrivateKey_bio(bio, key.key)) != 1 {
|
|
return nil, errors.New("failed dumping private key der")
|
|
}
|
|
|
|
return ioutil.ReadAll(asAnyBio(bio))
|
|
}
|
|
|
|
func (key *pKey) MarshalPKIXPublicKeyPEM() (pem_block []byte,
|
|
err error) {
|
|
bio := C.BIO_new(C.BIO_s_mem())
|
|
if bio == nil {
|
|
return nil, errors.New("failed to allocate memory BIO")
|
|
}
|
|
defer C.BIO_free(bio)
|
|
|
|
if int(C.PEM_write_bio_PUBKEY(bio, key.key)) != 1 {
|
|
return nil, errors.New("failed dumping public key pem")
|
|
}
|
|
|
|
return ioutil.ReadAll(asAnyBio(bio))
|
|
}
|
|
|
|
func (key *pKey) MarshalPKIXPublicKeyDER() (der_block []byte,
|
|
err error) {
|
|
bio := C.BIO_new(C.BIO_s_mem())
|
|
if bio == nil {
|
|
return nil, errors.New("failed to allocate memory BIO")
|
|
}
|
|
defer C.BIO_free(bio)
|
|
|
|
if int(C.i2d_PUBKEY_bio(bio, key.key)) != 1 {
|
|
return nil, errors.New("failed dumping public key der")
|
|
}
|
|
|
|
return ioutil.ReadAll(asAnyBio(bio))
|
|
}
|
|
|
|
// LoadPrivateKeyFromPEM loads a private key from a PEM-encoded block.
|
|
func LoadPrivateKeyFromPEM(pem_block []byte) (PrivateKey, error) {
|
|
if len(pem_block) == 0 {
|
|
return nil, errors.New("empty pem block")
|
|
}
|
|
bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]),
|
|
C.int(len(pem_block)))
|
|
if bio == nil {
|
|
return nil, errors.New("failed creating bio")
|
|
}
|
|
defer C.BIO_free(bio)
|
|
|
|
key := C.PEM_read_bio_PrivateKey(bio, nil, nil, nil)
|
|
if key == nil {
|
|
return nil, errors.New("failed reading private key")
|
|
}
|
|
|
|
p := &pKey{key: key}
|
|
runtime.SetFinalizer(p, func(p *pKey) {
|
|
C.X_EVP_PKEY_free(p.key)
|
|
})
|
|
return p, nil
|
|
}
|
|
|
|
// LoadPrivateKeyFromPEMWithPassword loads a private key from a PEM-encoded block.
|
|
func LoadPrivateKeyFromPEMWithPassword(pem_block []byte, password string) (
|
|
PrivateKey, error) {
|
|
if len(pem_block) == 0 {
|
|
return nil, errors.New("empty pem block")
|
|
}
|
|
bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]),
|
|
C.int(len(pem_block)))
|
|
if bio == nil {
|
|
return nil, errors.New("failed creating bio")
|
|
}
|
|
defer C.BIO_free(bio)
|
|
cs := C.CString(password)
|
|
defer C.free(unsafe.Pointer(cs))
|
|
key := C.PEM_read_bio_PrivateKey(bio, nil, nil, unsafe.Pointer(cs))
|
|
if key == nil {
|
|
return nil, errors.New("failed reading private key")
|
|
}
|
|
|
|
p := &pKey{key: key}
|
|
runtime.SetFinalizer(p, func(p *pKey) {
|
|
C.X_EVP_PKEY_free(p.key)
|
|
})
|
|
return p, nil
|
|
}
|
|
|
|
// LoadPrivateKeyFromDER loads a private key from a DER-encoded block.
|
|
func LoadPrivateKeyFromDER(der_block []byte) (PrivateKey, error) {
|
|
if len(der_block) == 0 {
|
|
return nil, errors.New("empty der block")
|
|
}
|
|
bio := C.BIO_new_mem_buf(unsafe.Pointer(&der_block[0]),
|
|
C.int(len(der_block)))
|
|
if bio == nil {
|
|
return nil, errors.New("failed creating bio")
|
|
}
|
|
defer C.BIO_free(bio)
|
|
|
|
key := C.d2i_PrivateKey_bio(bio, nil)
|
|
if key == nil {
|
|
return nil, errors.New("failed reading private key der")
|
|
}
|
|
|
|
p := &pKey{key: key}
|
|
runtime.SetFinalizer(p, func(p *pKey) {
|
|
C.X_EVP_PKEY_free(p.key)
|
|
})
|
|
return p, nil
|
|
}
|
|
|
|
// LoadPrivateKeyFromPEMWidthPassword loads a private key from a PEM-encoded block.
|
|
// Backwards-compatible with typo
|
|
func LoadPrivateKeyFromPEMWidthPassword(pem_block []byte, password string) (
|
|
PrivateKey, error) {
|
|
return LoadPrivateKeyFromPEMWithPassword(pem_block, password)
|
|
}
|
|
|
|
// LoadPublicKeyFromPEM loads a public key from a PEM-encoded block.
|
|
func LoadPublicKeyFromPEM(pem_block []byte) (PublicKey, error) {
|
|
if len(pem_block) == 0 {
|
|
return nil, errors.New("empty pem block")
|
|
}
|
|
bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]),
|
|
C.int(len(pem_block)))
|
|
if bio == nil {
|
|
return nil, errors.New("failed creating bio")
|
|
}
|
|
defer C.BIO_free(bio)
|
|
|
|
key := C.PEM_read_bio_PUBKEY(bio, nil, nil, nil)
|
|
if key == nil {
|
|
return nil, errors.New("failed reading public key der")
|
|
}
|
|
|
|
p := &pKey{key: key}
|
|
runtime.SetFinalizer(p, func(p *pKey) {
|
|
C.X_EVP_PKEY_free(p.key)
|
|
})
|
|
return p, nil
|
|
}
|
|
|
|
// LoadPublicKeyFromDER loads a public key from a DER-encoded block.
|
|
func LoadPublicKeyFromDER(der_block []byte) (PublicKey, error) {
|
|
if len(der_block) == 0 {
|
|
return nil, errors.New("empty der block")
|
|
}
|
|
bio := C.BIO_new_mem_buf(unsafe.Pointer(&der_block[0]),
|
|
C.int(len(der_block)))
|
|
if bio == nil {
|
|
return nil, errors.New("failed creating bio")
|
|
}
|
|
defer C.BIO_free(bio)
|
|
|
|
key := C.d2i_PUBKEY_bio(bio, nil)
|
|
if key == nil {
|
|
return nil, errors.New("failed reading public key der")
|
|
}
|
|
|
|
p := &pKey{key: key}
|
|
runtime.SetFinalizer(p, func(p *pKey) {
|
|
C.X_EVP_PKEY_free(p.key)
|
|
})
|
|
return p, nil
|
|
}
|
|
|
|
// GenerateRSAKey generates a new RSA private key with an exponent of 3.
|
|
func GenerateRSAKey(bits int) (PrivateKey, error) {
|
|
return GenerateRSAKeyWithExponent(bits, 3)
|
|
}
|
|
|
|
// GenerateRSAKeyWithExponent generates a new RSA private key.
|
|
func GenerateRSAKeyWithExponent(bits int, exponent int) (PrivateKey, error) {
|
|
rsa := C.RSA_generate_key(C.int(bits), C.ulong(exponent), nil, nil)
|
|
if rsa == nil {
|
|
return nil, errors.New("failed to generate RSA key")
|
|
}
|
|
key := C.X_EVP_PKEY_new()
|
|
if key == nil {
|
|
return nil, errors.New("failed to allocate EVP_PKEY")
|
|
}
|
|
if C.X_EVP_PKEY_assign_charp(key, C.EVP_PKEY_RSA, (*C.char)(unsafe.Pointer(rsa))) != 1 {
|
|
C.X_EVP_PKEY_free(key)
|
|
return nil, errors.New("failed to assign RSA key")
|
|
}
|
|
p := &pKey{key: key}
|
|
runtime.SetFinalizer(p, func(p *pKey) {
|
|
C.X_EVP_PKEY_free(p.key)
|
|
})
|
|
return p, nil
|
|
}
|
|
|
|
// GenerateECKey generates a new elliptic curve private key on the speicified
|
|
// curve.
|
|
func GenerateECKey(curve EllipticCurve) (PrivateKey, error) {
|
|
|
|
// Create context for parameter generation
|
|
paramCtx := C.EVP_PKEY_CTX_new_id(C.EVP_PKEY_EC, nil)
|
|
if paramCtx == nil {
|
|
return nil, errors.New("failed creating EC parameter generation context")
|
|
}
|
|
defer C.EVP_PKEY_CTX_free(paramCtx)
|
|
|
|
// Intialize the parameter generation
|
|
if int(C.EVP_PKEY_paramgen_init(paramCtx)) != 1 {
|
|
return nil, errors.New("failed initializing EC parameter generation context")
|
|
}
|
|
|
|
// Set curve in EC parameter generation context
|
|
if int(C.X_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(paramCtx, C.int(curve))) != 1 {
|
|
return nil, errors.New("failed setting curve in EC parameter generation context")
|
|
}
|
|
|
|
// Create parameter object
|
|
var params *C.EVP_PKEY
|
|
if int(C.EVP_PKEY_paramgen(paramCtx, ¶ms)) != 1 {
|
|
return nil, errors.New("failed creating EC key generation parameters")
|
|
}
|
|
defer C.EVP_PKEY_free(params)
|
|
|
|
// Create context for the key generation
|
|
keyCtx := C.EVP_PKEY_CTX_new(params, nil)
|
|
if keyCtx == nil {
|
|
return nil, errors.New("failed creating EC key generation context")
|
|
}
|
|
defer C.EVP_PKEY_CTX_free(keyCtx)
|
|
|
|
// Generate the key
|
|
var privKey *C.EVP_PKEY
|
|
if int(C.EVP_PKEY_keygen_init(keyCtx)) != 1 {
|
|
return nil, errors.New("failed initializing EC key generation context")
|
|
}
|
|
if int(C.EVP_PKEY_keygen(keyCtx, &privKey)) != 1 {
|
|
return nil, errors.New("failed generating EC private key")
|
|
}
|
|
|
|
p := &pKey{key: privKey}
|
|
runtime.SetFinalizer(p, func(p *pKey) {
|
|
C.X_EVP_PKEY_free(p.key)
|
|
})
|
|
return p, nil
|
|
}
|
|
|
|
// GenerateED25519Key generates a Ed25519 key
|
|
func GenerateED25519Key() (PrivateKey, error) {
|
|
// Key context
|
|
keyCtx := C.EVP_PKEY_CTX_new_id(C.X_EVP_PKEY_ED25519, nil)
|
|
if keyCtx == nil {
|
|
return nil, errors.New("failed creating EC parameter generation context")
|
|
}
|
|
defer C.EVP_PKEY_CTX_free(keyCtx)
|
|
|
|
// Generate the key
|
|
var privKey *C.EVP_PKEY
|
|
if int(C.EVP_PKEY_keygen_init(keyCtx)) != 1 {
|
|
return nil, errors.New("failed initializing ED25519 key generation context")
|
|
}
|
|
if int(C.EVP_PKEY_keygen(keyCtx, &privKey)) != 1 {
|
|
return nil, errors.New("failed generating ED25519 private key")
|
|
}
|
|
|
|
p := &pKey{key: privKey}
|
|
runtime.SetFinalizer(p, func(p *pKey) {
|
|
C.X_EVP_PKEY_free(p.key)
|
|
})
|
|
return p, nil
|
|
}
|