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