/*
 * Signature stuff.
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * 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 the Initial Developer are Copyright (C) 1994-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
/* $Id: secsign.c,v 1.14.2.3 2006/04/28 03:35:29 rrelyea%redhat.com Exp $ */

#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;
  /* wshao: Add for randomized hash */
  RANDHashParams *rand_params;
};

/* SGNContext with Randomized hash support.
 * This new structure uses the HASH_ APIs instead of the low level hash objects.
 */
struct SGNContextExStr {
  SECOidTag signalg;

  HASHContext* hash_ctx_;
  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;

      case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
        hashalg = SEC_OID_SHA256;
        signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
        keyType = rsaKey;
        break;
      case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
        hashalg = SEC_OID_SHA384;
        signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
        keyType = rsaKey;
        break;
      case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
        hashalg = SEC_OID_SHA512;
        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;
      case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
        hashalg = SEC_OID_SHA1;
        signalg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
        keyType = ecKey;
        break;
      case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
        hashalg = SEC_OID_SHA256;
        signalg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
        keyType = ecKey;
        break;
      case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
        hashalg = SEC_OID_SHA384;
        signalg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
        keyType = ecKey;
        break;
      case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
        hashalg = SEC_OID_SHA512;
        signalg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
        keyType = ecKey;
        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_ALGORITHM);
        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;
    }

#ifndef NSS_ECC_MORE_THAN_SUITE_B
    if (key->keyType == ecKey) {
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
        return 0;
    }
#endif

    cx = (SGNContext*) PORT_ZAlloc(sizeof(SGNContext));
    if (cx) {
        cx->hashalg = hashalg;
        cx->signalg = signalg;
        cx->key = key;
    }
    return cx;
}

/*
 * For new structure. Copy&paste the above method with minor modifications.
 * Use HASHContext instead of direct use of low-level hash objects.
 * TODO: need to add support for algs other than RAND_MD5 and RAND_SHA1
 */
SGNContextEx*
SGN_NewContextEx(SECOidTag alg, SECKEYPrivateKey *key,
                 SECItem *params)
{
    SGNContextEx *cx;
    SECOidTag signalg;
    KeyType keyType;

    HASHContext* hash_ctx;
    HASH_HashType hash_type = HASH_AlgNULL;

    /* 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:
        hash_type = HASH_AlgMD2;
        signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
        keyType = rsaKey;
        break;
      case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
      case SEC_OID_PKCS1_RAND_MD5_WITH_RSA_ENCRYPTION:
        hash_type = HASH_AlgMD5;
        signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
        keyType = rsaKey;
      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:
        hash_type = HASH_AlgSHA1;
        signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
        keyType = rsaKey;
        break;
      case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
        hash_type = HASH_AlgSHA256;
        signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
        keyType = rsaKey;
        break;
      case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
        hash_type = HASH_AlgSHA384;
        signalg = SEC_OID_PKCS1_RSA_ENCRYPTION;
        keyType = rsaKey;
        break;
      case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
        hash_type = HASH_AlgSHA512;
        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:
        hash_type = HASH_AlgSHA1;
        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:
        hash_type = HASH_AlgSHA1;
        signalg = SEC_OID_MISSI_DSS; /* XXX Is there a better algid? */
        keyType = fortezzaKey;
        break;
      case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
        hash_type = HASH_AlgSHA1;
        signalg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
        keyType = ecKey;
        break;
      case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
        hash_type = HASH_AlgSHA256;
        signalg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
        keyType = ecKey;
        break;
      case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
        hash_type = HASH_AlgSHA384;
        signalg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
        keyType = ecKey;
        break;
      case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
        hash_type = HASH_AlgSHA512;
        signalg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
        keyType = ecKey;
        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_ALGORITHM);
        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;
    }

#ifndef NSS_ECC_MORE_THAN_SUITE_B
    if (key->keyType == ecKey) {
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
        return 0;
    }
