1 // Copyright 1999-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 #ifndef OPENSSL_HEADER_CRYPTO_FIPSMODULE_DIGEST_MD32_COMMON_H
16 #define OPENSSL_HEADER_CRYPTO_FIPSMODULE_DIGEST_MD32_COMMON_H
17
18 #include <openssl/base.h>
19 #include <openssl/span.h>
20
21 #include <assert.h>
22
23 #include "../../internal.h"
24
25 BSSL_NAMESPACE_BEGIN
26
27
28 // This is a generic 32-bit "collector" for message digest algorithms. It
29 // collects input character stream into chunks of 32-bit values and invokes the
30 // block function that performs the actual hash calculations.
31 //
32 // To make use of this mechanism, the hash context should be defined with the
33 // following parameters.
34 //
35 // typedef struct <name>_state_st {
36 // uint32_t h[<chaining length> / sizeof(uint32_t)];
37 // uint32_t Nl, Nh;
38 // uint8_t data[<block size>];
39 // unsigned num;
40 // ...
41 // } <NAME>_CTX;
42 //
43 // <chaining length> is the output length of the hash in bytes, before
44 // any truncation (e.g. 64 for SHA-224 and SHA-256, 128 for SHA-384 and
45 // SHA-512).
46 //
47 // |h| is the hash state and is updated by a function of type
48 // |crypto_md32_block_func|. |data| is the partial unprocessed block and has
49 // |num| bytes. |Nl| and |Nh| maintain the number of bits processed so far.
50 //
51 // The template parameter is then a traits struct defined as follows:
52 //
53 // struct HashTraits {
54 // // HashContext is the hash type defined above.
55 // using HashContext = <NAME>_CTX;
56 //
57 // // kBlockSize is the block size of the hash function.
58 // static constexpr size_t kBlockSize = <block size>;
59 //
60 // // kLengthIsBigEndian determines whether the final length is encoded in
61 // // big or little endian.
62 // static constexpr bool kLengthIsBigEndian = ...;
63 //
64 // // HashBlocks incorporates |num_blocks| blocks of input from |data|
65 // // into |state|. It is assumed the caller has sized |state| and |data|
66 // // for the hash function.
67 // static void HashBlocks(uint32_t *state, const uint8_t *data,
68 // size_t num_blocks) {
69 // <name>_block_data_order(state, data, num_blocks);
70 // }
71 // };
72 //
73 // The reason for this formulation is to encourage the compiler to specialize
74 // all the code for the block size and block function.
75
76 // crypto_md32_update hashes |in| to |ctx|.
77 template <typename Traits>
crypto_md32_update(typename Traits::HashContext * ctx,Span<const uint8_t> in)78 inline void crypto_md32_update(typename Traits::HashContext *ctx,
79 Span<const uint8_t> in) {
80 static_assert(Traits::kBlockSize == sizeof(ctx->data), "block size is wrong");
81 if (in.empty()) {
82 return;
83 }
84
85 uint32_t l = ctx->Nl + ((static_cast<uint32_t>(in.size())) << 3);
86 if (l < ctx->Nl) {
87 // Handle carries.
88 ctx->Nh++;
89 }
90 ctx->Nh += static_cast<uint32_t>(in.size() >> 29);
91 ctx->Nl = l;
92
93 size_t n = ctx->num;
94 if (n != 0) {
95 if (in.size() >= Traits::kBlockSize ||
96 in.size() + n >= Traits::kBlockSize) {
97 OPENSSL_memcpy(ctx->data + n, in.data(), Traits::kBlockSize - n);
98 Traits::HashBlocks(ctx->h, ctx->data, 1);
99 in = in.subspan(Traits::kBlockSize - n);
100 ctx->num = 0;
101 // Keep |data| zeroed when unused.
102 OPENSSL_memset(ctx->data, 0, Traits::kBlockSize);
103 } else {
104 OPENSSL_memcpy(ctx->data + n, in.data(), in.size());
105 ctx->num += static_cast<unsigned>(in.size());
106 return;
107 }
108 }
109
110 n = in.size() / Traits::kBlockSize;
111 if (n > 0) {
112 Traits::HashBlocks(ctx->h, in.data(), n);
113 in = in.subspan(n * Traits::kBlockSize);
114 }
115
116 if (!in.empty()) {
117 ctx->num = static_cast<unsigned>(in.size());
118 OPENSSL_memcpy(ctx->data, in.data(), in.size());
119 }
120 }
121
122 // crypto_md32_final incorporates the partial block and trailing length into the
123 // digest state in |ctx|. The trailing length is encoded in little-endian if
124 // |is_big_endian| is zero and big-endian otherwise. |data| must be a buffer of
125 // length |block_size| with the first |*num| bytes containing a partial block.
126 // |Nh| and |Nl| contain the total number of bits processed. On return, this
127 // function clears the partial block in |data| and
128 // |*num|.
129 //
130 // This function does not serialize |h| into a final digest. This is the
131 // responsibility of the caller.
132 template <typename Traits>
crypto_md32_final(typename Traits::HashContext * ctx)133 inline void crypto_md32_final(typename Traits::HashContext *ctx) {
134 static_assert(Traits::kBlockSize == sizeof(ctx->data), "block size is wrong");
135 // |data| always has room for at least one byte. A full block would have
136 // been consumed.
137 size_t n = ctx->num;
138 assert(n < Traits::kBlockSize);
139 ctx->data[n] = 0x80;
140 n++;
141
142 // Fill the block with zeros if there isn't room for a 64-bit length.
143 if (n > Traits::kBlockSize - 8) {
144 OPENSSL_memset(ctx->data + n, 0, Traits::kBlockSize - n);
145 n = 0;
146 Traits::HashBlocks(ctx->h, ctx->data, 1);
147 }
148 OPENSSL_memset(ctx->data + n, 0, Traits::kBlockSize - 8 - n);
149
150 // Append a 64-bit length to the block and process it.
151 if constexpr (Traits::kLengthIsBigEndian) {
152 CRYPTO_store_u32_be(ctx->data + Traits::kBlockSize - 8, ctx->Nh);
153 CRYPTO_store_u32_be(ctx->data + Traits::kBlockSize - 4, ctx->Nl);
154 } else {
155 CRYPTO_store_u32_le(ctx->data + Traits::kBlockSize - 8, ctx->Nl);
156 CRYPTO_store_u32_le(ctx->data + Traits::kBlockSize - 4, ctx->Nh);
157 }
158 Traits::HashBlocks(ctx->h, ctx->data, 1);
159 ctx->num = 0;
160 OPENSSL_memset(ctx->data, 0, Traits::kBlockSize);
161 }
162
163
164 BSSL_NAMESPACE_END
165
166 #endif // OPENSSL_HEADER_CRYPTO_FIPSMODULE_DIGEST_MD32_COMMON_H
167