blob: 8dfaa0c427c5b2a6dfe24ce54e4f4ba11b4da8a5 [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 Schuh99f7c6a2024-06-25 22:07:44 -070011#include "absl/flags/flag.h"
12#include "absl/log/check.h"
13#include "absl/log/log.h"
Austin Schuh20ac95d2020-12-05 17:24:19 -080014
Austin Schuh99f7c6a2024-06-25 22:07:44 -070015ABSL_FLAG(std::string, boot_uuid, "",
16 "If set, override the boot UUID to have this value instead.");
Austin Schuh8902fa52021-03-14 22:39:24 -070017
Austin Schuh64fab802020-09-09 22:47:47 -070018namespace aos {
19namespace {
Austin Schuh5e2bfb82021-03-13 22:46:55 -080020void ToHex(const uint8_t *val, char *result, size_t count) {
21 while (count > 0) {
22 int upper = ((*val) >> 4) & 0xf;
23 if (upper < 10) {
24 result[0] = upper + '0';
25 } else {
26 result[0] = upper - 10 + 'a';
27 }
28
29 int lower = (*val) & 0xf;
30 if (lower < 10) {
31 result[1] = lower + '0';
32 } else {
33 result[1] = lower - 10 + 'a';
34 }
35
36 ++val;
37 result += 2;
38 --count;
Austin Schuh64fab802020-09-09 22:47:47 -070039 }
40}
Austin Schuh5e2bfb82021-03-13 22:46:55 -080041
42void FromHex(const char *val, uint8_t *result, size_t count) {
43 while (count > 0) {
44 CHECK((val[0] >= '0' && val[0] <= '9') || (val[0] >= 'a' && val[0] <= 'f'))
45 << ": Invalid hex '" << val[0] << "'";
46 CHECK((val[1] >= '0' && val[1] <= '9') || (val[1] >= 'a' && val[1] <= 'f'))
47 << ": Invalid hex '" << val[1] << "'";
48
49 uint8_t converted = 0;
50 if (val[0] < 'a') {
51 converted |= static_cast<uint8_t>(val[0] - '0') << 4;
52 } else {
53 converted |= (static_cast<uint8_t>(val[0] - 'a') + 0xa) << 4;
54 }
55 if (val[1] < 'a') {
56 converted |= static_cast<uint8_t>(val[1] - '0');
57 } else {
58 converted |= (static_cast<uint8_t>(val[1] - 'a') + 0xa);
59 }
60 *result = converted;
61
62 val += 2;
63 ++result;
64 --count;
65 }
66}
67
Austin Schuh64fab802020-09-09 22:47:47 -070068} // namespace
69
James Kuszmaula791b762023-07-13 14:56:21 -070070namespace internal {
71std::mt19937 FullySeededRandomGenerator() {
72 // Total bits that the mt19937 has internally that we could plausibly
73 // initialize with.
74 // The internal state ends up being ~1200 bytes, which is significantly more
75 // than the 128 bits we want for UUIDs, but since we should only need to
76 // generate this randomness once, it should be fine.
77 // If the performance cost ends up causing issues, then we can revisit the
78 // need to *fully* seed the twister.
79 constexpr size_t kInternalEntropy =
80 std::mt19937::state_size * sizeof(std::mt19937::result_type);
81 // Number, rounded up, of random values required.
82 constexpr size_t kSeedsRequired =
83 ((kInternalEntropy - 1) / sizeof(std::random_device::result_type)) + 1;
84 std::random_device random_device;
85// Older LLVM libstdc++'s just return 0 for the random device entropy.
86#if !defined(__clang__) || (__clang_major__ > 13)
87 CHECK_EQ(sizeof(std::random_device::result_type) * 8, random_device.entropy())
88 << ": Does your random_device actually support generating entropy?";
89#endif
90 std::array<std::random_device::result_type, kSeedsRequired> random_data;
91 std::generate(std::begin(random_data), std::end(random_data),
92 std::ref(random_device));
93 std::seed_seq seeds(std::begin(random_data), std::end(random_data));
94 return std::mt19937(seeds);
95}
96} // namespace internal
97
Austin Schuh64fab802020-09-09 22:47:47 -070098UUID UUID::Random() {
James Kuszmaul05ccb272023-07-13 10:58:14 -070099 // thread_local to guarantee safe use of the generator itself.
James Kuszmaula791b762023-07-13 14:56:21 -0700100 thread_local std::mt19937 gen(internal::FullySeededRandomGenerator());
Austin Schuh64fab802020-09-09 22:47:47 -0700101
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800102 std::uniform_int_distribution<> dis(0, 255);
Austin Schuh64fab802020-09-09 22:47:47 -0700103 UUID result;
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800104 for (size_t i = 0; i < kDataSize; ++i) {
105 result.data_[i] = dis(gen);
106 }
Austin Schuh64fab802020-09-09 22:47:47 -0700107
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800108 // Mark the reserved bits in the data that this is a uuid4, a random UUID.
109 result.data_[6] = (result.data_[6] & 0x0f) | 0x40;
110 result.data_[8] = (result.data_[6] & 0x3f) | 0x80;
Austin Schuh64fab802020-09-09 22:47:47 -0700111
112 return result;
113}
114
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800115std::string UUID::ToString() const {
116 std::string out;
117 out.resize(UUID::kStringSize);
118 CopyTo(out.data());
119 return out;
120}
121
122std::ostream &operator<<(std::ostream &os, const UUID &uuid) {
123 return os << uuid.ToString();
124}
125
126flatbuffers::Offset<flatbuffers::String> UUID::PackString(
127 flatbuffers::FlatBufferBuilder *fbb) const {
128 std::array<char, kStringSize> data;
129 CopyTo(data.data());
130
131 return fbb->CreateString(data.data(), data.size());
132}
133
134flatbuffers::Offset<flatbuffers::Vector<uint8_t>> UUID::PackVector(
135 flatbuffers::FlatBufferBuilder *fbb) const {
136 return fbb->CreateVector(data_.data(), data_.size());
137}
138
139void UUID::CopyTo(char *result) const {
140 ToHex(&data_[0], result, 4);
141 result[8] = '-';
142 ToHex(&data_[4], result + 9, 2);
143 result[13] = '-';
144 ToHex(&data_[6], result + 14, 2);
145 result[18] = '-';
146 ToHex(&data_[8], result + 19, 2);
147 result[23] = '-';
148 ToHex(&data_[10], result + 24, 6);
149}
150
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800151UUID UUID::FromString(const flatbuffers::String *str) {
152 return FromString(str->string_view());
153}
154
155UUID UUID::FromVector(const flatbuffers::Vector<uint8_t> *data) {
156 CHECK(data != nullptr);
157 CHECK_EQ(data->size(), kDataSize);
158
159 UUID result;
160 std::memcpy(result.data_.data(), data->Data(), kDataSize);
161 return result;
162}
Austin Schuh20ac95d2020-12-05 17:24:19 -0800163
Alexei Strots72060d62022-10-10 19:23:53 -0700164UUID UUID::FromSpan(absl::Span<const uint8_t> data) {
165 CHECK_EQ(data.size(), kDataSize);
166
167 UUID result;
168 std::copy(data.begin(), data.end(), result.data_.begin());
169 return result;
170}
171
Austin Schuh20ac95d2020-12-05 17:24:19 -0800172UUID UUID::FromString(std::string_view str) {
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800173 CHECK_EQ(str.size(), kStringSize);
Austin Schuh20ac95d2020-12-05 17:24:19 -0800174
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800175 UUID result;
176 FromHex(str.data(), result.data_.data(), 4);
177 CHECK(str.data()[8] == '-' && str.data()[13] == '-' &&
178 str.data()[18] == '-' && str.data()[23] == '-')
179 << ": Invalid uuid.";
180 FromHex(str.data() + 9, result.data_.data() + 4, 2);
181 FromHex(str.data() + 14, result.data_.data() + 6, 2);
182 FromHex(str.data() + 19, result.data_.data() + 8, 2);
183 FromHex(str.data() + 24, result.data_.data() + 10, 6);
Austin Schuh20ac95d2020-12-05 17:24:19 -0800184 return result;
185}
186
187UUID UUID::BootUUID() {
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700188 auto flag = absl::GetFlag(FLAGS_boot_uuid);
189 if (!flag.empty()) {
190 return UUID::FromString(flag);
Austin Schuh8902fa52021-03-14 22:39:24 -0700191 }
192
Austin Schuh20ac95d2020-12-05 17:24:19 -0800193 int fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY);
194 PCHECK(fd != -1);
195
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800196 std::array<char, kStringSize> data;
197 CHECK_EQ(static_cast<ssize_t>(kStringSize),
198 read(fd, data.begin(), kStringSize));
Austin Schuh20ac95d2020-12-05 17:24:19 -0800199 close(fd);
200
Austin Schuh5e2bfb82021-03-13 22:46:55 -0800201 return UUID::FromString(std::string_view(data.data(), data.size()));
Brian Silverman1f345222020-09-24 21:14:48 -0700202}
203
Austin Schuh64fab802020-09-09 22:47:47 -0700204} // namespace aos