Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
lecmaccalculator.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:uses-kernel-cryptography
5
6#include "bluez/bluez_data_p.h"
7
8#include <QtCore/qbytearray.h>
9#include <QtCore/qloggingcategory.h>
10#include <QtCore/private/qcore_unix_p.h>
11
12#include <cstring>
13#include <sys/socket.h>
14#include <sys/types.h>
15#include <unistd.h>
16
17#ifdef CONFIG_LINUX_CRYPTO_API
18#include <linux/if_alg.h>
19#endif
20
21QT_BEGIN_NAMESPACE
22
23Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
24
25LeCmacCalculator::LeCmacCalculator()
26{
27#ifdef CONFIG_LINUX_CRYPTO_API
28 m_baseSocket = socket(AF_ALG, SOCK_SEQPACKET, 0);
29 if (m_baseSocket == -1) {
30 qCWarning(QT_BT_BLUEZ) << "failed to create first level crypto socket:"
31 << strerror(errno);
32 return;
33 }
34 sockaddr_alg sa;
35 using namespace std;
36 memset(&sa, 0, sizeof sa);
37 sa.salg_family = AF_ALG;
38 strcpy(reinterpret_cast<char *>(sa.salg_type), "hash");
39 strcpy(reinterpret_cast<char *>(sa.salg_name), "cmac(aes)");
40 if (::bind(m_baseSocket, reinterpret_cast<sockaddr *>(&sa), sizeof sa) == -1) {
41 qCWarning(QT_BT_BLUEZ) << "bind() failed for crypto socket:" << strerror(errno);
42 return;
43 }
44#else // CONFIG_LINUX_CRYPTO_API
45 qCWarning(QT_BT_BLUEZ) << "Linux crypto API not present, CMAC verification will fail.";
46#endif
47}
48
49LeCmacCalculator::~LeCmacCalculator()
50{
51 if (m_baseSocket != -1)
52 close(m_baseSocket);
53}
54
55QByteArray LeCmacCalculator::createFullMessage(const QByteArray &message, quint32 signCounter)
56{
57 // Spec v4.2, Vol 3, Part H, 2.4.5
58 QByteArray fullMessage = message;
59 fullMessage.resize(fullMessage.size() + sizeof signCounter);
60 putBtData(signCounter, fullMessage.data() + message.size());
61 return fullMessage;
62}
63
64quint64 LeCmacCalculator::calculateMac(const QByteArray &message, QUuid::Id128Bytes csrk) const
65{
66#ifdef CONFIG_LINUX_CRYPTO_API
67 if (m_baseSocket == -1)
68 return false;
69 QUuid::Id128Bytes csrkMsb;
70 std::reverse_copy(std::begin(csrk.data), std::end(csrk.data), std::begin(csrkMsb.data));
71 qCDebug(QT_BT_BLUEZ) << "CSRK (MSB):" << QByteArray(reinterpret_cast<char *>(csrkMsb.data),
72 sizeof csrkMsb).toHex();
73 if (setsockopt(m_baseSocket, 279 /* SOL_ALG */, ALG_SET_KEY, csrkMsb.data, sizeof csrkMsb) == -1) {
74 qCWarning(QT_BT_BLUEZ) << "setsockopt() failed for crypto socket:" << strerror(errno);
75 return 0;
76 }
77
78 class SocketWrapper
79 {
80 public:
81 SocketWrapper(int socket) : m_socket(socket) {}
82 ~SocketWrapper() {
83 if (m_socket != -1)
84 close(m_socket);
85 }
86
87 int value() const { return m_socket; }
88 private:
89 int m_socket;
90 };
91 SocketWrapper cryptoSocket(accept(m_baseSocket, nullptr, nullptr));
92 if (cryptoSocket.value() == -1) {
93 qCWarning(QT_BT_BLUEZ) << "accept() failed for crypto socket:" << strerror(errno);
94 return 0;
95 }
96
97 QByteArray messageSwapped(message.size(), Qt::Uninitialized);
98 std::reverse_copy(message.begin(), message.end(), messageSwapped.begin());
99 qint64 totalBytesWritten = 0;
100 do {
101 const qint64 bytesWritten = qt_safe_write(cryptoSocket.value(),
102 messageSwapped.constData() + totalBytesWritten,
103 messageSwapped.size() - totalBytesWritten);
104 if (bytesWritten == -1) {
105 qCWarning(QT_BT_BLUEZ) << "writing to crypto socket failed:" << strerror(errno);
106 return 0;
107 }
108 totalBytesWritten += bytesWritten;
109 } while (totalBytesWritten < messageSwapped.size());
110 quint64 mac;
111 quint8 * const macPtr = reinterpret_cast<quint8 *>(&mac);
112 qint64 totalBytesRead = 0;
113 do {
114 const qint64 bytesRead = qt_safe_read(cryptoSocket.value(), macPtr + totalBytesRead,
115 sizeof mac - totalBytesRead);
116 if (bytesRead == -1) {
117 qCWarning(QT_BT_BLUEZ) << "reading from crypto socket failed:" << strerror(errno);
118 return 0;
119 }
120 totalBytesRead += bytesRead;
121 } while (totalBytesRead < qint64(sizeof mac));
122 return qFromBigEndian(mac);
123#else // CONFIG_LINUX_CRYPTO_API
124 Q_UNUSED(message);
125 Q_UNUSED(csrk);
126 qCWarning(QT_BT_BLUEZ) << "CMAC calculation failed due to missing Linux crypto API.";
127 return 0;
128#endif
129}
130
131bool LeCmacCalculator::verify(const QByteArray &message, QUuid::Id128Bytes csrk,
132 quint64 expectedMac) const
133{
134#ifdef CONFIG_LINUX_CRYPTO_API
135 const quint64 actualMac = calculateMac(message, csrk);
136 if (actualMac != expectedMac) {
137 qCWarning(QT_BT_BLUEZ) << Qt::hex << "signature verification failed: calculated mac:"
138 << actualMac << "expected mac:" << expectedMac;
139 return false;
140 }
141 return true;
142#else // CONFIG_LINUX_CRYPTO_API
143 Q_UNUSED(message);
144 Q_UNUSED(csrk);
145 Q_UNUSED(expectedMac);
146 qCWarning(QT_BT_BLUEZ) << "CMAC verification failed due to missing Linux crypto API.";
147 return false;
148#endif
149}
150
151QT_END_NAMESPACE