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