andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public
andre@0:  * License, v. 2.0. If a copy of the MPL was not distributed with this
andre@0:  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
andre@0: #include "cryptohi.h"
andre@0: #include "secasn1.h"
andre@0: #include "secitem.h"
andre@0: #include "prerr.h"
andre@0: 
andre@0: #ifndef DSA1_SUBPRIME_LEN
andre@0: #define DSA1_SUBPRIME_LEN 20	/* bytes */
andre@0: #endif
andre@0: 
andre@0: typedef struct {
andre@0:     SECItem r;
andre@0:     SECItem s;
andre@0: } DSA_ASN1Signature;
andre@0: 
andre@0: const SEC_ASN1Template DSA_SignatureTemplate[] =
andre@0: {
andre@0:     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(DSA_ASN1Signature) },
andre@0:     { SEC_ASN1_INTEGER, offsetof(DSA_ASN1Signature,r) },
andre@0:     { SEC_ASN1_INTEGER, offsetof(DSA_ASN1Signature,s) },
andre@0:     { 0, }
andre@0: };
andre@0: 
andre@0: /* Input is variable length multi-byte integer, MSB first (big endian).
andre@0: ** Most signficant bit of first byte is NOT treated as a sign bit. 
andre@0: ** May be one or more leading bytes of zeros. 
andre@0: ** Output is variable length multi-byte integer, MSB first (big endian).
andre@0: ** Most significant bit of first byte will be zero (positive sign bit)
andre@0: ** No more than one leading zero byte.
andre@0: ** Caller supplies dest buffer, and assures that it is long enough,
andre@0: ** e.g. at least one byte longer that src's buffer.
andre@0: */
andre@0: void
andre@0: DSAU_ConvertUnsignedToSigned(SECItem *dest, SECItem *src)
andre@0: {
andre@0:     unsigned char *pSrc = src->data;
andre@0:     unsigned char *pDst = dest->data;
andre@0:     unsigned int   cntSrc = src->len;
andre@0: 
andre@0:     /* skip any leading zeros. */
andre@0:     while (cntSrc && !(*pSrc)) { 
andre@0:     	pSrc++; 
andre@0: 	cntSrc--;
andre@0:     }
andre@0:     if (!cntSrc) {
andre@0:     	*pDst = 0; 
andre@0: 	dest->len = 1; 
andre@0: 	return; 
andre@0:     }
andre@0: 
andre@0:     if (*pSrc & 0x80)
andre@0:     	*pDst++ = 0;
andre@0: 
andre@0:     PORT_Memcpy(pDst, pSrc, cntSrc);
andre@0:     dest->len = (pDst - dest->data) + cntSrc;
andre@0: }
andre@0: 
andre@0: /*
andre@0: ** src is a buffer holding a signed variable length integer.
andre@0: ** dest is a buffer which will be filled with an unsigned integer,
andre@0: ** MSB first (big endian) with leading zeros, so that the last byte
andre@0: ** of src will be the LSB of the integer.  The result will be exactly
andre@0: ** the length specified by the caller in dest->len.
andre@0: ** src can be shorter than dest.  src can be longer than dst, but only
andre@0: ** if the extra leading bytes are zeros.
andre@0: */
andre@0: SECStatus
andre@0: DSAU_ConvertSignedToFixedUnsigned(SECItem *dest, SECItem *src)
andre@0: {
andre@0:     unsigned char *pSrc = src->data;
andre@0:     unsigned char *pDst = dest->data;
andre@0:     unsigned int   cntSrc = src->len;
andre@0:     unsigned int   cntDst = dest->len;
andre@0:     int            zCount = cntDst - cntSrc;
andre@0: 
andre@0:     if (zCount > 0) {
andre@0:     	PORT_Memset(pDst, 0, zCount);
andre@0: 	PORT_Memcpy(pDst + zCount, pSrc, cntSrc);
andre@0: 	return SECSuccess;
andre@0:     }
andre@0:     if (zCount <= 0) {
andre@0: 	/* Source is longer than destination.  Check for leading zeros. */
andre@0: 	while (zCount++ < 0) {
andre@0: 	    if (*pSrc++ != 0)
andre@0: 		goto loser;
andre@0: 	}
andre@0:     }
andre@0:     PORT_Memcpy(pDst, pSrc, cntDst);
andre@0:     return SECSuccess;
andre@0: 
andre@0: loser:
andre@0:     PORT_SetError( PR_INVALID_ARGUMENT_ERROR );
andre@0:     return SECFailure;
andre@0: }
andre@0: 
andre@0: /* src is a "raw" ECDSA or DSA signature, the first half contains r
andre@0:  * and the second half contains s. dest is the DER encoded signature.
andre@0: */
andre@0: static SECStatus
andre@0: common_EncodeDerSig(SECItem *dest, SECItem *src)
andre@0: {
andre@0:     SECItem *         item;
andre@0:     SECItem           srcItem;
andre@0:     DSA_ASN1Signature sig;
andre@0:     unsigned char     *signedR;
andre@0:     unsigned char     *signedS;
andre@0:     unsigned int len;
andre@0: 
andre@0:     /* Allocate memory with room for an extra byte that
andre@0:      * may be required if the top bit in the first byte
andre@0:      * is already set.
andre@0:      */
andre@0:     len = src->len/2;
andre@0:     signedR = (unsigned char *) PORT_Alloc(len + 1);
andre@0:     if (!signedR) return SECFailure;
andre@0:     signedS = (unsigned char *) PORT_ZAlloc(len + 1);
andre@0:     if (!signedS) {
andre@0:         if (signedR) PORT_Free(signedR);
andre@0: 	return SECFailure;
andre@0:     }
andre@0: 
andre@0:     PORT_Memset(&sig, 0, sizeof(sig));
andre@0: 
andre@0:     /* Must convert r and s from "unsigned" integers to "signed" integers.
andre@0:     ** If the high order bit of the first byte (MSB) is 1, then must
andre@0:     ** prepend with leading zero.  
andre@0:     ** Must remove all but one leading zero byte from numbers.
andre@0:     */
andre@0:     sig.r.type = siUnsignedInteger;
andre@0:     sig.r.data = signedR;
andre@0:     sig.r.len  = sizeof signedR;
andre@0:     sig.s.type = siUnsignedInteger;
andre@0:     sig.s.data = signedS;
andre@0:     sig.s.len  = sizeof signedR;
andre@0: 
andre@0:     srcItem.data = src->data;
andre@0:     srcItem.len  = len;
andre@0: 
andre@0:     DSAU_ConvertUnsignedToSigned(&sig.r, &srcItem);
andre@0:     srcItem.data += len;
andre@0:     DSAU_ConvertUnsignedToSigned(&sig.s, &srcItem);
andre@0: 
andre@0:     item = SEC_ASN1EncodeItem(NULL, dest, &sig, DSA_SignatureTemplate);
andre@0:     if (signedR) PORT_Free(signedR);
andre@0:     if (signedS) PORT_Free(signedS);
andre@0:     if (item == NULL)
andre@0: 	return SECFailure;
andre@0: 
andre@0:     /* XXX leak item? */
andre@0:     return SECSuccess;
andre@0: }
andre@0: 
andre@0: /* src is a DER-encoded ECDSA or DSA signature.
andre@0: ** Returns a newly-allocated SECItem structure, pointing at a newly allocated
andre@0: ** buffer containing the "raw" signature, which is len bytes of r,
andre@0: ** followed by len bytes of s. For DSA, len is the length of q.
andre@0: ** For ECDSA, len depends on the key size used to create the signature.
andre@0: */
andre@0: static SECItem *
andre@0: common_DecodeDerSig(const SECItem *item, unsigned int len)
andre@0: {
andre@0:     SECItem *         result = NULL;
andre@0:     SECStatus         status;
andre@0:     DSA_ASN1Signature sig;
andre@0:     SECItem           dst;
andre@0: 
andre@0:     PORT_Memset(&sig, 0, sizeof(sig));
andre@0: 
andre@0:     result = PORT_ZNew(SECItem);
andre@0:     if (result == NULL)
andre@0: 	goto loser;
andre@0: 
andre@0:     result->len  = 2 * len;
andre@0:     result->data = (unsigned char*)PORT_Alloc(2 * len);
andre@0:     if (result->data == NULL)
andre@0: 	goto loser;
andre@0: 
andre@0:     sig.r.type = siUnsignedInteger;
andre@0:     sig.s.type = siUnsignedInteger;
andre@0:     status = SEC_ASN1DecodeItem(NULL, &sig, DSA_SignatureTemplate, item);
andre@0:     if (status != SECSuccess)
andre@0: 	goto loser;
andre@0: 
andre@0:     /* Convert sig.r and sig.s from variable  length signed integers to 
andre@0:     ** fixed length unsigned integers.
andre@0:     */
andre@0:     dst.data = result->data;
andre@0:     dst.len  = len;
andre@0:     status = DSAU_ConvertSignedToFixedUnsigned(&dst, &sig.r);
andre@0:     if (status != SECSuccess)
andre@0:     	goto loser;
andre@0: 
andre@0:     dst.data += len;
andre@0:     status = DSAU_ConvertSignedToFixedUnsigned(&dst, &sig.s);
andre@0:     if (status != SECSuccess)
andre@0:     	goto loser;
andre@0: 
andre@0: done:
andre@0:     if (sig.r.data != NULL)
andre@0: 	PORT_Free(sig.r.data);
andre@0:     if (sig.s.data != NULL)
andre@0: 	PORT_Free(sig.s.data);
andre@0: 
andre@0:     return result;
andre@0: 
andre@0: loser:
andre@0:     if (result != NULL) {
andre@0: 	SECITEM_FreeItem(result, PR_TRUE);
andre@0: 	result = NULL;
andre@0:     }
andre@0:     goto done;
andre@0: }
andre@0: 
andre@0: /* src is a "raw" DSA1 signature, 20 bytes of r followed by 20 bytes of s.
andre@0: ** dest is the signature DER encoded. ?
andre@0: */
andre@0: SECStatus
andre@0: DSAU_EncodeDerSig(SECItem *dest, SECItem *src)
andre@0: {
andre@0:     PORT_Assert(src->len == 2 * DSA1_SUBPRIME_LEN);
andre@0:     if (src->len != 2 * DSA1_SUBPRIME_LEN) {
andre@0:     	PORT_SetError( PR_INVALID_ARGUMENT_ERROR );
andre@0: 	return SECFailure;
andre@0:     }
andre@0: 
andre@0:     return common_EncodeDerSig(dest, src);
andre@0: }
andre@0: 
andre@0: /* src is a "raw" DSA signature of length len (len/2 bytes of r followed
andre@0: ** by len/2 bytes of s). dest is the signature DER encoded.
andre@0: */
andre@0: SECStatus
andre@0: DSAU_EncodeDerSigWithLen(SECItem *dest, SECItem *src, unsigned int len)
andre@0: {
andre@0: 
andre@0:     PORT_Assert((src->len == len) && (len % 2 == 0));
andre@0:     if ((src->len != len) || (src->len % 2 != 0)) {
andre@0:     	PORT_SetError( PR_INVALID_ARGUMENT_ERROR );
andre@0: 	return SECFailure;
andre@0:     }
andre@0: 
andre@0:     return common_EncodeDerSig(dest, src);
andre@0: }
andre@0: 
andre@0: /* src is a DER-encoded DSA signature.
andre@0: ** Returns a newly-allocated SECItem structure, pointing at a newly allocated
andre@0: ** buffer containing the "raw" DSA1 signature, which is 20 bytes of r,
andre@0: ** followed by 20 bytes of s.
andre@0: */
andre@0: SECItem *
andre@0: DSAU_DecodeDerSig(const SECItem *item)
andre@0: {
andre@0:     return common_DecodeDerSig(item, DSA1_SUBPRIME_LEN);
andre@0: }
andre@0: 
andre@0: /* src is a DER-encoded ECDSA signature.
andre@0: ** Returns a newly-allocated SECItem structure, pointing at a newly allocated
andre@0: ** buffer containing the "raw" ECDSA signature of length len containing
andre@0: ** r followed by s (both padded to take up exactly len/2 bytes).
andre@0: */
andre@0: SECItem *
andre@0: DSAU_DecodeDerSigToLen(const SECItem *item, unsigned int len)
andre@0: {
andre@0:     return common_DecodeDerSig(item, len/2);
andre@0: }