mirror of
https://github.com/libp2p/go-openssl.git
synced 2025-03-10 11:10:23 +08:00
430 lines
12 KiB
Go
430 lines
12 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"
|
|
)
|
|
|
|
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()
|
|
)
|
|
|
|
type KeyType int
|
|
|
|
const (
|
|
KeyTypeNone KeyType = C.EVP_PKEY_NONE
|
|
KeyTypeRSA KeyType = C.EVP_PKEY_RSA
|
|
KeyTypeRSA2 KeyType = C.EVP_PKEY_RSA2
|
|
KeyTypeDSA KeyType = C.EVP_PKEY_DSA
|
|
KeyTypeDSA1 KeyType = C.EVP_PKEY_DSA1
|
|
KeyTypeDSA2 KeyType = C.EVP_PKEY_DSA2
|
|
KeyTypeDSA3 KeyType = C.EVP_PKEY_DSA3
|
|
KeyTypeDSA4 KeyType = C.EVP_PKEY_DSA4
|
|
KeyTypeDH KeyType = C.EVP_PKEY_DH
|
|
KeyTypeDHX KeyType = C.EVP_PKEY_DHX
|
|
KeyTypeEC KeyType = C.EVP_PKEY_EC
|
|
KeyTypeHMAC KeyType = C.EVP_PKEY_HMAC
|
|
KeyTypeCMAC KeyType = C.EVP_PKEY_CMAC
|
|
KeyTypeTLS1PRF KeyType = C.EVP_PKEY_TLS1_PRF
|
|
KeyTypeHKDF KeyType = C.EVP_PKEY_HKDF
|
|
)
|
|
|
|
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)
|
|
|
|
// Type returns an identifier for what kind of key is represented by this
|
|
// object.
|
|
Type() KeyType
|
|
|
|
// 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 `Type() == KeyTypeRSA` and a key with a
|
|
// `Type() == KeyTypeRSA2` would both have `BaseType() == KeyTypeRSA`.
|
|
BaseType() KeyType
|
|
|
|
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) Type() KeyType {
|
|
return KeyType(C.EVP_PKEY_id(key.key))
|
|
}
|
|
|
|
func (key *pKey) BaseType() KeyType {
|
|
return KeyType(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 1 != C.X_EVP_SignInit(ctx, method) {
|
|
return nil, errors.New("signpkcs1v15: failed to init signature")
|
|
}
|
|
if len(data) > 0 {
|
|
if 1 != C.X_EVP_SignUpdate(
|
|
ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) {
|
|
return nil, errors.New("signpkcs1v15: failed to update signature")
|
|
}
|
|
}
|
|
sig := make([]byte, C.X_EVP_PKEY_size(key.key))
|
|
var sigblen C.uint
|
|
if 1 != C.X_EVP_SignFinal(ctx,
|
|
((*C.uchar)(unsafe.Pointer(&sig[0]))), &sigblen, key.key) {
|
|
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 1 != C.X_EVP_VerifyInit(ctx, method) {
|
|
return errors.New("verifypkcs1v15: failed to init verify")
|
|
}
|
|
if len(data) > 0 {
|
|
if 1 != C.X_EVP_VerifyUpdate(
|
|
ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) {
|
|
return errors.New("verifypkcs1v15: failed to update verify")
|
|
}
|
|
}
|
|
if 1 != C.X_EVP_VerifyFinal(ctx,
|
|
((*C.uchar)(unsafe.Pointer(&sig[0]))), C.uint(len(sig)), key.key) {
|
|
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.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)
|
|
|
|
rc := C.PEM_write_bio_PUBKEY(bio, key.key)
|
|
if rc != 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) {
|
|
var rc C.int
|
|
|
|
// 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
|
|
rc = C.EVP_PKEY_paramgen_init(paramCtx)
|
|
if rc != 1 {
|
|
return nil, errors.New("failed initializing EC parameter generation context")
|
|
}
|
|
|
|
// Set curve in EC parameter generation context
|
|
rc = C.X_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(paramCtx, C.int(curve))
|
|
if rc != 1 {
|
|
return nil, errors.New("failed setting curve in EC parameter generation context")
|
|
}
|
|
|
|
// Create parameter object
|
|
var params *C.EVP_PKEY
|
|
rc = C.EVP_PKEY_paramgen(paramCtx, ¶ms)
|
|
if rc != 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
|
|
rc = C.EVP_PKEY_keygen_init(keyCtx)
|
|
if rc != 1 {
|
|
return nil, errors.New("failed initializing EC key generation context")
|
|
}
|
|
rc = C.EVP_PKEY_keygen(keyCtx, &privKey)
|
|
if rc != 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
|
|
}
|