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
56QT_BEGIN_NAMESPACE
57
58Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
59
60LeCmacCalculator::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
84LeCmacCalculator::~LeCmacCalculator()
85{
86 if (m_baseSocket != -1)
87 close(m_baseSocket);
88}
89
90QByteArray 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
99quint64 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
166bool 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
186QT_END_NAMESPACE
187