Line | Count | Source (jump to first uncovered line) |
1 | | /* pss.c |
2 | | |
3 | | PKCS#1 RSA-PSS padding (RFC-3447). |
4 | | |
5 | | Copyright (C) 2017 Daiki Ueno |
6 | | |
7 | | This file is part of GNU Nettle. |
8 | | |
9 | | GNU Nettle is free software: you can redistribute it and/or |
10 | | modify it under the terms of either: |
11 | | |
12 | | * the GNU Lesser General Public License as published by the Free |
13 | | Software Foundation; either version 3 of the License, or (at your |
14 | | option) any later version. |
15 | | |
16 | | or |
17 | | |
18 | | * the GNU General Public License as published by the Free |
19 | | Software Foundation; either version 2 of the License, or (at your |
20 | | option) any later version. |
21 | | |
22 | | or both in parallel, as here. |
23 | | |
24 | | GNU Nettle is distributed in the hope that it will be useful, |
25 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
26 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
27 | | General Public License for more details. |
28 | | |
29 | | You should have received copies of the GNU General Public License and |
30 | | the GNU Lesser General Public License along with this program. If |
31 | | not, see http://www.gnu.org/licenses/. |
32 | | */ |
33 | | |
34 | | #if HAVE_CONFIG_H |
35 | | # include "config.h" |
36 | | #endif |
37 | | |
38 | | #include <assert.h> |
39 | | #include <string.h> |
40 | | |
41 | | #include "pss.h" |
42 | | #include "pss-mgf1.h" |
43 | | |
44 | | #include "bignum.h" |
45 | | #include "gmp-glue.h" |
46 | | |
47 | | #include "memxor.h" |
48 | | #include "nettle-internal.h" |
49 | | |
50 | | /* Masks to clear the leftmost N bits. */ |
51 | | static const uint8_t pss_masks[8] = { |
52 | | 0xFF, 0x7F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x1 |
53 | | }; |
54 | | |
55 | | static const uint8_t pss_pad[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |
56 | | |
57 | | /* Format the PKCS#1 PSS padding for given salt and digest, using |
58 | | * pss_mgf1() as the mask generation function. |
59 | | * |
60 | | * The encoded messsage is stored in M, and the consistency can be |
61 | | * checked with pss_verify_mgf1(), which takes the encoded message, |
62 | | * the length of salt, and the digest. */ |
63 | | int |
64 | | pss_encode_mgf1(mpz_t m, size_t bits, |
65 | | const struct nettle_hash *hash, |
66 | | size_t salt_length, const uint8_t *salt, |
67 | | const uint8_t *digest) |
68 | 0 | { |
69 | 0 | TMP_GMP_DECL(em, uint8_t); |
70 | 0 | TMP_DECL_ALIGN(state, NETTLE_MAX_HASH_CONTEXT_SIZE); |
71 | 0 | size_t key_size = (bits + 7) / 8; |
72 | 0 | size_t j; |
73 | |
|
74 | 0 | TMP_GMP_ALLOC(em, key_size); |
75 | 0 | TMP_ALLOC_ALIGN(state, hash->context_size); |
76 | |
|
77 | 0 | if (key_size < hash->digest_size + salt_length + 2) |
78 | 0 | { |
79 | 0 | TMP_GMP_FREE(em); |
80 | 0 | return 0; |
81 | 0 | } |
82 | | |
83 | | /* Compute M'. */ |
84 | 0 | hash->init(state); |
85 | 0 | hash->update(state, sizeof(pss_pad), pss_pad); |
86 | 0 | hash->update(state, hash->digest_size, digest); |
87 | 0 | hash->update(state, salt_length, salt); |
88 | | |
89 | | /* Store H in EM, right after maskedDB. */ |
90 | 0 | hash->digest(state, hash->digest_size, em + key_size - hash->digest_size - 1); |
91 | | |
92 | | /* Compute dbMask. */ |
93 | 0 | hash->init(state); |
94 | 0 | hash->update(state, hash->digest_size, em + key_size - hash->digest_size - 1); |
95 | |
|
96 | 0 | pss_mgf1(state, hash, key_size - hash->digest_size - 1, em); |
97 | | |
98 | | /* Compute maskedDB and store it in front of H in EM. */ |
99 | 0 | j = key_size - salt_length - hash->digest_size - 2; |
100 | |
|
101 | 0 | em[j++] ^= 1; |
102 | 0 | memxor(em + j, salt, salt_length); |
103 | 0 | j += salt_length; |
104 | | |
105 | | /* Store the trailer field following H. */ |
106 | 0 | j += hash->digest_size; |
107 | 0 | em[j] = 0xbc; |
108 | | |
109 | | /* Clear the leftmost 8 * emLen - emBits of the leftmost octet in EM. */ |
110 | 0 | *em &= pss_masks[(8 * key_size - bits)]; |
111 | |
|
112 | 0 | nettle_mpz_set_str_256_u(m, key_size, em); |
113 | 0 | TMP_GMP_FREE(em); |
114 | 0 | return 1; |
115 | 0 | } |
116 | | |
117 | | /* Check the consistency of given PKCS#1 PSS encoded message, created |
118 | | * with pss_encode_mgf1(). |
119 | | * |
120 | | * Returns 1 if the encoded message is consistent, 0 if it is |
121 | | * inconsistent. */ |
122 | | int |
123 | | pss_verify_mgf1(const mpz_t m, size_t bits, |
124 | | const struct nettle_hash *hash, |
125 | | size_t salt_length, |
126 | | const uint8_t *digest) |
127 | 0 | { |
128 | 0 | TMP_GMP_DECL(em, uint8_t); |
129 | 0 | TMP_DECL(h2, uint8_t, NETTLE_MAX_HASH_DIGEST_SIZE); |
130 | 0 | TMP_DECL_ALIGN(state, NETTLE_MAX_HASH_CONTEXT_SIZE); |
131 | 0 | uint8_t *h, *db, *salt; |
132 | 0 | size_t key_size = (bits + 7) / 8; |
133 | 0 | size_t j; |
134 | 0 | int ret = 0; |
135 | | |
136 | | /* Allocate twice the key size to store the intermediate data DB |
137 | | * following the EM value. */ |
138 | 0 | TMP_GMP_ALLOC(em, key_size * 2); |
139 | |
|
140 | 0 | TMP_ALLOC(h2, hash->digest_size); |
141 | 0 | TMP_ALLOC_ALIGN(state, hash->context_size); |
142 | |
|
143 | 0 | if (key_size < hash->digest_size + salt_length + 2) |
144 | 0 | goto cleanup; |
145 | | |
146 | 0 | if (mpz_sizeinbase(m, 2) > bits) |
147 | 0 | goto cleanup; |
148 | | |
149 | 0 | nettle_mpz_get_str_256(key_size, em, m); |
150 | | |
151 | | /* Check the trailer field. */ |
152 | 0 | if (em[key_size - 1] != 0xbc) |
153 | 0 | goto cleanup; |
154 | | |
155 | | /* Extract H. */ |
156 | 0 | h = em + (key_size - hash->digest_size - 1); |
157 | | |
158 | | /* The leftmost 8 * emLen - emBits bits of the leftmost octet of EM |
159 | | * must all equal to zero. Always true here, thanks to the above |
160 | | * check on the bit size of m. */ |
161 | 0 | assert((*em & ~pss_masks[(8 * key_size - bits)]) == 0); |
162 | | |
163 | | /* Compute dbMask. */ |
164 | 0 | hash->init(state); |
165 | 0 | hash->update(state, hash->digest_size, h); |
166 | |
|
167 | 0 | db = em + key_size; |
168 | 0 | pss_mgf1(state, hash, key_size - hash->digest_size - 1, db); |
169 | | |
170 | | /* Compute DB. */ |
171 | 0 | memxor(db, em, key_size - hash->digest_size - 1); |
172 | |
|
173 | 0 | *db &= pss_masks[(8 * key_size - bits)]; |
174 | 0 | for (j = 0; j < key_size - salt_length - hash->digest_size - 2; j++) |
175 | 0 | if (db[j] != 0) |
176 | 0 | goto cleanup; |
177 | | |
178 | | /* Check the octet right after PS is 0x1. */ |
179 | 0 | if (db[j] != 0x1) |
180 | 0 | goto cleanup; |
181 | 0 | salt = db + j + 1; |
182 | | |
183 | | /* Compute H'. */ |
184 | 0 | hash->init(state); |
185 | 0 | hash->update(state, sizeof(pss_pad), pss_pad); |
186 | 0 | hash->update(state, hash->digest_size, digest); |
187 | 0 | hash->update(state, salt_length, salt); |
188 | 0 | hash->digest(state, hash->digest_size, h2); |
189 | | |
190 | | /* Check if H' = H. */ |
191 | 0 | if (memcmp(h2, h, hash->digest_size) != 0) |
192 | 0 | goto cleanup; |
193 | | |
194 | 0 | ret = 1; |
195 | 0 | cleanup: |
196 | 0 | TMP_GMP_FREE(em); |
197 | 0 | return ret; |
198 | 0 | } |