8 #include <Quotient/converters.h>
9 #include <Quotient/expected.h>
11 #include <QtCore/QMetaType>
12 #include <QtCore/QStringBuilder>
18 #include <olm/error.h>
22 constexpr inline auto AlgorithmKeyL =
"algorithm"_L1;
23 constexpr inline auto RotationPeriodMsKeyL =
"rotation_period_ms"_L1;
24 constexpr inline auto RotationPeriodMsgsKeyL =
"rotation_period_msgs"_L1;
26 constexpr inline auto AlgorithmKey =
"algorithm"_L1;
27 constexpr inline auto RotationPeriodMsKey =
"rotation_period_ms"_L1;
28 constexpr inline auto RotationPeriodMsgsKey =
"rotation_period_msgs"_L1;
30 constexpr inline auto Ed25519Key =
"ed25519"_L1;
31 constexpr inline auto Curve25519Key =
"curve25519"_L1;
32 constexpr inline auto SignedCurve25519Key =
"signed_curve25519"_L1;
34 constexpr inline auto OlmV1Curve25519AesSha2AlgoKey =
"m.olm.v1.curve25519-aes-sha2"_L1;
35 constexpr inline auto MegolmV1AesSha2AlgoKey =
"m.megolm.v1.aes-sha2"_L1;
37 constexpr std::array SupportedAlgorithms { OlmV1Curve25519AesSha2AlgoKey,
38 MegolmV1AesSha2AlgoKey };
40 inline bool isSupportedAlgorithm(
const QString& algorithm)
42 return std::find(SupportedAlgorithms.cbegin(), SupportedAlgorithms.cend(),
44 != SupportedAlgorithms.cend();
47 #define QOLM_INTERNAL_ERROR_X(Message_, LastError_)
48 qFatal("%s, internal error: %s", QUO_CSTR(Message_), LastError_)
50 #define QOLM_INTERNAL_ERROR(Message_)
53 #define QOLM_FAIL_OR_LOG_X(InternalCondition_, Message_, LastErrorText_)
55 if (InternalCondition_)
57 qWarning(E2EE).nospace() << (Message_) << ": " << (LastErrorText_);
60 #define QOLM_FAIL_OR_LOG(InternalFailureValue_, Message_)
61 QOLM_FAIL_OR_LOG_X(lastErrorCode() == (InternalFailureValue_), (Message_), lastError())
64 using QOlmExpected = Expected<T, OlmErrorCode>;
77 inline size_t unsignedSize(
const auto& buffer)
78 requires (
sizeof(std::size(buffer)) <=
sizeof(size_t))
80 return static_cast<size_t>(std::size(buffer));
89 using byte_t = uint8_t;
91 template <size_t N = std::dynamic_extent>
92 using byte_view_t = std::span<
const byte_t, N>;
94 template <size_t N = std::dynamic_extent>
95 using byte_span_t = std::span<byte_t, N>;
98 QUOTIENT_API void checkForSpanShortfall(QByteArray::size_type inputSize,
int neededSize);
100 template <
typename SpanT>
101 inline auto spanFromBytes(
auto& byteArray)
104 Q_ASSERT_X(std::in_range<
int>(std::size(byteArray)),
__func__,
"Too long array for OpenSSL");
105 if constexpr (SpanT::extent != std::dynamic_extent) {
106 static_assert(std::in_range<
int>(SpanT::extent));
107 checkForSpanShortfall(std::size(byteArray),
static_cast<
int>(SpanT::extent));
109 return SpanT(std::bit_cast<
typename SpanT::pointer>(std::data(byteArray)),
110 std::min(SpanT::extent, unsignedSize(byteArray)));
123 template <size_t N = std::dynamic_extent>
124 inline auto asCBytes(
const auto& buf)
126 return _impl::spanFromBytes<byte_view_t<N>>(buf);
130 template <size_t N = std::dynamic_extent>
131 inline auto asWritableCBytes(
auto& buf)
133 return _impl::spanFromBytes<byte_span_t<N>>(buf);
136 inline auto viewAsByteArray(
const auto& aRange) ->
auto
137 requires (
sizeof(*aRange.data()) ==
sizeof(
char))
139 return QByteArray::fromRawData(std::bit_cast<
const char*>(std::data(aRange)),
140 static_cast<
int>(std::size(aRange)));
146 enum InitOptions { Uninitialized, FillWithZeros, FillWithRandom };
148 using value_type = byte_t;
149 using size_type = size_t;
151 static constexpr auto TotalSecureHeapSize = 65'536;
153 auto size()
const {
return data_ ==
nullptr ? 0 : size_; }
154 auto empty()
const {
return data_ ==
nullptr || size_ == 0; }
167 QByteArray viewAsByteArray()
const
169 static_assert(std::in_range<QByteArray::size_type>(TotalSecureHeapSize));
170 return QByteArray::fromRawData(std::bit_cast<
const char*>(data_),
171 static_cast<QByteArray::size_type>(size_));
178 QByteArray copyToByteArray(QByteArray::size_type untilPos = -1)
const
180 if (untilPos < 0 ||
static_cast<size_type>(untilPos) > size_)
181 untilPos =
static_cast<QByteArray::size_type>(size_);
182 return { std::bit_cast<
const char*>(data_), untilPos };
185 QByteArray toBase64()
const {
return viewAsByteArray().toBase64(); }
186 QByteArray toBase64(QByteArray::Base64Options options)
const
188 return viewAsByteArray().toBase64(options);
191 Q_DISABLE_COPY(FixedBufferBase)
192 FixedBufferBase& operator=(FixedBufferBase&&) =
delete;
195 FixedBufferBase(size_type bufferSize, InitOptions options);
196 ~FixedBufferBase() { clear(); }
198 FixedBufferBase(FixedBufferBase&& other)
199 : data_(std::exchange(other.data_,
nullptr)), size_(other.size_)
202 void fillFrom(QByteArray&& source);
204 value_type* dataForWriting() {
return data_; }
205 const value_type* data()
const {
return data_; }
208 value_type* data_ =
nullptr;
212 template <size_t ExtentN = std::dynamic_extent,
bool DataIsWriteable =
true>
215 static constexpr auto extent = ExtentN;
216 static_assert(extent == std::dynamic_extent
217 || (extent < TotalSecureHeapSize / 2 && extent % 4 == 0));
219 explicit FixedBuffer(InitOptions fillMode = FillWithZeros)
220 requires(extent != std::dynamic_extent)
221 : FixedBufferBase(ExtentN, fillMode)
223 explicit FixedBuffer(size_type bufferSize)
224 requires(extent == std::dynamic_extent)
225 : FixedBuffer(bufferSize, FillWithZeros)
227 explicit FixedBuffer(size_type bufferSize, InitOptions fillMode)
228 requires(extent == std::dynamic_extent)
229 : FixedBufferBase(bufferSize, fillMode)
232 using FixedBufferBase::data;
233 value_type* data() requires DataIsWriteable {
return dataForWriting(); }
235 Q_IMPLICIT operator byte_view_t<ExtentN>()
const
237 return byte_view_t<ExtentN>(data(), size());
240 Q_IMPLICIT operator byte_span_t<ExtentN>()
241 requires DataIsWriteable
243 return byte_span_t<ExtentN>(dataForWriting(), size());
247 inline auto getRandom(size_t bytes)
249 return FixedBuffer<>{ bytes, FixedBufferBase::FillWithRandom };
252 template <size_t SizeN>
253 inline auto getRandom()
255 return FixedBuffer<SizeN>{ FixedBufferBase::FillWithRandom };
268 class PicklingKey :
public FixedBuffer<128,
false> {
271 explicit PicklingKey(InitOptions options) : FixedBuffer(options)
273 Q_ASSERT(options != FillWithZeros);
277 static PicklingKey generate() {
return PicklingKey(FillWithRandom); }
278 static PicklingKey fromByteArray(QByteArray&& keySource)
280 PicklingKey k(Uninitialized);
281 k.fillFrom(std::move(keySource));
284 static PicklingKey mock() {
return PicklingKey(Uninitialized); }
297 struct UnsignedOneTimeKeys
299 QHash<QString, QHash<QString, QString>> keys;
302 QHash<QString, QString> curve25519()
const {
return keys[Curve25519Key]; }
307 explicit SignedOneTimeKey(
const QString& unsignedKey,
const QString& userId,
308 const QString& deviceId,
309 const QByteArray& signature)
311 {
"key"_L1, unsignedKey },
314 { userId, QJsonObject{ {
"ed25519:"_L1 % deviceId,
315 QString::fromUtf8(signature) } } } } }
318 explicit SignedOneTimeKey(
const QJsonObject& jo = {})
323 QByteArray key()
const {
return payload[
"key"_L1].toString().toLatin1(); }
329 auto signatures()
const
331 return fromJson<QHash<QString, QHash<QString, QString>>>(
332 payload[
"signatures"_L1]);
335 QByteArray signature(QStringView userId, QStringView deviceId)
const
337 return payload[
"signatures"_L1][userId][
"ed25519:"_L1 % deviceId]
343 bool isFallback()
const {
return payload[
"fallback"_L1].toBool(); }
344 auto toJson()
const {
return payload; }
345 auto toJsonForVerification()
const
348 json.remove(
"signatures"_L1);
349 json.remove(
"unsigned"_L1);
350 return QJsonDocument(json).toJson(QJsonDocument::Compact);
357 using OneTimeKeys = QHash<QString, std::variant<QString, SignedOneTimeKey>>;
361 Q_DECLARE_METATYPE(Quotient::SignedOneTimeKey)