1eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/*
2eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * PKCS#1 encoding and decoding functions.
3eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * This file is believed to contain no code licensed from other parties.
4eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch *
5eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * This Source Code Form is subject to the terms of the Mozilla Public
6eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * License, v. 2.0. If a copy of the MPL was not distributed with this
7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "seccomon.h"
10eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "secerr.h"
11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "sechash.h"
12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/* Needed for RSA-PSS functions */
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochstatic const unsigned char eightZeros[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/*
17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Mask generation function MGF1 as defined in PKCS #1 v2.1 / RFC 3447.
18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */
19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochstatic SECStatus
20eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochMGF1(HASH_HashType hashAlg, unsigned char *mask, unsigned int maskLen,
21eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     const unsigned char *mgfSeed, unsigned int mgfSeedLen)
22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch{
23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned int digestLen;
24eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    PRUint32 counter, rounds;
25eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned char *tempHash, *temp;
26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const SECHashObject *hash;
27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    void *hashContext;
28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned char C[4];
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    hash = HASH_GetHashObject(hashAlg);
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (hash == NULL)
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        return SECFailure;
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    hashContext = (*hash->create)();
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    rounds = (maskLen + hash->length - 1) / hash->length;
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    for (counter = 0; counter < rounds; counter++) {
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        C[0] = (unsigned char)((counter >> 24) & 0xff);
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        C[1] = (unsigned char)((counter >> 16) & 0xff);
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        C[2] = (unsigned char)((counter >> 8) & 0xff);
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        C[3] = (unsigned char)(counter & 0xff);
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        /* This could be optimized when the clone functions in
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch         * rawhash.c are implemented. */
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        (*hash->begin)(hashContext);
45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        (*hash->update)(hashContext, mgfSeed, mgfSeedLen);
46eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        (*hash->update)(hashContext, C, sizeof C);
47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        tempHash = mask + counter * hash->length;
49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if (counter != (rounds-1)) {
50eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            (*hash->end)(hashContext, tempHash, &digestLen, hash->length);
51eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        } else { /* we're in the last round and need to cut the hash */
52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            temp = (unsigned char *)PORT_Alloc(hash->length);
53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            (*hash->end)(hashContext, temp, &digestLen, hash->length);
54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            PORT_Memcpy(tempHash, temp, maskLen - counter * hash->length);
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            PORT_Free(temp);
56eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        }
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    (*hash->destroy)(hashContext, PR_TRUE);
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return SECSuccess;
61eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
62eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
63eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/*
64eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Verify a RSA-PSS signature.
65eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Described in RFC 3447, section 9.1.2.
66eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * We use mHash instead of M as input.
67eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * emBits from the RFC is just modBits - 1, see section 8.1.2.
68eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * We only support MGF1 as the MGF.
69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch *
70eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * NOTE: this code assumes modBits is a multiple of 8.
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */
72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochSECStatus
73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochemsa_pss_verify(const unsigned char *mHash,
74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                const unsigned char *em, unsigned int emLen,
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                HASH_HashType hashAlg, HASH_HashType maskHashAlg,
76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                unsigned int sLen)
77eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch{
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const SECHashObject *hash;
79eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    void *hash_context;
80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned char *db;
81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned char *H_;  /* H' from the RFC */
82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned int i, dbMaskLen;
83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    SECStatus rv;
84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
85eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    hash = HASH_GetHashObject(hashAlg);
86eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    dbMaskLen = emLen - hash->length - 1;
87eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    /* Step 3 + 4 + 6 */
89eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if ((emLen < (hash->length + sLen + 2)) ||
90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	(em[emLen - 1] != 0xbc) ||
91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	((em[0] & 0x80) != 0)) {
92eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	return SECFailure;
94eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
95eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
96eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    /* Step 7 */
97eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    db = (unsigned char *)PORT_Alloc(dbMaskLen);
98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (db == NULL) {
99eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_SetError(SEC_ERROR_NO_MEMORY);
100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	return SECFailure;
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    /* &em[dbMaskLen] points to H, used as mgfSeed */
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    MGF1(maskHashAlg, db, dbMaskLen, &em[dbMaskLen], hash->length);
104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
105eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    /* Step 8 */
106eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    for (i = 0; i < dbMaskLen; i++) {
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	db[i] ^= em[i];
108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    /* Step 9 */
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    db[0] &= 0x7f;
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    /* Step 10 */
114eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    for (i = 0; i < (dbMaskLen - sLen - 1); i++) {
115eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	if (db[i] != 0) {
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	    PORT_Free(db);
117eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	    PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	    return SECFailure;
119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	}
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (db[dbMaskLen - sLen - 1] != 0x01) {
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_Free(db);
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	return SECFailure;
125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    /* Step 12 + 13 */
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    H_ = (unsigned char *)PORT_Alloc(hash->length);
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (H_ == NULL) {
130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_Free(db);
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_SetError(SEC_ERROR_NO_MEMORY);
132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	return SECFailure;
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    hash_context = (*hash->create)();
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (hash_context == NULL) {
136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_Free(db);
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_Free(H_);
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_SetError(SEC_ERROR_NO_MEMORY);
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	return SECFailure;
140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
141eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    (*hash->begin)(hash_context);
142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    (*hash->update)(hash_context, eightZeros, 8);
143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    (*hash->update)(hash_context, mHash, hash->length);
144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    (*hash->update)(hash_context, &db[dbMaskLen - sLen], sLen);
145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    (*hash->end)(hash_context, H_, &i, hash->length);
146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    (*hash->destroy)(hash_context, PR_TRUE);
147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    PORT_Free(db);
149eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
150eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    /* Step 14 */
151eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (PORT_Memcmp(H_, &em[dbMaskLen], hash->length) != 0) {
152eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
153eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	rv = SECFailure;
154eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    } else {
155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	rv = SECSuccess;
156eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    PORT_Free(H_);
159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return rv;
160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
161