Skip to content

Instantly share code, notes, and snippets.

@nqb
Created November 11, 2020 14:36
Show Gist options
  • Save nqb/43ec0120d4cbf8103228b79089e0c4bf to your computer and use it in GitHub Desktop.
Save nqb/43ec0120d4cbf8103228b79089e0c4bf to your computer and use it in GitHub Desktop.
SMIME.xs
#include <assert.h>
#include <string.h>
#if defined(HAVE_SYS_TIME_H)
# include <sys/time.h>
#endif
#include <openssl/crypto.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
#include <openssl/x509.h>
#if defined(HAVE_TIME_H)
# include <time.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
struct crypt_smime {
EVP_PKEY *priv_key;
X509* priv_cert;
bool priv_key_is_tainted;
bool priv_cert_is_tainted;
const EVP_CIPHER* cipher;
/* 暗号化, 添付用 */
STACK_OF(X509)* pubkeys_stack;
/* 検証用 */
X509_STORE* pubkeys_store;
bool pubkeys_are_tainted;
};
typedef struct crypt_smime * Crypt_SMIME;
#define OPENSSL_CROAK(description) \
croak("%s: %s", \
description, \
ERR_error_string(ERR_get_error(), NULL))
static inline bool is_string(SV const* sv) {
/* It's not sufficient to call SvPOK() to see if an SV contains a
* character string. It returns false for all SV if taint checking
* is enabled.
*/
return SvPOK(sv) || SvPOKp(sv);
}
/* B64_write_PKCS7 is copyed from openssl/crypto/pkcs7/pk7_mime.c */
static int B64_write_PKCS7(BIO *bio, PKCS7 *p7)
{
BIO *b64;
if(!(b64 = BIO_new(BIO_f_base64()))) {
PKCS7err(PKCS7_F_B64_WRITE_PKCS7,ERR_R_MALLOC_FAILURE);
return 0;
}
bio = BIO_push(b64, bio);
i2d_PKCS7_bio(bio, p7);
(void)BIO_flush(bio);
bio = BIO_pop(bio);
BIO_free(b64);
return 1;
}
static EVP_PKEY* load_privkey(Crypt_SMIME this, char* pem, char* password) {
BIO *buf;
EVP_PKEY *key;
buf = BIO_new_mem_buf(pem, -1);
if (buf == NULL) {
return NULL;
}
key = PEM_read_bio_PrivateKey(
buf, NULL, (pem_password_cb*)NULL, password);
BIO_free(buf);
return key;
}
/* ----------------------------------------------------------------------------
* X509* x509 = load_cert(crt);
* extract X509 information from cert data.
* not from file, from just data.
* ------------------------------------------------------------------------- */
static X509* load_cert(char* crt) {
BIO* buf;
X509 *x;
buf = BIO_new_mem_buf(crt, -1);
if (buf == NULL) {
return NULL;
}
x = PEM_read_bio_X509_AUX(buf, NULL, NULL, NULL);
BIO_free(buf);
return x;
}
static SV* sign(Crypt_SMIME this, char* plaintext) {
BIO* inbuf;
BIO* outbuf;
PKCS7* pkcs7;
int flags = PKCS7_DETACHED;
BUF_MEM* bufmem;
SV* result;
int err;
inbuf = BIO_new_mem_buf(plaintext, -1);
if (inbuf == NULL) {
return NULL;
}
/*クリア署名を作る */
pkcs7 = PKCS7_sign(this->priv_cert, this->priv_key, NULL, inbuf, flags);
if (pkcs7 == NULL) {
return NULL;
}
outbuf = BIO_new(BIO_s_mem());
if (outbuf == NULL) {
PKCS7_free(pkcs7);
return NULL;
}
(void)BIO_reset(inbuf);
{
int i;
for( i=0; i< sk_X509_num(this->pubkeys_stack); ++i )
{
X509* x509 = sk_X509_value(this->pubkeys_stack,i);
assert( x509!=NULL );
PKCS7_add_certificate(pkcs7, x509);
}
}
err = SMIME_write_PKCS7(outbuf, pkcs7, inbuf, flags);
PKCS7_free(pkcs7);
BIO_free(inbuf);
if (err != 1) {
return NULL;
}
BIO_get_mem_ptr(outbuf, &bufmem);
result = newSVpv(bufmem->data, bufmem->length);
BIO_free(outbuf);
if (this->priv_key_is_tainted || this->priv_cert_is_tainted || this->pubkeys_are_tainted) {
SvTAINTED_on(result);
}
return result;
}
static SV* signonly(Crypt_SMIME this, char* plaintext, size_t length, int flags) {
BIO* inbuf;
BIO* outbuf;
PKCS7* pkcs7;
BUF_MEM* bufmem;
SV* result;
int err;
inbuf = BIO_new_mem_buf(plaintext, length);
if (inbuf == NULL) {
return NULL;
}
/*クリア署名を作る */
pkcs7 = PKCS7_sign(this->priv_cert, this->priv_key, NULL, inbuf, flags);
BIO_free(inbuf);
if (pkcs7 == NULL) {
return NULL;
}
outbuf = BIO_new(BIO_s_mem());
if (outbuf == NULL) {
PKCS7_free(pkcs7);
return NULL;
}
{
int i;
for( i=0; i< sk_X509_num(this->pubkeys_stack); ++i )
{
X509* x509 = sk_X509_value(this->pubkeys_stack,i);
assert( x509!=NULL );
PKCS7_add_certificate(pkcs7, x509);
}
}
err = B64_write_PKCS7(outbuf, pkcs7);
PKCS7_free(pkcs7);
if (err != 1) {
return NULL;
}
BIO_get_mem_ptr(outbuf, &bufmem);
result = newSVpv(bufmem->data, bufmem->length);
BIO_free(outbuf);
if (this->priv_key_is_tainted || this->priv_cert_is_tainted || this->pubkeys_are_tainted) {
SvTAINTED_on(result);
}
return result;
}
static SV* check(Crypt_SMIME this, char* signed_mime) {
BIO* inbuf;
BIO* detached = NULL;
BIO* outbuf;
PKCS7* sign;
int flags = 0;
int err;
BUF_MEM* bufmem;
SV* result;
inbuf = BIO_new_mem_buf(signed_mime, -1);
if (inbuf == NULL) {
return NULL;
}
sign = SMIME_read_PKCS7(inbuf, &detached);
BIO_free(inbuf);
if (sign == NULL) {
return NULL;
}
outbuf = BIO_new(BIO_s_mem());
if (outbuf == NULL) {
PKCS7_free(sign);
return NULL;
}
err = PKCS7_verify(sign, NULL, this->pubkeys_store, detached, outbuf, flags);
PKCS7_free(sign);
if (detached != NULL) {
BIO_free(detached);
}
if (err <= 0) {
BIO_free(outbuf);
return NULL;
}
BIO_get_mem_ptr(outbuf, &bufmem);
result = newSVpv(bufmem->data, bufmem->length);
BIO_free(outbuf);
if (this->pubkeys_are_tainted) {
SvTAINTED_on(result);
}
return result;
}
static SV* _encrypt(Crypt_SMIME this, char* plaintext) {
BIO* inbuf;
BIO* outbuf;
PKCS7* enc;
int flags = 0;
int err;
BUF_MEM* bufmem;
SV* result;
inbuf = BIO_new_mem_buf(plaintext, -1);
if (inbuf == NULL) {
return NULL;
}
enc = PKCS7_encrypt(this->pubkeys_stack, inbuf, this->cipher, flags);
BIO_free(inbuf);
if (enc == NULL) {
return NULL;
}
outbuf = BIO_new(BIO_s_mem());
if (outbuf == NULL) {
PKCS7_free(enc);
return NULL;
}
err = SMIME_write_PKCS7(outbuf, enc, NULL, flags);
PKCS7_free(enc);
if (err != 1) {
BIO_free(outbuf);
return NULL;
}
BIO_get_mem_ptr(outbuf, &bufmem);
result = newSVpv(bufmem->data, bufmem->length);
BIO_free(outbuf);
if (this->pubkeys_are_tainted) {
SvTAINTED_on(result);
}
return result;
}
static SV* _decrypt(Crypt_SMIME this, char* encrypted_mime) {
BIO* inbuf;
BIO* outbuf;
PKCS7* enc;
int flags = 0;
int err;
BUF_MEM* bufmem;
SV* result;
inbuf = BIO_new_mem_buf(encrypted_mime, -1);
if (inbuf == NULL) {
return NULL;
}
enc = SMIME_read_PKCS7(inbuf, NULL);
BIO_free(inbuf);
if (enc == NULL) {
return NULL;
}
outbuf = BIO_new(BIO_s_mem());
if (outbuf == NULL) {
PKCS7_free(enc);
return NULL;
}
err = PKCS7_decrypt(enc, this->priv_key, this->priv_cert, outbuf, flags);
PKCS7_free(enc);
if (err != 1) {
BIO_free(outbuf);
return NULL;
}
BIO_get_mem_ptr(outbuf, &bufmem);
result = newSVpv(bufmem->data, bufmem->length);
BIO_free(outbuf);
if (this->priv_key_is_tainted || this->priv_cert_is_tainted) {
SvTAINTED_on(result);
}
return result;
}
static void seed_rng() {
/* OpenSSL automatically seeds the random number generator from
* /dev/urandom (on UNIX) or CryptGenRandom (on Windows). But if
* we are on an exotic platform, we must somehow seed the RNG.
*/
RAND_poll();
while (RAND_status() == 0) {
#if defined(HAVE_GETTIMEOFDAY)
struct timeval tv;
gettimeofday(&tv, NULL);
RAND_seed(&tv, sizeof(struct timeval));
#elif defined(HAVE_TIME)
time_t t;
t = time(NULL);
RAND_seed(&t, sizeof(time_t));
#else
croak("Crypt::SMIME#import: don't know how to seed the CSPRNG on this platform");
#endif
}
}
MODULE = Crypt::SMIME PACKAGE = Crypt::SMIME
void
_init(char* /*CLASS*/)
CODE:
/* libcryptoの初期化 */
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
seed_rng();
Crypt_SMIME
new(char* /*CLASS*/)
CODE:
RETVAL = safemalloc(sizeof(struct crypt_smime));
if (RETVAL == NULL) {
croak("Crypt::SMIME#new: unable to allocate Crypt_SMIME");
}
memset(RETVAL, '\0', sizeof(struct crypt_smime));
OUTPUT:
RETVAL
void
DESTROY(Crypt_SMIME this)
CODE:
if (this->priv_cert) {
X509_free(this->priv_cert);
}
if (this->priv_key) {
EVP_PKEY_free(this->priv_key);
}
if (this->pubkeys_stack) {
sk_X509_free(this->pubkeys_stack);
}
if (this->pubkeys_store) {
X509_STORE_free(this->pubkeys_store);
}
safefree(this);
SV*
setPrivateKey(Crypt_SMIME this, char* pem, char* crt, ...)
PROTOTYPE: $$$;$
PREINIT:
char* password = "";
STRLEN n_a;
CODE:
if (items > 3) {
password = (char*)SvPV(ST(3), n_a);
}
/* 古い鍵があったら消す */
if (this->priv_cert) {
X509_free(this->priv_cert);
this->priv_cert = NULL;
}
if (this->priv_key) {
EVP_PKEY_free(this->priv_key);
this->priv_key = NULL;
}
this->priv_key = load_privkey(this, pem, password);
if (this->priv_key == NULL) {
OPENSSL_CROAK("Crypt::SMIME#setPrivateKey: failed to load the private key");
}
this->priv_cert = load_cert(crt);
if (this->priv_cert == NULL) {
OPENSSL_CROAK("Crypt::SMIME#setPrivateKey: failed to load the private cert");
}
this->priv_key_is_tainted = SvTAINTED(ST(1));
this->priv_cert_is_tainted = SvTAINTED(ST(2));
SvREFCNT_inc(ST(0));
RETVAL = ST(0);
OUTPUT:
RETVAL
SV*
setPublicKey(Crypt_SMIME this, SV* crt)
CODE:
/*
crt: ARRAY Refなら、その各要素が公開鍵
SCALARなら、それが公開鍵
*/
/* 古い鍵があったら消す */
if (this->pubkeys_stack) {
sk_X509_free(this->pubkeys_stack);
this->pubkeys_stack = NULL;
}
if (this->pubkeys_store) {
X509_STORE_free(this->pubkeys_store);
this->pubkeys_store = NULL;
}
this->pubkeys_store = X509_STORE_new();
if (this->pubkeys_store == NULL) {
croak("Crypt::SMIME#setPublicKey: failed to allocate X509_STORE");
}
/* 何故STACK_OF(X509)とX509_STOREの二つを使う必要があるのか。 */
this->pubkeys_stack = sk_X509_new_null();
if (this->pubkeys_stack == NULL) {
croak("Crypt::SMIME#setPublicKey: failed to allocate STACK_OF(X509)");
}
this->pubkeys_are_tainted = FALSE;
if (SvROK(crt) && SvTYPE(SvRV(crt)) == SVt_PVAV) {
AV* array = (AV*)SvRV(crt);
I32 i, len = av_len(array);
for (i = 0; i <= len; i++) {
SV** val = av_fetch(array, i, 1);
if (val == NULL) {
continue; /* 多分起こらないが… */
}
if (is_string(*val)) {
SV* this_sv = ST(0);
dSP;
ENTER;
PUSHMARK(SP);
XPUSHs(this_sv);
XPUSHs(*val);
PUTBACK;
call_method("_addPublicKey", G_DISCARD);
LEAVE;
}
else {
croak("Crypt::SMIME#setPublicKey: ARG[1] is an array but it contains some non-string values");
}
}
}
else if (is_string(crt)) {
SV* this_sv = ST(0);
dSP;
ENTER;
PUSHMARK(SP);
XPUSHs(this_sv);
XPUSHs(crt);
PUTBACK;
call_method("_addPublicKey", G_DISCARD);
LEAVE;
}
else {
croak("Crypt::SMIME#setPublicKey: ARG[1] is not a string nor an ARRAY Ref");
}
SvREFCNT_inc(ST(0));
RETVAL = ST(0);
OUTPUT:
RETVAL
void
_addPublicKey(Crypt_SMIME this, char* crt)
PREINIT:
BIO* buf;
CODE:
/* Be aware; 'crt' may contain two or more certificates.
*/
buf = BIO_new_mem_buf(crt, -1);
if (buf == NULL) {
OPENSSL_CROAK("Crypt::SMIME#setPublicKey: failed to allocate a buffer");
}
while (1) {
X509* pub_cert;
pub_cert = PEM_read_bio_X509_AUX(buf, NULL, NULL, NULL);
if (pub_cert == NULL) {
if (ERR_GET_REASON(ERR_get_error()) == PEM_R_NO_START_LINE) {
break;
}
else {
BIO_free(buf);
OPENSSL_CROAK("Crypt::SMIME#setPublicKey: failed to load the public cert");
}
}
/* X509_STORE_add_cert() has an undocumented behavior that
* increments a refcount in X509 unlike sk_X509_push(). So
* we must not call X509_dup() here.
*/
if (X509_STORE_add_cert(this->pubkeys_store, pub_cert) == 0) {
X509_free(pub_cert);
BIO_free(buf);
OPENSSL_CROAK("Crypt::SMIME#setPublicKey: failed to store the public cert");
}
if (sk_X509_push(this->pubkeys_stack, pub_cert) == 0) {
X509_free(pub_cert);
BIO_free(buf);
OPENSSL_CROAK("Crypt::SMIME#setPublicKey: failed to push the public cert onto the stack");
}
}
BIO_free(buf);
if (SvTAINTED(ST(1))) {
this->pubkeys_are_tainted = TRUE;
}
SV*
setPublicKeyStore(Crypt_SMIME this, ...)
INIT:
X509_STORE* store;
X509* pub_cert;
X509_LOOKUP *lookup_file, *lookup_path;
int i, has_file = 0, has_path = 0;
char* pathname;
struct stat bufstat;
CODE:
/* 古い証明書ストアがあったら消す */
if (this->pubkeys_store) {
X509_STORE_free(this->pubkeys_store);
this->pubkeys_store = NULL;
}
store = X509_STORE_new();
if (store == NULL) {
croak("Crypt::SMIME#setPublicKeyStore: failed to allocate X509_STORE");
}
/* setPublicKey()で設定した証明書があれば追加する */
for (i = 0; i < sk_X509_num(this->pubkeys_stack); i++) {
pub_cert = sk_X509_value(this->pubkeys_stack, i);
if (pub_cert == NULL || X509_STORE_add_cert(store, pub_cert) == 0) {
X509_STORE_free(store);
croak("Crypt::SMIME#setPublicKeyStore: failed to store the public cert");
}
}
if (sk_X509_num(this->pubkeys_stack) == 0) {
this->pubkeys_are_tainted = FALSE;
}
/* 引数があれば証明書ストアとして追加する */
lookup_file = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (lookup_file == NULL) {
X509_STORE_free(store);
croak("Crypt::SMIME#setPublicKeyStore: failed to allocate X509_LOOKUP");
}
lookup_path = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
if (lookup_path == NULL) {
X509_STORE_free(store);
croak("Crypt::SMIME#setPublicKeyStore: failed to allocate X509_LOOKUP");
}
for (i = 1; i < items; i++) {
if (ST(i) == NULL) {
continue; /* 多分起こらないが… */
}
if (!is_string(ST(i))) {
X509_STORE_free(store);
croak("Crypt::SMIME#setPublicKeyStore: ARG[%d] is non-string value", i);
}
pathname = (char *)SvPV_nolen(ST(i));
if (stat(pathname, &bufstat) != 0) {
X509_STORE_free(store);
croak("Crypt::SMIME#setPublicKeyStore: cannot stat %s",
pathname);
} else if (bufstat.st_mode & S_IFDIR) {
if (!X509_LOOKUP_add_dir(lookup_path, pathname,
X509_FILETYPE_PEM)) {
X509_STORE_free(store);
croak("Crypt::SMIME#setPublicKeyStore: failed to add ARG[%d] as store", i);
}
has_path = 1;
} else {
if (!X509_LOOKUP_load_file(lookup_file, pathname,
X509_FILETYPE_PEM)) {
X509_STORE_free(store);
croak("Crypt::SMIME#setPublicKeyStore: failed to add ARG[%d] as store", i);
}
has_file = 1;
}
if (SvTAINTED(ST(i))) {
this->pubkeys_are_tainted = TRUE;
}
}
/* 引数がなければ初期値の場所のストアを (存在すれば) 追加する */
if (!has_file) {
X509_LOOKUP_load_file(lookup_file, NULL, X509_FILETYPE_DEFAULT);
}
if (!has_path) {
X509_LOOKUP_add_dir(lookup_path, NULL, X509_FILETYPE_DEFAULT);
}
ERR_clear_error();
this->pubkeys_store = store;
SvREFCNT_inc(ST(0));
RETVAL = ST(0);
OUTPUT:
RETVAL
SV*
_sign(Crypt_SMIME this, char* plaintext)
CODE:
/* 秘密鍵がまだセットされていなければエラー */
if (this->priv_key == NULL) {
croak("Crypt::SMIME#sign: private key has not yet been set. Set one before signing");
}
if (this->priv_cert == NULL) {
croak("Crypt::SMIME#sign: private cert has not yet been set. Set one before signing");
}
RETVAL = sign(this, plaintext);
if (RETVAL == NULL) {
OPENSSL_CROAK("Crypt::SMIME#sign: failed to sign the message");
}
OUTPUT:
RETVAL
SV*
_signonly(Crypt_SMIME this, char* plaintext)
CODE:
/* 秘密鍵がまだセットされていなければエラー */
if (this->priv_key == NULL) {
croak("Crypt::SMIME#signonly: private key has not yet been set. Set one before signing");
}
if (this->priv_cert == NULL) {
croak("Crypt::SMIME#signonly: private cert has not yet been set. Set one before signing");
}
RETVAL = signonly(this, plaintext, -1, PKCS7_DETACHED);
if (RETVAL == NULL) {
OPENSSL_CROAK("Crypt::SMIME#signonly: failed to sign the message");
}
OUTPUT:
RETVAL
SV*
_signonly_attached(Crypt_SMIME this, char* plaintext, size_t length(plaintext))
CODE:
/* 秘密鍵がまだセットされていなければエラー */
if (this->priv_key == NULL) {
croak("Crypt::SMIME#signonly: private key has not yet been set. Set one before signing");
}
if (this->priv_cert == NULL) {
croak("Crypt::SMIME#signonly: private cert has not yet been set. Set one before signing");
}
RETVAL = signonly(this, plaintext, XSauto_length_of_plaintext, 0);
if (RETVAL == NULL) {
OPENSSL_CROAK("Crypt::SMIME#signonly: failed to sign the message");
}
OUTPUT:
RETVAL
SV*
_encrypt(Crypt_SMIME this, char* plaintext)
CODE:
/* 公開鍵がまだセットされていなければエラー */
if (this->pubkeys_stack == NULL) {
croak("Crypt::SMIME#encrypt: public cert has not yet been set. Set one before encrypting");
}
/* cipherがまだ無ければ設定 */
if (this->cipher == NULL) {
this->cipher = EVP_des_ede3_cbc();
}
RETVAL = _encrypt(this, plaintext);
if (RETVAL == NULL) {
OPENSSL_CROAK("Crypt::SMIME#encrypt: failed to encrypt the message");
}
OUTPUT:
RETVAL
SV*
check(Crypt_SMIME this, char* signed_mime)
CODE:
/* 公開鍵がまだセットされていなければエラー */
if (this->pubkeys_store == NULL) {
croak("Crypt::SMIME#check: public cert has not yet been set. Set one before checking");
}
RETVAL = check(this, signed_mime);
if (RETVAL == NULL) {
OPENSSL_CROAK("Crypt::SMIME#check: failed to check the signature");
}
OUTPUT:
RETVAL
SV*
decrypt(Crypt_SMIME this, char* encrypted_mime)
CODE:
/* 秘密鍵がまだセットされていなければエラー */
if (this->priv_key == NULL) {
croak("Crypt::SMIME#decrypt: private key has not yet been set. Set one before decrypting");
}
if (this->priv_cert == NULL) {
croak("Crypt::SMIME#decrypt: private cert has not yet been set. Set one before decrypting");
}
RETVAL = _decrypt(this, encrypted_mime);
if (RETVAL == NULL) {
OPENSSL_CROAK("Crypt::SMIME#decrypt: failed to decrypt the message");
}
OUTPUT:
RETVAL
SV*
x509_subject_hash(char* cert)
CODE:
{
X509* x509 = load_cert(cert);
if( x509!=NULL )
{
RETVAL = newSVuv(X509_subject_name_hash(x509));
X509_free(x509);
}else
{
RETVAL = &PL_sv_undef;
}
}
OUTPUT:
RETVAL
SV*
x509_issuer_hash(char* cert)
CODE:
{
X509* x509 = load_cert(cert);
if( x509!=NULL )
{
RETVAL = newSVuv(X509_issuer_name_hash(x509));
X509_free(x509);
}else
{
RETVAL = &PL_sv_undef;
}
}
OUTPUT:
RETVAL
#define CRYPT_SMIME_FORMAT_ASN1 1
#define CRYPT_SMIME_FORMAT_PEM 3
#define CRYPT_SMIME_FORMAT_SMIME 6
int
FORMAT_ASN1()
PROTOTYPE:
CODE:
RETVAL = CRYPT_SMIME_FORMAT_ASN1;
OUTPUT:
RETVAL
int
FORMAT_PEM()
PROTOTYPE:
CODE:
RETVAL = CRYPT_SMIME_FORMAT_PEM;
OUTPUT:
RETVAL
int
FORMAT_SMIME()
PROTOTYPE:
CODE:
RETVAL = CRYPT_SMIME_FORMAT_SMIME;
OUTPUT:
RETVAL
SV*
extractCertificates(SV* indata, int informat=CRYPT_SMIME_FORMAT_SMIME)
PROTOTYPE: $;$
INIT:
BIO* bio;
PKCS7* p7 = NULL;
STACK_OF(X509)* certs = NULL;
STACK_OF(X509_CRL)* crls = NULL;
int i;
AV* result;
BUF_MEM* bufmem;
if (!SvOK(indata)) {
XSRETURN_UNDEF;
}
bio = BIO_new_mem_buf(SvPV_nolen(indata), SvCUR(indata));
if (bio == NULL) {
OPENSSL_CROAK(
"Crypt::SMIME#extractCertificates: failed to allocate a buffer"
);
}
switch (informat) {
case CRYPT_SMIME_FORMAT_SMIME:
p7 = SMIME_read_PKCS7(bio, NULL);
break;
case CRYPT_SMIME_FORMAT_PEM:
p7 = PEM_read_bio_PKCS7(bio, NULL, NULL, NULL);
break;
case CRYPT_SMIME_FORMAT_ASN1:
p7 = d2i_PKCS7_bio(bio, NULL);
break;
default:
BIO_free(bio);
croak("Crypt::SMIME#extractCertificates: unknown format %d",
informat);
}
BIO_free(bio);
if (p7 == NULL) {
XSRETURN_UNDEF;
}
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_signed:
certs = p7->d.sign->cert;
crls = p7->d.sign->crl;
break;
case NID_pkcs7_signedAndEnveloped:
certs = p7->d.signed_and_enveloped->cert;
crls = p7->d.signed_and_enveloped->crl;
break;
default:
break;
}
result = (AV*)sv_2mortal((SV*)newAV());
CODE:
if (certs != NULL && 0 < sk_X509_num(certs)) {
for (i = 0; i < sk_X509_num(certs); i++) {
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
PKCS7_free(p7);
croak("Crypt::SMIME#extractCertificates: failed to allocate a buffer");
}
PEM_write_bio_X509(bio, sk_X509_value(certs, i));
BIO_get_mem_ptr(bio, &bufmem);
av_push(result, newSVpv(bufmem->data, bufmem->length));
BIO_free(bio);
}
}
if (crls != NULL && 0 < sk_X509_CRL_num(crls)) {
for (i = 0; i < sk_X509_CRL_num(crls); i++) {
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
PKCS7_free(p7);
croak("Crypt::SMIME#extractCertificates: failed to allocate a buffer");
}
PEM_write_bio_X509_CRL(bio, sk_X509_CRL_value(crls, i));
BIO_get_mem_ptr(bio, &bufmem);
av_push(result, newSVpv(bufmem->data, bufmem->length));
BIO_free(bio);
}
}
PKCS7_free(p7);
RETVAL = newRV((SV*) result);
OUTPUT:
RETVAL
SV*
getSigners(SV* indata, int informat=CRYPT_SMIME_FORMAT_SMIME)
PROTOTYPE: $;$
INIT:
BIO* bio;
PKCS7* p7 = NULL;
STACK_OF(X509)* signers;
int i;
AV* result;
BUF_MEM* bufmem;
if (!SvOK(indata)) {
XSRETURN_UNDEF;
}
bio = BIO_new_mem_buf(SvPV_nolen(indata), SvCUR(indata));
if (bio == NULL) {
OPENSSL_CROAK(
"Crypt::SMIME#getSigners: failed to allocate a buffer"
);
}
switch (informat) {
case CRYPT_SMIME_FORMAT_SMIME:
p7 = SMIME_read_PKCS7(bio, NULL);
break;
case CRYPT_SMIME_FORMAT_PEM:
p7 = PEM_read_bio_PKCS7(bio, NULL, NULL, NULL);
break;
case CRYPT_SMIME_FORMAT_ASN1:
p7 = d2i_PKCS7_bio(bio, NULL);
break;
default:
BIO_free(bio);
croak("Crypt::SMIME#getSigners: unknown format %d",
informat);
}
BIO_free(bio);
if (p7 == NULL) {
XSRETURN_UNDEF;
}
signers = PKCS7_get0_signers(p7, NULL, 0);
if (signers == NULL) {
PKCS7_free(p7);
XSRETURN_UNDEF;
}
result = (AV*)sv_2mortal((SV*)newAV());
CODE:
if (0 < sk_X509_num(signers)) {
for (i = 0; i < sk_X509_num(signers); i++) {
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
sk_X509_free(signers);
PKCS7_free(p7);
croak("Crypt::SMIME#getSigners: failed to allocate a buffer");
}
PEM_write_bio_X509(bio, sk_X509_value(signers, i));
BIO_get_mem_ptr(bio, &bufmem);
av_push(result, newSVpv(bufmem->data, bufmem->length));
BIO_free(bio);
}
}
sk_X509_free(signers);
PKCS7_free(p7);
RETVAL = newRV((SV*) result);
OUTPUT:
RETVAL
# -----------------------------------------------------------------------------
# End of File.
# -----------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment