#include <stdio.h>
#include "cryptohi.h"
#include "sechash.h"
#include "keyhi.h"
#include "secasn1.h"
#include "secoid.h"
#include "pk11func.h"
#include "secdig.h"
#include "secerr.h"
#include "secport.h"
static SECStatus
DecryptSigBlock(SECOidTag *tagp, unsigned char *digest,
unsigned int *digestlen, unsigned int maxdigestlen,
SECKEYPublicKey *key, const SECItem *sig, char *wincx)
{
SGNDigestInfo *di = NULL;
unsigned char *buf = NULL;
SECStatus rv;
SECOidTag tag;
SECItem it;
if (key == NULL) goto loser;
it.len = SECKEY_PublicKeyStrength(key);
if (!it.len) goto loser;
it.data = buf = (unsigned char *)PORT_Alloc(it.len);
if (!buf) goto loser;
rv = PK11_VerifyRecover(key, (SECItem *)sig, &it, wincx);
if (rv != SECSuccess) goto loser;
di = SGN_DecodeDigestInfo(&it);
if (di == NULL) goto sigloser;
tag = SECOID_GetAlgorithmTag(&di->digestAlgorithm);
if (tag == SEC_OID_UNKNOWN) {
goto sigloser;
}
if (di->digestAlgorithm.parameters.len > 2 &&
di->digestAlgorithm.parameters.type != siSaltValue) {
goto sigloser;
}
if (di->digest.len > maxdigestlen) {
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
goto loser;
}
PORT_Memcpy(digest, di->digest.data, di->digest.len);
*tagp = tag;
*digestlen = di->digest.len;
goto done;
sigloser:
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
loser:
rv = SECFailure;
done:
if (di != NULL) SGN_DestroyDigestInfo(di);
if (buf != NULL) PORT_Free(buf);
return rv;
}
typedef enum { VFY_RSA, VFY_DSA, VFY_ECDSA } VerifyType;
struct VFYContextStr {
SECOidTag alg;
VerifyType type;
SECKEYPublicKey *key;
union {
unsigned char buffer[1];
unsigned char rsadigest[HASH_LENGTH_MAX];
unsigned char dsasig[DSA_SIGNATURE_LEN];
unsigned char ecdsasig[2 * MAX_ECKEY_LEN];
} u;
unsigned int rsadigestlen;
void * wincx;
void *hashcx;
const SECHashObject *hashobj;
SECOidTag sigAlg;
PRBool hasSignature;
HASHContext* hash_ctx_;
};
static SECStatus
decodeECorDSASignature(SECOidTag algid, SECItem *sig, unsigned char *dsig,
unsigned int len) {
SECItem *dsasig = NULL;
SECStatus rv=SECSuccess;
switch (algid) {
case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SIGNATURE_RECOMMENDED_DIGEST:
case SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST:
if (len > MAX_ECKEY_LEN * 2) {
PORT_SetError(SEC_ERROR_BAD_DER);
return SECFailure;
}
case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
case SEC_OID_ANSIX9_DSA_SIGNATURE:
dsasig = DSAU_DecodeDerSigToLen(sig, len);
if ((dsasig == NULL) || (dsasig->len != len)) {
rv = SECFailure;
} else {
PORT_Memcpy(dsig, dsasig->data, dsasig->len);
}
break;
default:
if (sig->len != len) {
rv = SECFailure;
} else {
PORT_Memcpy(dsig, sig->data, sig->len);
}
break;
}
if (dsasig != NULL) SECITEM_FreeItem(dsasig, PR_TRUE);
if (rv == SECFailure) PORT_SetError(SEC_ERROR_BAD_DER);
return rv;
}
const static SEC_ASN1Template hashParameterTemplate[] =
{
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) },
{ SEC_ASN1_OBJECT_ID, 0 },
{ SEC_ASN1_SKIP_REST },
{ 0, }
};
static SECStatus
decodeSigAlg(SECOidTag alg, const SECItem *params, const SECKEYPublicKey *key,
SECOidTag *hashalg)
{
PRArenaPool *arena;
SECStatus rv;
SECItem oid;
unsigned int len;
PR_ASSERT(hashalg!=NULL);
switch (alg) {
case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
*hashalg = SEC_OID_MD2;
break;
case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_RAND_MD5_WITH_RSA_ENCRYPTION:
*hashalg = SEC_OID_MD5;
break;
case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_RAND_SHA1_WITH_RSA_ENCRYPTION:
case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
*hashalg = SEC_OID_SHA1;
break;
case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
*hashalg = SEC_OID_SHA256;
break;
case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
*hashalg = SEC_OID_SHA384;
break;
case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
*hashalg = SEC_OID_SHA512;
break;
case SEC_OID_ANSIX962_ECDSA_SIGNATURE_RECOMMENDED_DIGEST:
len = SECKEY_PublicKeyStrength((SECKEYPublicKey *)key);
if (len < 28) {
*hashalg = SEC_OID_SHA1;
} else if (len < 32) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return SECFailure;
} else if (len < 48) {
*hashalg = SEC_OID_SHA256;
} else if (len < 64) {
*hashalg = SEC_OID_SHA384;
} else {
*hashalg = SEC_OID_SHA512;
}
break;
case SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST:
if (params == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return SECFailure;
}
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
return SECFailure;
}
rv = SEC_QuickDERDecodeItem(arena, &oid, hashParameterTemplate, params);
if (rv != SECSuccess) {
PORT_FreeArena(arena, PR_FALSE);
return rv;
}
*hashalg = SECOID_FindOIDTag(&oid);
PORT_FreeArena(arena, PR_FALSE);
if (*hashalg == SEC_OID_UNKNOWN) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return SECFailure;
}
break;
case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
*hashalg = SEC_OID_SHA1;
break;
case SEC_OID_MISSI_DSS:
case SEC_OID_MISSI_KEA_DSS:
case SEC_OID_MISSI_KEA_DSS_OLD:
case SEC_OID_MISSI_DSS_OLD:
*hashalg = SEC_OID_SHA1;
break;
case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION:
default:
return SECFailure;
}
return SECSuccess;
}
static VFYContext *
vfy_CreateContextPrivate(const SECKEYPublicKey *key, const SECItem *sig,
SECOidTag algid, const SECItem *params, void *wincx)
{
VFYContext *cx;
SECStatus rv;
unsigned int sigLen;
HASH_HashType type;
SECItem *p;
size_t rand_length;
cx = (VFYContext*) PORT_ZAlloc(sizeof(VFYContext));
if (cx) {
cx->wincx = wincx;
cx->hasSignature = (sig != NULL);
cx->sigAlg = algid;
rv = SECSuccess;
switch (key->keyType) {
case rsaKey:
cx->type = VFY_RSA;
cx->key = SECKEY_CopyPublicKey((SECKEYPublicKey *)key);
if (sig) {
SECOidTag hashid = SEC_OID_UNKNOWN;
unsigned int digestlen = 0;
rv = DecryptSigBlock(&hashid, cx->u.buffer, &digestlen,
HASH_LENGTH_MAX, cx->key, sig, (char*)wincx);
cx->alg = hashid;
cx->rsadigestlen = digestlen;
} else {
rv = decodeSigAlg(algid, params, key, &cx->alg);
}
break;
case fortezzaKey:
case dsaKey:
case ecKey:
if (key->keyType == ecKey) {
cx->type = VFY_ECDSA;
sigLen = SECKEY_SignatureLen(key);
} else {
cx->type = VFY_DSA;
sigLen = DSA_SIGNATURE_LEN;
}
if (sigLen == 0) {
rv = SECFailure;
break;
}
rv = decodeSigAlg(algid, params, key, &cx->alg);
if (rv != SECSuccess) {
break;
}
cx->key = SECKEY_CopyPublicKey((SECKEYPublicKey *)key);
if (sig) {
rv = decodeECorDSASignature(algid,sig,cx->u.buffer,sigLen);
}
break;
default:
rv = SECFailure;
break;
}
if (rv) goto loser;
switch (cx->alg) {
case SEC_OID_MD2:
case SEC_OID_MD5:
case SEC_OID_SHA1:
case SEC_OID_SHA256:
case SEC_OID_SHA384:
case SEC_OID_SHA512:
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
goto loser;
}
cx->hash_ctx_ = NULL;
if (algid == SEC_OID_PKCS1_RAND_SHA1_WITH_RSA_ENCRYPTION ||
algid == SEC_OID_PKCS1_RAND_MD5_WITH_RSA_ENCRYPTION) {
type = HASH_AlgMD5;
if (algid == SEC_OID_PKCS1_RAND_SHA1_WITH_RSA_ENCRYPTION) {
type = HASH_AlgSHA1;
}
printf("RAND MD5/SHA1 create context rand_data length %d\n", params->len);
printf("RAND MD5/SHA1 %x %x %x %x\n", params->data[0], params->data[1],
params->data[2], params->data[3]);
PORT_Assert(params->data[0] == 0x04);
p = (SECItem*) PORT_Alloc(sizeof(SECItem));
p->type = siSaltValue;
rand_length = (size_t) params->data[1];
p->data = (unsigned char*) PORT_Alloc(rand_length);
memcpy(p->data, ¶ms->data[2], rand_length);
p->len = rand_length;
cx->hash_ctx_ = HASH_CreateEx(type, p);
PORT_Free(p->data);
PORT_Free(p);
}
}
return cx;
loser:
VFY_DestroyContext(cx, PR_TRUE);
return 0;
}
VFYContext *
VFY_CreateContext(SECKEYPublicKey *key, SECItem *sig, SECOidTag algid,
void *wincx)
{
return vfy_CreateContextPrivate(key, sig, algid, NULL, wincx);
}
void
VFY_DestroyContext(VFYContext *cx, PRBool freeit)
{
if (cx) {
if (cx->hashcx != NULL) {
(*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
cx->hashcx = NULL;
}
if (cx->key) {
SECKEY_DestroyPublicKey(cx->key);
}
if (freeit) {
PORT_ZFree(cx, sizeof(VFYContext));
}
}
}
SECStatus
VFY_Begin(VFYContext *cx)
{
if (cx->hash_ctx_ != NULL) {
HASH_BeginEx(cx->hash_ctx_);
return SECSuccess;
}
if (cx->hashcx != NULL) {
(*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
cx->hashcx = NULL;
}
cx->hashobj = HASH_GetHashObjectByOidTag(cx->alg);
if (!cx->hashobj)
return SECFailure;
cx->hashcx = (*cx->hashobj->create)();
if (cx->hashcx == NULL)
return SECFailure;
(*cx->hashobj->begin)(cx->hashcx);
return SECSuccess;
}
SECStatus
VFY_Update(VFYContext *cx, unsigned char *input, unsigned inputLen)
{
if (cx->hash_ctx_ != NULL) {
HASH_UpdateEx(cx->hash_ctx_, input, inputLen);
return SECSuccess;
}
if (cx->hashcx == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
(*cx->hashobj->update)(cx->hashcx, input, inputLen);
return SECSuccess;
}
SECStatus
VFY_EndWithSignature(VFYContext *cx, SECItem *sig)
{
unsigned char final[HASH_LENGTH_MAX];
unsigned part;
SECItem hash,dsasig;
SECStatus rv;
if ((cx->hasSignature == PR_FALSE) && (sig == NULL)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (cx->hash_ctx_ != NULL) {
HASH_EndEx(cx->hash_ctx_, final, &part, sizeof(final));
} else {
if (cx->hashcx == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
(*cx->hashobj->end)(cx->hashcx, final, &part, sizeof(final));
}
switch (cx->type) {
case VFY_DSA:
case VFY_ECDSA:
dsasig.data = cx->u.buffer;
if (cx->type == VFY_DSA) {
dsasig.len = DSA_SIGNATURE_LEN;
} else {
dsasig.len = SECKEY_SignatureLen(cx->key);
}
if (dsasig.len == 0) {
return SECFailure;
}
if (sig) {
rv = decodeECorDSASignature(cx->sigAlg,sig,dsasig.data,
dsasig.len);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
return SECFailure;
}
}
hash.data = final;
hash.len = part;
if (PK11_Verify(cx->key,&dsasig,&hash,cx->wincx) != SECSuccess) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
return SECFailure;
}
break;
case VFY_RSA:
if (sig) {
SECOidTag hashid = SEC_OID_UNKNOWN;
rv = DecryptSigBlock(&hashid, cx->u.buffer, &cx->rsadigestlen,
HASH_LENGTH_MAX, cx->key, sig, (char*)cx->wincx);
if ((rv != SECSuccess) || (hashid != cx->alg)) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
return SECFailure;
}
}
if ((part != cx->rsadigestlen) ||
PORT_Memcmp(final, cx->u.buffer, part)) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
return SECFailure;
}
break;
default:
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
return SECFailure;
}
return SECSuccess;
}
SECStatus
VFY_End(VFYContext *cx)
{
return VFY_EndWithSignature(cx,NULL);
}
SECStatus
VFY_VerifyDigest(SECItem *digest, SECKEYPublicKey *key, SECItem *sig,
SECOidTag algid, void *wincx)
{
SECStatus rv;
VFYContext *cx;
SECItem dsasig;
rv = SECFailure;
cx = VFY_CreateContext(key, sig, algid, wincx);
if (cx != NULL) {
switch (key->keyType) {
case rsaKey:
if ((digest->len != cx->rsadigestlen) ||
PORT_Memcmp(digest->data, cx->u.buffer, digest->len)) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
} else {
rv = SECSuccess;
}
break;
case fortezzaKey:
case dsaKey:
case ecKey:
dsasig.data = cx->u.buffer;
if (key->keyType == ecKey) {
dsasig.len = SECKEY_SignatureLen(cx->key);
} else {
dsasig.len = DSA_SIGNATURE_LEN;
}
if (dsasig.len == 0) {
break;
}
if (PK11_Verify(cx->key, &dsasig, digest, cx->wincx)
!= SECSuccess) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
} else {
rv = SECSuccess;
}
break;
default:
break;
}
VFY_DestroyContext(cx, PR_TRUE);
}
return rv;
}
static SECStatus
vfy_VerifyDataPrivate(const unsigned char *buf, int len,
const SECKEYPublicKey *key, const SECItem *sig,
SECOidTag algid, const SECItem *params, void *wincx)
{
SECStatus rv;
VFYContext *cx;
cx = vfy_CreateContextPrivate(key, sig, algid, params, wincx);
if (cx == NULL)
return SECFailure;
rv = VFY_Begin(cx);
if (rv == SECSuccess) {
rv = VFY_Update(cx, (unsigned char *)buf, len);
if (rv == SECSuccess)
rv = VFY_End(cx);
}
VFY_DestroyContext(cx, PR_TRUE);
return rv;
}
SECStatus
VFY_VerifyData(unsigned char *buf, int len, SECKEYPublicKey *key,
SECItem *sig, SECOidTag algid, void *wincx)
{
return vfy_VerifyDataPrivate(buf, len, key, sig, algid, NULL, wincx);
}
SECStatus
VFY_VerifyDataWithAlgorithmID(const unsigned char *buf, int len,
const SECKEYPublicKey *key,
const SECItem *sig,
const SECAlgorithmID *sigAlgorithm,
SECOidTag *reserved, void *wincx)
{
PORT_Assert(reserved == NULL);
if (reserved) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
return vfy_VerifyDataPrivate(buf, len, key, sig,
SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm),
&sigAlgorithm->parameters, wincx);
}