#endif

    hash_ctx = HASH_CreateEx(hash_type, params);
    if (hash_ctx == NULL) {
      return NULL;
    }
    cx = (SGNContextEx*) PORT_ZAlloc(sizeof(SGNContextEx));
    if (cx) {
      cx->signalg = signalg;
      cx->hash_ctx_ = hash_ctx;
      cx->key = key;
    } else {
      HASH_Destroy(hash_ctx);
    }
    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;
    }

    cx->hashobj = HASH_GetHashObjectByOidTag(cx->hashalg);
    if (!cx->hashobj)
        return SECFailure;      /* error code is already set */

    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[HASH_LENGTH_MAX];
    unsigned part1;
    int 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);
    if (signatureLen <= 0) {
        PORT_SetError(SEC_ERROR_INVALID_KEY);
        rv = SECFailure;
        goto loser;
    }
    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;
        goto loser;
    }

    if ((cx->signalg == SEC_OID_ANSIX9_DSA_SIGNATURE) ||
        (cx->signalg == SEC_OID_ANSIX962_EC_PUBLIC_KEY)) {
        /* DSAU_EncodeDerSigWithLen works for DSA and ECDSA */
        rv = DSAU_EncodeDerSigWithLen(result, &sigitem, sigitem.len); 
        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;
}

SECStatus
SGN_BeginEx(SGNContextEx *cx) {
  HASH_BeginEx(cx->hash_ctx_);
  return SECSuccess;
}

SECStatus
SGN_UpdateEx(SGNContextEx *cx, unsigned char *input, unsigned inputLen)
{
    if (cx->hash_ctx_ == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    HASH_UpdateEx(cx->hash_ctx_, input, inputLen);
    return SECSuccess;
}

SECStatus
SGN_EndEx(SGNContextEx *cx, SECItem *result)
{
    unsigned char digest[HASH_LENGTH_MAX];
    unsigned part1;
    int signatureLen;
    SECStatus rv;
    SECOidTag hashalg;
    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->hash_ctx_ == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    HASH_EndEx(cx->hash_ctx_, digest, &part1, sizeof(digest));

    if (privKey->keyType == rsaKey) {

        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
        if ( !arena ) {
            rv = SECFailure;
            goto loser;
        }

        switch (cx->hash_ctx_->hashobj->type) {
          case HASH_AlgMD2:
            hashalg = SEC_OID_MD2;
            break;
          case HASH_AlgMD5:
            hashalg = SEC_OID_MD5;
            break;
          case HASH_AlgSHA1:
            hashalg = SEC_OID_SHA1;
            break;
          case HASH_AlgSHA256:
            hashalg = SEC_OID_SHA256;
            break;
          case HASH_AlgSHA384:
            hashalg = SEC_OID_SHA384;
            break;
          case HASH_AlgSHA512:
            hashalg = SEC_OID_SHA512;
            break;
          default:
            hashalg = SEC_OID_UNKNOWN;
        }

        /* Construct digest info */
        di = SGN_CreateDigestInfo(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);
    if (signatureLen <= 0) {
        PORT_SetError(SEC_ERROR_INVALID_KEY);
        rv = SECFailure;
        goto loser;
    }
    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;
        goto loser;
    }

    if ((cx->signalg == SEC_OID_ANSIX9_DSA_SIGNATURE) ||
        (cx->signalg == SEC_OID_ANSIX962_EC_PUBLIC_KEY)) {
        /* DSAU_EncodeDerSigWithLen works for DSA and ECDSA */
        rv = DSAU_EncodeDerSigWithLen(result, &sigitem, sigitem.len); 
        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;
}


/*
 * This only supports randomized PKCS#1 MD5 and SHA1 with RSA
 * Encryption
 */
SECStatus
SEC_SignDataEx(SECItem *res, unsigned char *buf, int len,
               SECKEYPrivateKey *pk, SECOidTag algid,
               SECItem *randParams)
{
  if (randParams != NULL && randParams->type == siSaltValue &&
      (algid == SEC_OID_PKCS1_RAND_MD5_WITH_RSA_ENCRYPTION ||
       algid == SEC_OID_PKCS1_RAND_SHA1_WITH_RSA_ENCRYPTION)) {

    SECStatus rv;
    SGNContextEx *sgn;
    sgn = SGN_NewContextEx(algid, pk, randParams);

    if (sgn == NULL)
        return SECFailure;

    rv = SGN_BeginEx(sgn);
    if (rv != SECSuccess)
        goto loser;

    rv = SGN_UpdateEx(sgn, buf, len);
    if (rv != SECSuccess)
        goto loser;

    rv = SGN_EndEx(sgn, res);

 loser:
    return rv;

  } else {
    return SEC_SignData(res, buf, len, pk, algid);
  }
}

/************************************************************************/
    
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;
          case ecKey:
            algID = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE;
            break;
          default:
            PORT_SetError(SEC_ERROR_INVALID_KEY);
            return SECFailure;
        }
    }

    /* 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
SEC_DerSignDataEx(PRArenaPool *arena, SECItem *result,
                  unsigned char *buf, int len, SECKEYPrivateKey *pk,
                  SECOidTag algID, SECItem *randParams)
{
    SECItem it, *params;
    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;
          case ecKey:
            algID = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE;
            break;
          default:
            PORT_SetError(SEC_ERROR_INVALID_KEY);
            return SECFailure;
        }
        /* In this case, we ignore randParams */
        /* Sign input buffer */
        randParams = NULL;
        rv = SEC_SignData(&it, buf, len, pk, algID);
    } else {
        rv = SEC_SignDataEx(&it, buf, len, pk, algID, randParams);
    }

    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 */

    /* randParams should only be passed when algID is a randomized algorithm */
    if (randParams != NULL && randParams->type == siSaltValue) {
      /* Need to rewrite params */
      printf("OKAY ==== set alg params \n");
      params = (SECItem*) PORT_Alloc(sizeof(SECItem));
      params->type = siSaltValue;
      params->data = (unsigned char*) PORT_Alloc(randParams->len + 2);
      params->data[0] = 0x04;
      params->data[1] = randParams->len;
      params->len = randParams->len + 2;
      memcpy(&params->data[2], randParams->data, randParams->len);
      rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, params);
      PORT_Free(params->data);
      PORT_Free(params);
    } else {
      rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, 0);
      printf("Oh well, we are not setting  alg params \n");
    }

    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)
{
    int 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);
    if (modulusLen <= 0) {
        PORT_SetError(SEC_ERROR_INVALID_KEY);
        rv = SECFailure;
        goto loser;
    }
    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;
}

