From 56ac6d09dac97fcff12f1a800c0cc5a0df626cc5 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Tue, 7 Apr 2020 15:43:29 -0400
Subject: [PATCH 01/35] add pkcs7 support for loading certificates

---
 cert.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 shim.h  |  3 +++
 2 files changed, 56 insertions(+)

diff --git a/cert.go b/cert.go
index e841e22c..4e891141 100644
--- a/cert.go
+++ b/cert.go
@@ -413,3 +413,56 @@ func (c *Certificate) SetVersion(version X509_Version) error {
 	}
 	return nil
 }
+
+// LoadCertificatesFromPKCS7 loads certificates from a DER-encoded pkcs7.
+func LoadCertificatesFromPKCS7(der_block []byte) ([]*Certificate, 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)
+
+	var p7 *C.PKCS7
+	p7 = C.d2i_PKCS7_bio(bio, nil)
+	if p7 == nil {
+		return nil, errors.New("failed reading pkcs7 data")
+	}
+	defer C.PKCS7_free(p7)
+
+	var certs *C.struct_stack_st_X509
+	i := C.OBJ_obj2nid(p7._type)
+
+	// credit goes to Chris Bandy who referenced Alan Shen's article for this cgo representation of a union:
+	// https://sunzenshen.github.io/tutorials/2015/05/09/cgotchas-intro.html
+	switch i {
+	case C.NID_pkcs7_signed:
+		signed := *(**C.PKCS7_SIGNED)(unsafe.Pointer(&p7.d[0]))
+		certs = signed.cert
+	case C.NID_pkcs7_signedAndEnveloped:
+		signedAndEnveloped := *(**C.PKCS7_SIGN_ENVELOPE)(unsafe.Pointer(&p7.d[0]))
+		certs = signedAndEnveloped.cert
+	}
+
+	ret := loadCertificateStack(certs)
+	return ret, nil
+}
+
+// loadCertificateStack loads up a stack of x509 certificates and returns them.
+func loadCertificateStack(sk *C.struct_stack_st_X509) (rv []*Certificate) {
+	sk_num := int(C.X_sk_X509_num(sk))
+	rv = make([]*Certificate, 0, sk_num)
+	for i := 0; i < sk_num; i++ {
+		x := C.X_sk_X509_value(sk, C.int(i))
+
+		cert := &Certificate{x: x}
+		runtime.SetFinalizer(cert, func(cert *Certificate) {
+			C.X509_free(cert.x)
+		})
+		rv = append(rv, cert)
+	}
+	return rv
+}
diff --git a/shim.h b/shim.h
index b792822b..cf1a1b82 100644
--- a/shim.h
+++ b/shim.h
@@ -28,6 +28,9 @@
 #include <openssl/ssl.h>
 #include <openssl/x509v3.h>
 #include <openssl/ec.h>
+#include <openssl/pkcs7.h>
+#include <openssl/objects.h>
+#include <openssl/obj_mac.h>
 
 #ifndef SSL_MODE_RELEASE_BUFFERS
 #define SSL_MODE_RELEASE_BUFFERS 0

From c9b885c1e983ba8804c2851a71040f6d55bd2d5a Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 9 Apr 2020 10:42:15 -0400
Subject: [PATCH 02/35] wip: verify trust of cert for CA file

---
 cert.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/cert.go b/cert.go
index 4e891141..4417ddb1 100644
--- a/cert.go
+++ b/cert.go
@@ -19,6 +19,7 @@ import "C"
 
 import (
 	"errors"
+	"fmt"
 	"io/ioutil"
 	"math/big"
 	"runtime"
@@ -414,8 +415,13 @@ func (c *Certificate) SetVersion(version X509_Version) error {
 	return nil
 }
 
+type PKCS7 struct {
+	p7    *C.PKCS7
+	Certs []*Certificate
+}
+
 // LoadCertificatesFromPKCS7 loads certificates from a DER-encoded pkcs7.
-func LoadCertificatesFromPKCS7(der_block []byte) ([]*Certificate, error) {
+func LoadCertificatesFromPKCS7(der_block []byte) (*PKCS7, error) {
 	if len(der_block) == 0 {
 		return nil, errors.New("empty der block")
 	}
@@ -431,7 +437,12 @@ func LoadCertificatesFromPKCS7(der_block []byte) ([]*Certificate, error) {
 	if p7 == nil {
 		return nil, errors.New("failed reading pkcs7 data")
 	}
-	defer C.PKCS7_free(p7)
+	ret := &PKCS7{
+		p7: p7,
+	}
+	runtime.SetFinalizer(ret, func(pkcs7 *PKCS7) {
+		C.PKCS7_free(pkcs7.p7)
+	})
 
 	var certs *C.struct_stack_st_X509
 	i := C.OBJ_obj2nid(p7._type)
@@ -447,7 +458,7 @@ func LoadCertificatesFromPKCS7(der_block []byte) ([]*Certificate, error) {
 		certs = signedAndEnveloped.cert
 	}
 
-	ret := loadCertificateStack(certs)
+	ret.Certs = loadCertificateStack(certs)
 	return ret, nil
 }
 
@@ -466,3 +477,52 @@ func loadCertificateStack(sk *C.struct_stack_st_X509) (rv []*Certificate) {
 	}
 	return rv
 }
+
+// VerifyTrustAndGetIssuerCertificate takes a chained PEM file, loading all certificates into a Store,
+// and verifies trust for the certificate.  The issuing certificate from the chained PEM file is returned.
+func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certificate, error) {
+	cert_ctx, err := NewCertificateStore()
+	if err != nil {
+		return nil, err
+	}
+	err = cert_ctx.LoadCertificatesFromPEM(ca_file)
+	if err != nil {
+		return nil, err
+	}
+	// TODO: implement custom callback/verification logic?
+	// C.X509_STORE_set_verify_cb(cert_ctx, (*[0]byte)(C.X_SSL_CTX_test_verify_cb))
+
+	// TODO: load masterList from file?
+	// lookup := C.X509_STORE_add_lookup(cert_ctx.store, C.X509_LOOKUP_file())
+	// if lookup == nil {
+	// 	return nil, errors.New("unable to add lookup to store")
+	// }
+	// rc := C.X509_LOOKUP_ctrl(lookup, C.X509_L_FILE_LOAD, C.CString("/Users/etammaru/go/src/github.mitekcloud.local/engineering/nfcsvc/masterList.pem"), C.long(C.X509_FILETYPE_PEM), nil)
+	// if rc == 0 {
+	// 	return nil, errors.New("unable to load master list")
+	// }
+
+	store := C.X509_STORE_CTX_new()
+	if store == nil {
+		return nil, errors.New("failed to create new X509_STORE_CTX")
+	}
+	defer C.X509_STORE_CTX_free(store)
+
+	C.X509_STORE_set_flags(cert_ctx.store, 0)
+	rc := C.X509_STORE_CTX_init(store, cert_ctx.store, c.x, nil)
+	if rc == 0 {
+		return nil, errors.New("unable to init X509_STORE_CTX")
+	}
+
+	i := C.X509_verify_cert(store)
+	if i != 1 {
+		errCode := C.X509_STORE_CTX_get_error(store)
+		return nil, fmt.Errorf("verification of certificate failed - errorCode %v", errCode)
+	}
+	// TODO: figure out how to access current_issuer
+	// issuer := &Certificate{x: store.current_issuer}
+	// runtime.SetFinalizer(cert, func(cert *Certificate) {
+	// 	C.X509_free(cert.x)
+	// })
+	return nil, nil
+}

From 2cd79eabd95ca2c81dd2bcd09edfd9e88d188072 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 9 Apr 2020 12:52:19 -0400
Subject: [PATCH 03/35] fix race condition with freeing X509 memory

---
 cert.go | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/cert.go b/cert.go
index 4417ddb1..e967d1f4 100644
--- a/cert.go
+++ b/cert.go
@@ -458,24 +458,22 @@ func LoadCertificatesFromPKCS7(der_block []byte) (*PKCS7, error) {
 		certs = signedAndEnveloped.cert
 	}
 
-	ret.Certs = loadCertificateStack(certs)
+	ret.loadCertificateStack(certs)
 	return ret, nil
 }
 
