Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 1 | /* ==================================================================== |
| 2 | * Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer in |
| 13 | * the documentation and/or other materials provided with the |
| 14 | * distribution. |
| 15 | * |
| 16 | * 3. All advertising materials mentioning features or use of this |
| 17 | * software must display the following acknowledgment: |
| 18 | * "This product includes software developed by the OpenSSL Project |
| 19 | * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" |
| 20 | * |
| 21 | * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to |
| 22 | * endorse or promote products derived from this software without |
| 23 | * prior written permission. For written permission, please contact |
| 24 | * licensing@OpenSSL.org. |
| 25 | * |
| 26 | * 5. Products derived from this software may not be called "OpenSSL" |
| 27 | * nor may "OpenSSL" appear in their names without prior written |
| 28 | * permission of the OpenSSL Project. |
| 29 | * |
| 30 | * 6. Redistributions of any form whatsoever must retain the following |
| 31 | * acknowledgment: |
| 32 | * "This product includes software developed by the OpenSSL Project |
| 33 | * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" |
| 34 | * |
| 35 | * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY |
| 36 | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 37 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 38 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR |
| 39 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 40 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 41 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 42 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 43 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| 44 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 45 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
| 46 | * OF THE POSSIBILITY OF SUCH DAMAGE. |
| 47 | * ==================================================================== */ |
| 48 | |
| 49 | #ifndef OPENSSL_HEADER_MD32_COMMON_H |
| 50 | #define OPENSSL_HEADER_MD32_COMMON_H |
| 51 | |
| 52 | #include <openssl/base.h> |
| 53 | |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 54 | #include <assert.h> |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 55 | |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame^] | 56 | #include "../internal.h" |
| 57 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 58 | #if defined(__cplusplus) |
| 59 | extern "C" { |
| 60 | #endif |
| 61 | |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 62 | |
Brian Smith | ac9404c | 2015-11-01 10:13:24 -1000 | [diff] [blame] | 63 | /* This is a generic 32-bit "collector" for message digest algorithms. It |
| 64 | * collects input character stream into chunks of 32-bit values and invokes the |
| 65 | * block function that performs the actual hash calculations. To make use of |
| 66 | * this mechanism, the following macros must be defined before including |
| 67 | * md32_common.h. |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 68 | * |
Brian Smith | ac9404c | 2015-11-01 10:13:24 -1000 | [diff] [blame] | 69 | * One of |DATA_ORDER_IS_BIG_ENDIAN| or |DATA_ORDER_IS_LITTLE_ENDIAN| must be |
| 70 | * defined to specify the byte order of the input stream. |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 71 | * |
Brian Smith | ac9404c | 2015-11-01 10:13:24 -1000 | [diff] [blame] | 72 | * |HASH_CBLOCK| must be defined as the integer block size, in bytes. |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 73 | * |
Brian Smith | ac9404c | 2015-11-01 10:13:24 -1000 | [diff] [blame] | 74 | * |HASH_CTX| must be defined as the name of the context structure, which must |
| 75 | * have at least the following members: |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 76 | * |
Brian Smith | ac9404c | 2015-11-01 10:13:24 -1000 | [diff] [blame] | 77 | * typedef struct <name>_state_st { |
| 78 | * uint32_t h[<chaining length> / sizeof(uint32_t)]; |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 79 | * uint32_t Nl, Nh; |
David Benjamin | 0aff3ff | 2015-12-07 13:17:03 -0500 | [diff] [blame] | 80 | * uint8_t data[HASH_CBLOCK]; |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 81 | * unsigned num; |
Brian Smith | ac9404c | 2015-11-01 10:13:24 -1000 | [diff] [blame] | 82 | * ... |
| 83 | * } <NAME>_CTX; |
| 84 | * |
| 85 | * <chaining length> is the output length of the hash in bytes, before |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 86 | * any truncation (e.g. 64 for SHA-224 and SHA-256, 128 for SHA-384 and |
| 87 | * SHA-512). |
Brian Smith | ac9404c | 2015-11-01 10:13:24 -1000 | [diff] [blame] | 88 | * |
| 89 | * |HASH_UPDATE| must be defined as the name of the "Update" function to |
| 90 | * generate. |
| 91 | * |
| 92 | * |HASH_TRANSFORM| must be defined as the the name of the "Transform" |
| 93 | * function to generate. |
| 94 | * |
David Benjamin | 862c0aa | 2016-04-27 14:59:12 -0400 | [diff] [blame] | 95 | * |HASH_FINAL| must be defined as the name of "Final" function to generate. |
Brian Smith | ac9404c | 2015-11-01 10:13:24 -1000 | [diff] [blame] | 96 | * |
| 97 | * |HASH_BLOCK_DATA_ORDER| must be defined as the name of the "Block" function. |
| 98 | * That function must be implemented manually. It must be capable of operating |
| 99 | * on *unaligned* input data in its original (data) byte order. It must have |
| 100 | * this signature: |
| 101 | * |
| 102 | * void HASH_BLOCK_DATA_ORDER(uint32_t *state, const uint8_t *data, |
| 103 | * size_t num); |
| 104 | * |
| 105 | * It must update the hash state |state| with |num| blocks of data from |data|, |
| 106 | * where each block is |HASH_CBLOCK| bytes; i.e. |data| points to a array of |
| 107 | * |HASH_CBLOCK * num| bytes. |state| points to the |h| member of a |HASH_CTX|, |
David Benjamin | 862c0aa | 2016-04-27 14:59:12 -0400 | [diff] [blame] | 108 | * and so will have |<chaining length> / sizeof(uint32_t)| elements. |
| 109 | * |
| 110 | * |HASH_MAKE_STRING(c, s)| must be defined as a block statement that converts |
| 111 | * the hash state |c->h| into the output byte order, storing the result in |s|. |
| 112 | */ |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 113 | |
| 114 | #if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) |
| 115 | #error "DATA_ORDER must be defined!" |
| 116 | #endif |
| 117 | |
| 118 | #ifndef HASH_CBLOCK |
| 119 | #error "HASH_CBLOCK must be defined!" |
| 120 | #endif |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 121 | #ifndef HASH_CTX |
| 122 | #error "HASH_CTX must be defined!" |
| 123 | #endif |
| 124 | |
| 125 | #ifndef HASH_UPDATE |
| 126 | #error "HASH_UPDATE must be defined!" |
| 127 | #endif |
| 128 | #ifndef HASH_TRANSFORM |
| 129 | #error "HASH_TRANSFORM must be defined!" |
| 130 | #endif |
David Benjamin | 862c0aa | 2016-04-27 14:59:12 -0400 | [diff] [blame] | 131 | #ifndef HASH_FINAL |
| 132 | #error "HASH_FINAL must be defined!" |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 133 | #endif |
| 134 | |
| 135 | #ifndef HASH_BLOCK_DATA_ORDER |
| 136 | #error "HASH_BLOCK_DATA_ORDER must be defined!" |
| 137 | #endif |
| 138 | |
David Benjamin | 862c0aa | 2016-04-27 14:59:12 -0400 | [diff] [blame] | 139 | #ifndef HASH_MAKE_STRING |
| 140 | #error "HASH_MAKE_STRING must be defined!" |
| 141 | #endif |
| 142 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 143 | #if defined(DATA_ORDER_IS_BIG_ENDIAN) |
| 144 | |
David Benjamin | b1133e9 | 2016-10-18 13:05:01 -0400 | [diff] [blame] | 145 | #define HOST_c2l(c, l) \ |
| 146 | do { \ |
| 147 | (l) = (((uint32_t)(*((c)++))) << 24); \ |
| 148 | (l) |= (((uint32_t)(*((c)++))) << 16); \ |
| 149 | (l) |= (((uint32_t)(*((c)++))) << 8); \ |
| 150 | (l) |= (((uint32_t)(*((c)++)))); \ |
| 151 | } while (0) |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 152 | |
David Benjamin | b1133e9 | 2016-10-18 13:05:01 -0400 | [diff] [blame] | 153 | #define HOST_l2c(l, c) \ |
| 154 | do { \ |
| 155 | *((c)++) = (uint8_t)(((l) >> 24) & 0xff); \ |
| 156 | *((c)++) = (uint8_t)(((l) >> 16) & 0xff); \ |
| 157 | *((c)++) = (uint8_t)(((l) >> 8) & 0xff); \ |
| 158 | *((c)++) = (uint8_t)(((l)) & 0xff); \ |
| 159 | } while (0) |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 160 | |
| 161 | #elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) |
| 162 | |
David Benjamin | b1133e9 | 2016-10-18 13:05:01 -0400 | [diff] [blame] | 163 | #define HOST_c2l(c, l) \ |
| 164 | do { \ |
| 165 | (l) = (((uint32_t)(*((c)++)))); \ |
| 166 | (l) |= (((uint32_t)(*((c)++))) << 8); \ |
| 167 | (l) |= (((uint32_t)(*((c)++))) << 16); \ |
| 168 | (l) |= (((uint32_t)(*((c)++))) << 24); \ |
| 169 | } while (0) |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 170 | |
David Benjamin | b1133e9 | 2016-10-18 13:05:01 -0400 | [diff] [blame] | 171 | #define HOST_l2c(l, c) \ |
| 172 | do { \ |
| 173 | *((c)++) = (uint8_t)(((l)) & 0xff); \ |
| 174 | *((c)++) = (uint8_t)(((l) >> 8) & 0xff); \ |
| 175 | *((c)++) = (uint8_t)(((l) >> 16) & 0xff); \ |
| 176 | *((c)++) = (uint8_t)(((l) >> 24) & 0xff); \ |
| 177 | } while (0) |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 178 | |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 179 | #endif /* DATA_ORDER */ |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 180 | |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 181 | int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len) { |
| 182 | const uint8_t *data = data_; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 183 | |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 184 | if (len == 0) { |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 185 | return 1; |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 186 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 187 | |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 188 | uint32_t l = c->Nl + (((uint32_t)len) << 3); |
| 189 | if (l < c->Nl) { |
| 190 | /* Handle carries. */ |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 191 | c->Nh++; |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 192 | } |
| 193 | c->Nh += (uint32_t)(len >> 29); |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 194 | c->Nl = l; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 195 | |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 196 | size_t n = c->num; |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 197 | if (n != 0) { |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 198 | if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) { |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame^] | 199 | OPENSSL_memcpy(c->data + n, data, HASH_CBLOCK - n); |
David Benjamin | 0aff3ff | 2015-12-07 13:17:03 -0500 | [diff] [blame] | 200 | HASH_BLOCK_DATA_ORDER(c->h, c->data, 1); |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 201 | n = HASH_CBLOCK - n; |
| 202 | data += n; |
| 203 | len -= n; |
| 204 | c->num = 0; |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 205 | /* Keep |c->data| zeroed when unused. */ |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame^] | 206 | OPENSSL_memset(c->data, 0, HASH_CBLOCK); |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 207 | } else { |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame^] | 208 | OPENSSL_memcpy(c->data + n, data, len); |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 209 | c->num += (unsigned)len; |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 210 | return 1; |
| 211 | } |
| 212 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 213 | |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 214 | n = len / HASH_CBLOCK; |
| 215 | if (n > 0) { |
| 216 | HASH_BLOCK_DATA_ORDER(c->h, data, n); |
| 217 | n *= HASH_CBLOCK; |
| 218 | data += n; |
| 219 | len -= n; |
| 220 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 221 | |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 222 | if (len != 0) { |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 223 | c->num = (unsigned)len; |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame^] | 224 | OPENSSL_memcpy(c->data, data, len); |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 225 | } |
| 226 | return 1; |
| 227 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 228 | |
| 229 | |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 230 | void HASH_TRANSFORM(HASH_CTX *c, const uint8_t *data) { |
| 231 | HASH_BLOCK_DATA_ORDER(c->h, data, 1); |
| 232 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 233 | |
| 234 | |
David Benjamin | 862c0aa | 2016-04-27 14:59:12 -0400 | [diff] [blame] | 235 | int HASH_FINAL(uint8_t *md, HASH_CTX *c) { |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 236 | /* |c->data| always has room for at least one byte. A full block would have |
| 237 | * been consumed. */ |
David Benjamin | 0aff3ff | 2015-12-07 13:17:03 -0500 | [diff] [blame] | 238 | size_t n = c->num; |
David Benjamin | 78fefbf | 2015-12-06 21:48:45 -0500 | [diff] [blame] | 239 | assert(n < HASH_CBLOCK); |
David Benjamin | 0aff3ff | 2015-12-07 13:17:03 -0500 | [diff] [blame] | 240 | c->data[n] = 0x80; |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 241 | n++; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 242 | |
David Benjamin | 0aff3ff | 2015-12-07 13:17:03 -0500 | [diff] [blame] | 243 | /* Fill the block with zeros if there isn't room for a 64-bit length. */ |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 244 | if (n > (HASH_CBLOCK - 8)) { |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame^] | 245 | OPENSSL_memset(c->data + n, 0, HASH_CBLOCK - n); |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 246 | n = 0; |
David Benjamin | 0aff3ff | 2015-12-07 13:17:03 -0500 | [diff] [blame] | 247 | HASH_BLOCK_DATA_ORDER(c->h, c->data, 1); |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 248 | } |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame^] | 249 | OPENSSL_memset(c->data + n, 0, HASH_CBLOCK - 8 - n); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 250 | |
David Benjamin | 0aff3ff | 2015-12-07 13:17:03 -0500 | [diff] [blame] | 251 | /* Append a 64-bit length to the block and process it. */ |
| 252 | uint8_t *p = c->data + HASH_CBLOCK - 8; |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 253 | #if defined(DATA_ORDER_IS_BIG_ENDIAN) |
David Benjamin | 793c21e | 2015-12-07 13:36:34 -0500 | [diff] [blame] | 254 | HOST_l2c(c->Nh, p); |
| 255 | HOST_l2c(c->Nl, p); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 256 | #elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) |
David Benjamin | 793c21e | 2015-12-07 13:36:34 -0500 | [diff] [blame] | 257 | HOST_l2c(c->Nl, p); |
| 258 | HOST_l2c(c->Nh, p); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 259 | #endif |
David Benjamin | 0aff3ff | 2015-12-07 13:17:03 -0500 | [diff] [blame] | 260 | assert(p == c->data + HASH_CBLOCK); |
| 261 | HASH_BLOCK_DATA_ORDER(c->h, c->data, 1); |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 262 | c->num = 0; |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame^] | 263 | OPENSSL_memset(c->data, 0, HASH_CBLOCK); |
David Benjamin | 862c0aa | 2016-04-27 14:59:12 -0400 | [diff] [blame] | 264 | |
| 265 | HASH_MAKE_STRING(c, md); |
| 266 | return 1; |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 267 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 268 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 269 | |
| 270 | #if defined(__cplusplus) |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 271 | } /* extern C */ |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 272 | #endif |
| 273 | |
David Benjamin | fea1137 | 2015-12-06 21:16:31 -0500 | [diff] [blame] | 274 | #endif /* OPENSSL_HEADER_MD32_COMMON_H */ |