Uniot Core
0.8.1
Loading...
Searching...
No Matches
COSEMessage.h
Go to the documentation of this file.
1/*
2 * This is a part of the Uniot project.
3 * Copyright (C) 2016-2023 Uniot <contact@uniot.io>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <Arduino.h>
22#include <Bytes.h>
23#include <Ed25519.h>
24#include <Logger.h>
25
26#include "CBORObject.h"
27#include "COSE.h"
28#include "ICOSESigner.h"
29
30namespace uniot {
41 public:
43 COSEMessage(COSEMessage const &) = delete;
45 void operator=(COSEMessage const &) = delete;
46
53 : mpProtectedHeader(nullptr),
54 mpUnprotectedHeader(nullptr),
55 mpPayload(nullptr),
56 mpSignature(nullptr),
57 mReadSuccess(false) {
58 _create();
59 }
60
67 : mpProtectedHeader(nullptr),
68 mpUnprotectedHeader(nullptr),
69 mpPayload(nullptr),
70 mpSignature(nullptr) {
71 mReadSuccess = _read(buf);
72 }
73
75 virtual ~COSEMessage() {
76 _clean();
77 }
78
86 bool read(const Bytes &buf) {
87 _clean();
88 mReadSuccess = _read(buf);
89 return mReadSuccess;
90 }
91
98 inline bool wasReadSuccessful() const {
99 return mReadSuccess;
100 }
101
108 return mRoot._getBytes(mpProtectedHeader);
109 }
110
117 return mRoot._getMap(mpUnprotectedHeader);
118 }
119
127 return getUnprotectedHeader().getBytes(COSEHeaderLabel::KeyIdentifier);
128 }
129
135 inline Bytes getPayload() {
136 return mRoot._getBytes(mpPayload);
137 }
138
145 return mRoot._getBytes(mpSignature);
146 }
147
154 inline bool isSigned() {
155 auto signature = getSignature();
157 auto alg = pHeader.getInt(COSEHeaderLabel::Algorithm);
158
159 return alg != 0 && signature.size() > 0;
160 }
161
167 void setUnprotectedKid(const Bytes& kid) {
168 getUnprotectedHeader().put(COSEHeaderLabel::KeyIdentifier, kid.raw(), kid.size());
169 }
170
178 bool setPayload(const Bytes &payload) {
179 mRawPayload = payload;
180 return cn_cbor_data_update(mpPayload, mRawPayload.raw(), mRawPayload.size());
181 }
182
189 void sign(const ICOSESigner &signer, const Bytes &external = Bytes()) {
190 auto alg = signer.signerAlgorithm();
191 if (alg != COSEAlgorithm::EdDSA) {
192 UNIOT_LOG_ERROR("sign failed: alg '%d' is not supported", alg);
193 return;
194 }
195
196 CBORObject pHeader;
197 pHeader.put(COSEHeaderLabel::Algorithm, alg);
198 _setProtectedHeader(pHeader);
199
200 auto toSign = _toBeSigned(external);
201 auto signature = signer.sign(toSign);
202 _setSignature(signature);
203 }
204
212 bool verify(const Bytes &publicKey) {
214 auto alg = pHeader.getInt(COSEHeaderLabel::Algorithm);
215 if (alg != COSEAlgorithm::EdDSA) {
216 UNIOT_LOG_ERROR("verify failed: alg '%d' is not supported", alg);
217 return false;
218 }
219
220 auto toVerify = _toBeSigned();
221 auto signature = getSignature();
222
223 switch (alg) {
224 case COSEAlgorithm::EdDSA:
225 return Ed25519::verify(signature.raw(), publicKey.raw(), toVerify.raw(), toVerify.size());
226 default:
227 return false;
228 }
229 }
230
236 Bytes build() const {
237 return mRoot.build();
238 }
239
243 void clean() {
244 _clean();
245 _create();
246 }
247
248 private:
252 void _create() {
253 mRoot._clean();
254 auto root = cn_cbor_array_create(mRoot._errback());
255 cn_cbor_array_append(root, mpProtectedHeader = cn_cbor_data_create(nullptr, 0, mRoot._errback()), mRoot._errback());
256 cn_cbor_array_append(root, mpUnprotectedHeader = cn_cbor_map_create(mRoot._errback()), mRoot._errback());
257 cn_cbor_array_append(root, mpPayload = cn_cbor_data_create(nullptr, 0, mRoot._errback()), mRoot._errback());
258 cn_cbor_array_append(root, mpSignature = cn_cbor_data_create(nullptr, 0, mRoot._errback()), mRoot._errback());
259 mRoot.mpMapNode = cn_cbor_tag_create(COSETag::Sign1, root, mRoot._errback());
260 }
261
269 bool _read(const Bytes &buf) {
270 mRoot.read(buf);
271
272 if (mRoot.mErr.err != CN_CBOR_NO_ERROR) {
273 UNIOT_LOG_ERROR("read failed: %s", cn_cbor_error_str[mRoot.mErr.err]);
274 return false;
275 }
276 if (mRoot.mpMapNode->type != CN_CBOR_TAG) {
277 UNIOT_LOG_ERROR("read failed: there is no tag");
278 return false;
279 }
280 if (mRoot.mpMapNode->v.sint != COSETag::Sign1) {
281 UNIOT_LOG_ERROR("read failed: tag is not 18 (COSE_Sign1)");
282 return false;
283 }
284 auto rootArray = mRoot.mpMapNode->first_child;
285 if (!rootArray) {
286 UNIOT_LOG_ERROR("read failed: root not found");
287 return false;
288 }
289 if (rootArray->type != CN_CBOR_ARRAY) {
290 UNIOT_LOG_ERROR("read failed: root is not an array");
291 return false;
292 }
293 auto protectedHeader = cn_cbor_index(rootArray, 0);
294 if (!protectedHeader) {
295 UNIOT_LOG_ERROR("read failed: protectedHeader not found");
296 return false;
297 }
298 if (protectedHeader->type != CN_CBOR_BYTES) {
299 UNIOT_LOG_ERROR("read failed: protectedHeader is not bytes");
300 return false;
301 }
302 auto unprotectedHeader = cn_cbor_index(rootArray, 1);
303 if (!unprotectedHeader) {
304 UNIOT_LOG_ERROR("read failed: unprotectedHeader not found");
305 return false;
306 }
307 if (unprotectedHeader->type != CN_CBOR_MAP) {
308 UNIOT_LOG_ERROR("read failed: unprotectedHeader is not map");
309 return false;
310 }
311 auto payload = cn_cbor_index(rootArray, 2);
312 if (!payload) {
313 UNIOT_LOG_ERROR("read failed: payload not found");
314 return false;
315 }
316 if (payload->type != CN_CBOR_BYTES) {
317 UNIOT_LOG_ERROR("read failed: payload is not bytes");
318 return false;
319 }
320 auto signature = cn_cbor_index(rootArray, 3);
321 if (!signature) {
322 UNIOT_LOG_ERROR("read failed: signature not found");
323 return false;
324 }
325 if (signature->type != CN_CBOR_BYTES) {
326 UNIOT_LOG_ERROR("read failed: signature is not bytes");
327 return false;
328 }
329
330 mpProtectedHeader = protectedHeader;
331 mpUnprotectedHeader = unprotectedHeader;
332 mpPayload = payload;
333 mpSignature = signature;
334
335 return true;
336 }
337
341 void _clean() {
342 mRoot._clean();
343 mpProtectedHeader = nullptr;
344 mpUnprotectedHeader = nullptr;
345 mpPayload = nullptr;
346 mpSignature = nullptr;
347
348 mRawProtectedHeader.clean();
349 mRawPayload.clean();
350 mRawSignature.clean();
351 }
352
360 bool _setProtectedHeader(const CBORObject &pHeader) {
361 mRawProtectedHeader = pHeader.build();
362 return cn_cbor_data_update(mpProtectedHeader, mRawProtectedHeader.raw(), mRawProtectedHeader.size());
363 }
364
372 bool _setSignature(const Bytes &signature) {
373 mRawSignature = signature;
374 return cn_cbor_data_update(mpSignature, mRawSignature.raw(), mRawSignature.size());
375 }
376
383 Bytes _toBeSigned(const Bytes &external = Bytes()) {
384 auto protectedHeader = getProtectedHeader();
385 auto payload = getPayload();
386
387 auto sigStruct = cn_cbor_array_create(mRoot._errback());
388 cn_cbor_array_append(sigStruct, cn_cbor_string_create("Signature1", mRoot._errback()), mRoot._errback()); // context
389 cn_cbor_array_append(sigStruct, cn_cbor_data_create(protectedHeader.raw(), protectedHeader.size(), mRoot._errback()), mRoot._errback()); // body_protected
390 cn_cbor_array_append(sigStruct, cn_cbor_data_create(external.raw(), external.size(), mRoot._errback()), mRoot._errback()); // external_aad
391 cn_cbor_array_append(sigStruct, cn_cbor_data_create(payload.raw(), payload.size(), mRoot._errback()), mRoot._errback()); // payload
392 auto rawSigStruct = mRoot._build(sigStruct);
393 cn_cbor_free(sigStruct);
394
395 return rawSigStruct;
396 }
397
398 CBORObject mRoot;
399 cn_cbor *mpProtectedHeader;
400 cn_cbor *mpUnprotectedHeader;
401 cn_cbor *mpPayload;
402 cn_cbor *mpSignature;
403
404 Bytes mRawProtectedHeader;
405 Bytes mRawPayload;
406 Bytes mRawSignature;
407 bool mReadSuccess;
408};
409
410} // namespace uniot
Definition Bytes.h:38
const uint8_t * raw() const
Gets a const pointer to the raw byte array.
Definition Bytes.h:235
size_t size() const
Gets the size of the byte array.
Definition Bytes.h:303
Definition CBORObject.h:40
long getInt(int key) const
Get an integer value at a specific integer key.
Definition CBORObject.h:399
Bytes getBytes(int key) const
Get binary data at a specific integer key.
Definition CBORObject.h:461
CBORObject & put(int key, int value)
Put an integer value at a specific integer key.
Definition CBORObject.h:170
void read(const Bytes &buf)
Read CBOR data from a buffer.
Definition CBORObject.h:480
bool verify(const Bytes &publicKey)
Verifies the signature of the message using the provided public key.
Definition COSEMessage.h:212
bool setPayload(const Bytes &payload)
Sets the payload of the message.
Definition COSEMessage.h:178
CBORObject getUnprotectedHeader()
Gets the unprotected header as a CBORObject.
Definition COSEMessage.h:116
Bytes getSignature()
Gets the signature of the message.
Definition COSEMessage.h:144
void operator=(COSEMessage const &)=delete
Assignment operator disabled.
bool read(const Bytes &buf)
Reads a CBOR-encoded COSE_Sign1 message.
Definition COSEMessage.h:86
bool wasReadSuccessful() const
Checks if the message was read successfully.
Definition COSEMessage.h:98
virtual ~COSEMessage()
Destructor.
Definition COSEMessage.h:75
void clean()
Resets the message to its initial empty state.
Definition COSEMessage.h:243
Bytes getProtectedHeader()
Gets the protected header as a byte string.
Definition COSEMessage.h:107
COSEMessage()
Default constructor.
Definition COSEMessage.h:52
Bytes build() const
Builds the CBOR representation of the message.
Definition COSEMessage.h:236
COSEMessage(Bytes buf)
Constructs a message from existing CBOR data.
Definition COSEMessage.h:66
Bytes getPayload()
Gets the payload of the message.
Definition COSEMessage.h:135
Bytes getUnprotectedKid()
Gets the key identifier from the unprotected header.
Definition COSEMessage.h:126
void sign(const ICOSESigner &signer, const Bytes &external=Bytes())
Signs the message using the provided signer.
Definition COSEMessage.h:189
bool isSigned()
Checks if the message has a valid signature structure.
Definition COSEMessage.h:154
void setUnprotectedKid(const Bytes &kid)
Sets the key identifier in the unprotected header.
Definition COSEMessage.h:167
COSEMessage(COSEMessage const &)=delete
Copy constructor disabled.
Interface for CBOR Object Signing and Encryption (COSE) signing operations.
Definition ICOSESigner.h:35
virtual Bytes sign(const Bytes &data) const =0
Signs the provided data using the implementation's cryptographic algorithm.
virtual COSEAlgorithm signerAlgorithm() const =0
Gets the COSE algorithm identifier used by this signer.
#define UNIOT_LOG_ERROR(...)
Log an ERROR level message Used for critical errors that may prevent normal operation....
Definition Logger.h:226
Contains all classes and functions related to the Uniot Core.