/*
* PKCS#1 encoding and decoding functions.
* This file is believed to contain no code licensed from other parties.
*
* 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 "seccomon.h"
#include "secerr.h"
#include "sechash.h"
/* Needed for RSA-PSS functions */
static const unsigned char eightZeros[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
/*
* Mask generation function MGF1 as defined in PKCS #1 v2.1 / RFC 3447.
*/
static SECStatus
MGF1(HASH_HashType hashAlg, unsigned char *mask, unsigned int maskLen,
const unsigned char *mgfSeed, unsigned int mgfSeedLen)
{
unsigned int digestLen;
PRUint32 counter, rounds;
unsigned char *tempHash, *temp;
const SECHashObject *hash;
void *hashContext;
unsigned char C[4];
hash = HASH_GetHashObject(hashAlg);
if (hash == NULL)
return SECFailure;
hashContext = (*hash->create)();
rounds = (maskLen + hash->length - 1) / hash->length;
for (counter = 0; counter < rounds; counter++) {
C[0] = (unsigned char)((counter >> 24) & 0xff);
C[1] = (unsigned char)((counter >> 16) & 0xff);
C[2] = (unsigned char)((counter >> 8) & 0xff);
C[3] = (unsigned char)(counter & 0xff);
/* This could be optimized when the clone functions in
* rawhash.c are implemented. */
(*hash->begin)(hashContext);
(*hash->update)(hashContext, mgfSeed, mgfSeedLen);
(*hash->update)(hashContext, C, sizeof C);
tempHash = mask + counter * hash->length;
if (counter != (rounds-1)) {
(*hash->end)(hashContext, tempHash, &digestLen, hash->length);
} else { /* we're in the last round and need to cut the hash */
temp = (unsigned char *)PORT_Alloc(hash->length);
(*hash->end)(hashContext, temp, &digestLen, hash->length);
PORT_Memcpy(tempHash, temp, maskLen - counter * hash->length);
PORT_Free(temp);
}
}
(*hash->destroy)(hashContext, PR_TRUE);
return SECSuccess;
}
/*
* Verify a RSA-PSS signature.
* Described in RFC 3447, section 9.1.2.
* We use mHash instead of M as input.
* emBits from the RFC is just modBits - 1, see section 8.1.2.
* We only support MGF1 as the MGF.
*
* NOTE: this code assumes modBits is a multiple of 8.
*/
SECStatus
emsa_pss_verify(const unsigned char *mHash,
const unsigned char *em, unsigned int emLen,
HASH_HashType hashAlg, HASH_HashType maskHashAlg,
unsigned int sLen)
{
const SECHashObject *hash;
void *hash_context;
unsigned char *db;
unsigned char *H_; /* H' from the RFC */
unsigned int i, dbMaskLen;
SECStatus rv;
hash = HASH_GetHashObject(hashAlg);
dbMaskLen = emLen - hash->length - 1;
/* Step 3 + 4 + 6 */
if ((emLen < (hash->length + sLen + 2)) ||
(em[emLen - 1] != 0xbc) ||
((em[0] & 0x80) != 0)) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
return SECFailure;
}
/* Step 7 */
db = (unsigned char *)PORT_Alloc(dbMaskLen);
if (db == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
/* &em[dbMaskLen] points to H, used as mgfSeed */
MGF1(maskHashAlg, db, dbMaskLen, &em[dbMaskLen], hash->length);
/* Step 8 */
for (i = 0; i < dbMaskLen; i++) {
db[i] ^= em[i];
}
/* Step 9 */
db[0] &= 0x7f;
/* Step 10 */
for (i = 0; i < (dbMaskLen - sLen - 1); i++) {
if (db[i] != 0) {
PORT_Free(db);
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
return SECFailure;
}
}
if (db[dbMaskLen - sLen - 1] != 0x01) {
PORT_Free(db);
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
return SECFailure;
}
/* Step 12 + 13 */
H_ = (unsigned char *)PORT_Alloc(hash->length);
if (H_ == NULL) {
PORT_Free(db);
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
hash_context = (*hash->create)();
if (hash_context == NULL) {
PORT_Free(db);
PORT_Free(H_);
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
(*hash->begin)(hash_context);
(*hash->update)(hash_context, eightZeros, 8);
(*hash->update)(hash_context, mHash, hash->length);
(*hash->update)(hash_context, &db[dbMaskLen - sLen], sLen);
(*hash->end)(hash_context, H_, &i, hash->length);
(*hash->destroy)(hash_context, PR_TRUE);
PORT_Free(db);
/* Step 14 */
if (PORT_Memcmp(H_, &em[dbMaskLen], hash->length) != 0) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
rv = SECFailure;
} else {
rv = SECSuccess;
}
PORT_Free(H_);
return rv;
}