Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | #include "cryptohi.h" |
michael@0 | 5 | #include "secasn1.h" |
michael@0 | 6 | #include "secitem.h" |
michael@0 | 7 | #include "prerr.h" |
michael@0 | 8 | |
michael@0 | 9 | #ifndef DSA1_SUBPRIME_LEN |
michael@0 | 10 | #define DSA1_SUBPRIME_LEN 20 /* bytes */ |
michael@0 | 11 | #endif |
michael@0 | 12 | |
michael@0 | 13 | typedef struct { |
michael@0 | 14 | SECItem r; |
michael@0 | 15 | SECItem s; |
michael@0 | 16 | } DSA_ASN1Signature; |
michael@0 | 17 | |
michael@0 | 18 | const SEC_ASN1Template DSA_SignatureTemplate[] = |
michael@0 | 19 | { |
michael@0 | 20 | { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(DSA_ASN1Signature) }, |
michael@0 | 21 | { SEC_ASN1_INTEGER, offsetof(DSA_ASN1Signature,r) }, |
michael@0 | 22 | { SEC_ASN1_INTEGER, offsetof(DSA_ASN1Signature,s) }, |
michael@0 | 23 | { 0, } |
michael@0 | 24 | }; |
michael@0 | 25 | |
michael@0 | 26 | /* Input is variable length multi-byte integer, MSB first (big endian). |
michael@0 | 27 | ** Most signficant bit of first byte is NOT treated as a sign bit. |
michael@0 | 28 | ** May be one or more leading bytes of zeros. |
michael@0 | 29 | ** Output is variable length multi-byte integer, MSB first (big endian). |
michael@0 | 30 | ** Most significant bit of first byte will be zero (positive sign bit) |
michael@0 | 31 | ** No more than one leading zero byte. |
michael@0 | 32 | ** Caller supplies dest buffer, and assures that it is long enough, |
michael@0 | 33 | ** e.g. at least one byte longer that src's buffer. |
michael@0 | 34 | */ |
michael@0 | 35 | void |
michael@0 | 36 | DSAU_ConvertUnsignedToSigned(SECItem *dest, SECItem *src) |
michael@0 | 37 | { |
michael@0 | 38 | unsigned char *pSrc = src->data; |
michael@0 | 39 | unsigned char *pDst = dest->data; |
michael@0 | 40 | unsigned int cntSrc = src->len; |
michael@0 | 41 | |
michael@0 | 42 | /* skip any leading zeros. */ |
michael@0 | 43 | while (cntSrc && !(*pSrc)) { |
michael@0 | 44 | pSrc++; |
michael@0 | 45 | cntSrc--; |
michael@0 | 46 | } |
michael@0 | 47 | if (!cntSrc) { |
michael@0 | 48 | *pDst = 0; |
michael@0 | 49 | dest->len = 1; |
michael@0 | 50 | return; |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | if (*pSrc & 0x80) |
michael@0 | 54 | *pDst++ = 0; |
michael@0 | 55 | |
michael@0 | 56 | PORT_Memcpy(pDst, pSrc, cntSrc); |
michael@0 | 57 | dest->len = (pDst - dest->data) + cntSrc; |
michael@0 | 58 | } |
michael@0 | 59 | |
michael@0 | 60 | /* |
michael@0 | 61 | ** src is a buffer holding a signed variable length integer. |
michael@0 | 62 | ** dest is a buffer which will be filled with an unsigned integer, |
michael@0 | 63 | ** MSB first (big endian) with leading zeros, so that the last byte |
michael@0 | 64 | ** of src will be the LSB of the integer. The result will be exactly |
michael@0 | 65 | ** the length specified by the caller in dest->len. |
michael@0 | 66 | ** src can be shorter than dest. src can be longer than dst, but only |
michael@0 | 67 | ** if the extra leading bytes are zeros. |
michael@0 | 68 | */ |
michael@0 | 69 | SECStatus |
michael@0 | 70 | DSAU_ConvertSignedToFixedUnsigned(SECItem *dest, SECItem *src) |
michael@0 | 71 | { |
michael@0 | 72 | unsigned char *pSrc = src->data; |
michael@0 | 73 | unsigned char *pDst = dest->data; |
michael@0 | 74 | unsigned int cntSrc = src->len; |
michael@0 | 75 | unsigned int cntDst = dest->len; |
michael@0 | 76 | int zCount = cntDst - cntSrc; |
michael@0 | 77 | |
michael@0 | 78 | if (zCount > 0) { |
michael@0 | 79 | PORT_Memset(pDst, 0, zCount); |
michael@0 | 80 | PORT_Memcpy(pDst + zCount, pSrc, cntSrc); |
michael@0 | 81 | return SECSuccess; |
michael@0 | 82 | } |
michael@0 | 83 | if (zCount <= 0) { |
michael@0 | 84 | /* Source is longer than destination. Check for leading zeros. */ |
michael@0 | 85 | while (zCount++ < 0) { |
michael@0 | 86 | if (*pSrc++ != 0) |
michael@0 | 87 | goto loser; |
michael@0 | 88 | } |
michael@0 | 89 | } |
michael@0 | 90 | PORT_Memcpy(pDst, pSrc, cntDst); |
michael@0 | 91 | return SECSuccess; |
michael@0 | 92 | |
michael@0 | 93 | loser: |
michael@0 | 94 | PORT_SetError( PR_INVALID_ARGUMENT_ERROR ); |
michael@0 | 95 | return SECFailure; |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | /* src is a "raw" ECDSA or DSA signature, the first half contains r |
michael@0 | 99 | * and the second half contains s. dest is the DER encoded signature. |
michael@0 | 100 | */ |
michael@0 | 101 | static SECStatus |
michael@0 | 102 | common_EncodeDerSig(SECItem *dest, SECItem *src) |
michael@0 | 103 | { |
michael@0 | 104 | SECItem * item; |
michael@0 | 105 | SECItem srcItem; |
michael@0 | 106 | DSA_ASN1Signature sig; |
michael@0 | 107 | unsigned char *signedR; |
michael@0 | 108 | unsigned char *signedS; |
michael@0 | 109 | unsigned int len; |
michael@0 | 110 | |
michael@0 | 111 | /* Allocate memory with room for an extra byte that |
michael@0 | 112 | * may be required if the top bit in the first byte |
michael@0 | 113 | * is already set. |
michael@0 | 114 | */ |
michael@0 | 115 | len = src->len/2; |
michael@0 | 116 | signedR = (unsigned char *) PORT_Alloc(len + 1); |
michael@0 | 117 | if (!signedR) return SECFailure; |
michael@0 | 118 | signedS = (unsigned char *) PORT_ZAlloc(len + 1); |
michael@0 | 119 | if (!signedS) { |
michael@0 | 120 | if (signedR) PORT_Free(signedR); |
michael@0 | 121 | return SECFailure; |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | PORT_Memset(&sig, 0, sizeof(sig)); |
michael@0 | 125 | |
michael@0 | 126 | /* Must convert r and s from "unsigned" integers to "signed" integers. |
michael@0 | 127 | ** If the high order bit of the first byte (MSB) is 1, then must |
michael@0 | 128 | ** prepend with leading zero. |
michael@0 | 129 | ** Must remove all but one leading zero byte from numbers. |
michael@0 | 130 | */ |
michael@0 | 131 | sig.r.type = siUnsignedInteger; |
michael@0 | 132 | sig.r.data = signedR; |
michael@0 | 133 | sig.r.len = sizeof signedR; |
michael@0 | 134 | sig.s.type = siUnsignedInteger; |
michael@0 | 135 | sig.s.data = signedS; |
michael@0 | 136 | sig.s.len = sizeof signedR; |
michael@0 | 137 | |
michael@0 | 138 | srcItem.data = src->data; |
michael@0 | 139 | srcItem.len = len; |
michael@0 | 140 | |
michael@0 | 141 | DSAU_ConvertUnsignedToSigned(&sig.r, &srcItem); |
michael@0 | 142 | srcItem.data += len; |
michael@0 | 143 | DSAU_ConvertUnsignedToSigned(&sig.s, &srcItem); |
michael@0 | 144 | |
michael@0 | 145 | item = SEC_ASN1EncodeItem(NULL, dest, &sig, DSA_SignatureTemplate); |
michael@0 | 146 | if (signedR) PORT_Free(signedR); |
michael@0 | 147 | if (signedS) PORT_Free(signedS); |
michael@0 | 148 | if (item == NULL) |
michael@0 | 149 | return SECFailure; |
michael@0 | 150 | |
michael@0 | 151 | /* XXX leak item? */ |
michael@0 | 152 | return SECSuccess; |
michael@0 | 153 | } |
michael@0 | 154 | |
michael@0 | 155 | /* src is a DER-encoded ECDSA or DSA signature. |
michael@0 | 156 | ** Returns a newly-allocated SECItem structure, pointing at a newly allocated |
michael@0 | 157 | ** buffer containing the "raw" signature, which is len bytes of r, |
michael@0 | 158 | ** followed by len bytes of s. For DSA, len is the length of q. |
michael@0 | 159 | ** For ECDSA, len depends on the key size used to create the signature. |
michael@0 | 160 | */ |
michael@0 | 161 | static SECItem * |
michael@0 | 162 | common_DecodeDerSig(const SECItem *item, unsigned int len) |
michael@0 | 163 | { |
michael@0 | 164 | SECItem * result = NULL; |
michael@0 | 165 | SECStatus status; |
michael@0 | 166 | DSA_ASN1Signature sig; |
michael@0 | 167 | SECItem dst; |
michael@0 | 168 | |
michael@0 | 169 | PORT_Memset(&sig, 0, sizeof(sig)); |
michael@0 | 170 | |
michael@0 | 171 | result = PORT_ZNew(SECItem); |
michael@0 | 172 | if (result == NULL) |
michael@0 | 173 | goto loser; |
michael@0 | 174 | |
michael@0 | 175 | result->len = 2 * len; |
michael@0 | 176 | result->data = (unsigned char*)PORT_Alloc(2 * len); |
michael@0 | 177 | if (result->data == NULL) |
michael@0 | 178 | goto loser; |
michael@0 | 179 | |
michael@0 | 180 | sig.r.type = siUnsignedInteger; |
michael@0 | 181 | sig.s.type = siUnsignedInteger; |
michael@0 | 182 | status = SEC_ASN1DecodeItem(NULL, &sig, DSA_SignatureTemplate, item); |
michael@0 | 183 | if (status != SECSuccess) |
michael@0 | 184 | goto loser; |
michael@0 | 185 | |
michael@0 | 186 | /* Convert sig.r and sig.s from variable length signed integers to |
michael@0 | 187 | ** fixed length unsigned integers. |
michael@0 | 188 | */ |
michael@0 | 189 | dst.data = result->data; |
michael@0 | 190 | dst.len = len; |
michael@0 | 191 | status = DSAU_ConvertSignedToFixedUnsigned(&dst, &sig.r); |
michael@0 | 192 | if (status != SECSuccess) |
michael@0 | 193 | goto loser; |
michael@0 | 194 | |
michael@0 | 195 | dst.data += len; |
michael@0 | 196 | status = DSAU_ConvertSignedToFixedUnsigned(&dst, &sig.s); |
michael@0 | 197 | if (status != SECSuccess) |
michael@0 | 198 | goto loser; |
michael@0 | 199 | |
michael@0 | 200 | done: |
michael@0 | 201 | if (sig.r.data != NULL) |
michael@0 | 202 | PORT_Free(sig.r.data); |
michael@0 | 203 | if (sig.s.data != NULL) |
michael@0 | 204 | PORT_Free(sig.s.data); |
michael@0 | 205 | |
michael@0 | 206 | return result; |
michael@0 | 207 | |
michael@0 | 208 | loser: |
michael@0 | 209 | if (result != NULL) { |
michael@0 | 210 | SECITEM_FreeItem(result, PR_TRUE); |
michael@0 | 211 | result = NULL; |
michael@0 | 212 | } |
michael@0 | 213 | goto done; |
michael@0 | 214 | } |
michael@0 | 215 | |
michael@0 | 216 | /* src is a "raw" DSA1 signature, 20 bytes of r followed by 20 bytes of s. |
michael@0 | 217 | ** dest is the signature DER encoded. ? |
michael@0 | 218 | */ |
michael@0 | 219 | SECStatus |
michael@0 | 220 | DSAU_EncodeDerSig(SECItem *dest, SECItem *src) |
michael@0 | 221 | { |
michael@0 | 222 | PORT_Assert(src->len == 2 * DSA1_SUBPRIME_LEN); |
michael@0 | 223 | if (src->len != 2 * DSA1_SUBPRIME_LEN) { |
michael@0 | 224 | PORT_SetError( PR_INVALID_ARGUMENT_ERROR ); |
michael@0 | 225 | return SECFailure; |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | return common_EncodeDerSig(dest, src); |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | /* src is a "raw" DSA signature of length len (len/2 bytes of r followed |
michael@0 | 232 | ** by len/2 bytes of s). dest is the signature DER encoded. |
michael@0 | 233 | */ |
michael@0 | 234 | SECStatus |
michael@0 | 235 | DSAU_EncodeDerSigWithLen(SECItem *dest, SECItem *src, unsigned int len) |
michael@0 | 236 | { |
michael@0 | 237 | |
michael@0 | 238 | PORT_Assert((src->len == len) && (len % 2 == 0)); |
michael@0 | 239 | if ((src->len != len) || (src->len % 2 != 0)) { |
michael@0 | 240 | PORT_SetError( PR_INVALID_ARGUMENT_ERROR ); |
michael@0 | 241 | return SECFailure; |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | return common_EncodeDerSig(dest, src); |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | /* src is a DER-encoded DSA signature. |
michael@0 | 248 | ** Returns a newly-allocated SECItem structure, pointing at a newly allocated |
michael@0 | 249 | ** buffer containing the "raw" DSA1 signature, which is 20 bytes of r, |
michael@0 | 250 | ** followed by 20 bytes of s. |
michael@0 | 251 | */ |
michael@0 | 252 | SECItem * |
michael@0 | 253 | DSAU_DecodeDerSig(const SECItem *item) |
michael@0 | 254 | { |
michael@0 | 255 | return common_DecodeDerSig(item, DSA1_SUBPRIME_LEN); |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | /* src is a DER-encoded ECDSA signature. |
michael@0 | 259 | ** Returns a newly-allocated SECItem structure, pointing at a newly allocated |
michael@0 | 260 | ** buffer containing the "raw" ECDSA signature of length len containing |
michael@0 | 261 | ** r followed by s (both padded to take up exactly len/2 bytes). |
michael@0 | 262 | */ |
michael@0 | 263 | SECItem * |
michael@0 | 264 | DSAU_DecodeDerSigToLen(const SECItem *item, unsigned int len) |
michael@0 | 265 | { |
michael@0 | 266 | return common_DecodeDerSig(item, len/2); |
michael@0 | 267 | } |