blob: f794ec83acf5efccfa788638978458570d161526 [file] [log] [blame]
Austin Schuh4385b142021-03-14 21:31:13 -07001#include "aos/uuid.h"
Austin Schuh64fab802020-09-09 22:47:47 -07002
Austin Schuh20ac95d2020-12-05 17:24:19 -08003#include <fcntl.h>
4#include <sys/stat.h>
5#include <sys/types.h>
Austin Schuh60e77942022-05-16 17:48:24 -07006
Austin Schuh64fab802020-09-09 22:47:47 -07007#include <array>
8#include <random>
9#include <string_view>
10
Austin Schuh8902fa52021-03-14 22:39:24 -070011#include "gflags/gflags.h"
Austin Schuh20ac95d2020-12-05 17:24:19 -080012#include "glog/logging.h"
13
Austin Schuh8902fa52021-03-14 22:39:24 -070014DEFINE_string(boot_uuid, "",
15 "If set, override the boot UUID to have this value instead.");
16
Austin Schuh64fab802020-09-09 22:47:47 -070017namespace aos {
18namespace {
Austin Schuh5e2bfb82021-03-13 22:46:55 -080019void ToHex(const uint8_t *val, char *result, size_t count) {
20 while (count > 0) {
21 int upper = ((*val) >> 4) & 0xf;
22 if (upper < 10) {
23 result[0] = upper + '0';
24 } else {
25 result[0] = upper - 10 + 'a';
26 }
27
28 int lower = (*val) & 0xf;
29 if (lower < 10) {
30 result[1] = lower + '0';
31 } else {
32 result[1] = lower - 10 + 'a';
33 }
34
35 ++val;
36 result += 2;
37 --count;
Austin Schuh64fab802020-09-09 22:47:47 -070038 }
39}
Austin Schuh5e2bfb82021-03-13 22:46:55 -080040
41void FromHex(const char *val, uint8_t *result, size_t count) {
42 while (count > 0) {
43 CHECK((val[0] >= '0' && val[0] <= '9') || (val[0] >= 'a' && val[0] <= 'f'))
44 << ": Invalid hex '" << val[0] << "'";
45 CHECK((val[1] >= '0' && val[1] <= '9') || (val[1] >= 'a' && val[1] <= 'f'))
46 << ": Invalid hex '" << val[1] << "'";
47
48 uint8_t converted = 0;
49 if (val[0] < 'a') {
50 converted |= static_cast<uint8_t>(val[0] - '0') << 4;
51 } else {
52 converted |= (static_cast<uint8_t>(val[0] - 'a') + 0xa) << 4;
53 }
54 if (val[1] < 'a') {
55 converted |= static_cast<uint8_t>(val[1] - '0');
56 } else {
57 converted |= (static_cast<uint8_t>(val[1] - 'a') + 0xa);
58 }
59 *result = converted;
60
61 val += 2;
62 ++result;
63 --count;
64 }
65}
66
Austin Schuh64fab802020-09-09 22:47:47 -070067} // namespace
68
James Kuszmaula791b762023-07-13 14:56:21 -070069namespace internal {
70std::mt19937 FullySeededRandomGenerator() {
71 // Total bits that the mt19937 has internally that we could plausibly
72 // initialize with.
73 // The internal state ends up being ~1200 bytes, which is significantly more
74 // than the 128 bits we want for UUIDs, but since we should only need to
75 // generate this randomness once, it should be fine.
76 // If the performance cost ends up causing issues, then we can revisit the
77 // need to *fully* seed the twister.
78 constexpr size_t kInternalEntropy =
79 std::mt19937::state_size * sizeof(std::mt19937::result_type);
80 // Number, rounded up, of random values required.
81 constexpr size_t kSeedsRequired =
82 ((kInternalEntropy - 1) / sizeof(std::random_device::result_type)) + 1;
83 std::random_device random_device;
84// Older LLVM libstdc++'s just return 0 for the random device entropy.
85#if !defined(__clang__) || (__clang_major__ > 13)
86 CHECK_EQ(sizeof(std::random_device::result_type) * 8, random_device.entropy())
87 << ": Does your random_device actually support generating entropy?";
88#endif
89 std::array<std::random_device::result_type, kSeedsRequired> random_data;
90 std::generate(std::begin(random_data), std::end(random_data),
91 std::ref(random_device));
92 std::seed_seq seeds(std::begin(random_data), std::end(random_data));
93 return std::mt19937(seeds);
94}
95} // namespace internal
96
Austin Schuh64fab802020-09-09 22:47:47 -070097UUID UUID::Random() {
James Kuszmaul05ccb272023-07-13 10:58:14 -070098 // thread_local to guarantee safe use of the generator itself.
James Kuszmaula791b762023-07-13 14:56:21 -070099 thread_local std::mt19937 gen(internal::FullySeededRandomGenerator());
Austin Schuh64fab802020-09-09 22:47:47 -0700100
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800101 std::uniform_int_distribution<> dis(0, 255);
Austin Schuh64fab802020-09-09 22:47:47 -0700102 UUID result;
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800103 for (size_t i = 0; i < kDataSize; ++i) {
104 result.data_[i] = dis(gen);
105 }
Austin Schuh64fab802020-09-09 22:47:47 -0700106
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800107 // Mark the reserved bits in the data that this is a uuid4, a random UUID.
108 result.data_[6] = (result.data_[6] & 0x0f) | 0x40;
109 result.data_[8] = (result.data_[6] & 0x3f) | 0x80;
Austin Schuh64fab802020-09-09 22:47:47 -0700110
111 return result;
112}
113
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800114std::string UUID::ToString() const {
115 std::string out;
116 out.resize(UUID::kStringSize);
117 CopyTo(out.data());
118 return out;
119}
120
121std::ostream &operator<<(std::ostream &os, const UUID &uuid) {
122 return os << uuid.ToString();
123}
124
125flatbuffers::Offset<flatbuffers::String> UUID::PackString(
126 flatbuffers::FlatBufferBuilder *fbb) const {
127 std::array<char, kStringSize> data;
128 CopyTo(data.data());
129
130 return fbb->CreateString(data.data(), data.size());
131}
132
133flatbuffers::Offset<flatbuffers::Vector<uint8_t>> UUID::PackVector(
134 flatbuffers::FlatBufferBuilder *fbb) const {
135 return fbb->CreateVector(data_.data(), data_.size());
136}
137
138void UUID::CopyTo(char *result) const {
139 ToHex(&data_[0], result, 4);
140 result[8] = '-';
141 ToHex(&data_[4], result + 9, 2);
142 result[13] = '-';
143 ToHex(&data_[6], result + 14, 2);
144 result[18] = '-';
145 ToHex(&data_[8], result + 19, 2);
146 result[23] = '-';
147 ToHex(&data_[10], result + 24, 6);
148}
149
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800150UUID UUID::FromString(const flatbuffers::String *str) {
151 return FromString(str->string_view());
152}
153
154UUID UUID::FromVector(const flatbuffers::Vector<uint8_t> *data) {
155 CHECK(data != nullptr);
156 CHECK_EQ(data->size(), kDataSize);
157
158 UUID result;
159 std::memcpy(result.data_.data(), data->Data(), kDataSize);
160 return result;
161}
Austin Schuh20ac95d2020-12-05 17:24:19 -0800162
Alexei Strots72060d62022-10-10 19:23:53 -0700163UUID UUID::FromSpan(absl::Span<const uint8_t> data) {
164 CHECK_EQ(data.size(), kDataSize);
165
166 UUID result;
167 std::copy(data.begin(), data.end(), result.data_.begin());
168 return result;
169}
170
Austin Schuh20ac95d2020-12-05 17:24:19 -0800171UUID UUID::FromString(std::string_view str) {
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800172 CHECK_EQ(str.size(), kStringSize);
Austin Schuh20ac95d2020-12-05 17:24:19 -0800173
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800174 UUID result;
175 FromHex(str.data(), result.data_.data(), 4);
176 CHECK(str.data()[8] == '-' && str.data()[13] == '-' &&
177 str.data()[18] == '-' && str.data()[23] == '-')
178 << ": Invalid uuid.";
179 FromHex(str.data() + 9, result.data_.data() + 4, 2);
180 FromHex(str.data() + 14, result.data_.data() + 6, 2);
181 FromHex(str.data() + 19, result.data_.data() + 8, 2);
182 FromHex(str.data() + 24, result.data_.data() + 10, 6);
Austin Schuh20ac95d2020-12-05 17:24:19 -0800183 return result;
184}
185
186UUID UUID::BootUUID() {
Austin Schuh8902fa52021-03-14 22:39:24 -0700187 if (!FLAGS_boot_uuid.empty()) {
188 return UUID::FromString(FLAGS_boot_uuid);
189 }
190
Austin Schuh20ac95d2020-12-05 17:24:19 -0800191 int fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY);
192 PCHECK(fd != -1);
193
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800194 std::array<char, kStringSize> data;
195 CHECK_EQ(static_cast<ssize_t>(kStringSize),
196 read(fd, data.begin(), kStringSize));
Austin Schuh20ac95d2020-12-05 17:24:19 -0800197 close(fd);
198
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800199 return UUID::FromString(std::string_view(data.data(), data.size()));
Brian Silverman1f345222020-09-24 21:14:48 -0700200}
201
Austin Schuh64fab802020-09-09 22:47:47 -0700202} // namespace aos