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