Initial checkin of signver source.

git-svn-id: svn://10.0.0.236/trunk@65044 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
mcgreer%netscape.com 2000-04-03 20:15:57 +00:00
parent 4d745519cb
commit e5fda9b634
4 changed files with 1498 additions and 0 deletions

View File

@ -0,0 +1,70 @@
#! gmake
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Netscape security libraries.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
#
#######################################################################
# (1) Include initial platform-independent assignments (MANDATORY). #
#######################################################################
include manifest.mn
#######################################################################
# (2) Include "global" configuration information. (OPTIONAL) #
#######################################################################
include $(CORE_DEPTH)/coreconf/config.mk
#######################################################################
# (3) Include "component" configuration information. (OPTIONAL) #
#######################################################################
#######################################################################
# (4) Include "local" platform-dependent assignments (OPTIONAL). #
#######################################################################
include $(CORE_DEPTH)/security/cmd/platlibs.mk
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
#######################################################################
include $(CORE_DEPTH)/coreconf/rules.mk
#######################################################################
# (6) Execute "component" rules. (OPTIONAL) #
#######################################################################
#######################################################################
# (7) Execute "local" rules. (OPTIONAL). #
#######################################################################

View File

@ -0,0 +1,52 @@
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Netscape security libraries.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
#
CORE_DEPTH = ../../..
MODULE = security
CSRCS = signver.c \
pk7print.c \
$(NULL)
REQUIRES = security dbm seccmd
PROGRAM = signver
PACKAGE_FILES = README.txt signedForm.html signedForm.pl form.pl
ifeq ($(subst /,_,$(shell uname -s)),WINNT)
PACKAGE_FILES += signedForm.nt.pl signver.exe
else
PACKAGE_FILES += signver
endif
ARCHIVE_NAME = signver

View File