-// loadCertificateStack loads up a stack of x509 certificates and returns them.
-func loadCertificateStack(sk *C.struct_stack_st_X509) (rv []*Certificate) {
+// loadCertificateStack loads up a stack of x509 certificates into the PKCS7 struct.
+func (p *PKCS7) loadCertificateStack(sk *C.struct_stack_st_X509) {
 	sk_num := int(C.X_sk_X509_num(sk))
-	rv = make([]*Certificate, 0, sk_num)
+	p.Certs = make([]*Certificate, 0, sk_num)
 	for i := 0; i < sk_num; i++ {
 		x := C.X_sk_X509_value(sk, C.int(i))
 
-		cert := &Certificate{x: x}
-		runtime.SetFinalizer(cert, func(cert *Certificate) {
-			C.X509_free(cert.x)
-		})
-		rv = append(rv, cert)
+		// ref holds on to the underlying connection memory so we don't need to
+		// worry about incrementing refcounts manually or freeing the X509
+		cert := &Certificate{x: x, ref: p}
+		p.Certs = append(p.Certs, cert)
 	}
-	return rv
 }
 
 // VerifyTrustAndGetIssuerCertificate takes a chained PEM file, loading all certificates into a Store,

From 0eed56a3c9fb6cfdb88c7a883b80983bbc4f6b92 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 9 Apr 2020 13:35:02 -0400
Subject: [PATCH 04/35] return VerifyResult

---
 cert.go | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/cert.go b/cert.go
index e967d1f4..d2d24911 100644
--- a/cert.go
+++ b/cert.go
@@ -19,7 +19,6 @@ import "C"
 
 import (
 	"errors"
-	"fmt"
 	"io/ioutil"
 	"math/big"
 	"runtime"
@@ -478,14 +477,14 @@ func (p *PKCS7) loadCertificateStack(sk *C.struct_stack_st_X509) {
 
 // VerifyTrustAndGetIssuerCertificate takes a chained PEM file, loading all certificates into a Store,
 // and verifies trust for the certificate.  The issuing certificate from the chained PEM file is returned.
-func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certificate, error) {
+func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certificate, VerifyResult, error) {
 	cert_ctx, err := NewCertificateStore()
 	if err != nil {
-		return nil, err
+		return nil, 0, err
 	}
 	err = cert_ctx.LoadCertificatesFromPEM(ca_file)
 	if err != nil {
-		return nil, err
+		return nil, 0, err
 	}
 	// TODO: implement custom callback/verification logic?
 	// C.X509_STORE_set_verify_cb(cert_ctx, (*[0]byte)(C.X_SSL_CTX_test_verify_cb))
@@ -502,25 +501,26 @@ func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certi
 
 	store := C.X509_STORE_CTX_new()
 	if store == nil {
-		return nil, errors.New("failed to create new X509_STORE_CTX")
+		return nil, 0, errors.New("failed to create new X509_STORE_CTX")
 	}
 	defer C.X509_STORE_CTX_free(store)
 
 	C.X509_STORE_set_flags(cert_ctx.store, 0)
 	rc := C.X509_STORE_CTX_init(store, cert_ctx.store, c.x, nil)
 	if rc == 0 {
-		return nil, errors.New("unable to init X509_STORE_CTX")
+		return nil, 0, errors.New("unable to init X509_STORE_CTX")
 	}
 
 	i := C.X509_verify_cert(store)
+	verifyResult := Ok
 	if i != 1 {
-		errCode := C.X509_STORE_CTX_get_error(store)
-		return nil, fmt.Errorf("verification of certificate failed - errorCode %v", errCode)
+		verifyResult = VerifyResult(C.X509_STORE_CTX_get_error(store))
 	}
+
 	// TODO: figure out how to access current_issuer
 	// issuer := &Certificate{x: store.current_issuer}
 	// runtime.SetFinalizer(cert, func(cert *Certificate) {
 	// 	C.X509_free(cert.x)
 	// })
-	return nil, nil
+	return nil, verifyResult, nil
 }

From 6a12e16c1d9df2575508921349848d66461934a1 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 9 Apr 2020 16:53:31 -0400
Subject: [PATCH 05/35] return issuer certificate from verification

---
 cert.go | 26 +++++++-------------------
 1 file changed, 7 insertions(+), 19 deletions(-)

diff --git a/cert.go b/cert.go
index d2d24911..4b4c86a6 100644
--- a/cert.go
+++ b/cert.go
@@ -486,18 +486,6 @@ func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certi
 	if err != nil {
 		return nil, 0, err
 	}
-	// TODO: implement custom callback/verification logic?
-	// C.X509_STORE_set_verify_cb(cert_ctx, (*[0]byte)(C.X_SSL_CTX_test_verify_cb))
-
-	// TODO: load masterList from file?
-	// lookup := C.X509_STORE_add_lookup(cert_ctx.store, C.X509_LOOKUP_file())
-	// if lookup == nil {
-	// 	return nil, errors.New("unable to add lookup to store")
-	// }
-	// rc := C.X509_LOOKUP_ctrl(lookup, C.X509_L_FILE_LOAD, C.CString("/Users/etammaru/go/src/github.mitekcloud.local/engineering/nfcsvc/masterList.pem"), C.long(C.X509_FILETYPE_PEM), nil)
-	// if rc == 0 {
-	// 	return nil, errors.New("unable to load master list")
-	// }
 
 	store := C.X509_STORE_CTX_new()
 	if store == nil {
@@ -512,15 +500,15 @@ func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certi
 	}
 
 	i := C.X509_verify_cert(store)
+	var issuer *Certificate
 	verifyResult := Ok
 	if i != 1 {
 		verifyResult = VerifyResult(C.X509_STORE_CTX_get_error(store))
+	} else {
+		issuer = &Certificate{x: C.X509_STORE_CTX_get0_current_issuer(store)}
+		runtime.SetFinalizer(issuer, func(cert *Certificate) {
+			C.X509_free(cert.x)
+		})
 	}
-
-	// TODO: figure out how to access current_issuer
-	// issuer := &Certificate{x: store.current_issuer}
-	// runtime.SetFinalizer(cert, func(cert *Certificate) {
-	// 	C.X509_free(cert.x)
-	// })
-	return nil, verifyResult, nil
+	return issuer, verifyResult, nil
 }

From d97af91a26362db38550b24ff107395ce5ebec29 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Tue, 14 Apr 2020 10:31:35 -0400
Subject: [PATCH 06/35] add crl_check support to verify method

---
 cert.go | 22 ++++++++++++++++++----
 crl.go  | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 ctx.go  | 29 +++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+), 4 deletions(-)
 create mode 100644 crl.go

diff --git a/cert.go b/cert.go
index 4b4c86a6..4a820102 100644
--- a/cert.go
+++ b/cert.go
@@ -477,7 +477,8 @@ func (p *PKCS7) loadCertificateStack(sk *C.struct_stack_st_X509) {
 
 // VerifyTrustAndGetIssuerCertificate takes a chained PEM file, loading all certificates into a Store,
 // and verifies trust for the certificate.  The issuing certificate from the chained PEM file is returned.
-func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certificate, VerifyResult, error) {
+// If crls are given, then crl_check is also performed by loading all crls into the Store.
+func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte, crls ...[]byte) (*Certificate, VerifyResult, error) {
 	cert_ctx, err := NewCertificateStore()
 	if err != nil {
 		return nil, 0, err
@@ -486,6 +487,12 @@ func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certi
 	if err != nil {
 		return nil, 0, err
 	}
+	for _, crl := range crls {
+		err = cert_ctx.LoadCRLsFromPEM(crl)
+		if err != nil {
+			return nil, 0, err
+		}
+	}
 
 	store := C.X509_STORE_CTX_new()
 	if store == nil {
@@ -493,7 +500,11 @@ func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certi
 	}
 	defer C.X509_STORE_CTX_free(store)
 
-	C.X509_STORE_set_flags(cert_ctx.store, 0)
+	flags := C.ulong(0)
+	if len(crls) > 0 {
+		flags = C.X509_V_FLAG_CRL_CHECK
+	}
+	C.X509_STORE_set_flags(cert_ctx.store, flags)
 	rc := C.X509_STORE_CTX_init(store, cert_ctx.store, c.x, nil)
 	if rc == 0 {
 		return nil, 0, errors.New("unable to init X509_STORE_CTX")
@@ -504,8 +515,11 @@ func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte) (*Certi
 	verifyResult := Ok
 	if i != 1 {
 		verifyResult = VerifyResult(C.X509_STORE_CTX_get_error(store))
-	} else {
-		issuer = &Certificate{x: C.X509_STORE_CTX_get0_current_issuer(store)}
+	}
+
+	currentIssuer := C.X509_STORE_CTX_get0_current_issuer(store)
+	if currentIssuer != nil {
+		issuer = &Certificate{x: currentIssuer}
 		runtime.SetFinalizer(issuer, func(cert *Certificate) {
 			C.X509_free(cert.x)
 		})
diff --git a/crl.go b/crl.go
new file mode 100644
index 00000000..b4293959
--- /dev/null
+++ b/crl.go
@@ -0,0 +1,50 @@
+// 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"
+	"runtime"
+	"unsafe"
+)
+
+type CRL struct {
+	x   *C.X509_CRL
+	ref interface{}
+}
+
+// LoadCRLFromPEM loads an X509_CRL from a PEM-encoded block.
+func LoadCRLFromPEM(pem_block []byte) (*CRL, error) {
+	if len(pem_block) == 0 {
+		return nil, errors.New("empty pem block")
+	}
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+	bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]),
+		C.int(len(pem_block)))
+	crl := C.PEM_read_bio_X509_CRL(bio, nil, nil, nil)
+	C.BIO_free(bio)
+	if crl == nil {
+		return nil, errorFromErrorQueue()
+	}
+	x := &CRL{x: crl}
+	runtime.SetFinalizer(x, func(x *CRL) {
+		C.X509_CRL_free(x.x)
+	})
+	return x, nil
+}
diff --git a/ctx.go b/ctx.go
index 33befc40..b4990132 100644
--- a/ctx.go
+++ b/ctx.go
@@ -244,6 +244,7 @@ type CertificateStore struct {
 	// for GC
 	ctx   *Ctx
 	certs []*Certificate
+	crls  []*CRL
 }
 
 // Allocate a new, empty CertificateStore
@@ -275,6 +276,22 @@ func (s *CertificateStore) LoadCertificatesFromPEM(data []byte) error {
 	return nil
 }
 
