1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtBluetooth module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | #include "lecmaccalculator_p.h" |
40 | |
41 | #include "bluez/bluez_data_p.h" |
42 | |
43 | #include <QtCore/qbytearray.h> |
44 | #include <QtCore/qloggingcategory.h> |
45 | #include <QtCore/private/qcore_unix_p.h> |
46 | |
47 | #include <cstring> |
48 | #include <sys/socket.h> |
49 | #include <sys/types.h> |
50 | #include <unistd.h> |
51 | |
52 | #ifdef CONFIG_LINUX_CRYPTO_API |
53 | #include <linux/if_alg.h> |
54 | #endif |
55 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) |
59 | |
60 | LeCmacCalculator::LeCmacCalculator() |
61 | { |
62 | #ifdef CONFIG_LINUX_CRYPTO_API |
63 | m_baseSocket = socket(AF_ALG, SOCK_SEQPACKET, 0); |
64 | if (m_baseSocket == -1) { |
65 | qCWarning(QT_BT_BLUEZ) << "failed to create first level crypto socket:" |
66 | << strerror(errno); |
67 | return; |
68 | } |
69 | sockaddr_alg sa; |
70 | using namespace std; |
71 | memset(&sa, 0, sizeof sa); |
72 | sa.salg_family = AF_ALG; |
73 | strcpy(reinterpret_cast<char *>(sa.salg_type), "hash" ); |
74 | strcpy(reinterpret_cast<char *>(sa.salg_name), "cmac(aes)" ); |
75 | if (::bind(m_baseSocket, reinterpret_cast<sockaddr *>(&sa), sizeof sa) == -1) { |
76 | qCWarning(QT_BT_BLUEZ) << "bind() failed for crypto socket:" << strerror(errno); |
77 | return; |
78 | } |
79 | #else // CONFIG_LINUX_CRYPTO_API |
80 | qCWarning(QT_BT_BLUEZ) << "Linux crypto API not present, CMAC verification will fail." ; |
81 | #endif |
82 | } |
83 | |
84 | LeCmacCalculator::~LeCmacCalculator() |
85 | { |
86 | if (m_baseSocket != -1) |
87 | close(m_baseSocket); |
88 | } |
89 | |
90 | QByteArray LeCmacCalculator::createFullMessage(const QByteArray &message, quint32 signCounter) |
91 | { |
92 | // Spec v4.2, Vol 3, Part H, 2.4.5 |
93 | QByteArray fullMessage = message; |
94 | fullMessage.resize(fullMessage.count() + sizeof signCounter); |
95 | putBtData(signCounter, fullMessage.data() + message.count()); |
96 | return fullMessage; |
97 | } |
98 | |
99 | quint64 LeCmacCalculator::calculateMac(const QByteArray &message, const quint128 &csrk) const |
100 | { |
101 | #ifdef CONFIG_LINUX_CRYPTO_API |
102 | if (m_baseSocket == -1) |
103 | return false; |
104 | quint128 csrkMsb; |
105 | std::reverse_copy(std::begin(csrk.data), std::end(csrk.data), std::begin(csrkMsb.data)); |
106 | qCDebug(QT_BT_BLUEZ) << "CSRK (MSB):" << QByteArray(reinterpret_cast<char *>(csrkMsb.data), |
107 | sizeof csrkMsb).toHex(); |
108 | if (setsockopt(m_baseSocket, 279 /* SOL_ALG */, ALG_SET_KEY, csrkMsb.data, sizeof csrkMsb) == -1) { |
109 | qCWarning(QT_BT_BLUEZ) << "setsockopt() failed for crypto socket:" << strerror(errno); |
110 | return 0; |
111 | } |
112 | |
113 | class SocketWrapper |
114 | { |
115 | public: |
116 | SocketWrapper(int socket) : m_socket(socket) {} |
117 | ~SocketWrapper() { |
118 | if (m_socket != -1) |
119 | close(m_socket); |
120 | } |
121 | |
122 | int value() const { return m_socket; } |
123 | private: |
124 | int m_socket; |
125 | }; |
126 | SocketWrapper cryptoSocket(accept(m_baseSocket, nullptr, nullptr)); |
127 | if (cryptoSocket.value() == -1) { |
128 | qCWarning(QT_BT_BLUEZ) << "accept() failed for crypto socket:" << strerror(errno); |
129 | return 0; |
130 | } |
131 | |
132 | QByteArray messageSwapped(message.count(), Qt::Uninitialized); |
133 | std::reverse_copy(message.begin(), message.end(), messageSwapped.begin()); |
134 | qint64 totalBytesWritten = 0; |
135 | do { |
136 | const qint64 bytesWritten = qt_safe_write(cryptoSocket.value(), |
137 | messageSwapped.constData() + totalBytesWritten, |
138 | messageSwapped.count() - totalBytesWritten); |
139 | if (bytesWritten == -1) { |
140 | qCWarning(QT_BT_BLUEZ) << "writing to crypto socket failed:" << strerror(errno); |
141 | return 0; |
142 | } |
143 | totalBytesWritten += bytesWritten; |
144 | } while (totalBytesWritten < messageSwapped.count()); |
145 | quint64 mac; |
146 | quint8 * const macPtr = reinterpret_cast<quint8 *>(&mac); |
147 | qint64 totalBytesRead = 0; |
148 | do { |
149 | const qint64 bytesRead = qt_safe_read(cryptoSocket.value(), macPtr + totalBytesRead, |
150 | sizeof mac - totalBytesRead); |
151 | if (bytesRead == -1) { |
152 | qCWarning(QT_BT_BLUEZ) << "reading from crypto socket failed:" << strerror(errno); |
153 | return 0; |
154 | } |
155 | totalBytesRead += bytesRead; |
156 | } while (totalBytesRead < qint64(sizeof mac)); |
157 | return qFromBigEndian(mac); |
158 | #else // CONFIG_LINUX_CRYPTO_API |
159 | Q_UNUSED(message); |
160 | Q_UNUSED(csrk); |
161 | qCWarning(QT_BT_BLUEZ) << "CMAC calculation failed due to missing Linux crypto API." ; |
162 | return 0; |
163 | #endif |
164 | } |
165 | |
166 | bool LeCmacCalculator::verify(const QByteArray &message, const quint128 &csrk, |
167 | quint64 expectedMac) const |
168 | { |
169 | #ifdef CONFIG_LINUX_CRYPTO_API |
170 | const quint64 actualMac = calculateMac(message, csrk); |
171 | if (actualMac != expectedMac) { |
172 | qCWarning(QT_BT_BLUEZ) << hex << "signature verification failed: calculated mac:" |
173 | << actualMac << "expected mac:" << expectedMac; |
174 | return false; |
175 | } |
176 | return true; |
177 | #else // CONFIG_LINUX_CRYPTO_API |
178 | Q_UNUSED(message); |
179 | Q_UNUSED(csrk); |
180 | Q_UNUSED(expectedMac); |
181 | qCWarning(QT_BT_BLUEZ) << "CMAC verification failed due to missing Linux crypto API." ; |
182 | return false; |
183 | #endif |
184 | } |
185 | |
186 | QT_END_NAMESPACE |
187 | |