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: }