+// Parse a chained PEM file, loading all crls into the Store.
+func (s *CertificateStore) LoadCRLsFromPEM(data []byte) error {
+	pems := SplitPEM(data)
+	for _, pem := range pems {
+		crl, err := LoadCRLFromPEM(pem)
+		if err != nil {
+			return err
+		}
+		err = s.AddCRL(crl)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 // GetCertificateStore returns the context's certificate store that will be
 // used for peer validation.
 func (c *Ctx) GetCertificateStore() *CertificateStore {
@@ -297,6 +314,18 @@ func (s *CertificateStore) AddCertificate(cert *Certificate) error {
 	return nil
 }
 
+// AddCRL adds the CRL (certificate-revocation-list) to the
+// the given CertificateStore to be used with the verification flag X509_V_FLAG_CRL_CHECK.
+func (s *CertificateStore) AddCRL(crl *CRL) error {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+	s.crls = append(s.crls, crl)
+	if int(C.X509_STORE_add_crl(s.store, crl.x)) != 1 {
+		return errorFromErrorQueue()
+	}
+	return nil
+}
+
 type CertificateStoreCtx struct {
 	ctx     *C.X509_STORE_CTX
 	ssl_ctx *Ctx

From 99188d1c2451e59847f3e67f7089f4be9ed0a947 Mon Sep 17 00:00:00 2001
From: Jeannie Kidd <jkidd@miteksystems.com>
Date: Tue, 14 Apr 2020 14:20:55 -0700
Subject: [PATCH 07/35] add cms support to verify & get pkcs7 signed data

---
 cms.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 shim.h |  1 +
 2 files changed, 51 insertions(+)
 create mode 100644 cms.go

diff --git a/cms.go b/cms.go
new file mode 100644
index 00000000..d76dcdd7
--- /dev/null
+++ b/cms.go
@@ -0,0 +1,50 @@
+package openssl
+
+// #include "shim.h"
+import "C"
+
+import (
+	"errors"
+	"unsafe"
+)
+
+func VerifyAndGetSignedDataFromPKCS7(der []byte) ([]byte, error) {
+	if len(der) == 0 {
+		return nil, errors.New("empty der block")
+	}
+
+	in := C.BIO_new_mem_buf(unsafe.Pointer(&der[0]), C.int(len(der)))
+	if in == nil {
+		return nil, errors.New("failed creating input buffer")
+	}
+	defer C.BIO_free(in)
+
+	var cms *C.CMS_ContentInfo
+	cms = C.d2i_CMS_bio(in, nil)
+	if cms == nil {
+		return nil, errors.New("failed creating cms")
+	}
+	defer C.CMS_ContentInfo_free(cms)
+
+	out := C.BIO_new(C.BIO_s_mem())
+	if out == nil {
+		return nil, errors.New("failed allocating output buffer")
+	}
+	defer C.BIO_free(out)
+	flags := C.uint(C.CMS_NO_SIGNER_CERT_VERIFY)
+
+	if int(C.CMS_verify(cms, nil, nil, nil, out, flags)) != 1 {
+		return nil, errors.New("failed to verify signature")
+	}
+
+	bufLen := C.BIO_ctrl(out, C.BIO_CTRL_PENDING, 0, nil)
+	buffer := C.X_OPENSSL_malloc(C.ulong(bufLen))
+	if buffer == nil {
+		return nil, errors.New("failed allocating buffer for signed data")
+	}
+	defer C.X_OPENSSL_free(buffer)
+	C.BIO_read(out, buffer, C.int(bufLen))
+	sigData := C.GoBytes(unsafe.Pointer(buffer), C.int(bufLen))
+
+	return sigData, nil
+}
diff --git a/shim.h b/shim.h
index cf1a1b82..c77a0a7d 100644
--- a/shim.h
+++ b/shim.h
@@ -31,6 +31,7 @@
 #include <openssl/pkcs7.h>
 #include <openssl/objects.h>
 #include <openssl/obj_mac.h>
+#include <openssl/cms.h>
 
 #ifndef SSL_MODE_RELEASE_BUFFERS
 #define SSL_MODE_RELEASE_BUFFERS 0

From 939c23c1d7fc79050e561cb47a3033d175c51487 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Wed, 15 Apr 2020 12:58:14 -0400
Subject: [PATCH 08/35] bugfix and refactor to take entire CertificateStore

---
 cert.go | 55 +++++++++++++++++++++++--------------------------------
 1 file changed, 23 insertions(+), 32 deletions(-)

diff --git a/cert.go b/cert.go
index 4a820102..b9fd541e 100644
--- a/cert.go
+++ b/cert.go
@@ -475,54 +475,45 @@ func (p *PKCS7) loadCertificateStack(sk *C.struct_stack_st_X509) {
 	}
 }
 
-// VerifyTrustAndGetIssuerCertificate takes a chained PEM file, loading all certificates into a Store,
-// and verifies trust for the certificate.  The issuing certificate from the chained PEM file is returned.
-// If crls are given, then crl_check is also performed by loading all crls into the Store.
-func (c *Certificate) VerifyTrustAndGetIssuerCertificate(ca_file []byte, crls ...[]byte) (*Certificate, VerifyResult, error) {
-	cert_ctx, err := NewCertificateStore()
-	if err != nil {
-		return nil, 0, err
-	}
-	err = cert_ctx.LoadCertificatesFromPEM(ca_file)
-	if err != nil {
-		return nil, 0, err
-	}
-	for _, crl := range crls {
-		err = cert_ctx.LoadCRLsFromPEM(crl)
-		if err != nil {
-			return nil, 0, err
-		}
-	}
-
-	store := C.X509_STORE_CTX_new()
-	if store == nil {
+// VerifyTrustAndGetIssuerCertificate takes a CertificateStore and verifies trust for the certificate.
+// The issuing certificate from the CertificateStore is returned if found.
+func (c *Certificate) VerifyTrustAndGetIssuerCertificate(store *CertificateStore, crlCheck bool) (*Certificate, VerifyResult, error) {
+	storeCtx := C.X509_STORE_CTX_new()
+	if storeCtx == nil {
 		return nil, 0, errors.New("failed to create new X509_STORE_CTX")
 	}
-	defer C.X509_STORE_CTX_free(store)
+	defer C.X509_STORE_CTX_free(storeCtx)
 
 	flags := C.ulong(0)
-	if len(crls) > 0 {
+	if crlCheck {
 		flags = C.X509_V_FLAG_CRL_CHECK
 	}
-	C.X509_STORE_set_flags(cert_ctx.store, flags)
-	rc := C.X509_STORE_CTX_init(store, cert_ctx.store, c.x, nil)
+	C.X509_STORE_set_flags(store.store, flags)
+	rc := C.X509_STORE_CTX_init(storeCtx, store.store, c.x, nil)
 	if rc == 0 {
 		return nil, 0, errors.New("unable to init X509_STORE_CTX")
 	}
 
-	i := C.X509_verify_cert(store)
+	i := C.X509_verify_cert(storeCtx)
 	var issuer *Certificate
 	verifyResult := Ok
 	if i != 1 {
-		verifyResult = VerifyResult(C.X509_STORE_CTX_get_error(store))
+		verifyResult = VerifyResult(C.X509_STORE_CTX_get_error(storeCtx))
 	}
 
-	currentIssuer := C.X509_STORE_CTX_get0_current_issuer(store)
+	currentIssuer := C.X509_STORE_CTX_get0_current_issuer(storeCtx)
 	if currentIssuer != nil {
-		issuer = &Certificate{x: currentIssuer}
-		runtime.SetFinalizer(issuer, func(cert *Certificate) {
-			C.X509_free(cert.x)
-		})
+		// need to clone the issuer cert so that it is not cleaned up when C.X509_STORE_CTX_free is called
+		ic := &Certificate{x: currentIssuer}
+		data, err := ic.MarshalPEM()
+		if err != nil {
+			return nil, 0, errors.New("error copying issuer cert")
+		}
+		issuer, err = LoadCertificateFromPEM(data)
+		if err != nil {
+			return nil, 0, errors.New("error loading issuer cert")
+		}
 	}
+
 	return issuer, verifyResult, nil
 }

From ba4b425493dc98210a5c67f8282b72d032dfdd54 Mon Sep 17 00:00:00 2001
From: Jeannie Kidd <jkidd@miteksystems.com>
Date: Wed, 15 Apr 2020 18:41:05 -0700
Subject: [PATCH 09/35] add comments

---
 cms.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/cms.go b/cms.go
index d76dcdd7..4eb57e4c 100644
--- a/cms.go
+++ b/cms.go
@@ -8,6 +8,9 @@ import (
 	"unsafe"
 )
 
+// VerifyAndGetSignedDataFromPKCS7 verifies a CMS SignedData structure from a DER-encolded PKCS7,
+// and returns the signed content if the verification is successful.
+// It does not verify the signing certificates.
 func VerifyAndGetSignedDataFromPKCS7(der []byte) ([]byte, error) {
 	if len(der) == 0 {
 		return nil, errors.New("empty der block")

From 49d74eeb291bf4d4a48621dbab8d71111106f96d Mon Sep 17 00:00:00 2001
From: Jeannie Kidd <jkidd@miteksystems.com>
Date: Thu, 16 Apr 2020 15:54:57 -0700
Subject: [PATCH 10/35] fix typo

---
 cms.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cms.go b/cms.go
index 4eb57e4c..ce837f42 100644
--- a/cms.go
+++ b/cms.go
@@ -8,7 +8,7 @@ import (
 	"unsafe"
 )
 
-// VerifyAndGetSignedDataFromPKCS7 verifies a CMS SignedData structure from a DER-encolded PKCS7,
+// VerifyAndGetSignedDataFromPKCS7 verifies a CMS SignedData structure from a DER-encoded PKCS7,
 // and returns the signed content if the verification is successful.
 // It does not verify the signing certificates.
 func VerifyAndGetSignedDataFromPKCS7(der []byte) ([]byte, error) {

From b524161802d261f7de7a5a3feb252b69f746679b Mon Sep 17 00:00:00 2001
From: Jeannie Kidd <jkidd@miteksystems.com>
Date: Fri, 17 Apr 2020 09:44:23 -0700
Subject: [PATCH 11/35] implement asn1 parsing

---
 asn1.go | 33 +++++++++++++++++++++++++++++++++
 shim.h  |  1 +
 2 files changed, 34 insertions(+)
 create mode 100644 asn1.go

diff --git a/asn1.go b/asn1.go
new file mode 100644
index 00000000..fdcff435
--- /dev/null
+++ b/asn1.go
@@ -0,0 +1,33 @@
+package openssl
+
+// #include "shim.h"
+import "C"
+
+import (
+	"errors"
+	"io/ioutil"
+)
+
+// ASN1Parse parses and extracts ASN.1 structure and returns the data in text format
+func ASN1Parse(asn1 []byte) (string, error) {
+	if len(asn1) == 0 {
+		return "", errors.New("empty asn1 structure")
+	}
+
+	out := C.BIO_new(C.BIO_s_mem())
+	if out == nil {
+		return "", errors.New("failed allocating output buffer")
+	}
+	defer C.BIO_free(out)
+
+	if int(C.ASN1_parse_dump(out, (*C.uchar)(&asn1[0]), C.long(len(asn1)), 1, 0)) == 0 {
+		return "", errors.New("failed to parse asn1 data")
+	}
+
+	parsed, err := ioutil.ReadAll(asAnyBio(out))
+	if err != nil {
+		return "", errors.New("failed to read bio data as bytes")
+	}
+
+	return string(parsed), nil
+}
diff --git a/shim.h b/shim.h
index c77a0a7d..2837d6d7 100644
--- a/shim.h
+++ b/shim.h
@@ -32,6 +32,7 @@
 #include <openssl/objects.h>
 #include <openssl/obj_mac.h>
 #include <openssl/cms.h>
+#include <openssl/asn1.h>
 
 #ifndef SSL_MODE_RELEASE_BUFFERS
 #define SSL_MODE_RELEASE_BUFFERS 0

From 0330328a1ebd1d07eee3e5c54d634a07a455f420 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Wed, 22 Apr 2020 16:37:00 -0400
Subject: [PATCH 12/35] fix memory leak in pkcs7 loadCertificateStack

---
 cert.go | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/cert.go b/cert.go
index b9fd541e..2fa51201 100644
--- a/cert.go
+++ b/cert.go
@@ -457,22 +457,31 @@ func LoadCertificatesFromPKCS7(der_block []byte) (*PKCS7, error) {
 		certs = signedAndEnveloped.cert
 	}
 
-	ret.loadCertificateStack(certs)
+	err := ret.loadCertificateStack(certs)
+	if err != nil {
+		return nil, err
+	}
 	return ret, nil
 }
 
 // loadCertificateStack loads up a stack of x509 certificates into the PKCS7 struct.
-func (p *PKCS7) loadCertificateStack(sk *C.struct_stack_st_X509) {
+func (p *PKCS7) loadCertificateStack(sk *C.struct_stack_st_X509) error {
 	sk_num := int(C.X_sk_X509_num(sk))
 	p.Certs = make([]*Certificate, 0, sk_num)
 	for i := 0; i < sk_num; i++ {
 		x := C.X_sk_X509_value(sk, C.int(i))
 
-		// ref holds on to the underlying connection memory so we don't need to
-		// worry about incrementing refcounts manually or freeing the X509
-		cert := &Certificate{x: x, ref: p}
+		// add a ref
+		if 1 != C.X_X509_add_ref(x) {
+			return errors.New("unable to add ref for X509")
+		}
+		cert := &Certificate{x: x}
+		runtime.SetFinalizer(cert, func(cert *Certificate) {
+			C.X509_free(cert.x)
+		})
 		p.Certs = append(p.Certs, cert)
 	}
+	return nil
 }
 
 // VerifyTrustAndGetIssuerCertificate takes a CertificateStore and verifies trust for the certificate.

From 285317dd698dfb484f26e3123098fdea9a8d24a1 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Mon, 27 Apr 2020 18:16:23 -0400
Subject: [PATCH 13/35] add ecdsa function to verify a signature

---
 ecdsa.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 ecdsa.go

diff --git a/ecdsa.go b/ecdsa.go
new file mode 100644
index 00000000..4bc649e1
--- /dev/null
+++ b/ecdsa.go
@@ -0,0 +1,92 @@
+package openssl
+
+// #include "shim.h"
+import "C"
+import (
+	"errors"
+	"unsafe"
+)
+
+/// VerifyECDSASignature verifies data valid against an ECDSA signature and ECDSA Public Key
+/// - Parameter publicKey: The OpenSSL EVP_PKEY ECDSA key
+/// - Parameter signature: The ECDSA signature to verify
+/// - Parameter data: The data used to generate the signature
+/// - Returns: True if the signature was verified
+func VerifyECDSASignature(publicKey, signature, data []byte) (bool, error) {
+	ecsig := C.ECDSA_SIG_new()
+	defer C.ECDSA_SIG_free(ecsig)
+	sigData := signature
+
+	C.BN_bin2bn((*C.uchar)(&sigData[0]), 32, ecsig.r)
+	C.BN_bin2bn((*C.uchar)(&sigData[32]), 32, ecsig.s)
+
+	sigSize := C.i2d_ECDSA_SIG(ecsig, nil)
+
+	derBytes := (*C.uchar)(C.malloc(C.size_t(sigSize)))
+	defer C.free(unsafe.Pointer(derBytes))
+
+	// ignoring result, because it is the same as sigSize
+	C.i2d_ECDSA_SIG(ecsig, &derBytes)
+
+	// read EC Public Key
+	inf := C.BIO_new(C.BIO_s_mem())
+	if inf == nil {
+		return false, errors.New("failed allocating input buffer")
+	}
+	defer C.BIO_free(inf)
+	_, err := asAnyBio(inf).Write(publicKey)
+	if err != nil {
+		return false, err
+	}
+
+	eckey := C.d2i_EC_PUBKEY_bio(inf, nil)
+	if eckey == nil {
+		return false, errors.New("failed to load ec public key")
+	}
+	defer C.EC_KEY_free(eckey)
+
+	out := C.BIO_new(C.BIO_s_mem())
+	if out == nil {
+		return false, errors.New("failed allocating output buffer")
+	}
+	defer C.BIO_free(out)
+	i := C.PEM_write_bio_EC_PUBKEY(out, eckey)
+	if i != 1 {
+		return false, errors.New("failed to write bio ec public key")
+	}
+	pemKey := C.PEM_read_bio_PUBKEY(out, nil, nil, nil)
+	defer C.EVP_PKEY_free(pemKey)
+
+	keyType := C.EVP_PKEY_base_id(pemKey)
+	// TODO: support other key types such as RSA, DSA, etc.
+	if keyType != C.EVP_PKEY_EC {
+		return false, errors.New("public key is incorrect type")
+	}
+
+	ctx := &C.EVP_MD_CTX{}
+	ctxPointer := unsafe.Pointer(ctx)
+	bmd := C.BIO_new(C.BIO_f_md())
+	defer C.BIO_free(bmd)
+
+	if C.BIO_ctrl(bmd, C.BIO_C_GET_MD_CTX, 0, ctxPointer) != 1 {
+		return false, errors.New("error getting context")
+	}
+
+	nRes := C.EVP_DigestVerifyInit(ctx, nil, nil, nil, pemKey)
+	if nRes != 1 {
+		return false, errors.New("unable to init digest verify")
+	}
+	defer C.EVP_MD_CTX_cleanup(ctx)
+
+	nRes = C.EVP_DigestUpdate(ctx, unsafe.Pointer((*C.uchar)(&data[0])), C.size_t(len(data)))
+	if nRes != 1 {
+		return false, errors.New("unable to update digest")
+	}
+
+	nRes = C.EVP_DigestVerifyFinal(ctx, derBytes, C.size_t(sigSize))
+	if nRes != 1 {
+		return false, nil
+	}
+
+	return true, nil
+}

From 1d3d340f45aef40086f144a57a67e0aa978ea9ee Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 30 Apr 2020 11:34:52 -0400
Subject: [PATCH 14/35] add copyright headers

---
 asn1.go  | 14 ++++++++++++++
 cms.go   | 14 ++++++++++++++
 ecdsa.go | 14 ++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/asn1.go b/asn1.go
index fdcff435..37706da5 100644
--- a/asn1.go
+++ b/asn1.go
@@ -1,3 +1,17 @@
+// 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"
diff --git a/cms.go b/cms.go
index ce837f42..e20540d3 100644
--- a/cms.go
+++ b/cms.go
@@ -1,3 +1,17 @@
+// 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"
diff --git a/ecdsa.go b/ecdsa.go
index 4bc649e1..c3a56cb3 100644
--- a/ecdsa.go
+++ b/ecdsa.go
@@ -1,3 +1,17 @@
+// 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"

From 1cc975dd5c58a05630bd26e05076b550d9de6bf5 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Tue, 5 May 2020 17:45:30 -0400
Subject: [PATCH 15/35] move crl check flag to a generic cert store method

---
 cert.go |  7 +------
 ctx.go  | 31 +++++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/cert.go b/cert.go
index 2fa51201..48e7ffa7 100644
--- a/cert.go
+++ b/cert.go
@@ -486,18 +486,13 @@ func (p *PKCS7) loadCertificateStack(sk *C.struct_stack_st_X509) error {
 
 // VerifyTrustAndGetIssuerCertificate takes a CertificateStore and verifies trust for the certificate.
 // The issuing certificate from the CertificateStore is returned if found.
-func (c *Certificate) VerifyTrustAndGetIssuerCertificate(store *CertificateStore, crlCheck bool) (*Certificate, VerifyResult, error) {
+func (c *Certificate) VerifyTrustAndGetIssuerCertificate(store *CertificateStore) (*Certificate, VerifyResult, error) {
 	storeCtx := C.X509_STORE_CTX_new()
 	if storeCtx == nil {
 		return nil, 0, errors.New("failed to create new X509_STORE_CTX")
 	}
 	defer C.X509_STORE_CTX_free(storeCtx)
 
-	flags := C.ulong(0)
-	if crlCheck {
-		flags = C.X509_V_FLAG_CRL_CHECK
-	}
-	C.X509_STORE_set_flags(store.store, flags)
 	rc := C.X509_STORE_CTX_init(storeCtx, store.store, c.x, nil)
 	if rc == 0 {
 		return nil, 0, errors.New("unable to init X509_STORE_CTX")
diff --git a/ctx.go b/ctx.go
index b4990132..d4e4a9a3 100644
--- a/ctx.go
+++ b/ctx.go
@@ -326,6 +326,37 @@ func (s *CertificateStore) AddCRL(crl *CRL) error {
 	return nil
 }
 
+type VerifyFlags int
+
+const (
+	CBIssuerCheck      VerifyFlags = C.X509_V_FLAG_CB_ISSUER_CHECK
+	UseCheckTime       VerifyFlags = C.X509_V_FLAG_USE_CHECK_TIME
+	CRLCheck           VerifyFlags = C.X509_V_FLAG_CRL_CHECK
+	CRLCheckAll        VerifyFlags = C.X509_V_FLAG_CRL_CHECK_ALL
+	IgnoreCritical     VerifyFlags = C.X509_V_FLAG_IGNORE_CRITICAL
+	X509Strict         VerifyFlags = C.X509_V_FLAG_X509_STRICT
+	AllowProxyCerts    VerifyFlags = C.X509_V_FLAG_ALLOW_PROXY_CERTS
+	PolicyCheck        VerifyFlags = C.X509_V_FLAG_POLICY_CHECK
+	ExplicitPolicy     VerifyFlags = C.X509_V_FLAG_EXPLICIT_POLICY
+	InhibitAny         VerifyFlags = C.X509_V_FLAG_INHIBIT_ANY
+	InhibitMap         VerifyFlags = C.X509_V_FLAG_INHIBIT_MAP
+	NotifyPolicy       VerifyFlags = C.X509_V_FLAG_NOTIFY_POLICY
+	ExtendedCRLSupport VerifyFlags = C.X509_V_FLAG_EXTENDED_CRL_SUPPORT
+	UseDeltas          VerifyFlags = C.X509_V_FLAG_USE_DELTAS
+	CheckSSSignature   VerifyFlags = C.X509_V_FLAG_CHECK_SS_SIGNATURE
+	TrustedFirst       VerifyFlags = C.X509_V_FLAG_TRUSTED_FIRST
+	SuiteB128LOSOnly   VerifyFlags = C.X509_V_FLAG_SUITEB_128_LOS_ONLY
+	SuiteB192LOS       VerifyFlags = C.X509_V_FLAG_SUITEB_192_LOS
+	SuiteB128LOS       VerifyFlags = C.X509_V_FLAG_SUITEB_128_LOS
+	PartialChain       VerifyFlags = C.X509_V_FLAG_PARTIAL_CHAIN
+	NoAltChains        VerifyFlags = C.X509_V_FLAG_NO_ALT_CHAINS
+)
+
+func (s *CertificateStore) SetFlags(flags VerifyFlags) {
+	cflags := C.ulong(flags)
+	C.X509_STORE_set_flags(s.store, cflags)
+}
+
 type CertificateStoreCtx struct {
 	ctx     *C.X509_STORE_CTX
 	ssl_ctx *Ctx

From 88089779a3471c7c73c70ec4a3dbe1040a04c91a Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 14 May 2020 09:50:25 -0400
Subject: [PATCH 16/35] update ecdsa code to work with openssl v1.1

---
 ecdsa.go | 47 +++++++++++------------------------------------
 1 file changed, 11 insertions(+), 36 deletions(-)

diff --git a/ecdsa.go b/ecdsa.go
index c3a56cb3..b66068be 100644
--- a/ecdsa.go
+++ b/ecdsa.go
@@ -21,27 +21,12 @@ import (
 	"unsafe"
 )
 
-/// VerifyECDSASignature verifies data valid against an ECDSA signature and ECDSA Public Key
-/// - Parameter publicKey: The OpenSSL EVP_PKEY ECDSA key
-/// - Parameter signature: The ECDSA signature to verify
-/// - Parameter data: The data used to generate the signature
-/// - Returns: True if the signature was verified
+// VerifyECDSASignature verifies data valid against an ECDSA signature and ECDSA Public Key
+// - Parameter publicKey: The OpenSSL EVP_PKEY ECDSA public key in DER format
+// - Parameter signature: The ECDSA signature to verify in DER format
+// - Parameter data: The raw data used to generate the signature
+// - Returns: True if the signature was verified
 func VerifyECDSASignature(publicKey, signature, data []byte) (bool, error) {
-	ecsig := C.ECDSA_SIG_new()
-	defer C.ECDSA_SIG_free(ecsig)
-	sigData := signature
-
-	C.BN_bin2bn((*C.uchar)(&sigData[0]), 32, ecsig.r)
-	C.BN_bin2bn((*C.uchar)(&sigData[32]), 32, ecsig.s)
-
-	sigSize := C.i2d_ECDSA_SIG(ecsig, nil)
-
-	derBytes := (*C.uchar)(C.malloc(C.size_t(sigSize)))
-	defer C.free(unsafe.Pointer(derBytes))
-
-	// ignoring result, because it is the same as sigSize
-	C.i2d_ECDSA_SIG(ecsig, &derBytes)
-
 	// read EC Public Key
 	inf := C.BIO_new(C.BIO_s_mem())
 	if inf == nil {
@@ -72,32 +57,22 @@ func VerifyECDSASignature(publicKey, signature, data []byte) (bool, error) {
 	defer C.EVP_PKEY_free(pemKey)
 
 	keyType := C.EVP_PKEY_base_id(pemKey)
-	// TODO: support other key types such as RSA, DSA, etc.
 	if keyType != C.EVP_PKEY_EC {
 		return false, errors.New("public key is incorrect type")
 	}
 
-	ctx := &C.EVP_MD_CTX{}
-	ctxPointer := unsafe.Pointer(ctx)
-	bmd := C.BIO_new(C.BIO_f_md())
-	defer C.BIO_free(bmd)
-
-	if C.BIO_ctrl(bmd, C.BIO_C_GET_MD_CTX, 0, ctxPointer) != 1 {
-		return false, errors.New("error getting context")
-	}
-
-	nRes := C.EVP_DigestVerifyInit(ctx, nil, nil, nil, pemKey)
+	// run digest verify with public key in pem format, signature in der format, and data in raw format
+	mdctx := C.EVP_MD_CTX_new()
+	nRes := C.EVP_DigestVerifyInit(mdctx, nil, nil, nil, pemKey)
 	if nRes != 1 {
 		return false, errors.New("unable to init digest verify")
 	}
-	defer C.EVP_MD_CTX_cleanup(ctx)
-
-	nRes = C.EVP_DigestUpdate(ctx, unsafe.Pointer((*C.uchar)(&data[0])), C.size_t(len(data)))
+	defer C.EVP_MD_CTX_free(mdctx)
+	nRes = C.EVP_DigestUpdate(mdctx, unsafe.Pointer((*C.uchar)(&data[0])), C.size_t(len(data)))
 	if nRes != 1 {
 		return false, errors.New("unable to update digest")
 	}
-
-	nRes = C.EVP_DigestVerifyFinal(ctx, derBytes, C.size_t(sigSize))
+	nRes = C.EVP_DigestVerifyFinal(mdctx, (*C.uchar)(&signature[0]), C.size_t(len(signature)))
 	if nRes != 1 {
 		return false, nil
 	}

From 2620389070ba42b66edf35933b8da23a9e4fd6ac Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 11 Jun 2020 13:49:40 -0400
Subject: [PATCH 17/35] add rsa signature recovery function

---
 rsa.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 shim.c |  4 +++
 shim.h |  1 +
 3 files changed, 82 insertions(+)
 create mode 100644 rsa.go

diff --git a/rsa.go b/rsa.go
new file mode 100644
index 00000000..8f837444
--- /dev/null
+++ b/rsa.go
@@ -0,0 +1,77 @@
+// 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"
+	"unsafe"
+)
+
+// VerifyRecoverRSASignature takes a DER encoded RSA public key and a raw signature
+// (assuming no padding currently) and returns the recoverable part of the signed data.
+// This follows the example shown here: https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_verify_recover.html
+// This should be roughly equivalent to the following openssl CLI command:
+// openssl rsautl -verify -pubin -inkey publicKey.pem -in signature.bin -raw
+func VerifyRecoverRSASignature(publicKey, signature []byte) ([]byte, error) {
+	// Read RSA Public Key
+	inf := C.BIO_new(C.BIO_s_mem())
+	if inf == nil {
+		return nil, errors.New("failed allocating input buffer")
+	}
+	defer C.BIO_free(inf)
+	_, err := asAnyBio(inf).Write(publicKey)
+	if err != nil {
+		return nil, err
+	}
+	pubKey := C.d2i_PUBKEY_bio(inf, nil)
+	if pubKey == nil {
+		return nil, errors.New("failed to load public key")
+	}
+	defer C.EVP_PKEY_free(pubKey)
+
+	// Setup context
+	ctx := C.EVP_PKEY_CTX_new(pubKey, nil)
+	if ctx == nil {
+		return nil, errors.New("failed to setup context")
+	}
+	defer C.EVP_PKEY_CTX_free(ctx)
+	if C.EVP_PKEY_verify_recover_init(ctx) <= 0 {
+		return nil, errors.New("failed to initialize verify recover")
+	}
+	if C.X_EVP_PKEY_CTX_set_rsa_padding(ctx, C.RSA_NO_PADDING) <= 0 {
+		return nil, errors.New("failed to set rsa padding")
+	}
+
+	// Determine buffer length
+	var routlen C.size_t
+	routlen = C.size_t(len(signature))
+	if C.EVP_PKEY_verify_recover(ctx, nil, &routlen, (*C.uchar)(&signature[0]), C.size_t(len(signature))) <= 0 {
+		return nil, errors.New("error getting buffer length")
+	}
+
+	// Recover the signed data
+	rout := C.X_OPENSSL_malloc(routlen)
+	if rout == nil {
+		return nil, errors.New("failed allocating rout")
+	}
+	defer C.X_OPENSSL_free(rout)
+	if C.EVP_PKEY_verify_recover(ctx, (*C.uchar)(rout), &routlen, (*C.uchar)(&signature[0]), C.size_t(len(signature))) <= 0 {
+		return nil, errors.New("error recovering signed data")
+	}
+	recoveredBytes := C.GoBytes(unsafe.Pointer(rout), C.int(routlen))
+	return recoveredBytes, nil
+}
diff --git a/shim.c b/shim.c
index 6e680841..13c75a50 100644
--- a/shim.c
+++ b/shim.c
@@ -737,6 +737,10 @@ int X_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid) {
 	return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid);
 }
 
+int X_EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad) {
+	return EVP_PKEY_CTX_set_rsa_padding(ctx, pad);
+}
+
 size_t X_HMAC_size(const HMAC_CTX *e) {
 	return HMAC_size(e);
 }
diff --git a/shim.h b/shim.h
index 2837d6d7..31a72275 100644
--- a/shim.h
+++ b/shim.h
@@ -155,6 +155,7 @@ extern void X_EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *ctx, int padding);
 extern const EVP_CIPHER *X_EVP_CIPHER_CTX_cipher(EVP_CIPHER_CTX *ctx);
 extern int X_EVP_CIPHER_CTX_encrypting(const EVP_CIPHER_CTX *ctx);
 extern int X_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid);
+extern int X_EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad);
 
 /* HMAC methods */
 extern size_t X_HMAC_size(const HMAC_CTX *e);

From 2cce650023de9f437af27ad3e3803f7bf139febe Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Wed, 1 Jul 2020 14:01:02 -0400
Subject: [PATCH 18/35] add function to get an ec key's bit size

---
 ecdsa.go | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/ecdsa.go b/ecdsa.go
index b66068be..dc0aa94c 100644
--- a/ecdsa.go
+++ b/ecdsa.go
@@ -79,3 +79,37 @@ func VerifyECDSASignature(publicKey, signature, data []byte) (bool, error) {
 
 	return true, nil
 }
+
+// GetECPublicKeyBitSize returns the bit size of an EC public key, using EVP_PKEY_bits.
+func GetECPublicKeyBitSize(publicKey []byte) (int, error) {
+	inf := C.BIO_new(C.BIO_s_mem())
+	if inf == nil {
+		return 0, errors.New("failed allocating input buffer")
+	}
+	defer C.BIO_free(inf)
+	_, err := asAnyBio(inf).Write(publicKey)
+	if err != nil {
+		return 0, err
+	}
+
+	eckey := C.d2i_EC_PUBKEY_bio(inf, nil)
+	if eckey == nil {
+		return 0, errors.New("failed to load ec public key")
+	}
+	defer C.EC_KEY_free(eckey)
+
+	out := C.BIO_new(C.BIO_s_mem())
+	if out == nil {
+		return 0, errors.New("failed allocating output buffer")
+	}
+	defer C.BIO_free(out)
+	i := C.PEM_write_bio_EC_PUBKEY(out, eckey)
+	if i != 1 {
+		return 0, errors.New("failed to write bio ec public key")
+	}
+	pemKey := C.PEM_read_bio_PUBKEY(out, nil, nil, nil)
+	defer C.EVP_PKEY_free(pemKey)
+
+	bitSize := C.EVP_PKEY_bits(pemKey)
+	return int(bitSize), nil
+}

From 0566178b04457ac00dadfd5654b2b31a5a060631 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Tue, 25 Aug 2020 16:41:55 -0400
Subject: [PATCH 19/35] add GetIssuer method for CRL

---
 crl.go | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/crl.go b/crl.go
index b4293959..e2172a7b 100644
--- a/crl.go
+++ b/crl.go
@@ -48,3 +48,11 @@ func LoadCRLFromPEM(pem_block []byte) (*CRL, error) {
 	})
 	return x, nil
 }
+
+func (c *CRL) GetIssuer() (*Name, error) {
+	n := C.X509_CRL_get_issuer(c.x)
+	if n == nil {
+		return nil, errors.New("failed to get issuer")
+	}
+	return &Name{name: n}, nil
+}

From ade12f11935c15ff1b7b122b1fb51e3f7f07bd43 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Mon, 16 Nov 2020 15:18:19 -0500
Subject: [PATCH 20/35] add digest param for VerifyECDSASignature

---
 ecdsa.go | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/ecdsa.go b/ecdsa.go
index dc0aa94c..67ff6b3f 100644
--- a/ecdsa.go
+++ b/ecdsa.go
@@ -25,8 +25,9 @@ import (
 // - Parameter publicKey: The OpenSSL EVP_PKEY ECDSA public key in DER format
 // - Parameter signature: The ECDSA signature to verify in DER format
 // - Parameter data: The raw data used to generate the signature
+// - Parameter digest: The name of the digest to use. The currently supported values are: sha1, sha224, sha256, sha384, sha512, ripemd160
 // - Returns: True if the signature was verified
-func VerifyECDSASignature(publicKey, signature, data []byte) (bool, error) {
+func VerifyECDSASignature(publicKey, signature, data []byte, digest string) (bool, error) {
 	// read EC Public Key
 	inf := C.BIO_new(C.BIO_s_mem())
 	if inf == nil {
@@ -61,9 +62,27 @@ func VerifyECDSASignature(publicKey, signature, data []byte) (bool, error) {
 		return false, errors.New("public key is incorrect type")
 	}
 
+	var digestType *C.EVP_MD
+	switch digest {
+	case "sha1":
+		digestType = C.EVP_sha1()
+	case "sha224":
+		digestType = C.EVP_sha224()
+	case "sha256":
+		digestType = C.EVP_sha256()
+	case "sha384":
+		digestType = C.EVP_sha384()
+	case "sha512":
+		digestType = C.EVP_sha512()
+	case "ripemd160":
+		digestType = C.EVP_ripemd160()
+	default:
+		return false, errors.New("unsupported digest value")
+	}
+
 	// run digest verify with public key in pem format, signature in der format, and data in raw format
 	mdctx := C.EVP_MD_CTX_new()
-	nRes := C.EVP_DigestVerifyInit(mdctx, nil, nil, nil, pemKey)
+	nRes := C.EVP_DigestVerifyInit(mdctx, nil, digestType, nil, pemKey)
 	if nRes != 1 {
 		return false, errors.New("unable to init digest verify")
 	}

From 91a89fb1b0fa57cce14cfcb7e885df72d930eb22 Mon Sep 17 00:00:00 2001
From: Stan Nelson <snelson@miteksystems.com>
Date: Thu, 11 Mar 2021 13:41:04 -0800
Subject: [PATCH 21/35] Fix module path

---
 go.mod | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/go.mod b/go.mod
index 73f3bbfe..bc2eb2e1 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
-module github.com/spacemonkeygo/openssl
+module github.com/emtammaru/openssl
 
 require github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572

From f41c7f60559b46d76e20a59487dddd3d6059ba1c Mon Sep 17 00:00:00 2001
From: Jeannie Kidd <jkidd@miteksystems.com>
Date: Tue, 6 Apr 2021 09:51:49 -0700
Subject: [PATCH 22/35] add func to verify rsa signature

---
 rsa.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 shim.c |  4 +++
 shim.h |  1 +
 3 files changed, 84 insertions(+)

diff --git a/rsa.go b/rsa.go
index 8f837444..16470286 100644
--- a/rsa.go
+++ b/rsa.go
@@ -18,6 +18,7 @@ package openssl
 import "C"
 import (
 	"errors"
+	"strings"
 	"unsafe"
 )
 
@@ -75,3 +76,81 @@ func VerifyRecoverRSASignature(publicKey, signature []byte) ([]byte, error) {
 	recoveredBytes := C.GoBytes(unsafe.Pointer(rout), C.int(routlen))
 	return recoveredBytes, nil
 }
+
+// VerifyRSASignature verifies that a signature is valid for some data and a Public Key
+// - Parameter publicKey: The OpenSSL EVP_PKEY public key in DER format
+// - Parameter signature: The signature to verify in DER format
+// - Parameter data: The data used to generate the signature
+// - Parameter digest: The name of the digest to use. The currently supported values are: sha1, sha224, sha256, sha384, sha512, ripemd160, rsassapss
+// - Returns: True if the signature was verified
+func VerifyRSASignature(publicKey, signature, data []byte, digest string) (bool, error) {
+	digest = strings.ToLower(digest)
+	digestName := "sha256"	
+	switch {
+	case strings.Contains(digest, "sha1"):
+		digestName = "sha1"
+	case strings.Contains(digest, "sha224"):
+		digestName = "sha224"
+	case strings.Contains(digest, "sha256"), strings.Contains(digest, "rsassapss"):
+		digestName = "sha256"
+	case strings.Contains(digest, "sha384"):
+		digestName= "sha384"
+	case strings.Contains(digest, "sha512"):
+		digestName = "sha512"
+	case strings.Contains(digest, "ripemd160"):
+		digestName = "ripemd160"
+	}
+	md, err := GetDigestByName(digestName)
+	if err != nil {
+		return false, err
+	}
+
+	inf := C.BIO_new(C.BIO_s_mem())
+	if inf == nil {
+		return false, errors.New("failed allocating input buffer")
+	}
+	defer C.BIO_free(inf)
+	_, err = asAnyBio(inf).Write(publicKey)
+	if err != nil {
+		return false, err
+	}
+	pubKey := C.d2i_PUBKEY_bio(inf, nil)
+	if pubKey == nil {
+		return false, errors.New("failed to load public key")
+	}
+	defer C.EVP_PKEY_free(pubKey)
+	ctx := C.EVP_PKEY_CTX_new(pubKey, nil)
+	if ctx == nil {
+		return false, errors.New("failed to setup context")
+	}
+	defer C.EVP_PKEY_CTX_free(ctx)
+
+	mdctx := C.EVP_MD_CTX_new()
+	defer C.EVP_MD_CTX_free(mdctx)
+
+	nRes := C.EVP_DigestVerifyInit(mdctx, &ctx, md.ptr, nil, pubKey)
+	if nRes != 1 {
+		return false, errors.New("unable to init digest verify")
+	}
+
+	if strings.Contains(digestName, "rsassapss") {
+		if C.X_EVP_PKEY_CTX_ctrl_str(ctx, C.CString("rsa_padding_mode"), C.CString("pss") ) <= 0 {
+			return false, errors.New("failed to set rsa padding mode")
+		}
+		if C.X_EVP_PKEY_CTX_ctrl_str(ctx, C.CString("rsa_pss_saltlen"), C.CString("auto")) <= 0 {
+			return false, errors.New("failed to set rsa pss saltlen")
+		}
+	}
+
+	nRes = C.EVP_DigestUpdate(mdctx, unsafe.Pointer((*C.uchar)(&data[0])), C.size_t(len(data)))
+	if nRes != 1 {
+		return false, errors.New("unable to update digest")
+	}
+
+	nRes = C.EVP_DigestVerifyFinal(mdctx, (*C.uchar)(&signature[0]), C.size_t(len(signature)))
+	if nRes != 1 {
+		return false, nil
+	}
+
+	return true, nil
+}
\ No newline at end of file
diff --git a/shim.c b/shim.c
index 13c75a50..2a579741 100644
--- a/shim.c
+++ b/shim.c
@@ -741,6 +741,10 @@ int X_EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad) {
 	return EVP_PKEY_CTX_set_rsa_padding(ctx, pad);
 }
 
+int X_EVP_PKEY_CTX_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value) {
+	return EVP_PKEY_CTX_ctrl_str(ctx, type, value);
+}
+
 size_t X_HMAC_size(const HMAC_CTX *e) {
 	return HMAC_size(e);
 }
diff --git a/shim.h b/shim.h
index 31a72275..24bcd3da 100644
--- a/shim.h
+++ b/shim.h
@@ -156,6 +156,7 @@ extern const EVP_CIPHER *X_EVP_CIPHER_CTX_cipher(EVP_CIPHER_CTX *ctx);
 extern int X_EVP_CIPHER_CTX_encrypting(const EVP_CIPHER_CTX *ctx);
 extern int X_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid);
 extern int X_EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad);
+extern int X_EVP_PKEY_CTX_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value);
 
 /* HMAC methods */
 extern size_t X_HMAC_size(const HMAC_CTX *e);

From 58095a44c8a347c35827ceb2f9b78ef2e4f7aca0 Mon Sep 17 00:00:00 2001
From: Jeannie Kidd <jkidd@miteksystems.com>
Date: Tue, 6 Apr 2021 11:29:25 -0700
Subject: [PATCH 23/35] make algo specific options as params

---
 rsa.go | 37 +++++++++++--------------------------
 1 file changed, 11 insertions(+), 26 deletions(-)

diff --git a/rsa.go b/rsa.go
index 16470286..fce8df2a 100644
--- a/rsa.go
+++ b/rsa.go
@@ -18,7 +18,7 @@ package openssl
 import "C"
 import (
 	"errors"
-	"strings"
+	"fmt"
 	"unsafe"
 )
 
@@ -81,26 +81,12 @@ func VerifyRecoverRSASignature(publicKey, signature []byte) ([]byte, error) {
 // - Parameter publicKey: The OpenSSL EVP_PKEY public key in DER format
 // - Parameter signature: The signature to verify in DER format
 // - Parameter data: The data used to generate the signature
-// - Parameter digest: The name of the digest to use. The currently supported values are: sha1, sha224, sha256, sha384, sha512, ripemd160, rsassapss
+// - Parameter digestType: The type of the digest to use. The currently supported values are: sha1, sha224, sha256, sha384, sha512, ripemd160, rsassapss
+// - Parameter pkeyopt: A map of any algorithm specific control operations in string form
 // - Returns: True if the signature was verified
-func VerifyRSASignature(publicKey, signature, data []byte, digest string) (bool, error) {
-	digest = strings.ToLower(digest)
-	digestName := "sha256"	
-	switch {
-	case strings.Contains(digest, "sha1"):
-		digestName = "sha1"
-	case strings.Contains(digest, "sha224"):
-		digestName = "sha224"
-	case strings.Contains(digest, "sha256"), strings.Contains(digest, "rsassapss"):
-		digestName = "sha256"
-	case strings.Contains(digest, "sha384"):
-		digestName= "sha384"
-	case strings.Contains(digest, "sha512"):
-		digestName = "sha512"
-	case strings.Contains(digest, "ripemd160"):
-		digestName = "ripemd160"
-	}
-	md, err := GetDigestByName(digestName)
+func VerifyRSASignature(publicKey, signature, data []byte, digestType string, pkeyopt map[string]string) (bool, error) {
+	
+	md, err := GetDigestByName(digestType)
 	if err != nil {
 		return false, err
 	}
@@ -133,12 +119,11 @@ func VerifyRSASignature(publicKey, signature, data []byte, digest string) (bool,
 		return false, errors.New("unable to init digest verify")
 	}
 
-	if strings.Contains(digestName, "rsassapss") {
-		if C.X_EVP_PKEY_CTX_ctrl_str(ctx, C.CString("rsa_padding_mode"), C.CString("pss") ) <= 0 {
-			return false, errors.New("failed to set rsa padding mode")
-		}
-		if C.X_EVP_PKEY_CTX_ctrl_str(ctx, C.CString("rsa_pss_saltlen"), C.CString("auto")) <= 0 {
-			return false, errors.New("failed to set rsa pss saltlen")
+	if pkeyopt != nil && len(pkeyopt) > 0 {
+		for k, v := range pkeyopt {
+			if C.X_EVP_PKEY_CTX_ctrl_str(ctx, C.CString(k), C.CString(v)) <= 0 {
+				return false, fmt.Errorf("failed to set %s", k)
+			}
 		}
 	}
 

From 9a247e86641e3ff9a45f8517c5927e377480254f Mon Sep 17 00:00:00 2001
From: Jeannie Kidd <jkidd@miteksystems.com>
Date: Tue, 6 Apr 2021 13:31:07 -0700
Subject: [PATCH 24/35] update comment

---
 rsa.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/rsa.go b/rsa.go
index fce8df2a..b6d19c34 100644
--- a/rsa.go
+++ b/rsa.go
@@ -81,7 +81,7 @@ func VerifyRecoverRSASignature(publicKey, signature []byte) ([]byte, error) {
 // - Parameter publicKey: The OpenSSL EVP_PKEY public key in DER format
 // - Parameter signature: The signature to verify in DER format
 // - Parameter data: The data used to generate the signature
-// - Parameter digestType: The type of the digest to use. The currently supported values are: sha1, sha224, sha256, sha384, sha512, ripemd160, rsassapss
+// - Parameter digestType: The type of the digest to use. The currently supported values are: sha1, sha224, sha256, sha384, sha512, ripemd160
 // - Parameter pkeyopt: A map of any algorithm specific control operations in string form
 // - Returns: True if the signature was verified
 func VerifyRSASignature(publicKey, signature, data []byte, digestType string, pkeyopt map[string]string) (bool, error) {

From 1907c98f21a54421d83d7616488886c53ff17087 Mon Sep 17 00:00:00 2001
From: eleniums <eleniums@yahoo.com>
Date: Thu, 29 Apr 2021 16:27:48 -0700
Subject: [PATCH 25/35] Fix pkeyopts to not range on map

Map order on range is nondeterministic, but some of the pkeyopts need to be set in a specific order. In this case, rsa_padding_mode needs to be set before rsa_pss_saltlen. This was causing some pss documents to randomly fail if salt length was set before the padding mode.
---
 rsa.go | 29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/rsa.go b/rsa.go
index b6d19c34..f050cf39 100644
--- a/rsa.go
+++ b/rsa.go
@@ -85,7 +85,7 @@ func VerifyRecoverRSASignature(publicKey, signature []byte) ([]byte, error) {
 // - Parameter pkeyopt: A map of any algorithm specific control operations in string form
 // - Returns: True if the signature was verified
 func VerifyRSASignature(publicKey, signature, data []byte, digestType string, pkeyopt map[string]string) (bool, error) {
-	
+
 	md, err := GetDigestByName(digestType)
 	if err != nil {
 		return false, err
@@ -120,10 +120,29 @@ func VerifyRSASignature(publicKey, signature, data []byte, digestType string, pk
 	}
 
 	if pkeyopt != nil && len(pkeyopt) > 0 {
-		for k, v := range pkeyopt {
-			if C.X_EVP_PKEY_CTX_ctrl_str(ctx, C.CString(k), C.CString(v)) <= 0 {
-				return false, fmt.Errorf("failed to set %s", k)
+		// This is a convenience function for calling X_EVP_PKEY_CTX_ctrl_str. The _Ctype_struct_evp_pkey_ctx_st type is not
+		// exposed, but ctx can be captured in a local function like this.
+		setKeyOpt := func(pkeyopt map[string]string, k string) error {
+			v, ok := pkeyopt[k]
+			if !ok {
+				return nil
+			}
+			ck := C.CString(k)
+			defer C.free(unsafe.Pointer(ck))
+			cv := C.CString(v)
+			defer C.free(unsafe.Pointer(cv))
+			if C.X_EVP_PKEY_CTX_ctrl_str(ctx, ck, cv) <= 0 {
+				return fmt.Errorf("failed to set %s", k)
 			}
+			return nil
+		}
+
+		// Set RSA padding mode if it exists. Order matters; mode must be set before salt length.
+		if err := setKeyOpt(pkeyopt, "rsa_padding_mode"); err != nil {
+			return false, err
+		}
+		if err := setKeyOpt(pkeyopt, "rsa_pss_saltlen"); err != nil {
+			return false, err
 		}
 	}
 
@@ -138,4 +157,4 @@ func VerifyRSASignature(publicKey, signature, data []byte, digestType string, pk
 	}
 
 	return true, nil
-}
\ No newline at end of file
+}

From 1ed5ad5ffa1b28a88652e288f532dd7f8850a083 Mon Sep 17 00:00:00 2001
From: eleniums <eleniums@yahoo.com>
Date: Fri, 30 Apr 2021 10:22:08 -0700
Subject: [PATCH 26/35] Adjust pss salt length so it will only be set if
 padding mode is pss

---
 rsa.go | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/rsa.go b/rsa.go
index f050cf39..8b349e36 100644
--- a/rsa.go
+++ b/rsa.go
@@ -137,12 +137,17 @@ func VerifyRSASignature(publicKey, signature, data []byte, digestType string, pk
 			return nil
 		}
 
-		// Set RSA padding mode if it exists. Order matters; mode must be set before salt length.
-		if err := setKeyOpt(pkeyopt, "rsa_padding_mode"); err != nil {
-			return false, err
-		}
-		if err := setKeyOpt(pkeyopt, "rsa_pss_saltlen"); err != nil {
-			return false, err
+		// Set RSA padding mode and salt length if they exist. Order matters; mode must be set before salt length.
+		if rsaPaddingMode, ok := pkeyopt["rsa_padding_mode"]; ok {
+			if err := setKeyOpt(pkeyopt, "rsa_padding_mode"); err != nil {
+				return false, err
+			}
+			switch rsaPaddingMode {
+			case "pss":
+				if err := setKeyOpt(pkeyopt, "rsa_pss_saltlen"); err != nil {
+					return false, err
+				}
+			}
 		}
 	}
 

From c9ff239a97cb4374b1755180e365bc4971202aba Mon Sep 17 00:00:00 2001
From: eleniums <eleniums@yahoo.com>
Date: Fri, 30 Apr 2021 10:29:35 -0700
Subject: [PATCH 27/35] Add fallback for setting pkeyopt

---
 rsa.go | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/rsa.go b/rsa.go
index 8b349e36..8fadfc08 100644
--- a/rsa.go
+++ b/rsa.go
@@ -22,6 +22,11 @@ import (
 	"unsafe"
 )
 
+var pkeyoptSkip = []string{
+	"rsa_padding_mode",
+	"rsa_pss_saltlen",
+}
+
 // VerifyRecoverRSASignature takes a DER encoded RSA public key and a raw signature
 // (assuming no padding currently) and returns the recoverable part of the signed data.
 // This follows the example shown here: https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_verify_recover.html
@@ -149,6 +154,16 @@ func VerifyRSASignature(publicKey, signature, data []byte, digestType string, pk
 				}
 			}
 		}
+
+		// Fallback to make sure all pkeyopt get processed. Skips any keys found in pkeyoptSkip.
+		for k := range pkeyopt {
+			if contains(pkeyoptSkip, k) {
+				continue
+			}
+			if err := setKeyOpt(pkeyopt, k); err != nil {
+				return false, err
+			}
+		}
 	}
 
 	nRes = C.EVP_DigestUpdate(mdctx, unsafe.Pointer((*C.uchar)(&data[0])), C.size_t(len(data)))
@@ -163,3 +178,12 @@ func VerifyRSASignature(publicKey, signature, data []byte, digestType string, pk
 
 	return true, nil
 }
+
+func contains(items []string, s string) bool {
+	for _, v := range items {
+		if v == s {
+			return true
+		}
+	}
+	return false
+}

From b691872c650ab3bf69713c20ffdd6481ef90d37f Mon Sep 17 00:00:00 2001
From: Nathan Shain <nathan@rookout.com>
Date: Wed, 23 Feb 2022 18:10:19 +0200
Subject: [PATCH 28/35] Fix build for m1

---
 build.go        | 6 ++++--
 build_static.go | 6 ++++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/build.go b/build.go
index 5fccc021..926ab050 100644
--- a/build.go
+++ b/build.go
@@ -18,7 +18,9 @@ package openssl
 
 // #cgo linux windows pkg-config: libssl libcrypto
 // #cgo linux CFLAGS: -Wno-deprecated-declarations
-// #cgo darwin CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations
-// #cgo darwin LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto
+// #cgo darwin 386 CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations
+// #cgo darwin 386 LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto
+// #cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/openssl@1.1/include -Wno-deprecated-declarations
+// #cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/openssl@1.1/lib -lssl -lcrypto
 // #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN
 import "C"
diff --git a/build_static.go b/build_static.go
index c84427bc..9e6d8a40 100644
--- a/build_static.go
+++ b/build_static.go
@@ -18,7 +18,9 @@ package openssl
 
 // #cgo linux windows pkg-config: --static libssl libcrypto
 // #cgo linux CFLAGS: -Wno-deprecated-declarations
-// #cgo darwin CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations
-// #cgo darwin LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto
+// #cgo darwin 386 CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations
+// #cgo darwin 386 LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto
+// #cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/openssl@1.1/include -Wno-deprecated-declarations
+// #cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/openssl@1.1/lib -lssl -lcrypto
 // #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN
 import "C"

From 870070f17d2f549c957f7c57ec7d06b3e60a0fb8 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 28 Jul 2022 12:38:24 -0400
Subject: [PATCH 29/35] Revert "Fix build for m1"

This reverts commit b691872c650ab3bf69713c20ffdd6481ef90d37f.
---
 build.go        | 6 ++----
 build_static.go | 6 ++----
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/build.go b/build.go
index 926ab050..5fccc021 100644
--- a/build.go
+++ b/build.go
@@ -18,9 +18,7 @@ package openssl
 
 // #cgo linux windows pkg-config: libssl libcrypto
 // #cgo linux CFLAGS: -Wno-deprecated-declarations
-// #cgo darwin 386 CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations
-// #cgo darwin 386 LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto
-// #cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/openssl@1.1/include -Wno-deprecated-declarations
-// #cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/openssl@1.1/lib -lssl -lcrypto
+// #cgo darwin CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations
+// #cgo darwin LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto
 // #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN
 import "C"
diff --git a/build_static.go b/build_static.go
index 9e6d8a40..c84427bc 100644
--- a/build_static.go
+++ b/build_static.go
@@ -18,9 +18,7 @@ package openssl
 
 // #cgo linux windows pkg-config: --static libssl libcrypto
 // #cgo linux CFLAGS: -Wno-deprecated-declarations
-// #cgo darwin 386 CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations
-// #cgo darwin 386 LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto
-// #cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/openssl@1.1/include -Wno-deprecated-declarations
-// #cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/openssl@1.1/lib -lssl -lcrypto
+// #cgo darwin CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations
+// #cgo darwin LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto
 // #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN
 import "C"

From 96484836ea8c4652d8338aa04fcf7e81fe251df4 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 28 Jul 2022 12:52:20 -0400
Subject: [PATCH 30/35] remove ripemd160 which is not  supported (without
 legacy build flags) - see https://github.com/openssl/openssl/issues/16994

---
 ecdsa.go | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/ecdsa.go b/ecdsa.go
index 67ff6b3f..bcd3a6b5 100644
--- a/ecdsa.go
+++ b/ecdsa.go
@@ -25,7 +25,7 @@ import (
 // - Parameter publicKey: The OpenSSL EVP_PKEY ECDSA public key in DER format
 // - Parameter signature: The ECDSA signature to verify in DER format
 // - Parameter data: The raw data used to generate the signature
-// - Parameter digest: The name of the digest to use. The currently supported values are: sha1, sha224, sha256, sha384, sha512, ripemd160
+// - Parameter digest: The name of the digest to use. The currently supported values are: sha1, sha224, sha256, sha384, sha512
 // - Returns: True if the signature was verified
 func VerifyECDSASignature(publicKey, signature, data []byte, digest string) (bool, error) {
 	// read EC Public Key
@@ -74,8 +74,6 @@ func VerifyECDSASignature(publicKey, signature, data []byte, digest string) (boo
 		digestType = C.EVP_sha384()
 	case "sha512":
 		digestType = C.EVP_sha512()
-	case "ripemd160":
-		digestType = C.EVP_ripemd160()
 	default:
 		return false, errors.New("unsupported digest value")
 	}

From c05281c179f264527213005d22584b90365c0f2e Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Wed, 24 May 2023 15:19:11 -0400
Subject: [PATCH 31/35] add function to parse rsa public key fields

---
 rsa.go      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 rsa_test.go | 39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)
 create mode 100644 rsa_test.go

diff --git a/rsa.go b/rsa.go
index 8fadfc08..a9a00a60 100644
--- a/rsa.go
+++ b/rsa.go
@@ -19,6 +19,8 @@ import "C"
 import (
 	"errors"
 	"fmt"
+	"math/big"
+	"strconv"
 	"unsafe"
 )
 
@@ -187,3 +189,51 @@ func contains(items []string, s string) bool {
 	}
 	return false
 }
+
+// RSAPublicKey represents the public part of an RSA key.
+type RSAPublicKey struct {
+	N *big.Int // modulus
+	E int      // public exponent
+}
+
+// This function specifically expects an RSA public key DER encoded in the PKCS#1 format
+func ParseRSAPublicKeyPKCS1(publicKey []byte) (key *RSAPublicKey, err error) {
+	inf := C.BIO_new(C.BIO_s_mem())
+	if inf == nil {
+		return nil, errors.New("failed allocating input buffer")
+	}
+	defer C.BIO_free(inf)
+	_, err = asAnyBio(inf).Write(publicKey)
+	if err != nil {
+		return nil, err
+	}
+
+	rsa := C.d2i_RSA_PUBKEY_bio(inf, nil)
+	if rsa == nil {
+		return nil, errors.New("failed to load public key")
+	}
+	defer C.RSA_free(rsa)
+
+	var n, e *C.BIGNUM
+	C.RSA_get0_key(rsa, &n, &e, nil)
+	defer C.BN_free(n)
+	defer C.BN_free(e)
+
+	CmodulusHex := C.BN_bn2hex(n)
+	defer C.X_OPENSSL_free(unsafe.Pointer(CmodulusHex))
+	CexponentHex := C.BN_bn2hex(e)
+	defer C.X_OPENSSL_free(unsafe.Pointer(CexponentHex))
+
+	modulusHex := C.GoString(CmodulusHex)
+	exponentHex := C.GoString(CexponentHex)
+
+	ret := &RSAPublicKey{N: new(big.Int)}
+	ret.N.SetString(modulusHex, 16)
+	exponent, err := strconv.ParseInt(exponentHex, 16, 64)
+	if err != nil {
+		return nil, fmt.Errorf("failed to convert hex exponent to int: %v", err)
+	}
+	ret.E = int(exponent)
+
+	return ret, nil
+}
diff --git a/rsa_test.go b/rsa_test.go
new file mode 100644
index 00000000..84177cdd
--- /dev/null
+++ b/rsa_test.go
@@ -0,0 +1,39 @@
+// 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
+
+import (
+	"encoding/hex"
+	"testing"
+)
+
+func Test_Unit_ParseRSAPublicKeyPKCS1(t *testing.T) {
+	rsaPubKeyHex := "30819f300d06092a864886f70d010101050003818d0030818902818100c66ee1df2b07469ec8a45d2307500cdd30fddf514356062a6e651ccdd667f050c462cca3932a7a1e28b59b20071a7897736b12fac21bbc5a66cc64e74adf222cef3dda627512efdbc89bf9a0d77dfcc33417110aaf218dbcb7090b95395535a557c0a621ab7dbdc764061fb3644141f363cd2bd82ce541a9e0a8f22b3e3581d70203010001"
+	rsaPubKeyDer, err := hex.DecodeString(rsaPubKeyHex)
+	if err != nil {
+		t.Fatal(err)
+	}
+	sut, err := ParseRSAPublicKeyPKCS1(rsaPubKeyDer)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if sut.E != 65537 {
+		t.Fatal()
+	}
+	actualN := hex.EncodeToString(sut.N.Bytes())
+	if actualN != "c66ee1df2b07469ec8a45d2307500cdd30fddf514356062a6e651ccdd667f050c462cca3932a7a1e28b59b20071a7897736b12fac21bbc5a66cc64e74adf222cef3dda627512efdbc89bf9a0d77dfcc33417110aaf218dbcb7090b95395535a557c0a621ab7dbdc764061fb3644141f363cd2bd82ce541a9e0a8f22b3e3581d7" {
+		t.Fatal(actualN)
+	}
+}

From 1f0160f7a517bbe0c6f79985161b06e1eb85c325 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Wed, 24 May 2023 15:34:23 -0400
Subject: [PATCH 32/35] fix for intermittent SIGTERM

---
 rsa.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/rsa.go b/rsa.go
index a9a00a60..e0f7fb77 100644
--- a/rsa.go
+++ b/rsa.go
@@ -216,8 +216,8 @@ func ParseRSAPublicKeyPKCS1(publicKey []byte) (key *RSAPublicKey, err error) {
 
 	var n, e *C.BIGNUM
 	C.RSA_get0_key(rsa, &n, &e, nil)
-	defer C.BN_free(n)
-	defer C.BN_free(e)
+	// Note: purposely not calling BN_free on n & e, because they are cleaned up by RSA_free.
+	// Calling both results in an intermittent SIGTERM.
 
 	CmodulusHex := C.BN_bn2hex(n)
 	defer C.X_OPENSSL_free(unsafe.Pointer(CmodulusHex))

From 66935e8c73cad0baa475cbb168ee09bf8f576b05 Mon Sep 17 00:00:00 2001
From: adrifern48 <adrifern48@gmail.com>
Date: Thu, 15 Jun 2023 16:11:27 -0700
Subject: [PATCH 33/35] add support for getting cert issue/expire dates

---
 cert.go      | 31 ++++++++++++++++++++++++++++
 cert_test.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+)

diff --git a/cert.go b/cert.go
index 48e7ffa7..62e0dfd8 100644
--- a/cert.go
+++ b/cert.go
@@ -19,6 +19,7 @@ import "C"
 
 import (
 	"errors"
+	"fmt"
 	"io/ioutil"
 	"math/big"
 	"runtime"
@@ -230,6 +231,21 @@ func (c *Certificate) SetSerial(serial *big.Int) error {
 	return nil
 }
 
+// GetIssueDate retrieves the certificate issue date.
+func (c *Certificate) GetIssueDate() (time.Time, error) {
+	var ts time.Time
+	result := C.X_X509_get0_notBefore(c.x)
+	if result == nil {
+		return ts, errors.New("failed to get issue date")
+	}
+	str := C.GoString((*C.char)(unsafe.Pointer(result.data)))
+	ts, err := time.Parse("060102150405Z", str)
+	if err != nil {
+		return ts, fmt.Errorf("unable to parse timestamp: %v", err)
+	}
+	return ts, nil
+}
+
 // SetIssueDate sets the certificate issue date relative to the current time.
 func (c *Certificate) SetIssueDate(when time.Duration) error {
 	offset := C.long(when / time.Second)
@@ -240,6 +256,21 @@ func (c *Certificate) SetIssueDate(when time.Duration) error {
 	return nil
 }
 
+// GetExpireDate retrieves the certificate expiry date.
+func (c *Certificate) GetExpireDate() (time.Time, error) {
+	var ts time.Time
+	result := C.X_X509_get0_notAfter(c.x)
+	if result == nil {
+		return ts, errors.New("failed to get expiry date")
+	}
+	str := C.GoString((*C.char)(unsafe.Pointer(result.data)))
+	ts, err := time.Parse("060102150405Z", str)
+	if err != nil {
+		return ts, fmt.Errorf("unable to parse timestamp: %v", err)
+	}
+	return ts, nil
+}
+
 // SetExpireDate sets the certificate issue date relative to the current time.
 func (c *Certificate) SetExpireDate(when time.Duration) error {
 	offset := C.long(when / time.Second)
diff --git a/cert_test.go b/cert_test.go
index 45107a0a..eef5d23e 100644
--- a/cert_test.go
+++ b/cert_test.go
@@ -138,6 +138,63 @@ func TestCertGetNameEntry(t *testing.T) {
 	}
 }
 
+func TestCertIssueDate(t *testing.T) {
+	key, err := GenerateRSAKey(768)
+	if err != nil {
+		t.Fatal(err)
+	}
+	info := &CertificateInfo{
+		Serial:       big.NewInt(int64(1)),
+		Expires:      24 * time.Hour,
+		Country:      "US",
+		Organization: "Test",
+		CommonName:   "localhost",
+	}
+	cert, err := NewCertificate(info, key)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := cert.SetIssueDate(0); err != nil {
+		t.Fatal(err)
+	}
+	issue, err := cert.GetIssueDate()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if issue.IsZero() || issue.After(time.Now()) {
+		t.Fatalf("invalid issue date")
+	}
+}
+
+func TestCertExpireDate(t *testing.T) {
+	key, err := GenerateRSAKey(768)
+	if err != nil {
+		t.Fatal(err)
+	}
+	info := &CertificateInfo{
+		Serial:       big.NewInt(int64(1)),
+		Issued:       0,
+		Country:      "US",
+		Organization: "Test",
+		CommonName:   "localhost",
+	}
+	cert, err := NewCertificate(info, key)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := cert.SetExpireDate(30 * time.Minute); err != nil {
+		t.Fatal(err)
+	}
+	exp, err := cert.GetExpireDate()
+	if err != nil {
+		t.Fatal(err)
+	}
+	now := time.Now()
+	if exp.IsZero() || exp.Before(now) || exp.After(now.Add(30*time.Minute)) {
+		t.Fatalf("invalid expire date")
+	}
+}
+
 func TestCertVersion(t *testing.T) {
 	key, err := GenerateRSAKey(768)
 	if err != nil {

From bcddcafbcccc6bbee293d4c185f34ef413f920d3 Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Thu, 21 Dec 2023 16:14:08 -0500
Subject: [PATCH 34/35] add function to parse a cert from DER

---
 cert.go | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/cert.go b/cert.go
index 62e0dfd8..805e028b 100644
--- a/cert.go
+++ b/cert.go
@@ -394,6 +394,27 @@ func LoadCertificateFromPEM(pem_block []byte) (*Certificate, error) {
 	return x, nil
 }
 
+// LoadCertificateFromDER loads an X509 certificate from a DER-encoded block.
+func LoadCertificateFromDER(der_block []byte) (*Certificate, error) {
+	if len(der_block) == 0 {
+		return nil, errors.New("empty der block")
+	}
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+	bio := C.BIO_new_mem_buf(unsafe.Pointer(&der_block[0]),
+		C.int(len(der_block)))
+	cert := C.d2i_X509_bio(bio, nil)
+	C.BIO_free(bio)
+	if cert == nil {
+		return nil, errorFromErrorQueue()
+	}
+	x := &Certificate{x: cert}
+	runtime.SetFinalizer(x, func(x *Certificate) {
+		C.X509_free(x.x)
+	})
+	return x, nil
+}
+
 // MarshalPEM converts the X509 certificate to PEM-encoded format
 func (c *Certificate) MarshalPEM() (pem_block []byte, err error) {
 	bio := C.BIO_new(C.BIO_s_mem())

From 193f8d0313b6407fc7b10f943fcd2854a2f57d8b Mon Sep 17 00:00:00 2001
From: Erik Tammaru <etammaru@miteksystems.com>
Date: Fri, 3 May 2024 12:25:16 -0400
Subject: [PATCH 35/35] fix for "panic: runtime error: makeslice: cap out of
 range"

---
 cert.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/cert.go b/cert.go
index 805e028b..ca8a641a 100644
--- a/cert.go
+++ b/cert.go
@@ -519,6 +519,9 @@ func LoadCertificatesFromPKCS7(der_block []byte) (*PKCS7, error) {
 // loadCertificateStack loads up a stack of x509 certificates into the PKCS7 struct.
 func (p *PKCS7) loadCertificateStack(sk *C.struct_stack_st_X509) error {
 	sk_num := int(C.X_sk_X509_num(sk))
+	if sk_num < 0 {
+		return fmt.Errorf("invalid value returned by sk_X509_num: %d", sk_num)
+	}
 	p.Certs = make([]*Certificate, 0, sk_num)
 	for i := 0; i < sk_num; i++ {
 		x := C.X_sk_X509_value(sk, C.int(i))