OXYGENHUB
◆ main openmsx / src / utils / sha1.hh
3.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef SHA1_HH
2
#define SHA1_HH
3
4
#include "serialize.hh"
5
#include "strCat.hh"
6
7
#include <array>
8
#include <cstdint>
9
#include <ostream>
10
#include <span>
11
#include <string>
12
#include <string_view>
13
14
namespace openmsx {
15
16
/** This class represents the result of a sha1 calculation (a 160-bit value).
17
  * Objects of this class can be constructed from/converted to 40-digit hex
18
  * string.
19
  * We treat the value '000...00' (all zeros) special. This value can be used
20
  * to indicate a null-sha1sum value (e.g. sha1 not yet calculated, or not
21
  * meaningful). In theory it's possible this special value is the result of an
22
  * actual sha1 calculation, but this has an _extremely_ low probability.
23
  */
24
class Sha1Sum
25
{
26
public:
27
	struct UninitializedTag {};
28
	explicit Sha1Sum(UninitializedTag) {}
29
30
	// note: default copy and assign are ok
31
	Sha1Sum();
32
	/** Construct from string, throws when string is malformed. */
33
	explicit Sha1Sum(std::string_view hex);
34
	void parse(std::string_view hex);
35
36
	/** Parse from a 40-character long buffer.
37
	 * @pre 'str' points to a buffer of at least 40 characters
38
	 * @throws MSXException if chars are not 0-9, a-f, A-F
39
	 */
40
	void parse40(std::span<const char, 40> str);
41
42
	void toBuffer(std::span<char, 40> buf) const;
43
	[[nodiscard]] std::string_view toString(std::span<char, 40> buf) const {
44
		toBuffer(buf);
45
		return {buf.data(), 40};
46
	}
47
48
	// Test or set 'null' value.
49
	[[nodiscard]] bool empty() const;
50
	void clear();
51
52
	[[nodiscard]] constexpr bool operator==(const Sha1Sum&) const = default;
53
	[[nodiscard]] constexpr auto operator<=>(const Sha1Sum&) const = default;
54
55
	friend std::ostream& operator<<(std::ostream& os, const Sha1Sum& sum) {
56
		std::array<char, 40> buf;
57
		sum.toBuffer(buf);
58
		os.write(buf.data(), 40);
59
		return os;
60
	}
61
62
	// We used to serialize sha1 as a string.
63
	// * For on-disk formats we want to continue to do that.
64
	// * Fos in-memory formats we can skip that formatting/parsing step
65
	void serialize(MemInputArchive& ar, unsigned /*version*/) {
66
		ar.serialize("a", a);
67
	}
68
	void serialize(MemOutputArchive& ar, unsigned /*version*/) const {
69
		ar.serialize("a", a);
70
	}
71
	void serialize(XmlInputArchive& ar, unsigned /*version*/) {
72
		std::string s;
73
		ar.load(s); // no label
74
		if (s.empty()) {
75
			clear();
76
		} else {
77
			parse(s);
78
		}
79
	}
80
	void serialize(XmlOutputArchive& ar, unsigned /*version*/) const {
81
		if (empty()) return;
82
		std::array<char, 40> buf;
83
		toBuffer(buf);
84
		std::string_view s(buf.data(), 40);
85
		ar.save(s);
86
	}
87
88
private:
89
	std::array<uint32_t, 5> a;
90
	friend class SHA1;
91
};
92
93
94
/** Helper class to perform a sha1 calculation.
95
  * Basic usage:
96
  *  - construct a SHA1 object
97
  *  - repeatedly call update()
98
  *  - call digest() to get the result
99
  * Alternatively, use calc() if all data can be passed at once (IOW when there
100
  * would be exactly one call to update() in the recipe above).
101
  */
102
class SHA1
103
{
104
public:
105
	SHA1();
106
107
	/** Incrementally calculate the hash value. */
108
	void update(std::span<const uint8_t> data);
109
110
	/** Get the final hash. After this method is called, calls to update()
111
	  * are invalid. */
112
	[[nodiscard]] Sha1Sum digest();
113
114
	/** Easier to use interface, if you can pass all data in one go. */
115
	[[nodiscard]] static Sha1Sum calc(std::span<const uint8_t> data);
116
117
private:
118
	void transform(std::span<const uint8_t, 64> buffer);
119
	void finalize();
120
121
private:
122
	uint64_t m_count = 0; // in bytes (sha1 reference implementation counts in bits)
123
	Sha1Sum m_state;
124
	std::array<uint8_t, 64> m_buffer;
125
	bool m_finalized = false;
126
};
127
128
} // namespace openmsx
129
130
// For efficient strCat() (without Sha1Sum::toString())
131
template<> struct strCatImpl::ConcatUnit<openmsx::Sha1Sum>
132
{
133
	explicit ConcatUnit(openmsx::Sha1Sum sum_)
134
		: sum(sum_) {}
135
136
	[[nodiscard]] size_t size() const { return 40; }
137
138
	[[nodiscard]] char* copy(char* dst) const {
139
		sum.toBuffer(std::span<char, 40>(dst, 40));
140
		return dst + 40;
141
	}
142
143
private:
144
	openmsx::Sha1Sum sum;
145
};
146
147
#endif