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: */ michael@0: michael@0: #include "pkcs1sig.h" michael@0: #include "hasht.h" michael@0: #include "secerr.h" michael@0: #include "secasn1t.h" michael@0: #include "secoid.h" michael@0: michael@0: typedef struct pkcs1PrefixStr pkcs1Prefix; michael@0: struct pkcs1PrefixStr { michael@0: unsigned int len; michael@0: PRUint8 *data; michael@0: }; michael@0: michael@0: typedef struct pkcs1PrefixesStr pkcs1Prefixes; michael@0: struct pkcs1PrefixesStr { michael@0: unsigned int digestLen; michael@0: pkcs1Prefix prefixWithParams; michael@0: pkcs1Prefix prefixWithoutParams; michael@0: }; michael@0: michael@0: /* The value for SGN_PKCS1_DIGESTINFO_MAX_PREFIX_LEN_EXCLUDING_OID is based on michael@0: * the possible prefix encodings as explained below. michael@0: */ michael@0: #define MAX_PREFIX_LEN_EXCLUDING_OID 10 michael@0: michael@0: static SECStatus michael@0: encodePrefix(const SECOidData *hashOid, unsigned int digestLen, michael@0: pkcs1Prefix *prefix, PRBool withParams) michael@0: { michael@0: /* with params coding is: michael@0: * Sequence (2 bytes) { michael@0: * Sequence (2 bytes) { michael@0: * Oid (2 bytes) { michael@0: * Oid value (derOid->oid.len) michael@0: * } michael@0: * NULL (2 bytes) michael@0: * } michael@0: * OCTECT (2 bytes); michael@0: * michael@0: * without params coding is: michael@0: * Sequence (2 bytes) { michael@0: * Sequence (2 bytes) { michael@0: * Oid (2 bytes) { michael@0: * Oid value (derOid->oid.len) michael@0: * } michael@0: * } michael@0: * OCTECT (2 bytes); michael@0: */ michael@0: michael@0: unsigned int innerSeqLen = 2 + hashOid->oid.len; michael@0: unsigned int outerSeqLen = 2 + innerSeqLen + 2 + digestLen; michael@0: unsigned int extra = 0; michael@0: michael@0: if (withParams) { michael@0: innerSeqLen += 2; michael@0: outerSeqLen += 2; michael@0: extra = 2; michael@0: } michael@0: michael@0: if (innerSeqLen >= 128 || michael@0: outerSeqLen >= 128 || michael@0: (outerSeqLen + 2 - digestLen) > michael@0: (MAX_PREFIX_LEN_EXCLUDING_OID + hashOid->oid.len)) { michael@0: /* this is actually a library failure, It shouldn't happen */ michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: prefix->len = 6 + hashOid->oid.len + extra + 2; michael@0: prefix->data = PORT_Alloc(prefix->len); michael@0: if (!prefix->data) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: prefix->data[0] = SEC_ASN1_SEQUENCE|SEC_ASN1_CONSTRUCTED; michael@0: prefix->data[1] = outerSeqLen; michael@0: prefix->data[2] = SEC_ASN1_SEQUENCE|SEC_ASN1_CONSTRUCTED; michael@0: prefix->data[3] = innerSeqLen; michael@0: prefix->data[4] = SEC_ASN1_OBJECT_ID; michael@0: prefix->data[5] = hashOid->oid.len; michael@0: PORT_Memcpy(&prefix->data[6], hashOid->oid.data, hashOid->oid.len); michael@0: if (withParams) { michael@0: prefix->data[6 + hashOid->oid.len] = SEC_ASN1_NULL; michael@0: prefix->data[6 + hashOid->oid.len + 1] = 0; michael@0: } michael@0: prefix->data[6 + hashOid->oid.len + extra] = SEC_ASN1_OCTET_STRING; michael@0: prefix->data[6 + hashOid->oid.len + extra + 1] = digestLen; michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: _SGN_VerifyPKCS1DigestInfo(SECOidTag digestAlg, michael@0: const SECItem* digest, michael@0: const SECItem* dataRecoveredFromSignature, michael@0: PRBool unsafeAllowMissingParameters) michael@0: { michael@0: SECOidData *hashOid; michael@0: pkcs1Prefixes pp; michael@0: const pkcs1Prefix* expectedPrefix; michael@0: SECStatus rv, rv2, rv3; michael@0: michael@0: if (!digest || !digest->data || michael@0: !dataRecoveredFromSignature || !dataRecoveredFromSignature->data) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: hashOid = SECOID_FindOIDByTag(digestAlg); michael@0: if (hashOid == NULL) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: pp.digestLen = digest->len; michael@0: pp.prefixWithParams.data = NULL; michael@0: pp.prefixWithoutParams.data = NULL; michael@0: michael@0: rv2 = encodePrefix(hashOid, pp.digestLen, &pp.prefixWithParams, PR_TRUE); michael@0: rv3 = encodePrefix(hashOid, pp.digestLen, &pp.prefixWithoutParams, PR_FALSE); michael@0: michael@0: rv = SECSuccess; michael@0: if (rv2 != SECSuccess || rv3 != SECSuccess) { michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: if (rv == SECSuccess) { michael@0: /* We don't attempt to avoid timing attacks on these comparisons because michael@0: * signature verification is a public key operation, not a private key michael@0: * operation. michael@0: */ michael@0: michael@0: if (dataRecoveredFromSignature->len == michael@0: pp.prefixWithParams.len + pp.digestLen) { michael@0: expectedPrefix = &pp.prefixWithParams; michael@0: } else if (unsafeAllowMissingParameters && michael@0: dataRecoveredFromSignature->len == michael@0: pp.prefixWithoutParams.len + pp.digestLen) { michael@0: expectedPrefix = &pp.prefixWithoutParams; michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: michael@0: if (rv == SECSuccess) { michael@0: if (memcmp(dataRecoveredFromSignature->data, expectedPrefix->data, michael@0: expectedPrefix->len) || michael@0: memcmp(dataRecoveredFromSignature->data + expectedPrefix->len, michael@0: digest->data, digest->len)) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: michael@0: if (pp.prefixWithParams.data) { michael@0: PORT_Free(pp.prefixWithParams.data); michael@0: } michael@0: if (pp.prefixWithoutParams.data) { michael@0: PORT_Free(pp.prefixWithoutParams.data); michael@0: } michael@0: michael@0: return rv; michael@0: }