summaryrefslogtreecommitdiff
path: root/security/nss/lib/cryptohi/secsign.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/cryptohi/secsign.c')
-rw-r--r--security/nss/lib/cryptohi/secsign.c500
1 files changed, 500 insertions, 0 deletions
diff --git a/security/nss/lib/cryptohi/secsign.c b/security/nss/lib/cryptohi/secsign.c
new file mode 100644
index 000000000..98131c6eb
--- /dev/null
+++ b/security/nss/lib/cryptohi/secsign.c
@@ -0,0 +1,500 @@
+/*
+ * Signature stuff.
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include "cryptohi.h"
+#include "sechash.h"
+#include "secder.h"
+#include "keyhi.h"
+#include "secoid.h"
+#include "secdig.h"
+#include "pk11func.h"
+#include "secerr.h"
+
+struct SGNContextStr {
+ SECOidTag signalg;
+ SECOidTag hashalg;
+ void *hashcx;
+ const SECHashObject *hashobj;
+ SECKEYPrivateKey *key;
+};
+
+SGNContext *
+SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key)
+{
+ SGNContext *cx;
+ SECOidTag hashalg, signalg;
+ 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:
+ hashalg = SEC_OID_MD2;
+ signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
+ keyType = rsaKey;
+ break;
+ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
+ hashalg = SEC_OID_MD5;
+ signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
+ keyType = rsaKey;
+ break;
+ case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
+ hashalg = SEC_OID_SHA1;
+ signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
+ keyType = rsaKey;
+ break;
+ /* what about normal DSA? */
+ case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
+ case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
+ hashalg = SEC_OID_SHA1;
+ signalg = SEC_OID_ANSIX9_DSA_SIGNATURE;
+ keyType = dsaKey;
+ 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;
+ signalg = SEC_OID_MISSI_DSS; /* XXX Is there a better algid? */
+ keyType = fortezzaKey;
+ break;
+ /* we don't implement MD4 hashes.
+ * 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 &&
+ !((key->keyType == dsaKey) && (keyType == fortezzaKey)) &&
+ !((key->keyType == fortezzaKey) && (keyType == dsaKey)) ) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return 0;
+ }
+
+ cx = (SGNContext*) PORT_ZAlloc(sizeof(SGNContext));
+ if (cx) {
+ cx->hashalg = hashalg;
+ cx->signalg = signalg;
+ 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->hashalg) {
+ 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, signatureLen;
+ SECStatus rv;
+ SECItem digder, sigitem;
+ 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->hashalg, 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
+ */
+ signatureLen = PK11_SignatureLen(privKey);
+ sigitem.len = signatureLen;
+ sigitem.data = (unsigned char*) PORT_Alloc(signatureLen);
+
+ if (sigitem.data == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = PK11_Sign(privKey, &sigitem, &digder);
+ if (rv != SECSuccess) {
+ PORT_Free(sigitem.data);
+ sigitem.data = NULL;
+ }
+
+ if (cx->signalg == SEC_OID_ANSIX9_DSA_SIGNATURE) {
+ rv = DSAU_EncodeDerSig(result, &sigitem);
+ PORT_Free(sigitem.data);
+ if (rv != SECSuccess)
+ goto loser;
+ } else {
+ result->len = sigitem.len;
+ result->data = sigitem.data;
+ }
+
+ 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),
+ SECOID_AlgorithmIDTemplate, },
+ { SEC_ASN1_BIT_STRING,
+ offsetof(CERTSignedData,signature), },
+ { 0, }
+};
+
+SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SignedDataTemplate)
+
+
+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 */
+ 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;
+}