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