| e8555c89 Andy McCall |
1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
#include "sha1.hh" |
| 22 |
|
| 23 |
#include "MSXException.hh" |
| 24 |
|
| 25 |
#include "endian.hh" |
| 26 |
#include "narrow.hh" |
| 27 |
#include "ranges.hh" |
| 28 |
#include "xrange.hh" |
| 29 |
|
| 30 |
#include <algorithm> |
| 31 |
#include <bit> |
| 32 |
#include <cassert> |
| 33 |
#include <cstring> |
| 34 |
#ifdef __SSE2__ |
| 35 |
#include <emmintrin.h> |
| 36 |
#endif |
| 37 |
|
| 38 |
namespace openmsx { |
| 39 |
|
| 40 |
|
| 41 |
[[nodiscard]] static constexpr uint32_t rol32(uint32_t value, int bits) |
| 42 |
{ |
| 43 |
return (value << bits) | (value >> (32 - bits)); |
| 44 |
} |
| 45 |
|
| 46 |
class WorkspaceBlock { |
| 47 |
private: |
| 48 |
std::array<uint32_t, 16> data; |
| 49 |
|
| 50 |
[[nodiscard]] uint32_t next0(int i) |
| 51 |
{ |
| 52 |
data[i] = Endian::readB32(&data[i]); |
| 53 |
return data[i]; |
| 54 |
} |
| 55 |
[[nodiscard]] uint32_t next(int i) |
| 56 |
{ |
| 57 |
return data[i & 15] = rol32( |
| 58 |
data[(i + 13) & 15] ^ data[(i + 8) & 15] ^ |
| 59 |
data[(i + 2) & 15] ^ data[ i & 15] |
| 60 |
, 1); |
| 61 |
} |
| 62 |
|
| 63 |
public: |
| 64 |
explicit WorkspaceBlock(std::span<const uint8_t, 64> buffer); |
| 65 |
|
| 66 |
|
| 67 |
void r0(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) |
| 68 |
{ |
| 69 |
z += ((w & (x ^ y)) ^ y) + next0(i) + 0x5A827999 + rol32(v, 5); |
| 70 |
w = rol32(w, 30); |
| 71 |
} |
| 72 |
void r1(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) |
| 73 |
{ |
| 74 |
z += ((w & (x ^ y)) ^ y) + next(i) + 0x5A827999 + rol32(v, 5); |
| 75 |
w = rol32(w, 30); |
| 76 |
} |
| 77 |
void r2(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) |
| 78 |
{ |
| 79 |
z += (w ^ x ^ y) + next(i) + 0x6ED9EBA1 + rol32(v, 5); |
| 80 |
w = rol32(w, 30); |
| 81 |
} |
| 82 |
void r3(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) |
| 83 |
{ |
| 84 |
z += (((w | x) & y) | (w & x)) + next(i) + 0x8F1BBCDC + rol32(v, 5); |
| 85 |
w = rol32(w, 30); |
| 86 |
} |
| 87 |
void r4(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) |
| 88 |
{ |
| 89 |
z += (w ^ x ^ y) + next(i) + 0xCA62C1D6 + rol32(v, 5); |
| 90 |
w = rol32(w, 30); |
| 91 |
} |
| 92 |
}; |
| 93 |
|
| 94 |
WorkspaceBlock::WorkspaceBlock(std::span<const uint8_t, 64> buffer) |
| 95 |
{ |
| 96 |
memcpy(data.data(), buffer.data(), sizeof(data)); |
| 97 |
} |
| 98 |
|
| 99 |
|
| 100 |
|
| 101 |
|
| 102 |
Sha1Sum::Sha1Sum() |
| 103 |
{ |
| 104 |
clear(); |
| 105 |
} |
| 106 |
|
| 107 |
Sha1Sum::Sha1Sum(std::string_view hex) |
| 108 |
{ |
| 109 |
parse(hex); |
| 110 |
} |
| 111 |
|
| 112 |
void Sha1Sum::parse(std::string_view hex) |
| 113 |
{ |
| 114 |
if (hex.size() != 40) { |
| 115 |
throw MSXException("Invalid sha1, should be exactly 40 digits long: ", hex); |
| 116 |
} |
| 117 |
parse40(subspan<40>(hex)); |
| 118 |
} |
| 119 |
|
| 120 |
#ifdef __SSE2__ |
| 121 |
|
| 122 |
[[nodiscard]] static inline __m128i _mm_cmpge_epu8(__m128i a, __m128i b) |
| 123 |
{ |
| 124 |
return _mm_cmpeq_epi8(_mm_max_epu8(a, b), a); |
| 125 |
} |
| 126 |
|
| 127 |
[[nodiscard]] static inline __m128i _mm_cmple_epu8(__m128i a, __m128i b) |
| 128 |
{ |
| 129 |
return _mm_cmpge_epu8(b, a); |
| 130 |
} |
| 131 |
|
| 132 |
|
| 133 |
[[nodiscard]] static inline int64_t loadSwap64(const char* s) |
| 134 |
{ |
| 135 |
return narrow_cast<int64_t>(Endian::read_UA_B64(s)); |
| 136 |
} |
| 137 |
|
| 138 |
#else |
| 139 |
|
| 140 |
[[nodiscard]] static constexpr unsigned hex(char x, const char* str) |
| 141 |
{ |
| 142 |
if (('0' <= x) && (x <= '9')) return x - '0'; |
| 143 |
if (('a' <= x) && (x <= 'f')) return x - 'a' + 10; |
| 144 |
if (('A' <= x) && (x <= 'F')) return x - 'A' + 10; |
| 145 |
throw MSXException("Invalid sha1, digits should be 0-9, a-f: ", |
| 146 |
std::string_view(str, 40)); |
| 147 |
} |
| 148 |
#endif |
| 149 |
|
| 150 |
void Sha1Sum::parse40(std::span<const char, 40> str) |
| 151 |
{ |
| 152 |
#ifdef __SSE2__ |
| 153 |
|
| 154 |
|
| 155 |
|
| 156 |
__m128i s0 = _mm_set_epi64x(loadSwap64(&str[ 8]), loadSwap64(&str[ 0])); |
| 157 |
__m128i s1 = _mm_set_epi64x(loadSwap64(&str[24]), loadSwap64(&str[16])); |
| 158 |
__m128i s2 = _mm_set_epi64x('0' * 0x0101010101010101, loadSwap64(&str[32])); |
| 159 |
|
| 160 |
|
| 161 |
__m128i cc0 = _mm_set1_epi8(char(-'0')); |
| 162 |
__m128i s0_0 = _mm_add_epi8(s0, cc0); |
| 163 |
__m128i s1_0 = _mm_add_epi8(s1, cc0); |
| 164 |
__m128i s2_0 = _mm_add_epi8(s2, cc0); |
| 165 |
|
| 166 |
|
| 167 |
__m128i c32 = _mm_set1_epi8(32); |
| 168 |
__m128i cca = _mm_set1_epi8(char(-'a')); |
| 169 |
__m128i s0_a = _mm_add_epi8(_mm_or_si128(s0, c32), cca); |
| 170 |
__m128i s1_a = _mm_add_epi8(_mm_or_si128(s1, c32), cca); |
| 171 |
__m128i s2_a = _mm_add_epi8(_mm_or_si128(s2, c32), cca); |
| 172 |
|
| 173 |
|
| 174 |
__m128i c9 = _mm_set1_epi8(9); |
| 175 |
__m128i c0_0 = _mm_cmple_epu8(s0_0, c9); |
| 176 |
__m128i c1_0 = _mm_cmple_epu8(s1_0, c9); |
| 177 |
__m128i c2_0 = _mm_cmple_epu8(s2_0, c9); |
| 178 |
|
| 179 |
|
| 180 |
__m128i c5 = _mm_set1_epi8(5); |
| 181 |
__m128i c0_a = _mm_cmple_epu8(s0_a, c5); |
| 182 |
__m128i c1_a = _mm_cmple_epu8(s1_a, c5); |
| 183 |
__m128i c2_a = _mm_cmple_epu8(s2_a, c5); |
| 184 |
|
| 185 |
|
| 186 |
__m128i ok0 = _mm_or_si128(c0_0, c0_a); |
| 187 |
__m128i ok1 = _mm_or_si128(c1_0, c1_a); |
| 188 |
__m128i ok2 = _mm_or_si128(c2_0, c2_a); |
| 189 |
__m128i ok = _mm_and_si128(_mm_and_si128(ok0, ok1), ok2); |
| 190 |
if (_mm_movemask_epi8(ok) != 0xffff) [[unlikely]] { |
| 191 |
throw MSXException("Invalid sha1, digits should be 0-9, a-f: ", |
| 192 |
std::string_view(str.data(), 40)); |
| 193 |
} |
| 194 |
|
| 195 |
|
| 196 |
__m128i d0_0 = _mm_and_si128(s0_0, c0_0); |
| 197 |
__m128i d1_0 = _mm_and_si128(s1_0, c1_0); |
| 198 |
__m128i d2_0 = _mm_and_si128(s2_0, c2_0); |
| 199 |
|
| 200 |
|
| 201 |
__m128i c10 = _mm_set1_epi8(10); |
| 202 |
__m128i d0_a = _mm_and_si128(_mm_add_epi8(s0_a, c10), c0_a); |
| 203 |
__m128i d1_a = _mm_and_si128(_mm_add_epi8(s1_a, c10), c1_a); |
| 204 |
__m128i d2_a = _mm_and_si128(_mm_add_epi8(s2_a, c10), c2_a); |
| 205 |
|
| 206 |
|
| 207 |
__m128i d0 = _mm_or_si128(d0_0, d0_a); |
| 208 |
__m128i d1 = _mm_or_si128(d1_0, d1_a); |
| 209 |
__m128i d2 = _mm_or_si128(d2_0, d2_a); |
| 210 |
|
| 211 |
|
| 212 |
__m128i c00ff = _mm_set1_epi16(0x00ff); |
| 213 |
__m128i e0 = _mm_and_si128(_mm_or_si128(d0, _mm_srli_epi16(d0, 4)), c00ff); |
| 214 |
__m128i e1 = _mm_and_si128(_mm_or_si128(d1, _mm_srli_epi16(d1, 4)), c00ff); |
| 215 |
__m128i e2 = _mm_and_si128(_mm_or_si128(d2, _mm_srli_epi16(d2, 4)), c00ff); |
| 216 |
__m128i f0 = _mm_packus_epi16(e0, e0); |
| 217 |
__m128i f1 = _mm_packus_epi16(e1, e1); |
| 218 |
__m128i f2 = _mm_packus_epi16(e2, e2); |
| 219 |
|
| 220 |
|
| 221 |
_mm_storeu_si128(std::bit_cast<__m128i*>(a.data()), _mm_unpacklo_epi64(f0, f1)); |
| 222 |
a[4] = _mm_cvtsi128_si32(f2); |
| 223 |
#else |
| 224 |
|
| 225 |
const char* p = str.data(); |
| 226 |
for (auto& ai : a) { |
| 227 |
unsigned t = 0; |
| 228 |
repeat(8, [&] { |
| 229 |
t <<= 4; |
| 230 |
t |= hex(*p++, str.data()); |
| 231 |
}); |
| 232 |
ai = t; |
| 233 |
} |
| 234 |
#endif |
| 235 |
} |
| 236 |
|
| 237 |
[[nodiscard]] static constexpr char digit(unsigned x) |
| 238 |
{ |
| 239 |
return narrow<char>((x < 10) ? (x + '0') : (x - 10 + 'a')); |
| 240 |
} |
| 241 |
void Sha1Sum::toBuffer(std::span<char, 40> buf) const |
| 242 |
{ |
| 243 |
size_t i = 0; |
| 244 |
for (const auto& ai : a) { |
| 245 |
for (int j = 28; j >= 0; j -= 4) { |
| 246 |
buf[i++] = digit((ai >> j) & 0xf); |
| 247 |
} |
| 248 |
} |
| 249 |
} |
| 250 |
|
| 251 |
bool Sha1Sum::empty() const |
| 252 |
{ |
| 253 |
return std::ranges::all_of(a, [](auto& e) { return e == 0; }); |
| 254 |
} |
| 255 |
void Sha1Sum::clear() |
| 256 |
{ |
| 257 |
std::ranges::fill(a, 0); |
| 258 |
} |
| 259 |
|
| 260 |
|
| 261 |
|
| 262 |
|
| 263 |
SHA1::SHA1() |
| 264 |
{ |
| 265 |
|
| 266 |
m_state.a[0] = 0x67452301; |
| 267 |
m_state.a[1] = 0xEFCDAB89; |
| 268 |
m_state.a[2] = 0x98BADCFE; |
| 269 |
m_state.a[3] = 0x10325476; |
| 270 |
m_state.a[4] = 0xC3D2E1F0; |
| 271 |
} |
| 272 |
|
| 273 |
void SHA1::transform(std::span<const uint8_t, 64> buffer) |
| 274 |
{ |
| 275 |
WorkspaceBlock block(buffer); |
| 276 |
|
| 277 |
|
| 278 |
uint32_t a = m_state.a[0]; |
| 279 |
uint32_t b = m_state.a[1]; |
| 280 |
uint32_t c = m_state.a[2]; |
| 281 |
uint32_t d = m_state.a[3]; |
| 282 |
uint32_t e = m_state.a[4]; |
| 283 |
|
| 284 |
|
| 285 |
block.r0(a,b,c,d,e, 0); block.r0(e,a,b,c,d, 1); block.r0(d,e,a,b,c, 2); |
| 286 |
block.r0(c,d,e,a,b, 3); block.r0(b,c,d,e,a, 4); block.r0(a,b,c,d,e, 5); |
| 287 |
block.r0(e,a,b,c,d, 6); block.r0(d,e,a,b,c, 7); block.r0(c,d,e,a,b, 8); |
| 288 |
block.r0(b,c,d,e,a, 9); block.r0(a,b,c,d,e,10); block.r0(e,a,b,c,d,11); |
| 289 |
block.r0(d,e,a,b,c,12); block.r0(c,d,e,a,b,13); block.r0(b,c,d,e,a,14); |
| 290 |
block.r0(a,b,c,d,e,15); block.r1(e,a,b,c,d,16); block.r1(d,e,a,b,c,17); |
| 291 |
block.r1(c,d,e,a,b,18); block.r1(b,c,d,e,a,19); block.r2(a,b,c,d,e,20); |
| 292 |
block.r2(e,a,b,c,d,21); block.r2(d,e,a,b,c,22); block.r2(c,d,e,a,b,23); |
| 293 |
block.r2(b,c,d,e,a,24); block.r2(a,b,c,d,e,25); block.r2(e,a,b,c,d,26); |
| 294 |
block.r2(d,e,a,b,c,27); block.r2(c,d,e,a,b,28); block.r2(b,c,d,e,a,29); |
| 295 |
block.r2(a,b,c,d,e,30); block.r2(e,a,b,c,d,31); block.r2(d,e,a,b,c,32); |
| 296 |
block.r2(c,d,e,a,b,33); block.r2(b,c,d,e,a,34); block.r2(a,b,c,d,e,35); |
| 297 |
block.r2(e,a,b,c,d,36); block.r2(d,e,a,b,c,37); block.r2(c,d,e,a,b,38); |
| 298 |
block.r2(b,c,d,e,a,39); block.r3(a,b,c,d,e,40); block.r3(e,a,b,c,d,41); |
| 299 |
block.r3(d,e,a,b,c,42); block.r3(c,d,e,a,b,43); block.r3(b,c,d,e,a,44); |
| 300 |
block.r3(a,b,c,d,e,45); block.r3(e,a,b,c,d,46); block.r3(d,e,a,b,c,47); |
| 301 |
block.r3(c,d,e,a,b,48); block.r3(b,c,d,e,a,49); block.r3(a,b,c,d,e,50); |
| 302 |
block.r3(e,a,b,c,d,51); block.r3(d,e,a,b,c,52); block.r3(c,d,e,a,b,53); |
| 303 |
block.r3(b,c,d,e,a,54); block.r3(a,b,c,d,e,55); block.r3(e,a,b,c,d,56); |
| 304 |
block.r3(d,e,a,b,c,57); block.r3(c,d,e,a,b,58); block.r3(b,c,d,e,a,59); |
| 305 |
block.r4(a,b,c,d,e,60); block.r4(e,a,b,c,d,61); block.r4(d,e,a,b,c,62); |
| 306 |
block.r4(c,d,e,a,b,63); block.r4(b,c,d,e,a,64); block.r4(a,b,c,d,e,65); |
| 307 |
block.r4(e,a,b,c,d,66); block.r4(d,e,a,b,c,67); block.r4(c,d,e,a,b,68); |
| 308 |
block.r4(b,c,d,e,a,69); block.r4(a,b,c,d,e,70); block.r4(e,a,b,c,d,71); |
| 309 |
block.r4(d,e,a,b,c,72); block.r4(c,d,e,a,b,73); block.r4(b,c,d,e,a,74); |
| 310 |
block.r4(a,b,c,d,e,75); block.r4(e,a,b,c,d,76); block.r4(d,e,a,b,c,77); |
| 311 |
block.r4(c,d,e,a,b,78); block.r4(b,c,d,e,a,79); |
| 312 |
|
| 313 |
|
| 314 |
m_state.a[0] += a; |
| 315 |
m_state.a[1] += b; |
| 316 |
m_state.a[2] += c; |
| 317 |
m_state.a[3] += d; |
| 318 |
m_state.a[4] += e; |
| 319 |
} |
| 320 |
|
| 321 |
|
| 322 |
void SHA1::update(std::span<const uint8_t> data) |
| 323 |
{ |
| 324 |
assert(!m_finalized); |
| 325 |
uint32_t j = m_count & 63; |
| 326 |
|
| 327 |
size_t len = data.size(); |
| 328 |
m_count += len; |
| 329 |
|
| 330 |
size_t i; |
| 331 |
if ((j + len) > 63) { |
| 332 |
i = 64 - j; |
| 333 |
copy_to_range(data.subspan(0, i), subspan(m_buffer, j)); |
| 334 |
transform(m_buffer); |
| 335 |
for (; i + 63 < len; i += 64) { |
| 336 |
transform(subspan<64>(data, i)); |
| 337 |
} |
| 338 |
j = 0; |
| 339 |
} else { |
| 340 |
i = 0; |
| 341 |
} |
| 342 |
copy_to_range(data.subspan(i, len - i), subspan(m_buffer, j)); |
| 343 |
} |
| 344 |
|
| 345 |
void SHA1::finalize() |
| 346 |
{ |
| 347 |
assert(!m_finalized); |
| 348 |
|
| 349 |
uint32_t j = m_count & 63; |
| 350 |
m_buffer[j++] = 0x80; |
| 351 |
if (j > 56) { |
| 352 |
std::ranges::fill(subspan(m_buffer, j, 64 - j), 0); |
| 353 |
transform(m_buffer); |
| 354 |
j = 0; |
| 355 |
} |
| 356 |
std::ranges::fill(subspan(m_buffer, j, 56 - j), 0); |
| 357 |
Endian::B64 finalCount(8 * m_count); |
| 358 |
memcpy(&m_buffer[56], &finalCount, 8); |
| 359 |
transform(m_buffer); |
| 360 |
|
| 361 |
m_finalized = true; |
| 362 |
} |
| 363 |
|
| 364 |
Sha1Sum SHA1::digest() |
| 365 |
{ |
| 366 |
if (!m_finalized) finalize(); |
| 367 |
return m_state; |
| 368 |
} |
| 369 |
|
| 370 |
Sha1Sum SHA1::calc(std::span<const uint8_t> data) |
| 371 |
{ |
| 372 |
SHA1 sha1; |
| 373 |
sha1.update(data); |
| 374 |
return sha1.digest(); |
| 375 |
} |
| 376 |
|
| 377 |
} |