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