/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
**/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#if defined(XP_UNIX)
#include <unistd.h>
#endif
// #include "prerror.h"
#include "pk11func.h"
#include "seccomon.h"
#include "secmod.h"
#include "secitem.h"
#include "secder.h"
#include "cert.h"
#include "certdb.h"
#include "ocsp.h"
#include "keyhi.h"
#include "secerr.h"
#include "blapit.h"
#include "nspr.h"
#include "plgetopt.h"
#include "prio.h"
#include "nss.h"
/* #include "vfyutil.h" */
#define RD_BUF_SIZE (60 * 1024)
/* fake our package name */
typedef CERTCertificate* Crypt__NSS__X509__Certificate;
typedef CERTCertList* Crypt__NSS__X509__CertList;
typedef CERTSignedCrl* Crypt__NSS__X509__CRL;
char* initstring;
int initialized = 0;
//---- Beginning here this is a direct copy from NSS vfychain.c
#define REVCONFIG_TEST_UNDEFINED 0
#define REVCONFIG_TEST_LEAF 1
#define REVCONFIG_TEST_CHAIN 2
#define REVCONFIG_METHOD_CRL 1
#define REVCONFIG_METHOD_OCSP 2
#define REV_METHOD_INDEX_MAX 4
typedef struct RevMethodsStruct {
uint testType;
char *testTypeStr;
uint testFlags;
char *testFlagsStr;
uint methodType;
char *methodTypeStr;
uint methodFlags;
char *methodFlagsStr;
} RevMethods;
RevMethods revMethodsData[REV_METHOD_INDEX_MAX];
static SECStatus
configureRevocationParams(CERTRevocationFlags *flags)
{
int i;
uint testType = REVCONFIG_TEST_UNDEFINED;
static CERTRevocationTests *revTests = NULL;
PRUint64 *revFlags = NULL;
for(i = 0;i < REV_METHOD_INDEX_MAX;i++) {
if (revMethodsData[i].testType == REVCONFIG_TEST_UNDEFINED) {
continue;
}
if (revMethodsData[i].testType != testType) {
testType = revMethodsData[i].testType;
if (testType == REVCONFIG_TEST_CHAIN) {
revTests = &flags->chainTests;
} else {
revTests = &flags->leafTests;
}
revTests->number_of_preferred_methods = 0;
revTests->preferred_methods = 0;
revFlags = revTests->cert_rev_flags_per_method;
}
/* Set the number of the methods independently to the max number of
* methods. If method flags are not set it will be ignored due to
* default DO_NOT_USE flag. */
revTests->number_of_defined_methods = cert_revocation_method_count;
revTests->cert_rev_method_independent_flags |=
revMethodsData[i].testFlags;
if (revMethodsData[i].methodType == REVCONFIG_METHOD_CRL) {
revFlags[cert_revocation_method_crl] =
revMethodsData[i].methodFlags;
} else if (revMethodsData[i].methodType == REVCONFIG_METHOD_OCSP) {
revFlags[cert_revocation_method_ocsp] =
revMethodsData[i].methodFlags;
}
}
return SECSuccess;
}
//---- end direct copy from vfychain.c
// adapted from secutil.c - removed unnecessary argument.
/*
* Find the issuer of a Crl. Use the authorityKeyID if it exists.
*/
CERTCertificate *
FindCrlIssuer(CERTCertDBHandle *dbhandle, SECItem* subject,
PRTime validTime)
{
CERTCertificate *issuerCert = NULL;
CERTCertList *certList = NULL;
if (!subject) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return NULL;
}
certList =
CERT_CreateSubjectCertList(NULL, dbhandle, subject,
validTime, PR_TRUE);
if (certList) {
CERTCertListNode *node = CERT_LIST_HEAD(certList);
/* XXX and authoritykeyid in the future */
while ( ! CERT_LIST_END(node, certList) ) {
CERTCertificate *cert = node->cert;
/* check cert CERTCertTrust data is allocated, check cert
usage extension, check that cert has pkey in db. Select
the first (newest) user cert */
if (cert->trust &&
CERT_CheckCertUsage(cert, KU_CRL_SIGN) == SECSuccess) {
issuerCert = CERT_DupCertificate(cert);
break;
}
node = CERT_LIST_NEXT(node);
}
CERT_DestroyCertList(certList);
}
return(issuerCert);
}
// function more or less ripped from nsNSSCertHelper.cpp, because NSS does apparently
// not support giving string names for OIDs. I mean, what the heck would you POSSIBLY
// want to use THAT for?
static
SV* oid_to_sv(SECItem *oid)
{
SECOidTag oidTag = SECOID_FindOIDTag(oid);
const char* out = 0;
switch (oidTag) {
case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
out = "MD2WithRSA";
break;
case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
out = "MD5WithRSA";
break;
case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
out = "SHA1WithRSA";
break;
case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
out = "SHA256WithRSA";
break;
case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
out = "SHA384WithRSA";
break;
case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
out = "SHA512WithRSA";
break;
case SEC_OID_PKCS1_RSA_ENCRYPTION:
out = "RSAEncr";
break;
case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
out = "RSAPSSSignature";
break;
case SEC_OID_NS_CERT_EXT_CERT_TYPE:
out = "CertType";
break;
case SEC_OID_NS_CERT_EXT_BASE_URL:
out = "NSCertExtBaseUrl";
break;
case SEC_OID_NS_CERT_EXT_REVOCATION_URL:
out = "NSCertExtRevocationUrl";
break;
case SEC_OID_NS_CERT_EXT_CA_REVOCATION_URL:
out = "NSCertExtCARevocationUrl";
break;
case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_URL:
out = "NSCertExtCertRenewalUrl";
break;
case SEC_OID_NS_CERT_EXT_CA_POLICY_URL:
out = "NSCertExtCAPolicyUrl";
break;
case SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME:
out = "NSCertExtSslServerName";
break;
case SEC_OID_NS_CERT_EXT_COMMENT:
out = "NSCertExtComment";
break;
case SEC_OID_NS_CERT_EXT_LOST_PASSWORD_URL:
out = "NSCertExtLostPasswordUrl";
break;
case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_TIME:
out = "NSCertExtCertRenewalTime";
break;
case SEC_OID_NETSCAPE_AOLSCREENNAME:
out = "NetscapeAolScreenname";
break;
case SEC_OID_AVA_COUNTRY_NAME:
out = "AVACountry";
break;
case SEC_OID_AVA_COMMON_NAME:
out = "AVACN";
break;
case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME:
out = "AVAOU";
break;
case SEC_OID_AVA_ORGANIZATION_NAME:
out = "AVAOrg";
break;
case SEC_OID_AVA_LOCALITY:
out = "AVALocality";
break;
case SEC_OID_AVA_DN_QUALIFIER:
out = "AVADN";
break;
case SEC_OID_AVA_DC:
out = "AVADC";
break;
case SEC_OID_AVA_STATE_OR_PROVINCE:
out = "AVAState";
break;
case SEC_OID_AVA_SURNAME:
out = "Surname";
break;
case SEC_OID_AVA_GIVEN_NAME:
out = "GivenName";
break;
case SEC_OID_X509_SUBJECT_DIRECTORY_ATTR:
out = "SubjectDirectoryAttr";
break;
case SEC_OID_X509_SUBJECT_KEY_ID:
out = "SubjectKeyID";
break;
case SEC_OID_X509_KEY_USAGE:
out = "KeyUsage";
break;
case SEC_OID_X509_SUBJECT_ALT_NAME:
out = "SubjectAltName";
break;
case SEC_OID_X509_ISSUER_ALT_NAME:
out = "IssuerAltName";
break;
case SEC_OID_X509_BASIC_CONSTRAINTS:
out = "BasicConstraints";
break;
case SEC_OID_X509_NAME_CONSTRAINTS:
out = "NameConstraints";
break;
case SEC_OID_X509_CRL_DIST_POINTS:
out = "CrlDistPoints";
break;
case SEC_OID_X509_CERTIFICATE_POLICIES:
out = "CertPolicies";
break;
case SEC_OID_X509_POLICY_MAPPINGS:
out = "PolicyMappings";
break;
case SEC_OID_X509_POLICY_CONSTRAINTS:
out = "PolicyConstraints";
break;
case SEC_OID_X509_AUTH_KEY_ID:
out = "AuthKeyID";
break;
case SEC_OID_X509_EXT_KEY_USAGE:
out = "ExtKeyUsage";
break;
case SEC_OID_X509_AUTH_INFO_ACCESS:
out = "AuthInfoAccess";
break;
case SEC_OID_ANSIX9_DSA_SIGNATURE:
out = "AnsiX9DsaSignature";
break;
case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
out = "AnsiX9DsaSignatureWithSha1";
break;
case SEC_OID_ANSIX962_ECDSA_SIGNATURE_WITH_SHA1_DIGEST:
out = "AnsiX962ECDsaSignatureWithSha1";
break;
case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
out = "AnsiX962ECDsaSignatureWithSha224";
break;
case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
out = "AnsiX962ECDsaSignatureWithSha256";
break;
case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
out = "AnsiX962ECDsaSignatureWithSha384";
break;
case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
out = "AnsiX962ECDsaSignatureWithSha512";
break;
case SEC_OID_RFC1274_UID:
out = "UserID";
break;
case SEC_OID_PKCS9_EMAIL_ADDRESS:
out = "PK9Email";
break;
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
out = "ECPublicKey";
break;
/* ANSI X9.62 named elliptic curves (prime field) */
case SEC_OID_ANSIX962_EC_PRIME192V1:
/* same as SEC_OID_SECG_EC_SECP192r1 */
out = "ECprime192v1";
break;
case SEC_OID_ANSIX962_EC_PRIME192V2:
out = "ECprime192v2";
break;
case SEC_OID_ANSIX962_EC_PRIME192V3:
out = "ECprime192v3";
break;
case SEC_OID_ANSIX962_EC_PRIME239V1:
out = "ECprime239v1";
break;
case SEC_OID_ANSIX962_EC_PRIME239V2:
out = "ECprime239v2";
break;
case SEC_OID_ANSIX962_EC_PRIME239V3:
out = "ECprime239v3";
break;
case SEC_OID_ANSIX962_EC_PRIME256V1:
/* same as SEC_OID_SECG_EC_SECP256r1 */
out = "ECprime256v1";
break;
/* SECG named elliptic curves (prime field) */
case SEC_OID_SECG_EC_SECP112R1:
out = "ECsecp112r1";
break;
case SEC_OID_SECG_EC_SECP112R2:
out = "ECsecp112r2";
break;
case SEC_OID_SECG_EC_SECP128R1:
out = "ECsecp128r1";
break;
case SEC_OID_SECG_EC_SECP128R2:
out = "ECsecp128r2";
break;
case SEC_OID_SECG_EC_SECP160K1:
out = "ECsecp160k1";
break;
case SEC_OID_SECG_EC_SECP160R1:
out = "ECsecp160r1";
break;
case SEC_OID_SECG_EC_SECP160R2:
out = "ECsecp160r2";
break;
case SEC_OID_SECG_EC_SECP192K1:
out = "ECsecp192k1";
break;
case SEC_OID_SECG_EC_SECP224K1:
out = "ECsecp224k1";
break;
case SEC_OID_SECG_EC_SECP224R1:
out = "ECsecp224r1";
break;
case SEC_OID_SECG_EC_SECP256K1:
out = "ECsecp256k1";
break;
case SEC_OID_SECG_EC_SECP384R1:
out = "ECsecp384r1";
break;
case SEC_OID_SECG_EC_SECP521R1:
out = "ECsecp521r1";
break;
/* ANSI X9.62 named elliptic curves (characteristic two field) */
case SEC_OID_ANSIX962_EC_C2PNB163V1:
out = "ECc2pnb163v1";
break;
case SEC_OID_ANSIX962_EC_C2PNB163V2:
out = "ECc2pnb163v2";
break;
case SEC_OID_ANSIX962_EC_C2PNB163V3:
out = "ECc2pnb163v3";
break;
case SEC_OID_ANSIX962_EC_C2PNB176V1:
out = "ECc2pnb176v1";
break;
case SEC_OID_ANSIX962_EC_C2TNB191V1:
out = "ECc2tnb191v1";
break;
case SEC_OID_ANSIX962_EC_C2TNB191V2:
out = "ECc2tnb191v2";
break;
case SEC_OID_ANSIX962_EC_C2TNB191V3:
out = "ECc2tnb191v3";
break;
case SEC_OID_ANSIX962_EC_C2ONB191V4:
out = "ECc2onb191v4";
break;
case SEC_OID_ANSIX962_EC_C2ONB191V5:
out = "ECc2onb191v5";
break;
case SEC_OID_ANSIX962_EC_C2PNB208W1:
out = "ECc2pnb208w1";
break;
case SEC_OID_ANSIX962_EC_C2TNB239V1:
out = "ECc2tnb239v1";
break;
case SEC_OID_ANSIX962_EC_C2TNB239V2:
out = "ECc2tnb239v2";
break;
case SEC_OID_ANSIX962_EC_C2TNB239V3:
out = "ECc2tnb239v3";
break;
case SEC_OID_ANSIX962_EC_C2ONB239V4:
out = "ECc2onb239v4";
break;
case SEC_OID_ANSIX962_EC_C2ONB239V5:
out = "ECc2onb239v5";
break;
case SEC_OID_ANSIX962_EC_C2PNB272W1:
out = "ECc2pnb272w1";
break;
case SEC_OID_ANSIX962_EC_C2PNB304W1:
out = "ECc2pnb304w1";
break;
case SEC_OID_ANSIX962_EC_C2TNB359V1:
out = "ECc2tnb359v1";
break;
case SEC_OID_ANSIX962_EC_C2PNB368W1:
out = "ECc2pnb368w1";
break;
case SEC_OID_ANSIX962_EC_C2TNB431R1:
out = "ECc2tnb431r1";
break;
/* SECG named elliptic curves (characteristic two field) */
case SEC_OID_SECG_EC_SECT113R1:
out = "ECsect113r1";
break;
case SEC_OID_SECG_EC_SECT113R2:
out = "ECsect113r2";
break;
case SEC_OID_SECG_EC_SECT131R1:
out = "ECsect131r1";
break;
case SEC_OID_SECG_EC_SECT131R2:
out = "ECsect131r2";
break;
case SEC_OID_SECG_EC_SECT163K1:
out = "ECsect163k1";
break;
case SEC_OID_SECG_EC_SECT163R1:
out = "ECsect163r1";
break;
case SEC_OID_SECG_EC_SECT163R2:
out = "ECsect163r2";
break;
case SEC_OID_SECG_EC_SECT193R1:
out = "ECsect193r1";
break;
case SEC_OID_SECG_EC_SECT193R2:
out = "ECsect193r2";
break;
case SEC_OID_SECG_EC_SECT233K1:
out = "ECsect233k1";
break;
case SEC_OID_SECG_EC_SECT233R1:
out = "ECsect233r1";
break;
case SEC_OID_SECG_EC_SECT239K1:
out = "ECsect239k1";
break;
case SEC_OID_SECG_EC_SECT283K1:
out = "ECsect283k1";
break;
case SEC_OID_SECG_EC_SECT283R1:
out = "ECsect283r1";
break;
case SEC_OID_SECG_EC_SECT409K1:
out = "ECsect409k1";
break;
case SEC_OID_SECG_EC_SECT409R1:
out = "ECsect409r1";
break;
case SEC_OID_SECG_EC_SECT571K1:
out = "ECsect571k1";
break;
case SEC_OID_SECG_EC_SECT571R1:
out = "ECsect571r1";
break;
case SEC_OID_X509_REASON_CODE:
out = "revocationReason";
break;
default: {
/*
if (oidTag == SEC_OID(MS_CERT_EXT_CERTTYPE)) {
out = "MSCerttype";
break;
}
if (oidTag == SEC_OID(MS_CERTSERV_CA_VERSION)) {
out = "MSCAVersion";
break;
}
if (oidTag == SEC_OID(PKIX_LOGOTYPE)) {
out = "Logotype";
break;
}
*/
char* oidchar = CERT_GetOidString(oid);
SV* oidstr = newSVpv(oidchar, 0);
PR_smprintf_free(oidchar);
return oidstr;
}
}
return newSVpvf("%s", out);
}
static PRInt64 cert_usage_to_certificate_usage(enum SECCertUsageEnum usage) {
switch(usage) {
case certUsageSSLClient:
return certificateUsageSSLClient;
case certUsageSSLServer:
return certificateUsageSSLServer;
case certUsageSSLServerWithStepUp:
return certificateUsageSSLServerWithStepUp;
case certUsageSSLCA:
return certificateUsageSSLCA;
case certUsageEmailSigner:
return certificateUsageEmailSigner;
case certUsageEmailRecipient:
return certificateUsageEmailRecipient;
case certUsageObjectSigner:
return certificateUsageObjectSigner;
case certUsageUserCertImport:
return certificateUsageUserCertImport;
case certUsageVerifyCA:
return certificateUsageVerifyCA;
case certUsageProtectedObjectSigner:
return certificateUsageProtectedObjectSigner;
case certUsageStatusResponder:
return certificateUsageStatusResponder;
case certUsageAnyCA:
return certificateUsageAnyCA;
default:
croak("Unknown certificate usage %d", usage);
}
}
static HV* node_to_hv(CERTVerifyLogNode* node) {
HV* out = newHV();
hv_stores(out, "depth", newSVuv(node->depth)) ? : croak("Could not store data in hv");
hv_stores(out, "error", newSViv(node->error)) ? : croak("Could not store data in hv");
if ( node->cert ) {
SV* cert = newSV(0);
sv_setref_pv(cert, "Crypt::NSS::X509::Certificate", node->cert); // the beauty of this is that it should be cleaned by perl refcounting now
hv_stores(out, "certificate", cert) ? : croak("Could not store data in hv");
}
return out;
}
static
SV* item_to_hex(SECItem *data) {
// do it like mozilla - if data <= 4 -> make integger
if ( data-> len <=4 ) {
int i = DER_GetInteger(data);
return newSViv(i);
}
// ok. That was easy. Now let's produce a hex-dump otherwise
SV* out = newSVpvn("",0);
for ( unsigned int i = 0; i < data->len; i++ ) {
sv_catpvf(out, "%02x", data->data[i]);
}
return out;
}
static
SV* item_to_hhex(SECItem *data) {
SV* out = newSVpvn("",0);
for ( unsigned int i = 0; i < data->len; i++ ) {
sv_catpvf(out, "%02x", data->data[i]);
}
return out;
}
static SECStatus sv_to_item(SV* certSv, SECItem* dst) {
STRLEN len;
char *cert;
cert = SvPV(certSv, len);
if ( len <= 0 ) {
return SECFailure;
}
dst->len = 0;
dst->data = NULL;
dst->data = (unsigned char*)PORT_Alloc(len);
PORT_Memcpy(dst->data, cert, len);
dst->len = len;
return SECSuccess;
}
static SV* item_to_sv(SECItem* item) {
return newSVpvn((const char*) item->data, item->len);
}
static
bool strip_tag_and_length(SECItem *i)
{
unsigned int start;
if (!i || !i->data || i->len < 2) { /* must be at least tag and length */
return false;
}
start = ((i->data[1] & 0x80) ? (i->data[1] & 0x7f) + 2 : 2);
if (i->len < start) {
return false;
}
i->data += start;
i->len -= start;
return true;
}
static
SV*
universal_to_sv(SECItem* item) {
switch (item->data[0] & SEC_ASN1_TAGNUM_MASK) {
case SEC_ASN1_ENUMERATED:
case SEC_ASN1_INTEGER:
strip_tag_and_length(item) ? : croak("could not strip tag and length");
return item_to_hex(item); // item_to_hex returns ints for data <=4
case SEC_ASN1_OBJECT_ID: {
strip_tag_and_length(item) ? : croak("could not strip tag and length");
char* oidchar = CERT_GetOidString(item);
SV* oidstr = newSVpv(oidchar, 0);
PR_smprintf_free(oidchar);
return oidstr;
}
case SEC_ASN1_BOOLEAN: {
int val = 0;
if ( !strip_tag_and_length(item) ) {
return &PL_sv_no; // guessing here...
}
if ( item->data && item->len ) {
val = item->data[0];
}
if ( val )
return &PL_sv_yes;
else
return &PL_sv_no;
}
case SEC_ASN1_UTF8_STRING:
case SEC_ASN1_PRINTABLE_STRING:
case SEC_ASN1_VISIBLE_STRING:
case SEC_ASN1_IA5_STRING:
case SEC_ASN1_T61_STRING:
croak("String not implemented");
break;
case SEC_ASN1_GENERALIZED_TIME:
croak("Time not implemented");
break;
case SEC_ASN1_UTC_TIME:
croak("UTCTime not implemented");
break;
case SEC_ASN1_NULL:
return &PL_sv_undef;
case SEC_ASN1_SET:
case SEC_ASN1_SEQUENCE:
croak("SET, Sequence not implemented");
break;
case SEC_ASN1_OCTET_STRING:
croak("Octet string not implemented");
break;
case SEC_ASN1_BIT_STRING:
croak("Bit string not implemented");
break;
case SEC_ASN1_BMP_STRING:
croak("BMP String not implemented");
break;
case SEC_ASN1_UNIVERSAL_STRING:
croak("Universal String not implemented");
break;
default:
return item_to_hhex(item);
break;
}
return 0;
}
static
SV* item_to_sv_parsed(SECItem* i) {
if ( !( i && i->len && i->data ) ) {
croak("Empty item");
}
switch ( i->data[0] & SEC_ASN1_CLASS_MASK ) {
case SEC_ASN1_CONTEXT_SPECIFIC:
croak("Cannot parse context specific");
case SEC_ASN1_UNIVERSAL:
return universal_to_sv(i);
default:
return item_to_hhex(i);
}
}
static
SV* extension_to_sv(CERTCertExtension *extension) {
SECOidTag oidTag;
SV* out = 0;
oidTag = SECOID_FindOIDTag (&extension->id);
switch ( oidTag ) {
case SEC_OID_X509_REASON_CODE:
return item_to_sv_parsed(&extension->value);
default: {
char* oidchar = CERT_GetOidString(&extension->id);
croak("Could not parse extension %s", oidchar);
PR_smprintf_free(oidchar); // memleak because this is never executed"
}
}
return out;
}
MODULE = Crypt::NSS::X509 PACKAGE = Crypt::NSS::X509
PROTOTYPES: DISABLE
BOOT:
{
HV *stash = gv_stashpvn("Crypt::NSS::X509", 16, TRUE);
struct { char *n; I32 s; } NSS__const[] = {
{"certUsageSSLClient", certUsageSSLClient},
{"certUsageSSLServer", certUsageSSLServer},
{"certUsageSSLServerWithStepUp", certUsageSSLServerWithStepUp},
{"certUsageSSLCA", certUsageSSLCA},
{"certUsageEmailSigner", certUsageEmailSigner},
{"certUsageEmailRecipient", certUsageEmailRecipient},
{"certUsageObjectSigner", certUsageObjectSigner},
{"certUsageUserCertImport", certUsageUserCertImport},
{"certUsageVerifyCA", certUsageVerifyCA},
{"certUsageProtectedObjectSigner", certUsageProtectedObjectSigner},
{"certUsageStatusResponder", certUsageStatusResponder},
{"certUsageAnyCA", certUsageAnyCA},
{Nullch,0}
};
char *name;
int i;
for (i = 0; (name = NSS__const[i].n); i++) {
newCONSTSUB(stash, name, newSViv(NSS__const[i].s));
}
PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
//SECU_RegisterDynamicOids();
}
void
_init_nodb()
PREINIT:
SECStatus secStatus;
//PRUint32 initFlags;
CODE:
//initFlags = NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT;
//secStatus = NSS_Initialize("test2", "", "", SECMOD_DB, initFlags);
secStatus = NSS_NoDB_Init(NULL);
initstring = NULL;
//SECMOD_AddNewModule("Builtins", DLL_PREFIX"nssckbi."DLL_SUFFIX, 0, 0);
if (secStatus != SECSuccess) {
croak("NSS init");
}
initialized = 1;
void
_init_db(string)
SV* string;
PREINIT:
SECStatus secStatus;
char* path;
STRLEN pathlen;
CODE:
path = SvPV(string, pathlen);
secStatus = NSS_InitReadWrite(path);
initstring = (char*) malloc(pathlen+1);
bzero(initstring, pathlen+1);
memcpy(initstring, path, pathlen);
//SECMOD_AddNewModule("Builtins", DLL_PREFIX"nssckbi."DLL_SUFFIX, 0, 0);
if (secStatus != SECSuccess) {
PRErrorCode err = PR_GetError();
croak("NSS Init failed: %d = %s\n",
err, PORT_ErrorToString(err));
}
initialized = 1;
void
__cleanup(void)
PREINIT:
SECStatus rv;
CODE:
if ( !initialized ) {
// no init, no shutdown
return;
}
rv = NSS_Shutdown();
if (rv != SECSuccess) {
PRErrorCode err = PR_GetError();
croak( "NSS Shutdown failed %d = %s\n",
err, PORT_ErrorToString(err));
}
//printf("Destroy was happy\n");
SV*
add_cert_to_db(cert, string)
Crypt::NSS::X509::Certificate cert;
SV* string;
ALIAS:
add_trusted_cert_to_db = 1
PREINIT:
PK11SlotInfo *slot = NULL;
CERTCertTrust *trust = NULL;
CERTCertDBHandle *defaultDB;
SECStatus rv;
char* nick;
CODE:
RETVAL = 0;
nick = SvPV_nolen(string);
defaultDB = CERT_GetDefaultCertDB();
slot = PK11_GetInternalKeySlot();
if ( ix == 1 ) {
// trusted Certificate
trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust));
if (!trust) {
croak("Could not create trust");
}
rv = CERT_DecodeTrustString(trust, "TCu,Cu,Tu"); // take THAT trust ;)
if (rv) {
croak("unable to decode trust string");
}
}
rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, nick, PR_FALSE);
if (rv != SECSuccess) {
PRErrorCode err = PR_GetError();
croak( "could not add certificate to db %d = %s\n",
err, PORT_ErrorToString(err));
}
if ( ix == 1 ) {
rv = CERT_ChangeCertTrust(defaultDB, cert, trust);
if (rv != SECSuccess) {
croak("Could not change cert trust");
}
}
PORT_Free(trust);
PK11_FreeSlot(slot);
RETVAL = newSViv(1);
OUTPUT:
RETVAL
void
_reinit()
PREINIT:
SECStatus rv;
CODE:
rv = NSS_Shutdown();
if (rv != SECSuccess) {
PRErrorCode err = PR_GetError();
croak( "NSS Shutdown failed during reinit. Last error-code: %d = %s\n", err, PORT_ErrorToString(err));
}
if ( initstring == NULL ) {
rv = NSS_NoDB_Init(NULL);
} else {
//printf("%s\n\n", initstring);
rv = NSS_InitReadWrite(initstring);
}
if (rv != SECSuccess) {
PRErrorCode err = PR_GetError();
croak("NSS Init failed: %d = %s\n",
err, PORT_ErrorToString(err));
}
void
dump_certificate_cache_info()
CODE:
nss_DumpCertificateCacheInfo();
MODULE = Crypt::NSS::X509 PACKAGE = Crypt::NSS::X509::CRL
Crypt::NSS::X509::CRL
new_from_der(class, string)
SV* string
PREINIT:
SECItem item;
CERTSignedCrl *signedCrl;
SECStatus rv;
CODE:
rv = sv_to_item(string, &item);
if (rv != SECSuccess) {
croak("sv_to_item failed");
}
//PRInt32 decodeOptions = CRL_DECODE_DEFAULT_OPTIONS;
signedCrl = CERT_DecodeDERCrlWithFlags(NULL, &item, SEC_CRL_TYPE, CRL_DECODE_DEFAULT_OPTIONS);
if ( !signedCrl ) {
PRErrorCode err = PR_GetError();
croak( "Could not decode CRL %d = %s\n",
err, PORT_ErrorToString(err));
}
RETVAL = signedCrl;
OUTPUT:
RETVAL
SV*
issuer(crl)
Crypt::NSS::X509::CRL crl
PREINIT:
char* c;
CODE:
c = CERT_DerNameToAscii(&crl->crl.derName);
RETVAL = newSVpvf("%s", c);
PORT_Free(c);
OUTPUT:
RETVAL
SV*
version(crl)
Crypt::NSS::X509::CRL crl
PREINIT:
int version;
CODE:
version = crl->crl.version.len ? DER_GetInteger(&crl->crl.version) : 0;
version++;
RETVAL = newSViv(version);
OUTPUT:
RETVAL
void
verify(crl, cert, timedouble = NO_INIT)
Crypt::NSS::X509::CRL crl
Crypt::NSS::X509::Certificate cert
SV* timedouble
PREINIT:
SECStatus rv;
PRTime time = 0;
PPCODE:
if ( items < 3 || SvIV(timedouble) == 0 ) {
time = PR_Now();
} else {
double tmptime = SvNV(timedouble);
// time contains seconds since epoch - netscape expects microseconds
tmptime = tmptime * 1000000;
LL_D2L(time, tmptime); // and convert to 64-bit int
}
rv = CERT_VerifySignedData(&crl->signatureWrap, cert, time, NULL);
if ( rv != SECSuccess ) {
XSRETURN_NO;
}
XSRETURN_YES;
Crypt::NSS::X509::Certificate
find_issuer(crl, timedouble = NO_INIT)
Crypt::NSS::X509::CRL crl
SV* timedouble
ALIAS:
verify_db = 1
PREINIT:
PRTime time = 0;
CERTCertDBHandle *defaultDB;
SECItem* subject = NULL;
CERTCertificate* cert;
SECStatus rv;
CODE:
if ( items < 2 || SvIV(timedouble) == 0 ) {
time = PR_Now();
} else {
double tmptime = SvNV(timedouble);
// time contains seconds since epoch - netscape expects microseconds
tmptime = tmptime * 1000000;
LL_D2L(time, tmptime); // and convert to 64-bit int
}
defaultDB = CERT_GetDefaultCertDB();
subject = &crl->crl.derName;
cert = FindCrlIssuer(defaultDB, subject, time);
if ( !cert ) {
XSRETURN_UNDEF;
}
if ( ix == 1 ) {
rv = CERT_VerifySignedData(&crl->signatureWrap, cert, time, NULL);
CERT_DestroyCertificate(cert);
if ( rv != SECSuccess ) {
XSRETURN_NO;
}
XSRETURN_YES;
}
RETVAL = cert;
OUTPUT:
RETVAL
void
DESTROY(crl)
Crypt::NSS::X509::CRL crl
PPCODE:
if ( crl ) {
SEC_DestroyCrl(crl);
crl = 0;
}
void
entries(crl)
Crypt::NSS::X509::CRL crl
PREINIT:
CERTCrlEntry *entry;
int iv;
PPCODE:
if ( crl->crl.entries != NULL ) {
iv = 0;
while( (entry = crl->crl.entries[iv++]) != NULL) {
HV* h = newHV();
hv_store(h, "serial", 6, item_to_hhex(&entry->serialNumber), 0) ? : croak("Could not store data in hv");
{
// TODO: factor out to time_to_sv
int64 time;
SECStatus rv;
char *timeString;
PRExplodedTime printableTime;
SV* timeSV;
rv = DER_UTCTimeToTime(&time, &(entry->revocationDate));
if (rv != SECSuccess)
croak("Could not parse CRL element revocation time");
PR_ExplodeTime(time, PR_GMTParameters, &printableTime);
timeString = PORT_Alloc(256);
if ( ! PR_FormatTime(timeString, 256, "%a %b %d %H:%M:%S %Y", &printableTime) ) {
croak("Could not format time string");
}
timeSV = newSVpvf("%s", timeString);
hv_store(h, "revocationDate", 14, timeSV, 0) ? : croak("Could not store data in hv");
}
{
CERTCertExtension **extensions = entry->extensions;
if ( extensions ) {
while ( *extensions ) {
hv_store_ent(h, oid_to_sv(&(*extensions)->id), extension_to_sv(*extensions), 0) ? : croak("Could not store data in hv");
extensions++;
}
}
}
mXPUSHs(newRV ((SV*)h) );
}
}
MODULE = Crypt::NSS::X509 PACKAGE = Crypt::NSS::X509::CertList
Crypt::NSS::X509::CertList
new(class)
PREINIT:
CERTCertList *certList;
CODE:
certList = CERT_NewCertList();
RETVAL = certList;
OUTPUT:
RETVAL
void
add(certlist, cert)
Crypt::NSS::X509::CertList certlist;
Crypt::NSS::X509::Certificate cert;
CODE:
CERTCertificate* addcert = CERT_DupCertificate(cert);
CERT_AddCertToListTail(certlist, addcert);
void
dump(certlist)
Crypt::NSS::X509::CertList certlist;
PREINIT:
CERTCertListNode *node;
PPCODE:
node = CERT_LIST_HEAD(certlist);
while ( !CERT_LIST_END(node, certlist) ) {
if ( node->cert ) {
CERTCertificate* currcert = CERT_DupCertificate(node->cert);
SV* cert = newSV(0);
sv_setref_pv(cert, "Crypt::NSS::X509::Certificate", currcert); // the beauty of this is that it should be cleaned by perl refcounting now
mXPUSHs(cert);
} else {
croak("Bad certificate list, encountered node without cert.");
}
node = CERT_LIST_NEXT(node);
}
void
DESTROY(certlist)
Crypt::NSS::X509::CertList certlist;
PPCODE:
if ( certlist ) {
CERT_DestroyCertList(certlist);
certlist = 0;
}
MODULE = Crypt::NSS::X509 PACKAGE = Crypt::NSS::X509::Certificate
SV*
curve(cert)
Crypt::NSS::X509::Certificate cert
PREINIT:
SECKEYPublicKey *key;
SECOidTag tag;
CODE:
// This function extracts the curve from a NSS Certificate
// As you will see, this is one of the moist straightforward
// things to do. Totally easy. Everyone can do that. And find
// out. Only took like 10 minutes.
// Srsly.
SECItem oid = { siBuffer, NULL, 0};
// this is the complicated part
key = SECKEY_ExtractPublicKey(&cert->subjectPublicKeyInfo);
if ( key->keyType != ecKey ) {
SECKEY_DestroyPublicKey(key);
croak("Only ec-keys have a curve");
}
// ok, now it gets simple.
oid.len = key->u.ec.DEREncodedParams.len - 2;
oid.data = key->u.ec.DEREncodedParams.data + 2;
if ((key->u.ec.DEREncodedParams.data[0] != SEC_ASN1_OBJECT_ID) ||
((tag = SECOID_FindOIDTag(&oid)) == SEC_OID_UNKNOWN)) {
SECKEY_DestroyPublicKey(key);
croak("Unknown EC-key");
}
RETVAL = oid_to_sv(&oid);
SECKEY_DestroyPublicKey(key);
OUTPUT:
RETVAL
SV*
raw_spki(cert)
Crypt::NSS::X509::Certificate cert
PREINIT:
SV* out;
CODE:
out = item_to_sv(&cert->derPublicKey);
RETVAL = out;
OUTPUT:
RETVAL
SV*
bit_length(cert)
Crypt::NSS::X509::Certificate cert
ALIAS:
modulus = 1
public_key = 1
exponent = 2
PREINIT:
SECKEYPublicKey *key;
CODE:
key = SECKEY_ExtractPublicKey(&cert->subjectPublicKeyInfo);
if ( key ) {
switch(key->keyType) {
case rsaKey: {
if ( ix == 0 ) {
RETVAL = newSViv(key->u.rsa.modulus.len * 8);
} else if ( ix == 1 ) {
RETVAL = item_to_hex(&key->u.rsa.modulus);
} else if ( ix == 2 ) {
RETVAL = item_to_hex(&key->u.rsa.publicExponent);
} else {
SECKEY_DestroyPublicKey(key);
croak("Internal error");
}
break;
}
case ecKey: {
if ( ix == 0 ) {
int len = SECKEY_ECParamsToKeySize(&key->u.ec.DEREncodedParams);
RETVAL = newSViv(len);
} else if ( ix == 1 ) {
RETVAL = item_to_hex(&key->u.ec.publicValue);
} else {
SECKEY_DestroyPublicKey(key);
croak("EC certificates do not have exponent");
}
break;
}
case dsaKey: {
if ( ix == 0 ) {
RETVAL = newSViv(key->u.dsa.publicValue.len * 8);
} else if ( ix == 1 ) {
RETVAL = item_to_hex(&key->u.dsa.publicValue);
} else {
SECKEY_DestroyPublicKey(key);
croak("DSA certificates do not have exponent");
}
break;
}
default:
croak("Unknown key type %d", key->keyType);
}
SECKEY_DestroyPublicKey(key);
} else {
XSRETURN_UNDEF;
}
OUTPUT:
RETVAL
SV*
fingerprint_md5(cert)
Crypt::NSS::X509::Certificate cert
ALIAS:
fingerprint_sha1 = 1
fingerprint_sha256 = 2
PREINIT:
SECItem item;
SECStatus rv;
unsigned char fingerprint[32];
CODE:
memset(fingerprint, 0, sizeof(fingerprint));
if ( ix == 0 ) {
rv = PK11_HashBuf(SEC_OID_MD5, fingerprint, cert->derCert.data, cert->derCert.len);
item.len = MD5_LENGTH;
} else if ( ix == 1 ) {
rv = PK11_HashBuf(SEC_OID_SHA1, fingerprint, cert->derCert.data, cert->derCert.len);
item.len = SHA1_LENGTH;
} else if ( ix == 2 ) {
rv = PK11_HashBuf(SEC_OID_SHA256, fingerprint, cert->derCert.data, cert->derCert.len);
item.len = SHA256_LENGTH;
}
if ( rv != SECSuccess ) {
croak("Could not calculate fingerprint");
}
item.data = fingerprint;
RETVAL = item_to_hhex(&item);
OUTPUT:
RETVAL
SV*
subject(cert)
Crypt::NSS::X509::Certificate cert
ALIAS:
issuer = 2
serial = 3
notBefore = 5
notAfter = 6
# email = 7
version = 8
subj_alt_name = 9
common_name = 10
is_root = 11
sig_alg_name = 12
key_alg_name = 13
nickname = 14
dbnickname = 15
der = 16
PREINIT:
CODE:
if ( ix == 0 ) {
RETVAL = newSVpvf("%s", cert->subjectName);
} else if ( ix == 2 ) {
RETVAL = newSVpvf("%s", cert->issuerName);
} else if ( ix == 3 ) {
RETVAL = item_to_hhex(&cert->serialNumber);
} else if ( ix == 16 ) {
RETVAL = item_to_sv(&cert->derCert);
//} else if ( ix == 7 ) { Function is not provided in all nss versions :(
// char * ce = CERT_GetCertificateEmailAddress(cert);
//if ( ce == NULL )
// XSRETURN_UNDEF;
//RETVAL = newSVpvf("%s", ce);
// PORT_Free(ce);
} else if ( ix == 14 ) {
RETVAL = newSVpvf("%s", cert->nickname);
} else if ( ix == 15 ) {
RETVAL = newSVpvf("%s", cert->dbnickname);
} else if ( ix == 10 ) {
char * cn = CERT_GetCommonName(&cert->subject);
RETVAL = newSVpvf("%s", cn);
PORT_Free(cn);
} else if ( ix == 11 ) {
if ( cert->isRoot == PR_TRUE ) {
XSRETURN_YES;
} else {
XSRETURN_NO;
}
} else if ( ix == 12 ) {
RETVAL = oid_to_sv(&cert->signature.algorithm);
} else if ( ix == 13 ) {
RETVAL = oid_to_sv(&cert->subjectPublicKeyInfo.algorithm.algorithm);
} else if ( ix == 5 || ix == 6 ) {
int64 time;
SECStatus rv;
char *timeString;
PRExplodedTime printableTime;
if ( ix == 5 )
rv = DER_UTCTimeToTime(&time, &cert->validity.notBefore);
else if ( ix == 6 )
rv = DER_UTCTimeToTime(&time, &cert->validity.notAfter);
else
croak("not possible");
if (rv != SECSuccess)
croak("Could not parse time");
PR_ExplodeTime(time, PR_GMTParameters, &printableTime);
timeString = PORT_Alloc(256);
if ( ! PR_FormatTime(timeString, 256, "%a %b %d %H:%M:%S %Y", &printableTime) ) {
croak("Could not format time string");
}
RETVAL = newSVpvf("%s", timeString);
PORT_Free(timeString);
} else if ( ix == 8 ) {
// if version is not specified it it 1 (0).
int version = cert->version.len ? DER_GetInteger(&cert->version) : 0;
RETVAL = newSViv(version+1);
} else if ( ix == 9 ) {
SECStatus rv;
SECItem subAltName;
CERTGeneralName * nameList;
CERTGeneralName * current;
PRArenaPool * arena = NULL;
SV* out = newSVpvn("", 0);
rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
&subAltName);
if (rv != SECSuccess) {
XSRETURN_NO;
}
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( !arena )
croak("Could not create arena");
nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName);
if(!current)
croak("No namelist");
bool first = true;
do {
switch (current->type) {
case certDNSName:
{
if ( !first )
sv_catpv(out, ",");
else
first = false;
sv_catpv(out, "DNS:");
sv_catpvn(out, (const char*) current->name.other.data, current->name.other.len);
break;
}
case certIPAddress:
if ( !first )
sv_catpv(out, ",");
else
first = false;
sv_catpv(out, "IP:");
sv_catpvn(out, (const char*) current->name.other.data, current->name.other.len);
break;
default:
sv_catpv(out, "UnknownElement,");
break;
}
current = CERT_GetNextGeneralName(current);
} while (current != nameList);
RETVAL = out;
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
if (subAltName.data) {
SECITEM_FreeItem(&subAltName, PR_FALSE);
}
} else {
croak("Unknown accessor %d", ix);
}
OUTPUT:
RETVAL
void
verify_certificate(cert, timedouble = NO_INIT, usage = certUsageSSLServer)
Crypt::NSS::X509::Certificate cert;
SV* timedouble;
I32 usage;
ALIAS:
verify_certificate_pkix = 1
verify_cert = 2
verify_certificate_log = 3
verify_certificate_pkix_log = 4
verify_cert_log = 5
PREINIT:
SECStatus secStatus;
PRTime time = 0;
CERTVerifyLog log;
CERTCertDBHandle *defaultDB;
PPCODE:
defaultDB = CERT_GetDefaultCertDB();
if ( items == 1 || SvIV(timedouble) == 0 ) {
time = PR_Now();
} else {
double tmptime = SvNV(timedouble);
// time contains seconds since epoch - netscape expects microseconds
tmptime = tmptime * 1000000;
LL_D2L(time, tmptime); // and convert to 64-bit int
}
if ( ix == 1 || ix == 4 )
CERT_SetUsePKIXForValidation(PR_TRUE);
else
CERT_SetUsePKIXForValidation(PR_FALSE);
log.arena = PORT_NewArena(512);
log.head = log.tail = NULL;
log.count = 0;
if ( ix == 2 ) {
secStatus = CERT_VerifyCert(defaultDB, cert,
PR_TRUE, // check sig
usage,
time,
NULL,
NULL);
} else if ( ix == 5) { // supplying log changes the return value
secStatus = CERT_VerifyCert(defaultDB, cert,
PR_TRUE, // check sig
usage,
time,
NULL,
&log);
} else {
secStatus = CERT_VerifyCertificate(defaultDB, cert,
PR_TRUE, // check sig
cert_usage_to_certificate_usage(usage),
time,
NULL,
&log, NULL);
}
if ( ix <= 2 ) { // no log
for (CERTVerifyLogNode *node = log.head; node; node = node->next) {
if (node->cert)
CERT_DestroyCertificate(node->cert);
}
PORT_FreeArena(log.arena, PR_FALSE);
if (secStatus != SECSuccess ) {
ST(0) = newSViv(PR_GetError());
} else {
ST(0) = newSViv(1); // return 1 on success
}
sv_2mortal(ST(0));
XSRETURN(1);
} else {
for (CERTVerifyLogNode *node = log.head; node; node = node->next) {
HV* out = node_to_hv(node);
SV* rv = newRV_noinc((SV*) out);
mXPUSHs(rv);
}
PORT_FreeArena(log.arena, PR_FALSE);
}
SV* match_name(cert, string)
Crypt::NSS::X509::Certificate cert;
SV* string;
PREINIT:
char* hostname;
SECStatus secStatus;
CODE:
hostname = SvPV_nolen(string);
secStatus = CERT_VerifyCertName(cert, hostname);
if ( secStatus != SECSuccess ) {
RETVAL = &PL_sv_no;
} else {
RETVAL = &PL_sv_yes;
}
OUTPUT:
RETVAL
SV*
verify_pkix(cert, timedouble = NO_INIT, usage = certUsageSSLServer, trustedCertList = NO_INIT)
Crypt::NSS::X509::Certificate cert;
SV* timedouble;
I32 usage;
Crypt::NSS::X509::CertList trustedCertList;
ALIAS:
verify_pkix_aia = 1
PREINIT:
SECStatus secStatus;
PRBool certFetching = PR_FALSE; // automatically get AIA certs
static CERTValOutParam cvout[4];
static CERTValInParam cvin[6];
int inParamIndex = 0;
static CERTRevocationFlags rev;
static PRUint64 revFlagsLeaf[2];
static PRUint64 revFlagsChain[2];
CERTVerifyLog log;
CODE:
if ( ix == 1 ) {
certFetching = PR_TRUE;
}
cvin[inParamIndex].type = cert_pi_useAIACertFetch;
cvin[inParamIndex].value.scalar.b = certFetching;
inParamIndex++;
rev.leafTests.cert_rev_flags_per_method = revFlagsLeaf;
rev.chainTests.cert_rev_flags_per_method = revFlagsChain;
secStatus = configureRevocationParams(&rev);
if (secStatus) {
croak("Can not configure revocation parameters");
}
cvin[inParamIndex].type = cert_pi_revocationFlags;
cvin[inParamIndex].value.pointer.revocation = &rev;
inParamIndex++;
if ( items >= 2 && SvIV(timedouble) > 0 ) {
PRTime time;
double tmptime = SvNV(timedouble);
// time contains seconds since epoch - netscape expects microseconds
tmptime = tmptime * 1000000;
LL_D2L(time, tmptime); // and convert to 64-bit int
cvin[inParamIndex].type = cert_pi_date;
cvin[inParamIndex].value.scalar.time = time;
inParamIndex++;
}
if ( items == 4 ) {
// we have a trustedCertList
cvin[inParamIndex].type = cert_pi_trustAnchors;
cvin[inParamIndex].value.pointer.chain = trustedCertList;
inParamIndex++;
}
cvin[inParamIndex].type = cert_pi_end;
// Initialize log
log.arena = PORT_NewArena(512);
log.head = log.tail = NULL;
log.count = 0;
/* cvout[0].type = cert_po_trustAnchor;
cvout[0].value.pointer.cert = NULL;
cvout[1].type = cert_po_certList;
cvout[1].value.pointer.chain = NULL;
cvout[2].type = cert_po_errorLog;
cvout[2].value.pointer.log = &log; */
cvout[0].type = cert_po_end;
secStatus = CERT_PKIXVerifyCert(cert, cert_usage_to_certificate_usage(usage),
cvin, cvout, NULL);
if (secStatus != SECSuccess ) {
RETVAL = newSViv(PR_GetError()); // return error code
} else {
/* CERTCertificate* issuerCert = cvout[0].value.pointer.cert;
CERTCertList* builtChain = cvout[1].value.pointer.chain;
CERT_DestroyCertList(builtChain);
CERT_DestroyCertificate(issuerCert); */
RETVAL = newSViv(1);
}
// destroy refs in the log
for (CERTVerifyLogNode *node = log.head; node; node = node->next) {
if (node->cert)
CERT_DestroyCertificate(node->cert);
}
PORT_FreeArena(log.arena, PR_FALSE);
OUTPUT:
RETVAL
Crypt::NSS::X509::CertList
get_cert_chain_from_cert(cert, timedouble = NO_INIT, usage = certUsageSSLServer)
Crypt::NSS::X509::Certificate cert;
I32 usage;
SV* timedouble;
PREINIT:
CERTCertList* list = NULL;
PRTime time;
CODE:
if ( items == 1 || SvIV(timedouble) == 0 ) {
time = PR_Now();
} else {
double tmptime = SvNV(timedouble);
// time contains seconds since epoch - netscape expects microseconds
tmptime = tmptime * 1000000;
LL_D2L(time, tmptime); // and convert to 64-bit int
}
list = CERT_GetCertChainFromCert(cert, time, usage);
if ( list == NULL ) {
XSRETURN_UNDEF;
}
RETVAL = list;
OUTPUT:
RETVAL
Crypt::NSS::X509::Certificate
new(class, string, nickSv = NO_INIT)
SV *string
SV *nickSv
PREINIT:
CERTCertificate *cert;
CERTCertDBHandle *defaultDB;
//PRFileDesc* fd;
SECStatus rv;
SECItem item = {0, NULL, 0};
char* nick = NULL;
CODE:
// Note: nick functionality seems to not really work in NSS
if ( items == 3 ) {
nick = SvPV_nolen(nickSv);
}
defaultDB = CERT_GetDefaultCertDB();
rv = sv_to_item(string, &item);
if (rv != SECSuccess) {
croak("sv_to_item failed");
}
cert = CERT_NewTempCertificate(defaultDB, &item,
nick /* nickname */,
PR_FALSE /* isPerm */,
PR_TRUE /* copyDER */);
if (!cert) {
PRErrorCode err = PR_GetError();
croak( "couldn't import certificate %d = %s\n",
err, PORT_ErrorToString(err));
}
PORT_Free(item.data);
RETVAL = cert;
OUTPUT:
RETVAL
Crypt::NSS::X509::Certificate
new_from_nick(class, string)
SV* string
PREINIT:
CERTCertDBHandle *defaultDB;
char* nick = NULL;
CODE:
nick = SvPV_nolen(string);
defaultDB = CERT_GetDefaultCertDB();
RETVAL = CERT_FindCertByNickname(defaultDB, nick);
if ( RETVAL == NULL )
XSRETURN_UNDEF;
OUTPUT:
RETVAL
void DESTROY(cert)
Crypt::NSS::X509::Certificate cert;
PPCODE:
if ( cert ) {
if ( cert->nssCertificate ) {
//printf("Is nsscertificate\n");
//printf("Refcount: %d\n", cert->nssCertificate->object.refCount);
}
//printf("Certificate %s destroyed\n", cert->subjectName);
CERT_DestroyCertificate(cert);
cert = 0;
}