@ -0,0 +1,918 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
** secutil.c - various functions used by security stuff
**
*/
/* pkcs #7 -related functions */
#include "secutil.h"
#include "secpkcs7.h"
#include "secoid.h"
#include <sys/stat.h>
#include <stdarg.h>
#ifdef XP_UNIX
#include <unistd.h>
#endif
/* for SEC_TraverseNames */
#include "cert.h"
#include "prtypes.h"
#include "prtime.h"
#include "prlong.h"
#include "secmod.h"
#include "pk11func.h"
#include "prerror.h"
/*
** PKCS7 Support
*/
/* forward declaration */
int
sv_PrintPKCS7ContentInfo(FILE *, SEC_PKCS7ContentInfo *, char *);
void
sv_PrintAsHex(FILE *out, SECItem *data, char *m)
{
unsigned i;
if (m) fprintf(out, m);
for (i = 0; i < data->len; i++) {
if (i < data->len - 1) {
fprintf(out, "%02x:", data->data[i]);
} else {
fprintf(out, "%02x\n", data->data[i]);
break;
}
}
}
void
sv_PrintInteger(FILE *out, SECItem *i, char *m)
{
int iv;
if (i->len > 4) {
sv_PrintAsHex(out, i, m);
} else {
iv = DER_GetInteger(i);
fprintf(out, "%s%d (0x%x)\n", m, iv, iv);
}
}
int
sv_PrintUTCTime(FILE *out, SECItem *t, char *m)
{
PRExplodedTime printableTime;
int64 time;
char *timeString;
int rv;
rv = DER_UTCTimeToTime(&time, t);
if (rv) return rv;
/* Converse to local time */
PR_ExplodeTime(time, PR_GMTParameters, &printableTime);
timeString = (char *)PORT_Alloc(100);
if ( timeString ) {
PR_FormatTime( timeString, 100, "%a %b %d %H:%M:%S %Y", &printableTime );
fprintf(out, "%s%s\n", m, timeString);
PORT_Free(timeString);
return 0;
}
return SECFailure;
}
int
sv_PrintValidity(FILE *out, CERTValidity *v, char *m)
{
int rv;
fprintf(out, m);
rv = sv_PrintUTCTime(out, &v->notBefore, "notBefore=");
if (rv) return rv;
fprintf(out, m);
sv_PrintUTCTime(out, &v->notAfter, "notAfter=");
return rv;
}
void
sv_PrintObjectID(FILE *out, SECItem *oid, char *m)
{
char *name;
SECOidData *oiddata;
oiddata = SECOID_FindOID(oid);
if (oiddata == NULL) {
sv_PrintAsHex(out, oid, m);
return;
}
name = oiddata->desc;
if (m != NULL)
fprintf(out, "%s", m);
fprintf(out, "%s\n", name);
}
void
sv_PrintAlgorithmID(FILE *out, SECAlgorithmID *a, char *m)
{
sv_PrintObjectID(out, &a->algorithm, m);
if ((a->parameters.len != 2) ||
(PORT_Memcmp(a->parameters.data, "\005\000", 2) != 0)) {
/* Print args to algorithm */
sv_PrintAsHex(out, &a->parameters, "Args=");
}
}
void
sv_PrintAttribute(FILE *out, SEC_PKCS7Attribute *attr, char *m)
{
SECItem *value;
int i;
char om[100];
fprintf(out, m);
/*
* XXX Make this smarter; look at the type field and then decode
* and print the value(s) appropriately!
*/
sv_PrintObjectID(out, &(attr->type), "type=");
if (attr->values != NULL) {
i = 0;
while ((value = attr->values[i]) != NULL) {
sprintf(om, "%svalue[%d]=%s", m, i++, attr->encoded ? "(encoded)" : "");
if (attr->encoded || attr->typeTag == NULL) {
sv_PrintAsHex(out, value, om);
} else {
switch (attr->typeTag->offset) {
default:
sv_PrintAsHex(out, value, om);
break;
case SEC_OID_PKCS9_CONTENT_TYPE:
sv_PrintObjectID(out, value, om);
break;
case SEC_OID_PKCS9_SIGNING_TIME:
sv_PrintUTCTime(out, value, om);
break;
}
}
}
}
}
void
sv_PrintName(FILE *out, CERTName *name, char *msg)
{
char *str;
str = CERT_NameToAscii(name);
fprintf(out, "%s%s\n", msg, str);
}
#if 0
/*
** secu_PrintPKCS7EncContent
** Prints a SEC_PKCS7EncryptedContentInfo (without decrypting it)
*/
void
secu_PrintPKCS7EncContent(FILE *out, SEC_PKCS7EncryptedContentInfo *src,
char *m, int level)
{
if (src->contentTypeTag == NULL)
src->contentTypeTag = SECOID_FindOID(&(src->contentType));
secu_Indent(out, level);
fprintf(out, "%s:\n", m);
secu_Indent(out, level + 1);
fprintf(out, "Content Type: %s\n",
(src->contentTypeTag != NULL) ? src->contentTypeTag->desc
: "Unknown");
sv_PrintAlgorithmID(out, &(src->contentEncAlg),
"Content Encryption Algorithm");
sv_PrintAsHex(out, &(src->encContent),
"Encrypted Content", level+1);
}
/*
** secu_PrintRecipientInfo
** Prints a PKCS7RecipientInfo type
*/
void
secu_PrintRecipientInfo(FILE *out, SEC_PKCS7RecipientInfo *info, char *m,
int level)
{
secu_Indent(out, level); fprintf(out, "%s:\n", m);
sv_PrintInteger(out, &(info->version), "Version");
sv_PrintName(out, &(info->issuerAndSN->issuer), "Issuer");
sv_PrintInteger(out, &(info->issuerAndSN->serialNumber),
"Serial Number");
/* Parse and display encrypted key */
sv_PrintAlgorithmID(out, &(info->keyEncAlg),
"Key Encryption Algorithm");
sv_PrintAsHex(out, &(info->encKey), "Encrypted Key", level + 1);
}
#endif
/*
** secu_PrintSignerInfo
** Prints a PKCS7SingerInfo type
*/
void
sv_PrintSignerInfo(FILE *out, SEC_PKCS7SignerInfo *info, char *m)
{
SEC_PKCS7Attribute *attr;
int iv;
fprintf(out, m);
sv_PrintInteger(out, &(info->version), "version=");
fprintf(out, m);
sv_PrintName(out, &(info->issuerAndSN->issuer), "issuerName=");
fprintf(out, m);
sv_PrintInteger(out, &(info->issuerAndSN->serialNumber),
"serialNumber=");
fprintf(out, m);
sv_PrintAlgorithmID(out, &(info->digestAlg), "digestAlgorithm=");
if (info->authAttr != NULL) {
char mm[120];
iv = 0;
while (info->authAttr[iv] != NULL) iv++;
fprintf(out, "%sauthenticatedAttributes=%d\n", m, iv);
iv = 0;
while ((attr = info->authAttr[iv]) != NULL) {
sprintf(mm, "%sattribute[%d].", m, iv++);
sv_PrintAttribute(out, attr, mm);
}
}
/* Parse and display signature */
fprintf(out, m);
sv_PrintAlgorithmID(out, &(info->digestEncAlg), "digestEncryptionAlgorithm=");
fprintf(out, m);
sv_PrintAsHex(out, &(info->encDigest), "encryptedDigest=");
if (info->unAuthAttr != NULL) {
char mm[120];
iv = 0;
while (info->unAuthAttr[iv] != NULL) iv++;
fprintf(out, "%sunauthenticatedAttributes=%d\n", m, iv);
iv = 0;
while ((attr = info->unAuthAttr[iv]) != NULL) {
sprintf(mm, "%sattribute[%d].", m, iv++);
sv_PrintAttribute(out, attr, mm);
}
}
}
void
sv_PrintRSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m)
{
fprintf(out, m);
sv_PrintInteger(out, &pk->u.rsa.modulus, "modulus=");
fprintf(out, m);
sv_PrintInteger(out, &pk->u.rsa.publicExponent, "exponent=");
}
void
sv_PrintDSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m)
{
fprintf(out, m);
sv_PrintInteger(out, &pk->u.dsa.params.prime, "prime=");
fprintf(out, m);
sv_PrintInteger(out, &pk->u.dsa.params.subPrime, "subprime=");
fprintf(out, m);
sv_PrintInteger(out, &pk->u.dsa.params.base, "base=");
fprintf(out, m);
sv_PrintInteger(out, &pk->u.dsa.publicValue, "publicValue=");
}
int
sv_PrintSubjectPublicKeyInfo(FILE *out, PRArenaPool *arena,
CERTSubjectPublicKeyInfo *i, char *msg)
{
SECKEYPublicKey *pk;
int rv;
char mm[200];
sprintf(mm, "%s.publicKeyAlgorithm=", msg);
sv_PrintAlgorithmID(out, &i->algorithm, mm);
pk = (SECKEYPublicKey*) PORT_ZAlloc(sizeof(SECKEYPublicKey));
if (!pk) return PORT_GetError();
DER_ConvertBitString(&i->subjectPublicKey);
switch(SECOID_FindOIDTag(&i->algorithm.algorithm)) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
rv = SEC_ASN1DecodeItem(arena, pk, SECKEY_RSAPublicKeyTemplate,
&i->subjectPublicKey);
if (rv) return rv;
sprintf(mm, "%s.rsaPublicKey.", msg);
sv_PrintRSAPublicKey(out, pk, mm);
break;
case SEC_OID_ANSIX9_DSA_SIGNATURE:
rv = SEC_ASN1DecodeItem(arena, pk, SECKEY_DSAPublicKeyTemplate,
&i->subjectPublicKey);
if (rv) return rv;
sprintf(mm, "%s.dsaPublicKey.", msg);
sv_PrintDSAPublicKey(out, pk, mm);
break;
default:
fprintf(out, "%s=bad SPKI algorithm type\n", msg);
return 0;
}
return 0;
}
SECStatus
sv_PrintInvalidDateExten (FILE *out, SECItem *value, char *msg)
{
SECItem decodedValue;
SECStatus rv;
int64 invalidTime;
char *formattedTime = NULL;
decodedValue.data = NULL;
rv = SEC_ASN1DecodeItem (NULL, &decodedValue, SEC_GeneralizedTimeTemplate,
value);
if (rv == SECSuccess) {
rv = DER_GeneralizedTimeToTime(&invalidTime, &decodedValue);
if (rv == SECSuccess) {
formattedTime = CERT_GenTime2FormattedAscii(invalidTime, "%a %b %d %H:%M:%S %Y");
fprintf (out, "%s: %s\n", msg, formattedTime);
PORT_Free (formattedTime);
}
}
PORT_Free (decodedValue.data);
return (rv);
}
int
sv_PrintExtensions(FILE *out, CERTCertExtension **extensions, char *msg)
{
SECOidTag oidTag;
if (extensions) {
while ( *extensions ) {
SECItem *tmpitem;
fprintf(out, "%sname=", msg);
tmpitem = &(*extensions)->id;
sv_PrintObjectID(out, tmpitem, NULL);
tmpitem = &(*extensions)->critical;
if ( tmpitem->len )
fprintf(out, "%scritical=%s\n", msg,
(tmpitem->data && tmpitem->data[0])? "True": "False");
oidTag = SECOID_FindOIDTag (&((*extensions)->id));
fprintf(out, msg);
tmpitem = &((*extensions)->value);
if (oidTag == SEC_OID_X509_INVALID_DATE)
sv_PrintInvalidDateExten (out, tmpitem,"invalidExt");
else
sv_PrintAsHex(out,tmpitem, "data=");
/*fprintf(out, "\n");*/
extensions++;
}
}
return 0;
}
void
sv_PrintCRLInfo(FILE *out, CERTCrl *crl, char *m)
{
CERTCrlEntry *entry;
int iv;
char om[100];
fprintf(out, m);
sv_PrintAlgorithmID(out, &(crl->signatureAlg), "signatureAlgorithm=");
fprintf(out, m);
sv_PrintName(out, &(crl->name), "name=");
fprintf(out, m);
sv_PrintUTCTime(out, &(crl->lastUpdate), "lastUpdate=");
fprintf(out, m);
sv_PrintUTCTime(out, &(crl->nextUpdate), "nextUpdate=");
if (crl->entries != NULL) {
iv = 0;
while ((entry = crl->entries[iv]) != NULL) {
fprintf(out, "%sentry[%d].", m, iv);
sv_PrintInteger(out, &(entry->serialNumber), "serialNumber=");
fprintf(out, "%sentry[%d].", m, iv);
sv_PrintUTCTime(out, &(entry->revocationDate), "revocationDate=");
sprintf(om, "%sentry[%d].signedCRLEntriesExtensions.", m, iv++);
sv_PrintExtensions(out, entry->extensions, om);
}
}
sprintf(om, "%ssignedCRLEntriesExtensions.", m);
sv_PrintExtensions(out, crl->extensions, om);
}
int
sv_PrintCertificate(FILE *out, SECItem *der, char *m, int level)
{
PRArenaPool *arena = NULL;
CERTCertificate *c;
int rv;
int iv;
char mm[200];
/* Decode certificate */
c = (CERTCertificate*) PORT_ZAlloc(sizeof(CERTCertificate));
if (!c) return PORT_GetError();
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) return SEC_ERROR_NO_MEMORY;
rv = SEC_ASN1DecodeItem(arena, c, CERT_CertificateTemplate, der);
if (rv) {
PORT_FreeArena(arena, PR_FALSE);
return rv;
}
/* Pretty print it out */
iv = DER_GetInteger(&c->version);
fprintf(out, "%sversion=%d (0x%x)\n", m, iv + 1, iv);
sprintf(mm, "%sserialNumber=", m);
sv_PrintInteger(out, &c->serialNumber, mm);
sprintf(mm, "%ssignatureAlgorithm=", m);
sv_PrintAlgorithmID(out, &c->signature, mm);
sprintf(mm, "%sissuerName=", m);
sv_PrintName(out, &c->issuer, mm);
sprintf(mm, "%svalidity.", m);
sv_PrintValidity(out, &c->validity, mm);
sprintf(mm, "%ssubject=", m);
sv_PrintName(out, &c->subject, mm);
sprintf(mm, "%ssubjectPublicKeyInfo", m);
rv = sv_PrintSubjectPublicKeyInfo(out, arena, &c->subjectPublicKeyInfo, mm);
if (rv) {
PORT_FreeArena(arena, PR_FALSE);
return rv;
}
sprintf(mm, "%ssignedExtensions.", m);
sv_PrintExtensions(out, c->extensions, mm);
PORT_FreeArena(arena, PR_FALSE);
return 0;
}
int
sv_PrintSignedData(FILE *out, SECItem *der, char *m, SECU_PPFunc inner)
{
PRArenaPool *arena = NULL;
CERTSignedData *sd;
int rv;
/* Strip off the signature */
sd = (CERTSignedData*) PORT_ZAlloc(sizeof(CERTSignedData));
if (!sd) return PORT_GetError();
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) return SEC_ERROR_NO_MEMORY;
rv = SEC_ASN1DecodeItem(arena, sd, CERT_SignedDataTemplate, der);
if (rv) {
PORT_FreeArena(arena, PR_FALSE);
return rv;
}
/* fprintf(out, "%s:\n", m); */
PORT_Strcat(m, "data.");
rv = (*inner)(out, &sd->data, m, 0);
if (rv) {
PORT_FreeArena(arena, PR_FALSE);
return rv;
}
m[PORT_Strlen(m) - 5] = 0;
fprintf(out, m);
sv_PrintAlgorithmID(out, &sd->signatureAlgorithm, "signatureAlgorithm=");
DER_ConvertBitString(&sd->signature);
fprintf(out, m);
sv_PrintAsHex(out, &sd->signature, "signature=");
PORT_FreeArena(arena, PR_FALSE);
return 0;
}
/*
** secu_PrintPKCS7Signed
** Pretty print a PKCS7 signed data type (up to version 1).
*/
int
sv_PrintPKCS7Signed(FILE *out, SEC_PKCS7SignedData *src)
{
SECAlgorithmID *digAlg; /* digest algorithms */
SECItem *aCert; /* certificate */
CERTSignedCrl *aCrl; /* certificate revocation list */
SEC_PKCS7SignerInfo *sigInfo; /* signer information */
int rv, iv;
char om[120];
sv_PrintInteger(out, &(src->version), "pkcs7.version=");
/* Parse and list digest algorithms (if any) */
if (src->digestAlgorithms != NULL) {
iv = 0;
while (src->digestAlgorithms[iv] != NULL)
iv++;
fprintf(out, "pkcs7.digestAlgorithmListLength=%d\n", iv);
iv = 0;
while ((digAlg = src->digestAlgorithms[iv]) != NULL) {
sprintf(om, "pkcs7.digestAlgorithm[%d]=", iv++);
sv_PrintAlgorithmID(out, digAlg, om);
}
}
/* Now for the content */
rv = sv_PrintPKCS7ContentInfo(out, &(src->contentInfo),
"pkcs7.contentInformation=");
if (rv != 0) return rv;
/* Parse and list certificates (if any) */
if (src->rawCerts != NULL) {
iv = 0;
while (src->rawCerts[iv] != NULL)
iv++;
fprintf(out, "pkcs7.certificateListLength=%d\n", iv);
iv = 0;
while ((aCert = src->rawCerts[iv]) != NULL) {
sprintf(om, "certificate[%d].", iv++);
rv = sv_PrintSignedData(out, aCert, om, sv_PrintCertificate);
if (rv) return rv;
}
}
/* Parse and list CRL's (if any) */
if (src->crls != NULL) {
iv = 0;
while (src->crls[iv] != NULL) iv++;
fprintf(out, "pkcs7.signedRevocationLists=%d\n", iv);
iv = 0;
while ((aCrl = src->crls[iv]) != NULL) {
sprintf(om, "signedRevocationList[%d].", iv);
fprintf(out, om);
sv_PrintAlgorithmID(out, &aCrl->signatureWrap.signatureAlgorithm,
"signatureAlgorithm=");
DER_ConvertBitString(&aCrl->signatureWrap.signature);
fprintf(out, om);
sv_PrintAsHex(out, &aCrl->signatureWrap.signature, "signature=");
sprintf(om, "certificateRevocationList[%d].", iv);
sv_PrintCRLInfo(out, &aCrl->crl, om);
iv++;
}
}
/* Parse and list signatures (if any) */
if (src->signerInfos != NULL) {
iv = 0;
while (src->signerInfos[iv] != NULL)
iv++;
fprintf(out, "pkcs7.signerInformationListLength=%d\n", iv);
iv = 0;
while ((sigInfo = src->signerInfos[iv]) != NULL) {
sprintf(om, "signerInformation[%d].", iv++);
sv_PrintSignerInfo(out, sigInfo, om);
}
}
return 0;
}
#if 0
/*
** secu_PrintPKCS7Enveloped
** Pretty print a PKCS7 enveloped data type (up to version 1).
*/
void
secu_PrintPKCS7Enveloped(FILE *out, SEC_PKCS7EnvelopedData *src,
char *m, int level)
{
SEC_PKCS7RecipientInfo *recInfo; /* pointer for signer information */
int iv;
char om[100];
secu_Indent(out, level); fprintf(out, "%s:\n", m);
sv_PrintInteger(out, &(src->version), "Version", level + 1);
/* Parse and list recipients (this is not optional) */
if (src->recipientInfos != NULL) {
secu_Indent(out, level + 1);
fprintf(out, "Recipient Information List:\n");
iv = 0;
while ((recInfo = src->recipientInfos[iv++]) != NULL) {
sprintf(om, "Recipient Information (%x)", iv);
secu_PrintRecipientInfo(out, recInfo, om, level + 2);
}
}
secu_PrintPKCS7EncContent(out, &src->encContentInfo,
"Encrypted Content Information", level + 1);
}
/*
** secu_PrintPKCS7SignedEnveloped
** Pretty print a PKCS7 singed and enveloped data type (up to version 1).
*/
int
secu_PrintPKCS7SignedAndEnveloped(FILE *out,
SEC_PKCS7SignedAndEnvelopedData *src,
char *m, int level)
{
SECAlgorithmID *digAlg; /* pointer for digest algorithms */
SECItem *aCert; /* pointer for certificate */
CERTSignedCrl *aCrl; /* pointer for certificate revocation list */
SEC_PKCS7SignerInfo *sigInfo; /* pointer for signer information */
SEC_PKCS7RecipientInfo *recInfo; /* pointer for recipient information */
int rv, iv;
char om[100];
secu_Indent(out, level); fprintf(out, "%s:\n", m);
sv_PrintInteger(out, &(src->version), "Version", level + 1);
/* Parse and list recipients (this is not optional) */
if (src->recipientInfos != NULL) {
secu_Indent(out, level + 1);
fprintf(out, "Recipient Information List:\n");
iv = 0;
while ((recInfo = src->recipientInfos[iv++]) != NULL) {
sprintf(om, "Recipient Information (%x)", iv);
secu_PrintRecipientInfo(out, recInfo, om, level + 2);
}
}
/* Parse and list digest algorithms (if any) */
if (src->digestAlgorithms != NULL) {
secu_Indent(out, level + 1); fprintf(out, "Digest Algorithm List:\n");
iv = 0;
while ((digAlg = src->digestAlgorithms[iv++]) != NULL) {
sprintf(om, "Digest Algorithm (%x)", iv);
sv_PrintAlgorithmID(out, digAlg, om);
}
}
secu_PrintPKCS7EncContent(out, &src->encContentInfo,
"Encrypted Content Information", level + 1);
/* Parse and list certificates (if any) */
if (src->rawCerts != NULL) {
secu_Indent(out, level + 1); fprintf(out, "Certificate List:\n");
iv = 0;
while ((aCert = src->rawCerts[iv++]) != NULL) {
sprintf(om, "Certificate (%x)", iv);
rv = SECU_PrintSignedData(out, aCert, om, level + 2,
SECU_PrintCertificate);
if (rv)
return rv;
}
}
/* Parse and list CRL's (if any) */
if (src->crls != NULL) {
secu_Indent(out, level + 1);
fprintf(out, "Signed Revocation Lists:\n");
iv = 0;
while ((aCrl = src->crls[iv++]) != NULL) {
sprintf(om, "Signed Revocation List (%x)", iv);
secu_Indent(out, level + 2); fprintf(out, "%s:\n", om);
sv_PrintAlgorithmID(out, &aCrl->signatureWrap.signatureAlgorithm,
"Signature Algorithm");
DER_ConvertBitString(&aCrl->signatureWrap.signature);
sv_PrintAsHex(out, &aCrl->signatureWrap.signature, "Signature",
level+3);
SECU_PrintCRLInfo(out, &aCrl->crl, "Certificate Revocation List",
level + 3);
}
}
/* Parse and list signatures (if any) */
if (src->signerInfos != NULL) {
secu_Indent(out, level + 1);
fprintf(out, "Signer Information List:\n");
iv = 0;
while ((sigInfo = src->signerInfos[iv++]) != NULL) {
sprintf(om, "Signer Information (%x)", iv);
secu_PrintSignerInfo(out, sigInfo, om, level + 2);
}
}
return 0;
}
PR_IMPLEMENT(int)
SECU_PrintCrl (FILE *out, SECItem *der, char *m, int level)
{
PRArenaPool *arena = NULL;
CERTCrl *c = NULL;
int rv;
do {
/* Decode CRL */
c = (CERTCrl*) PORT_ZAlloc(sizeof(CERTCrl));
if (!c) {
rv = PORT_GetError();
break;
}
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
rv = SEC_ERROR_NO_MEMORY;
break;
}
rv = SEC_ASN1DecodeItem(arena, c, CERT_CrlTemplate, der);
if (rv != SECSuccess)
break;
SECU_PrintCRLInfo (out, c, m, level);
} while (0);
PORT_FreeArena (arena, PR_FALSE);
PORT_Free (c);
return (rv);
}
/*
** secu_PrintPKCS7Encrypted
** Pretty print a PKCS7 encrypted data type (up to version 1).
*/
void
secu_PrintPKCS7Encrypted(FILE *out, SEC_PKCS7EncryptedData *src,
char *m, int level)
{
secu_Indent(out, level); fprintf(out, "%s:\n", m);
sv_PrintInteger(out, &(src->version), "Version", level + 1);
secu_PrintPKCS7EncContent(out, &src->encContentInfo,
"Encrypted Content Information", level + 1);
}
/*
** secu_PrintPKCS7Digested
** Pretty print a PKCS7 digested data type (up to version 1).
*/
void
sv_PrintPKCS7Digested(FILE *out, SEC_PKCS7DigestedData *src)
{
secu_Indent(out, level); fprintf(out, "%s:\n", m);
sv_PrintInteger(out, &(src->version), "Version", level + 1);
sv_PrintAlgorithmID(out, &src->digestAlg, "Digest Algorithm");
sv_PrintPKCS7ContentInfo(out, &src->contentInfo, "Content Information",
level + 1);
sv_PrintAsHex(out, &src->digest, "Digest", level + 1);
}
#endif
/*
** secu_PrintPKCS7ContentInfo
** Takes a SEC_PKCS7ContentInfo type and sends the contents to the
** appropriate function
*/
int
sv_PrintPKCS7ContentInfo(FILE *out, SEC_PKCS7ContentInfo *src, char *m)
{
char *desc;
SECOidTag kind;
int rv;
if (src->contentTypeTag == NULL)
src->contentTypeTag = SECOID_FindOID(&(src->contentType));
if (src->contentTypeTag == NULL) {
desc = "Unknown";
kind = SEC_OID_PKCS7_DATA;
} else {
desc = src->contentTypeTag->desc;
kind = src->contentTypeTag->offset;
}
fprintf(out, "%s%s\n", m, desc);
if (src->content.data == NULL) {
fprintf(out, "pkcs7.data=<no content>\n");
return 0;
}
rv = 0;
switch (kind) {
case SEC_OID_PKCS7_SIGNED_DATA: /* Signed Data */
rv = sv_PrintPKCS7Signed(out, src->content.signedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA: /* Enveloped Data */
fprintf(out, "pkcs7EnvelopedData=<unsupported>\n");
/*sv_PrintPKCS7Enveloped(out, src->content.envelopedData);*/
break;
case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: /* Signed and Enveloped */
fprintf(out, "pkcs7SignedEnvelopedData=<unsupported>\n");
/*rv = sv_PrintPKCS7SignedAndEnveloped(out,
src->content.signedAndEnvelopedData);*/
break;
case SEC_OID_PKCS7_DIGESTED_DATA: /* Digested Data */
fprintf(out, "pkcs7DigestedData=<unsupported>\n");
/*sv_PrintPKCS7Digested(out, src->content.digestedData);*/
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA: /* Encrypted Data */
fprintf(out, "pkcs7EncryptedData=<unsupported>\n");
/*sv_PrintPKCS7Encrypted(out, src->content.encryptedData);*/
break;
default:
fprintf(out, "pkcs7UnknownData=<unsupported>\n");
/*sv_PrintAsHex(out, src->content.data);*/
break;
}
return rv;
}
int
SV_PrintPKCS7ContentInfo(FILE *out, SECItem *der)
{
SEC_PKCS7ContentInfo *cinfo;
int rv = -1;
cinfo = SEC_PKCS7DecodeItem(der, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (cinfo != NULL) {
rv = sv_PrintPKCS7ContentInfo(out, cinfo, "pkcs7.contentInfo=");
SEC_PKCS7DestroyContentInfo(cinfo);
}
return rv;
}
/*
** End of PKCS7 functions
*/

View File

@ -0,0 +1,458 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
#include "secutil.h"
#include "secmod.h"
#include "cdbhdl.h"
#include "cert.h"
#include "secoid.h"
/* NSPR 2.0 header files */
#include "prinit.h"
#include "prprf.h"
#include "prsystem.h"
#include "prmem.h"
/* Portable layer header files */
#include "plstr.h"
static int debugInfo = 0;
static char *usageInfo[] = {
"Options:",
" -a signature file is ASCII",
" -d certdir directory containing cert database",
" -i dataFileName input file name containing data,",
" leave filename blank to use stdin",
" -s signatureFileName input file name containing signature,",
" leave filename blank to use stdin",
" -o outputFileName output file name, default stdout",
" -A display all information from pkcs #7",
" -C display all information from all certs",
" -C -n display number of certificates",
" -C -n certNum display all information from the certNum",
" certificate in pkcs #7 signed object",
" -C -n certNum f1 ... fN display information about specified",
" fields from the certNum certificate in",
" the pkcs #7 signed object",
" -S display signer information list",
" -S -n display number of signer information blocks",
" -S -n signerNum display all information from the signerNum",
" signer information block in pkcs #7",
" -S -n signerNum f1 ... fN display information about specified",
" fields from the signerNum signer",
" information block in pkcs #7 object",
" -V verify the signed object and display result",
" -V -v verify the signed object and display",
" result and reason for failure",
"Version 2.0"
};
static int nUsageInfo = sizeof(usageInfo)/sizeof(char *);
extern int SV_PrintPKCS7ContentInfo(FILE *, SECItem *);
static void Usage(char *progName, FILE *outFile)
{
int i;
fprintf(outFile, "Usage: %s options\n", progName);
for (i = 0; i < nUsageInfo; i++)
fprintf(outFile, "%s\n", usageInfo[i]);
exit(-1);
}
static HASH_HashType
AlgorithmToHashType(SECAlgorithmID *digestAlgorithms)
{
SECOidTag tag;
tag = SECOID_GetAlgorithmTag(digestAlgorithms);
switch (tag) {
case SEC_OID_MD2:
if (debugInfo) PR_fprintf(PR_STDERR, "Hash algorithm: HASH_AlgMD2 SEC_OID_MD2\n");
return HASH_AlgMD2;
case SEC_OID_MD5:
if (debugInfo) PR_fprintf(PR_STDERR, "Hash algorithm: HASH_AlgMD5 SEC_OID_MD5\n");
return HASH_AlgMD5;
case SEC_OID_SHA1:
if (debugInfo) PR_fprintf(PR_STDERR, "Hash algorithm: HASH_AlgSHA1 SEC_OID_SHA1\n");
return HASH_AlgSHA1;
default:
if (debugInfo) PR_fprintf(PR_STDERR, "should never get here\n");
return HASH_AlgNULL;
}
}
static int
DigestData (unsigned char *digest, unsigned char *data,
unsigned int *len, unsigned int maxLen,
HASH_HashType hashType)
{
SECHashObject *hashObj;
void *hashcx;
hashObj = &SECHashObjects[hashType];
hashcx = (* hashObj->create)();
if (hashcx == NULL)
return -1;
(* hashObj->begin)(hashcx);
(* hashObj->update)(hashcx, data, PORT_Strlen(data));
(* hashObj->end)(hashcx, digest, len, maxLen);
(* hashObj->destroy)(hashcx, PR_TRUE);
return 0;
}
enum {
cmd_DisplayAllPCKS7Info = 0,
cmd_DisplayCertInfo,
cmd_DisplaySignerInfo,
cmd_VerifySignedObj
};
enum {
opt_ASCII,
opt_CertDir,
opt_InputDataFile,
opt_ItemNumber,
opt_OutputFile,
opt_InputSigFile,
opt_TypeTag,
opt_PrintWhyFailure
};
static secuCommandFlag signver_commands[] =
{
{ /* cmd_DisplayAllPCKS7Info*/ 'A', PR_FALSE, 0, PR_FALSE },
{ /* cmd_DisplayCertInfo */ 'C', PR_FALSE, 0, PR_FALSE },
{ /* cmd_DisplaySignerInfo */ 'S', PR_FALSE, 0, PR_FALSE },
{ /* cmd_VerifySignedObj */ 'V', PR_FALSE, 0, PR_FALSE }
};
static secuCommandFlag signver_options[] =
{
{ /* opt_ASCII */ 'a', PR_FALSE, 0, PR_FALSE },
{ /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE },
{ /* opt_InputDataFile */ 'i', PR_TRUE, 0, PR_FALSE },
{ /* opt_ItemNumber */ 'n', PR_FALSE, 0, PR_FALSE },
{ /* opt_OutputFile */ 'o', PR_TRUE, 0, PR_FALSE },
{ /* opt_InputSigFile */ 's', PR_TRUE, 0, PR_FALSE },
{ /* opt_TypeTag */ 't', PR_TRUE, 0, PR_FALSE },
{ /* opt_PrintWhyFailure */ 'v', PR_FALSE, 0, PR_FALSE }
};
int main(int argc, char **argv)
{
PRExplodedTime explodedCurrent;
SECItem der, data;
char *progName;
int rv;
PRFileDesc *dataFile = 0;
PRFileDesc *signFile = 0;
FILE *outFile = stdout;
char *typeTag = 0;
PRBool displayAllCerts = PR_FALSE;
PRBool displayAllSigners = PR_FALSE;
PRFileInfo info;
PRInt32 nb;
secuCommand signver;
signver.numCommands = sizeof(signver_commands) /sizeof(secuCommandFlag);
signver.numOptions = sizeof(signver_options) / sizeof(secuCommandFlag);
signver.commands = signver_commands;
signver.options = signver_options;
#ifdef XP_PC
progName = strrchr(argv[0], '\\');
#else
progName = strrchr(argv[0], '/');
#endif
progName = progName ? progName+1 : argv[0];
/*_asm int 3*/
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &explodedCurrent);
#if 0
if (explodedCurrent.tm_year >= 1998
&& explodedCurrent.tm_month >= 5 /* months past tm_year (0-11, Jan = 0) */
&& explodedCurrent.tm_mday >= 1) {
PR_fprintf(PR_STDERR, "%s: expired\n", progName);
return -1;
}
#endif
SECU_ParseCommandLine(argc, argv, progName, &signver);
/* Set the certdb directory (default is ~/.{browser}) */
SECU_ConfigDirectory(signver.options[opt_CertDir].arg);
/* Set the certificate type. */
typeTag = SECU_GetOptionArg(&signver, opt_TypeTag);
/* -i and -s without filenames */
if (signver.options[opt_InputDataFile].activated &&
signver.options[opt_InputSigFile].activated &&
!signver.options[opt_InputDataFile].arg &&
!signver.options[opt_InputSigFile].arg)
PR_fprintf(PR_STDERR,
"%s: Only data or signature file can use stdin (not both).\n",
progName);
/* Open the input data file (no arg == use stdin). */
if (signver.options[opt_InputDataFile].activated) {
if (signver.options[opt_InputDataFile].arg)
dataFile = PR_Open(signver.options[opt_InputDataFile].arg,
PR_RDONLY, 0);
else
dataFile = PR_STDIN;
if (!dataFile) {
PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for reading.\n",
progName, signver.options[opt_InputDataFile].arg);
return -1;
}
}
/* Open the input signature file (no arg == use stdin). */
if (signver.options[opt_InputSigFile].activated) {
if (signver.options[opt_InputSigFile].arg)
signFile = PR_Open(signver.options[opt_InputSigFile].arg,
PR_RDONLY, 0);
else
signFile = PR_STDIN;
if (!signFile) {
PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for reading.\n",
progName, signver.options[opt_InputSigFile].arg);
return -1;
}
}
#if 0
if (!typeTag) ascii = 1;
#endif
/* Open|Create the output file. */
if (signver.options[opt_OutputFile].activated) {
outFile = fopen(signver.options[opt_OutputFile].arg, "w");
/*
outFile = PR_Open(signver.options[opt_OutputFile].arg,
PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 00660);
*/
if (!outFile) {
PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for writing.\n",
progName, signver.options[opt_OutputFile].arg);
return -1;
}
}
if (signver.commands[cmd_DisplayCertInfo].activated &&
!signver.options[opt_ItemNumber].arg)
displayAllCerts = PR_TRUE;
if (signver.commands[cmd_DisplaySignerInfo].activated &&
!signver.options[opt_ItemNumber].arg)
displayAllSigners = PR_TRUE;
#if 0
case 'W':
debugInfo = 1;
break;
#endif
if (!signFile && !dataFile && !typeTag)
Usage(progName, outFile);
if (!signFile && !dataFile &&
signver.commands[cmd_VerifySignedObj].activated) {
PR_fprintf(PR_STDERR,
"%s: unable to read all data from standard input\n",
progName);
return -1;
}
PR_SetError(0, 0); /* PR_Init("pp", 1, 1, 0);*/
SECU_PKCS11Init(PR_FALSE);
SEC_Init();
rv = SECU_ReadDERFromFile(&der, signFile,
signver.options[opt_ASCII].activated);
/* Data is untyped, using the specified type */
data.data = der.data;
data.len = der.len;
/* Signature Verification */
if (!signver.options[opt_TypeTag].activated) {
if (signver.commands[cmd_VerifySignedObj].activated) {
SEC_PKCS7ContentInfo *cinfo;
cinfo = SEC_PKCS7DecodeItem(&data, NULL, NULL, NULL, NULL,
NULL, NULL, NULL);
if (cinfo != NULL) {
#if 0
if (debugInfo) {
PR_fprintf(PR_STDERR, "Content %s encrypted.\n",
SEC_PKCS7ContentIsEncrypted(cinfo) ? "was" : "was not");
PR_fprintf(PR_STDERR, "Content %s signed.\n",
SEC_PKCS7ContentIsSigned(cinfo) ? "was" : "was not");
}
#endif
if (SEC_PKCS7ContentIsSigned(cinfo)) {
SEC_PKCS7SignedData *signedData;
HASH_HashType digestType;
SECItem digest, data;
unsigned char *dataToVerify, digestBuffer[32];
signedData = cinfo->content.signedData;
/* assume that there is only one digest algorithm for now */
digestType =
AlgorithmToHashType(signedData->digestAlgorithms[0]);
if (digestType == HASH_AlgNULL) {
PR_fprintf(PR_STDERR, "Invalid hash algorithmID\n");
return (-1);
}
rv = SECU_FileToItem(&data, dataFile);
dataToVerify = data.data;
if (dataToVerify) {
SECKEYKeyDBHandle *keyHandle;
CERTCertDBHandle *certHandle;
/*certUsageObjectSigner;*/
SECCertUsage usage = certUsageEmailSigner;
#if 0
if (debugInfo)
PR_fprintf(PR_STDERR, "dataToVerify=%s\n",
dataToVerify);
#endif
digest.data = digestBuffer;
if (DigestData (digest.data, dataToVerify,
&digest.len, 32, digestType)) {
PR_fprintf(PR_STDERR, "Fail to compute message digest for verification. Reason: %s\n",
SECU_ErrorString((int16)PORT_GetError()));
return (-1);
}
#if 0
if (debugInfo) {
PR_fprintf(PR_STDERR, "Data Digest=:");
for (i = 0; i < digest.len; i++)
PR_fprintf(PR_STDERR, "%02x:", digest.data[i]);
PR_fprintf(PR_STDERR, "\n");
}
#endif
keyHandle = SECKEY_GetDefaultKeyDB();
if (keyHandle == NULL) {
PR_fprintf(PR_STDERR, ": %s\n", SECU_ErrorString((int16)PORT_GetError()));
return -1;
}
/* open cert database */
certHandle = SECU_OpenCertDB(PR_TRUE);
if (certHandle == NULL) {
PR_fprintf(PR_STDERR, "%s Problem open the cert dbase\n",
progName);
return -1;
}
if (signver.commands[cmd_VerifySignedObj].activated)
fprintf(outFile, "signatureValid=");
PORT_SetError(0);
if (SEC_PKCS7VerifyDetachedSignature (cinfo, usage,
&digest, digestType, PR_FALSE)) {
if (signver.commands[cmd_VerifySignedObj].activated)
fprintf(outFile, "yes");
} else {
if (signver.commands[cmd_VerifySignedObj].activated){
fprintf(outFile, "no");
if (signver.options[opt_PrintWhyFailure].activated)
fprintf(outFile, ":%s", SECU_ErrorString((int16)PORT_GetError()));
}
}
if (signver.commands[cmd_VerifySignedObj].activated)
fprintf(outFile, "\n");
SECITEM_FreeItem(&data, PR_FALSE);
} else {
PR_fprintf(PR_STDERR, "Cannot read data\n");
return (-1);
}
}
SEC_PKCS7DestroyContentInfo(cinfo);
} else
PR_fprintf(PR_STDERR, "Unable to decode PKCS7 data\n");
}
if (signver.commands[cmd_DisplayAllPCKS7Info].activated)
SV_PrintPKCS7ContentInfo(outFile, &data);
if (displayAllCerts)
PR_fprintf(PR_STDERR, "-C option is not implemented in this version\n");
if (displayAllSigners)
PR_fprintf(PR_STDERR, "-S option is not implemented in this version\n");
/* Pretty print it */
} else if (PL_strcmp(typeTag, SEC_CT_CERTIFICATE) == 0) {
rv = SECU_PrintSignedData(outFile, &data, "Certificate", 0,
SECU_PrintCertificate);
} else if (PL_strcmp(typeTag, SEC_CT_CERTIFICATE_REQUEST) == 0) {
rv = SECU_PrintSignedData(outFile, &data, "Certificate Request", 0,
SECU_PrintCertificateRequest);
} else if (PL_strcmp (typeTag, SEC_CT_CRL) == 0) {
rv = SECU_PrintSignedData (outFile, &data, "CRL", 0, SECU_PrintCrl);
} else if (PL_strcmp(typeTag, SEC_CT_PRIVATE_KEY) == 0) {
rv = SECU_PrintPrivateKey(outFile, &data, "Private Key", 0);
} else if (PL_strcmp(typeTag, SEC_CT_PUBLIC_KEY) == 0) {
rv = SECU_PrintPublicKey(outFile, &data, "Public Key", 0);
} else if (PL_strcmp(typeTag, SEC_CT_PKCS7) == 0) {
rv = SECU_PrintPKCS7ContentInfo(outFile, &data,
"PKCS #7 Content Info", 0);
} else {
PR_fprintf(PR_STDERR, "%s: don't know how to print out '%s' files\n",
progName, typeTag);
return -1;
}
if (rv) {
PR_fprintf(PR_STDERR, "%s: problem converting data (%s)\n",
progName, SECU_ErrorString((int16)PORT_GetError()));
return -1;
}
return 0;
}