/src/boringssl/crypto/fipsmodule/sha/sha1.cc.inc
Line | Count | Source |
1 | | // Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <string.h> |
16 | | |
17 | | #include <openssl/mem.h> |
18 | | #include <openssl/span.h> |
19 | | |
20 | | #include "../../internal.h" |
21 | | #include "../bcm_interface.h" |
22 | | #include "../digest/md32_common.h" |
23 | | #include "../service_indicator/internal.h" |
24 | | #include "internal.h" |
25 | | |
26 | | |
27 | | using namespace bssl; |
28 | | |
29 | 200k | bcm_infallible bssl::BCM_sha1_init(SHA_CTX *sha) { |
30 | 200k | OPENSSL_memset(sha, 0, sizeof(SHA_CTX)); |
31 | 200k | sha->h[0] = 0x67452301UL; |
32 | 200k | sha->h[1] = 0xefcdab89UL; |
33 | 200k | sha->h[2] = 0x98badcfeUL; |
34 | 200k | sha->h[3] = 0x10325476UL; |
35 | 200k | sha->h[4] = 0xc3d2e1f0UL; |
36 | 200k | return bcm_infallible::approved; |
37 | 200k | } |
38 | | |
39 | | #if !defined(SHA1_ASM) |
40 | | static void sha1_block_data_order(uint32_t state[5], const uint8_t *data, |
41 | | size_t num); |
42 | | #endif |
43 | | |
44 | | bcm_infallible bssl::BCM_sha1_transform(SHA_CTX *c, |
45 | 4.20k | const uint8_t data[SHA_CBLOCK]) { |
46 | 4.20k | sha1_block_data_order(c->h, data, 1); |
47 | 4.20k | return bcm_infallible::approved; |
48 | 4.20k | } |
49 | | |
50 | | namespace { |
51 | | struct SHA1Traits { |
52 | | using HashContext = SHA_CTX; |
53 | | static constexpr size_t kBlockSize = SHA_CBLOCK; |
54 | | static constexpr bool kLengthIsBigEndian = true; |
55 | | static void HashBlocks(uint32_t *state, const uint8_t *data, |
56 | 404k | size_t num_blocks) { |
57 | 404k | sha1_block_data_order(state, data, num_blocks); |
58 | 404k | } |
59 | | }; |
60 | | } // namespace |
61 | | |
62 | 441k | bcm_infallible bssl::BCM_sha1_update(SHA_CTX *c, const void *data, size_t len) { |
63 | 441k | crypto_md32_update<SHA1Traits>(c, |
64 | 441k | Span(static_cast<const uint8_t *>(data), len)); |
65 | 441k | return bcm_infallible::approved; |
66 | 441k | } |
67 | | |
68 | | static void sha1_output_state(uint8_t out[SHA_DIGEST_LENGTH], |
69 | 246k | const SHA_CTX *ctx) { |
70 | 246k | CRYPTO_store_u32_be(out, ctx->h[0]); |
71 | 246k | CRYPTO_store_u32_be(out + 4, ctx->h[1]); |
72 | 246k | CRYPTO_store_u32_be(out + 8, ctx->h[2]); |
73 | 246k | CRYPTO_store_u32_be(out + 12, ctx->h[3]); |
74 | 246k | CRYPTO_store_u32_be(out + 16, ctx->h[4]); |
75 | 246k | } |
76 | | |
77 | | bcm_infallible bssl::BCM_sha1_final(uint8_t out[SHA_DIGEST_LENGTH], |
78 | 246k | SHA_CTX *c) { |
79 | 246k | crypto_md32_final<SHA1Traits>(c); |
80 | 246k | sha1_output_state(out, c); |
81 | 246k | FIPS_service_indicator_update_state(); |
82 | 246k | return bcm_infallible::approved; |
83 | 246k | } |
84 | | |
85 | | bcm_infallible bssl::BCM_fips_186_2_prf(uint8_t *out, size_t out_len, |
86 | 0 | const uint8_t xkey[SHA_DIGEST_LENGTH]) { |
87 | | // XKEY and XVAL are 160-bit values, but are internally right-padded up to |
88 | | // block size. See FIPS 186-2, Appendix 3.3. This buffer maintains both the |
89 | | // current value of XKEY and the padding. |
90 | 0 | uint8_t block[SHA_CBLOCK] = {0}; |
91 | 0 | OPENSSL_memcpy(block, xkey, SHA_DIGEST_LENGTH); |
92 | |
|
93 | 0 | while (out_len != 0) { |
94 | | // We always use a zero XSEED, so we can merge the inner and outer loops. |
95 | | // XVAL is also always equal to XKEY. |
96 | 0 | SHA_CTX ctx; |
97 | 0 | BCM_sha1_init(&ctx); |
98 | 0 | BCM_sha1_transform(&ctx, block); |
99 | | |
100 | | // XKEY = (1 + XKEY + w_i) mod 2^b |
101 | 0 | uint32_t carry = 1; |
102 | 0 | for (int i = 4; i >= 0; i--) { |
103 | 0 | uint32_t tmp = CRYPTO_load_u32_be(block + i * 4); |
104 | 0 | tmp = CRYPTO_addc_u32(tmp, ctx.h[i], carry, &carry); |
105 | 0 | CRYPTO_store_u32_be(block + i * 4, tmp); |
106 | 0 | } |
107 | | |
108 | | // Output w_i. |
109 | 0 | if (out_len < SHA_DIGEST_LENGTH) { |
110 | 0 | uint8_t buf[SHA_DIGEST_LENGTH]; |
111 | 0 | sha1_output_state(buf, &ctx); |
112 | 0 | OPENSSL_memcpy(out, buf, out_len); |
113 | 0 | break; |
114 | 0 | } |
115 | 0 | sha1_output_state(out, &ctx); |
116 | 0 | out += SHA_DIGEST_LENGTH; |
117 | 0 | out_len -= SHA_DIGEST_LENGTH; |
118 | 0 | } |
119 | 0 | return bcm_infallible::not_approved; |
120 | 0 | } |
121 | | |
122 | | #define Xupdate(a, ix, ia, ib, ic, id) \ |
123 | | do { \ |
124 | | (a) = ((ia) ^ (ib) ^ (ic) ^ (id)); \ |
125 | | (ix) = (a) = CRYPTO_rotl_u32((a), 1); \ |
126 | | } while (0) |
127 | | |
128 | | #define K_00_19 0x5a827999UL |
129 | | #define K_20_39 0x6ed9eba1UL |
130 | | #define K_40_59 0x8f1bbcdcUL |
131 | | #define K_60_79 0xca62c1d6UL |
132 | | |
133 | | // As pointed out by Wei Dai <weidai@eskimo.com>, F() below can be simplified |
134 | | // to the code in F_00_19. Wei attributes these optimisations to Peter |
135 | | // Gutmann's SHS code, and he attributes it to Rich Schroeppel. #define |
136 | | // F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) I've just become aware of another |
137 | | // tweak to be made, again from Wei Dai, in F_40_59, (x&a)|(y&a) -> (x|y)&a |
138 | | #define F_00_19(b, c, d) ((((c) ^ (d)) & (b)) ^ (d)) |
139 | | #define F_20_39(b, c, d) ((b) ^ (c) ^ (d)) |
140 | | #define F_40_59(b, c, d) (((b) & (c)) | (((b) | (c)) & (d))) |
141 | | #define F_60_79(b, c, d) F_20_39(b, c, d) |
142 | | |
143 | | #define BODY_00_15(i, a, b, c, d, e, f, xi) \ |
144 | | do { \ |
145 | | (f) = (xi) + (e) + K_00_19 + CRYPTO_rotl_u32((a), 5) + \ |
146 | | F_00_19((b), (c), (d)); \ |
147 | | (b) = CRYPTO_rotl_u32((b), 30); \ |
148 | | } while (0) |
149 | | |
150 | | #define BODY_16_19(i, a, b, c, d, e, f, xi, xa, xb, xc, xd) \ |
151 | | do { \ |
152 | | Xupdate(f, xi, xa, xb, xc, xd); \ |
153 | | (f) += (e) + K_00_19 + CRYPTO_rotl_u32((a), 5) + F_00_19((b), (c), (d)); \ |
154 | | (b) = CRYPTO_rotl_u32((b), 30); \ |
155 | | } while (0) |
156 | | |
157 | | #define BODY_20_31(i, a, b, c, d, e, f, xi, xa, xb, xc, xd) \ |
158 | | do { \ |
159 | | Xupdate(f, xi, xa, xb, xc, xd); \ |
160 | | (f) += (e) + K_20_39 + CRYPTO_rotl_u32((a), 5) + F_20_39((b), (c), (d)); \ |
161 | | (b) = CRYPTO_rotl_u32((b), 30); \ |
162 | | } while (0) |
163 | | |
164 | | #define BODY_32_39(i, a, b, c, d, e, f, xa, xb, xc, xd) \ |
165 | | do { \ |
166 | | Xupdate(f, xa, xa, xb, xc, xd); \ |
167 | | (f) += (e) + K_20_39 + CRYPTO_rotl_u32((a), 5) + F_20_39((b), (c), (d)); \ |
168 | | (b) = CRYPTO_rotl_u32((b), 30); \ |
169 | | } while (0) |
170 | | |
171 | | #define BODY_40_59(i, a, b, c, d, e, f, xa, xb, xc, xd) \ |
172 | | do { \ |
173 | | Xupdate(f, xa, xa, xb, xc, xd); \ |
174 | | (f) += (e) + K_40_59 + CRYPTO_rotl_u32((a), 5) + F_40_59((b), (c), (d)); \ |
175 | | (b) = CRYPTO_rotl_u32((b), 30); \ |
176 | | } while (0) |
177 | | |
178 | | #define BODY_60_79(i, a, b, c, d, e, f, xa, xb, xc, xd) \ |
179 | | do { \ |
180 | | Xupdate(f, xa, xa, xb, xc, xd); \ |
181 | | (f) = (xa) + (e) + K_60_79 + CRYPTO_rotl_u32((a), 5) + \ |
182 | | F_60_79((b), (c), (d)); \ |
183 | | (b) = CRYPTO_rotl_u32((b), 30); \ |
184 | | } while (0) |
185 | | |
186 | | #ifdef X |
187 | | #undef X |
188 | | #endif |
189 | | |
190 | | /* Originally X was an array. As it's automatic it's natural |
191 | | * to expect RISC compiler to accommodate at least part of it in |
192 | | * the register bank, isn't it? Unfortunately not all compilers |
193 | | * "find" this expectation reasonable:-( On order to make such |
194 | | * compilers generate better code I replace X[] with a bunch of |
195 | | * X0, X1, etc. See the function body below... |
196 | | * <appro@fy.chalmers.se> */ |
197 | | #define X(i) XX##i |
198 | | |
199 | | #if !defined(SHA1_ASM) |
200 | | |
201 | | #if !defined(SHA1_ASM_NOHW) |
202 | | static void sha1_block_data_order_nohw(uint32_t state[5], const uint8_t *data, |
203 | | size_t num) { |
204 | | uint32_t A, B, C, D, E, T; |
205 | | uint32_t XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12, |
206 | | XX13, XX14, XX15; |
207 | | |
208 | | A = state[0]; |
209 | | B = state[1]; |
210 | | C = state[2]; |
211 | | D = state[3]; |
212 | | E = state[4]; |
213 | | |
214 | | for (;;) { |
215 | | X(0) = CRYPTO_load_u32_be(data); |
216 | | data += 4; |
217 | | X(1) = CRYPTO_load_u32_be(data); |
218 | | data += 4; |
219 | | BODY_00_15(0, A, B, C, D, E, T, X(0)); |
220 | | X(2) = CRYPTO_load_u32_be(data); |
221 | | data += 4; |
222 | | BODY_00_15(1, T, A, B, C, D, E, X(1)); |
223 | | X(3) = CRYPTO_load_u32_be(data); |
224 | | data += 4; |
225 | | BODY_00_15(2, E, T, A, B, C, D, X(2)); |
226 | | X(4) = CRYPTO_load_u32_be(data); |
227 | | data += 4; |
228 | | BODY_00_15(3, D, E, T, A, B, C, X(3)); |
229 | | X(5) = CRYPTO_load_u32_be(data); |
230 | | data += 4; |
231 | | BODY_00_15(4, C, D, E, T, A, B, X(4)); |
232 | | X(6) = CRYPTO_load_u32_be(data); |
233 | | data += 4; |
234 | | BODY_00_15(5, B, C, D, E, T, A, X(5)); |
235 | | X(7) = CRYPTO_load_u32_be(data); |
236 | | data += 4; |
237 | | BODY_00_15(6, A, B, C, D, E, T, X(6)); |
238 | | X(8) = CRYPTO_load_u32_be(data); |
239 | | data += 4; |
240 | | BODY_00_15(7, T, A, B, C, D, E, X(7)); |
241 | | X(9) = CRYPTO_load_u32_be(data); |
242 | | data += 4; |
243 | | BODY_00_15(8, E, T, A, B, C, D, X(8)); |
244 | | X(10) = CRYPTO_load_u32_be(data); |
245 | | data += 4; |
246 | | BODY_00_15(9, D, E, T, A, B, C, X(9)); |
247 | | X(11) = CRYPTO_load_u32_be(data); |
248 | | data += 4; |
249 | | BODY_00_15(10, C, D, E, T, A, B, X(10)); |
250 | | X(12) = CRYPTO_load_u32_be(data); |
251 | | data += 4; |
252 | | BODY_00_15(11, B, C, D, E, T, A, X(11)); |
253 | | X(13) = CRYPTO_load_u32_be(data); |
254 | | data += 4; |
255 | | BODY_00_15(12, A, B, C, D, E, T, X(12)); |
256 | | X(14) = CRYPTO_load_u32_be(data); |
257 | | data += 4; |
258 | | BODY_00_15(13, T, A, B, C, D, E, X(13)); |
259 | | X(15) = CRYPTO_load_u32_be(data); |
260 | | data += 4; |
261 | | BODY_00_15(14, E, T, A, B, C, D, X(14)); |
262 | | BODY_00_15(15, D, E, T, A, B, C, X(15)); |
263 | | |
264 | | BODY_16_19(16, C, D, E, T, A, B, X(0), X(0), X(2), X(8), X(13)); |
265 | | BODY_16_19(17, B, C, D, E, T, A, X(1), X(1), X(3), X(9), X(14)); |
266 | | BODY_16_19(18, A, B, C, D, E, T, X(2), X(2), X(4), X(10), X(15)); |
267 | | BODY_16_19(19, T, A, B, C, D, E, X(3), X(3), X(5), X(11), X(0)); |
268 | | |
269 | | BODY_20_31(20, E, T, A, B, C, D, X(4), X(4), X(6), X(12), X(1)); |
270 | | BODY_20_31(21, D, E, T, A, B, C, X(5), X(5), X(7), X(13), X(2)); |
271 | | BODY_20_31(22, C, D, E, T, A, B, X(6), X(6), X(8), X(14), X(3)); |
272 | | BODY_20_31(23, B, C, D, E, T, A, X(7), X(7), X(9), X(15), X(4)); |
273 | | BODY_20_31(24, A, B, C, D, E, T, X(8), X(8), X(10), X(0), X(5)); |
274 | | BODY_20_31(25, T, A, B, C, D, E, X(9), X(9), X(11), X(1), X(6)); |
275 | | BODY_20_31(26, E, T, A, B, C, D, X(10), X(10), X(12), X(2), X(7)); |
276 | | BODY_20_31(27, D, E, T, A, B, C, X(11), X(11), X(13), X(3), X(8)); |
277 | | BODY_20_31(28, C, D, E, T, A, B, X(12), X(12), X(14), X(4), X(9)); |
278 | | BODY_20_31(29, B, C, D, E, T, A, X(13), X(13), X(15), X(5), X(10)); |
279 | | BODY_20_31(30, A, B, C, D, E, T, X(14), X(14), X(0), X(6), X(11)); |
280 | | BODY_20_31(31, T, A, B, C, D, E, X(15), X(15), X(1), X(7), X(12)); |
281 | | |
282 | | BODY_32_39(32, E, T, A, B, C, D, X(0), X(2), X(8), X(13)); |
283 | | BODY_32_39(33, D, E, T, A, B, C, X(1), X(3), X(9), X(14)); |
284 | | BODY_32_39(34, C, D, E, T, A, B, X(2), X(4), X(10), X(15)); |
285 | | BODY_32_39(35, B, C, D, E, T, A, X(3), X(5), X(11), X(0)); |
286 | | BODY_32_39(36, A, B, C, D, E, T, X(4), X(6), X(12), X(1)); |
287 | | BODY_32_39(37, T, A, B, C, D, E, X(5), X(7), X(13), X(2)); |
288 | | BODY_32_39(38, E, T, A, B, C, D, X(6), X(8), X(14), X(3)); |
289 | | BODY_32_39(39, D, E, T, A, B, C, X(7), X(9), X(15), X(4)); |
290 | | |
291 | | BODY_40_59(40, C, D, E, T, A, B, X(8), X(10), X(0), X(5)); |
292 | | BODY_40_59(41, B, C, D, E, T, A, X(9), X(11), X(1), X(6)); |
293 | | BODY_40_59(42, A, B, C, D, E, T, X(10), X(12), X(2), X(7)); |
294 | | BODY_40_59(43, T, A, B, C, D, E, X(11), X(13), X(3), X(8)); |
295 | | BODY_40_59(44, E, T, A, B, C, D, X(12), X(14), X(4), X(9)); |
296 | | BODY_40_59(45, D, E, T, A, B, C, X(13), X(15), X(5), X(10)); |
297 | | BODY_40_59(46, C, D, E, T, A, B, X(14), X(0), X(6), X(11)); |
298 | | BODY_40_59(47, B, C, D, E, T, A, X(15), X(1), X(7), X(12)); |
299 | | BODY_40_59(48, A, B, C, D, E, T, X(0), X(2), X(8), X(13)); |
300 | | BODY_40_59(49, T, A, B, C, D, E, X(1), X(3), X(9), X(14)); |
301 | | BODY_40_59(50, E, T, A, B, C, D, X(2), X(4), X(10), X(15)); |
302 | | BODY_40_59(51, D, E, T, A, B, C, X(3), X(5), X(11), X(0)); |
303 | | BODY_40_59(52, C, D, E, T, A, B, X(4), X(6), X(12), X(1)); |
304 | | BODY_40_59(53, B, C, D, E, T, A, X(5), X(7), X(13), X(2)); |
305 | | BODY_40_59(54, A, B, C, D, E, T, X(6), X(8), X(14), X(3)); |
306 | | BODY_40_59(55, T, A, B, C, D, E, X(7), X(9), X(15), X(4)); |
307 | | BODY_40_59(56, E, T, A, B, C, D, X(8), X(10), X(0), X(5)); |
308 | | BODY_40_59(57, D, E, T, A, B, C, X(9), X(11), X(1), X(6)); |
309 | | BODY_40_59(58, C, D, E, T, A, B, X(10), X(12), X(2), X(7)); |
310 | | BODY_40_59(59, B, C, D, E, T, A, X(11), X(13), X(3), X(8)); |
311 | | |
312 | | BODY_60_79(60, A, B, C, D, E, T, X(12), X(14), X(4), X(9)); |
313 | | BODY_60_79(61, T, A, B, C, D, E, X(13), X(15), X(5), X(10)); |
314 | | BODY_60_79(62, E, T, A, B, C, D, X(14), X(0), X(6), X(11)); |
315 | | BODY_60_79(63, D, E, T, A, B, C, X(15), X(1), X(7), X(12)); |
316 | | BODY_60_79(64, C, D, E, T, A, B, X(0), X(2), X(8), X(13)); |
317 | | BODY_60_79(65, B, C, D, E, T, A, X(1), X(3), X(9), X(14)); |
318 | | BODY_60_79(66, A, B, C, D, E, T, X(2), X(4), X(10), X(15)); |
319 | | BODY_60_79(67, T, A, B, C, D, E, X(3), X(5), X(11), X(0)); |
320 | | BODY_60_79(68, E, T, A, B, C, D, X(4), X(6), X(12), X(1)); |
321 | | BODY_60_79(69, D, E, T, A, B, C, X(5), X(7), X(13), X(2)); |
322 | | BODY_60_79(70, C, D, E, T, A, B, X(6), X(8), X(14), X(3)); |
323 | | BODY_60_79(71, B, C, D, E, T, A, X(7), X(9), X(15), X(4)); |
324 | | BODY_60_79(72, A, B, C, D, E, T, X(8), X(10), X(0), X(5)); |
325 | | BODY_60_79(73, T, A, B, C, D, E, X(9), X(11), X(1), X(6)); |
326 | | BODY_60_79(74, E, T, A, B, C, D, X(10), X(12), X(2), X(7)); |
327 | | BODY_60_79(75, D, E, T, A, B, C, X(11), X(13), X(3), X(8)); |
328 | | BODY_60_79(76, C, D, E, T, A, B, X(12), X(14), X(4), X(9)); |
329 | | BODY_60_79(77, B, C, D, E, T, A, X(13), X(15), X(5), X(10)); |
330 | | BODY_60_79(78, A, B, C, D, E, T, X(14), X(0), X(6), X(11)); |
331 | | BODY_60_79(79, T, A, B, C, D, E, X(15), X(1), X(7), X(12)); |
332 | | |
333 | | state[0] = (state[0] + E) & 0xffffffffL; |
334 | | state[1] = (state[1] + T) & 0xffffffffL; |
335 | | state[2] = (state[2] + A) & 0xffffffffL; |
336 | | state[3] = (state[3] + B) & 0xffffffffL; |
337 | | state[4] = (state[4] + C) & 0xffffffffL; |
338 | | |
339 | | if (--num == 0) { |
340 | | break; |
341 | | } |
342 | | |
343 | | A = state[0]; |
344 | | B = state[1]; |
345 | | C = state[2]; |
346 | | D = state[3]; |
347 | | E = state[4]; |
348 | | } |
349 | | } |
350 | | #endif // !SHA1_ASM_NOHW |
351 | | |
352 | | static void sha1_block_data_order(uint32_t state[5], const uint8_t *data, |
353 | 408k | size_t num) { |
354 | 408k | #if defined(SHA1_ASM_HW) |
355 | 408k | if (sha1_hw_capable()) { |
356 | 408k | sha1_block_data_order_hw(state, data, num); |
357 | 408k | return; |
358 | 408k | } |
359 | 0 | #endif |
360 | 0 | #if defined(SHA1_ASM_AVX2) |
361 | 0 | if (sha1_avx2_capable()) { |
362 | 0 | sha1_block_data_order_avx2(state, data, num); |
363 | 0 | return; |
364 | 0 | } |
365 | 0 | #endif |
366 | 0 | #if defined(SHA1_ASM_AVX) |
367 | 0 | if (sha1_avx_capable()) { |
368 | 0 | sha1_block_data_order_avx(state, data, num); |
369 | 0 | return; |
370 | 0 | } |
371 | 0 | #endif |
372 | 0 | #if defined(SHA1_ASM_SSSE3) |
373 | 0 | if (sha1_ssse3_capable()) { |
374 | 0 | sha1_block_data_order_ssse3(state, data, num); |
375 | 0 | return; |
376 | 0 | } |
377 | 0 | #endif |
378 | | #if defined(SHA1_ASM_NEON) |
379 | | if (CRYPTO_is_NEON_capable()) { |
380 | | sha1_block_data_order_neon(state, data, num); |
381 | | return; |
382 | | } |
383 | | #endif |
384 | 0 | sha1_block_data_order_nohw(state, data, num); |
385 | 0 | } |
386 | | |
387 | | #endif // !SHA1_ASM |
388 | | |
389 | | #undef Xupdate |
390 | | #undef K_00_19 |
391 | | #undef K_20_39 |
392 | | #undef K_40_59 |
393 | | #undef K_60_79 |
394 | | #undef F_00_19 |
395 | | #undef F_20_39 |
396 | | #undef F_40_59 |
397 | | #undef F_60_79 |
398 | | #undef BODY_00_15 |
399 | | #undef BODY_16_19 |
400 | | #undef BODY_20_31 |
401 | | #undef BODY_32_39 |
402 | | #undef BODY_40_59 |
403 | | #undef BODY_60_79 |
404 | | #undef X |