netscape-revival
security/lib/crypto/secsign.c
/*
* Signature stuff.
*
* Copyright © 1995 Netscape Communications Corporation, all rights reserved.
*
* $Id: secsign.c,v 1.35.2.6 1997/04/17 10:38:07 jwz Exp $
*/
extern int SEC_ERROR_INVALID_ALGORITHM;
extern int SEC_ERROR_INVALID_KEY;
extern int SEC_ERROR_INVALID_ARGS;
extern int SEC_ERROR_IO;
#include <stdio.h>
#include "crypto.h"
#include "sechash.h"
#include "secder.h"
#include "key.h"
#include "secoid.h"
#include "rsa.h"
#include "pk11func.h"
struct SGNContextStr {
SECOidTag alg;
void *hashcx;
SECHashObject *hashobj;
SECKEYPrivateKey *key;
};
SGNContext *
SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key)
{
SGNContext *cx;
SECOidTag newalg;
KeyType keyType;
/* OK, map a PKCS #7 hash and encrypt algorithm into
* a standard hashing algorithm. Why did we pass in the whole
* PKCS #7 algTag if we were just going to change here you might
* ask. Well the answer is for some cards we may have to do the
* hashing on card. It may not support CKM_RSA_PKCS sign algorithm,
* it may just support CKM_RSA_PKCS_WITH_SHA1 and/or CKM_RSA_PKCS_WITH_MD5.
*/
switch (alg) {
/* We probably shouldn't be generating MD2 signatures either */
case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
newalg = SEC_OID_MD2;
keyType = rsaKey;
break;
case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
newalg = SEC_OID_MD5;
keyType = rsaKey;
break;
case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
newalg = SEC_OID_SHA1;
keyType = rsaKey;
break;
/* what about normal DSA? */
case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
newalg = SEC_OID_SHA1;
keyType = dsaKey;
break;
case SEC_OID_MISSI_DSS:
newalg = SEC_OID_SHA1;
keyType = fortezzaKey;
break;
/* we don't implement MD4 hashes, do you think we're crazy... we
* *CERTAINLY* don't want to sign one! */
case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION:
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return 0;
}
/* verify our key type */
if (key->keyType != keyType) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return 0;
}
cx = (SGNContext*) PORT_ZAlloc(sizeof(SGNContext));
if (cx) {
cx->alg = newalg;
cx->key = key;
}
return cx;
}
void
SGN_DestroyContext(SGNContext *cx, PRBool freeit)
{
if (cx) {
if (cx->hashcx != NULL) {
(*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
cx->hashcx = NULL;
}
if (freeit) {
PORT_ZFree(cx, sizeof(SGNContext));
}
}
}
SECStatus
SGN_Begin(SGNContext *cx)
{
if (cx->hashcx != NULL) {
(*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
cx->hashcx = NULL;
}
switch (cx->alg) {
case SEC_OID_MD2:
cx->hashobj = &SECHashObjects[HASH_AlgMD2];
break;
case SEC_OID_MD5:
cx->hashobj = &SECHashObjects[HASH_AlgMD5];
break;
case SEC_OID_SHA1:
cx->hashobj = &SECHashObjects[HASH_AlgSHA1];
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return SECFailure;
}
cx->hashcx = (*cx->hashobj->create)();
if (cx->hashcx == NULL)
return SECFailure;
(*cx->hashobj->begin)(cx->hashcx);
return SECSuccess;
}
SECStatus
SGN_Update(SGNContext *cx, unsigned char *input, unsigned inputLen)
{
if (cx->hashcx == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
(*cx->hashobj->update)(cx->hashcx, input, inputLen);
return SECSuccess;
}
SECStatus
SGN_End(SGNContext *cx, SECItem *result)
{
unsigned char digest[32];
unsigned part1, modulusLen;
SECStatus rv;
SECItem digder;
PRArenaPool *arena = 0;
SECKEYPrivateKey *privKey = cx->key;
SGNDigestInfo *di = 0;
result->data = 0;
digder.data = 0;
/* Finish up digest function */
if (cx->hashcx == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
(*cx->hashobj->end)(cx->hashcx, digest, &part1, sizeof(digest));
if (privKey->keyType == rsaKey) {
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( !arena ) {
rv = SECFailure;
goto loser;
}
/* Construct digest info */
di = SGN_CreateDigestInfo(cx->alg, digest, part1);
if (!di) {
rv = SECFailure;
goto loser;
}
/* Der encode the digest as a DigestInfo */
rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, di);
if (rv != SECSuccess) {
goto loser;
}
} else {
digder.data = digest;
digder.len = part1;
}
/*
** Encrypt signature after constructing appropriate PKCS#1 signature
** block
*/
modulusLen = PK11_SignatureLen(privKey);
result->len = modulusLen;
result->data = (unsigned char*) PORT_Alloc(modulusLen);
if (result->data == NULL) {
rv = SECFailure;
goto loser;
}
rv = PK11_Sign(privKey, result, &digder);
if (rv != SECSuccess) {
PORT_Free(result->data);
result->data = NULL;
}
loser:
SGN_DestroyDigestInfo(di);
if (arena != NULL) {
PORT_FreeArena(arena, PR_FALSE);
}
return rv;
}
/************************************************************************/
/*
** Sign a block of data returning in result a bunch of bytes that are the
** signature. Returns zero on success, an error code on failure.
*/
SECStatus
SEC_SignData(SECItem *res, unsigned char *buf, int len,
SECKEYPrivateKey *pk, SECOidTag algid)
{
SECStatus rv;
SGNContext *sgn;
sgn = SGN_NewContext(algid,pk);
if (sgn == NULL)
return SECFailure;
rv = SGN_Begin(sgn);
if (rv != SECSuccess)
goto loser;
rv = SGN_Update(sgn, buf, len);
if (rv != SECSuccess)
goto loser;
rv = SGN_End(sgn, res);
loser:
SGN_DestroyContext(sgn, PR_TRUE);
return rv;
}
/*
** Sign the input file's contents returning in result a bunch of bytes
** that are the signature. Returns zero on success, an error code on
** failure.
*/
SECStatus
SEC_SignFile(SECItem *result, FILE *input,
SECKEYPrivateKey *pk, SECOidTag algid)
{
unsigned char buf[1024];
SECStatus rv;
int nb;
SGNContext *sgn;
sgn = SGN_NewContext(algid,pk);
if (sgn == NULL)
return SECFailure;
rv = SGN_Begin(sgn);
if (rv != SECSuccess)
goto loser;
/*
** Now feed the contents of the input file into the digest
** algorithm, one chunk at a time, until we have exhausted the
** input
*/
for (;;) {
if (feof(input)) break;
nb = fread(buf, 1, sizeof(buf), input);
if (nb == 0) {
if (ferror(input)) {
PORT_SetError(SEC_ERROR_IO);
rv = SECFailure;
goto loser;
}
break;
}
rv = SGN_Update(sgn, buf, nb);
if (rv != SECSuccess)
goto loser;
}
/* Sign the digest */
rv = SGN_End(sgn, result);
/* FALL THROUGH */
loser:
SGN_DestroyContext(sgn, PR_TRUE);
return rv;
}
/************************************************************************/
DERTemplate CERTSignedDataTemplate[] =
{
{ DER_SEQUENCE,
0, NULL, sizeof(CERTSignedData) },
{ DER_ANY,
offsetof(CERTSignedData,data), },
{ DER_INLINE,
offsetof(CERTSignedData,signatureAlgorithm),
SECAlgorithmIDTemplate, },
{ DER_BIT_STRING,
offsetof(CERTSignedData,signature), },
{ 0, }
};
const SEC_ASN1Template CERT_SignedDataTemplate[] =
{
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(CERTSignedData) },
{ SEC_ASN1_ANY,
offsetof(CERTSignedData,data), },
{ SEC_ASN1_INLINE,
offsetof(CERTSignedData,signatureAlgorithm),
SECAlgorithmIDTemplate, },
{ SEC_ASN1_BIT_STRING,
offsetof(CERTSignedData,signature), },
{ 0, }
};
SECStatus
SEC_DerSignData(PRArenaPool *arena, SECItem *result,
unsigned char *buf, int len, SECKEYPrivateKey *pk, SECOidTag algID)
{
SECItem it;
CERTSignedData sd;
SECStatus rv;
it.data = 0;
/* XXX We should probably have some asserts here to make sure the key type
* and algID match
*/
if (algID == SEC_OID_UNKNOWN) {
switch(pk->keyType) {
case rsaKey:
algID = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
break;
case dsaKey:
algID = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
break;
default:
return SECFailure;
break;
}
}
/* Sign input buffer */
if (algID == SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST) {
rv = DSAU_DerSignData(&it, buf, len, pk, algID);
if (rv) goto loser;
} else {
rv = SEC_SignData(&it, buf, len, pk, algID);
if (rv) goto loser;
}
/* Fill out SignedData object */
PORT_Memset(&sd, 0, sizeof(sd));
sd.data.data = buf;
sd.data.len = len;
sd.signature.data = it.data;
sd.signature.len = it.len << 3; /* convert to bit string */
rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, 0);
if (rv) goto loser;
/* DER encode the signed data object */
rv = DER_Encode(arena, result, CERTSignedDataTemplate, &sd);
/* FALL THROUGH */
loser:
PORT_Free(it.data);
return rv;
}
SECStatus
SGN_Digest(SECKEYPrivateKey *privKey,
SECOidTag algtag, SECItem *result, SECItem *digest)
{
unsigned modulusLen;
SECStatus rv;
SECItem digder;
PRArenaPool *arena = 0;
SGNDigestInfo *di = 0;
result->data = 0;
if (privKey->keyType == rsaKey) {
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( !arena ) {
rv = SECFailure;
goto loser;
}
/* Construct digest info */
di = SGN_CreateDigestInfo(algtag, digest->data, digest->len);
if (!di) {
rv = SECFailure;
goto loser;
}
/* Der encode the digest as a DigestInfo */
rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, di);
if (rv != SECSuccess) {
goto loser;
}
} else {
digder.data = digest->data;
digder.len = digest->len;
}
/*
** Encrypt signature after constructing appropriate PKCS#1 signature
** block
*/
modulusLen = PK11_SignatureLen(privKey);
result->len = modulusLen;
result->data = (unsigned char*) PORT_Alloc(modulusLen);
if (result->data == NULL) {
rv = SECFailure;
goto loser;
}
rv = PK11_Sign(privKey, result, &digder);
if (rv != SECSuccess) {
PORT_Free(result->data);
result->data = NULL;
}
loser:
SGN_DestroyDigestInfo(di);
if (arena != NULL) {
PORT_FreeArena(arena, PR_FALSE);
}
return rv;
}