/* ***** 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):
 *
 * 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: secdig.c,v 1.5 2004/04/27 23:04:40 gerv%gerv.net Exp $ */
#include "secdig.h"

#include "secoid.h"
#include "secasn1.h" 
#include "secerr.h"

/*
 * XXX OLD Template.  Once all uses have been switched over to new one,
 * remove this.
 */
DERTemplate SGNDigestInfoTemplate[] = {
    { DER_SEQUENCE,
	  0, NULL, sizeof(SGNDigestInfo) },
    { DER_INLINE,
	  offsetof(SGNDigestInfo,digestAlgorithm),
	  SECAlgorithmIDTemplate, },
    { DER_OCTET_STRING,
	  offsetof(SGNDigestInfo,digest), },
    { 0, }
};

/* XXX See comment below about SGN_DecodeDigestInfo -- keep this static! */
/* XXX Changed from static -- need to change name? */
const SEC_ASN1Template sgn_DigestInfoTemplate[] = {
    { SEC_ASN1_SEQUENCE,
	  0, NULL, sizeof(SGNDigestInfo) },
    { SEC_ASN1_INLINE,
	  offsetof(SGNDigestInfo,digestAlgorithm),
	  SECOID_AlgorithmIDTemplate },
    { SEC_ASN1_OCTET_STRING,
	  offsetof(SGNDigestInfo,digest) },
    { 0 }
};

SEC_ASN1_CHOOSER_IMPLEMENT(sgn_DigestInfoTemplate)

/*
 * XXX Want to have a SGN_DecodeDigestInfo, like:
 *	SGNDigestInfo *SGN_DecodeDigestInfo(SECItem *didata);
 * that creates a pool and allocates from it and decodes didata into
 * the newly allocated DigestInfo structure.  Then fix secvfy.c (it
 * will no longer need an arena itself) to call this and then call
 * DestroyDigestInfo when it is done, then can remove the old template
 * above and keep our new template static and "hidden".
 */

/*
 * XXX It might be nice to combine the following two functions (create
 * and encode).  I think that is all anybody ever wants to do anyway.
 */

SECItem *
SGN_EncodeDigestInfo(PRArenaPool *poolp, SECItem *dest, SGNDigestInfo *diginfo)
{
    return SEC_ASN1EncodeItem (poolp, dest, diginfo, sgn_DigestInfoTemplate);
}

SGNDigestInfo *
SGN_CreateDigestInfo(SECOidTag algorithm, unsigned char *sig, unsigned len)
{
    SGNDigestInfo *di;
    SECStatus rv;
    PRArenaPool *arena;
    SECItem *null_param;
    SECItem dummy_value;

    switch (algorithm) {
      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);
	return NULL;
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
	return NULL;
    }

    di = (SGNDigestInfo *) PORT_ArenaZAlloc(arena, sizeof(SGNDigestInfo));
    if (di == NULL) {
	PORT_FreeArena(arena, PR_FALSE);
	return NULL;
    }

    di->arena = arena;

    /*
     * PKCS #1 specifies that the AlgorithmID must have a NULL parameter
     * (as opposed to no parameter at all).
     */
    dummy_value.data = NULL;
    dummy_value.len = 0;
    null_param = SEC_ASN1EncodeItem(NULL, NULL, &dummy_value, SEC_NullTemplate);
    if (null_param == NULL) {
	goto loser;
    }

    rv = SECOID_SetAlgorithmID(arena, &di->digestAlgorithm, algorithm,
			       null_param);

    SECITEM_FreeItem(null_param, PR_TRUE);

    if (rv != SECSuccess) {
	goto loser;
    }

    di->digest.data = (unsigned char *) PORT_ArenaAlloc(arena, len);
    if (di->digest.data == NULL) {
	goto loser;
    }

    di->digest.len = len;
    PORT_Memcpy(di->digest.data, sig, len);
    return di;

  loser:
    SGN_DestroyDigestInfo(di);
    return NULL;
}

SGNDigestInfo *
SGN_DecodeDigestInfo(SECItem *didata)
{
    PRArenaPool *arena;
    SGNDigestInfo *di;
    SECStatus rv = SECFailure;

    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
    if(arena == NULL)
	return NULL;

    di = (SGNDigestInfo *)PORT_ArenaZAlloc(arena, sizeof(SGNDigestInfo));
    if(di != NULL)
    {
	di->arena = arena;
	rv = SEC_ASN1DecodeItem(arena, di, sgn_DigestInfoTemplate, didata);
    }
	
    if((di == NULL) || (rv != SECSuccess))
    {
	PORT_FreeArena(arena, PR_TRUE);
	di = NULL;
    }

    return di;
}

void
SGN_DestroyDigestInfo(SGNDigestInfo *di)
{
    if (di && di->arena) {
	PORT_FreeArena(di->arena, PR_FALSE);
    }

    return;
}

SECStatus 
SGN_CopyDigestInfo(PRArenaPool *poolp, SGNDigestInfo *a, SGNDigestInfo *b)
{
    SECStatus rv;
    void *mark;

    if((poolp == NULL) || (a == NULL) || (b == NULL))
	return SECFailure;

    mark = PORT_ArenaMark(poolp);
    a->arena = poolp;
    rv = SECOID_CopyAlgorithmID(poolp, &a->digestAlgorithm, 
	&b->digestAlgorithm);
    if (rv == SECSuccess)
	rv = SECITEM_CopyItem(poolp, &a->digest, &b->digest);

    if (rv != SECSuccess) {
	PORT_ArenaRelease(poolp, mark);
    } else {
	PORT_ArenaUnmark(poolp, mark);
    }

    return rv;
}

SECComparison
SGN_CompareDigestInfo(SGNDigestInfo *a, SGNDigestInfo *b)
{
    SECComparison rv;

    /* Check signature algorithm's */
    rv = SECOID_CompareAlgorithmID(&a->digestAlgorithm, &b->digestAlgorithm);
    if (rv) return rv;

    /* Compare signature block length's */
    rv = SECITEM_CompareItem(&a->digest, &b->digest);
    return rv;
}