SECOidTag
SEC_GetSignatureAlgorithmOidTag(KeyType keyType, SECOidTag hashAlgTag)
{
    SECOidTag sigTag = SEC_OID_UNKNOWN;

    switch (keyType) {
    case rsaKey:
        switch (hashAlgTag) {
        case SEC_OID_MD2:
            sigTag = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;     break;
        case SEC_OID_UNKNOWN:   /* default for RSA if not specified */
        case SEC_OID_MD5:
            sigTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;     break;
        case SEC_OID_SHA1:
            sigTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;    break;
        case SEC_OID_SHA256:
            sigTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;  break;
        case SEC_OID_SHA384:
            sigTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;  break;
        case SEC_OID_SHA512:
            sigTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;  break;
        default:
            break;
        }
        break;
    case dsaKey:
        switch (hashAlgTag) {
        case SEC_OID_UNKNOWN:   /* default for DSA if not specified */
        case SEC_OID_SHA1:
            sigTag = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST; break;
        default:
            break;
        }
        break;
    case ecKey:
        switch (hashAlgTag) {
        case SEC_OID_UNKNOWN:   /* default for ECDSA if hash not specified */
        case SEC_OID_SHA1:      /*  is ECDSA_SHA1_SIGNTARURE */
            sigTag = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE; break;
        case SEC_OID_SHA256:
            sigTag = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; break;
        case SEC_OID_SHA384:
            sigTag = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE; break;
        case SEC_OID_SHA512:
            sigTag = SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE; break;
        default:
            break;
        }
        break;
    default:
        break;
    }
    return sigTag;
}