14
14
#include " ccf/crypto/verifier.h"
15
15
#include " crypto/certs.h"
16
16
#include " crypto/csr.h"
17
+ #include " crypto/openssl/cose_sign.h"
18
+ #include " crypto/openssl/cose_verifier.h"
17
19
#include " crypto/openssl/key_pair.h"
18
20
#include " crypto/openssl/rsa_key_pair.h"
19
21
#include " crypto/openssl/symmetric_key.h"
26
28
#include < ctime>
27
29
#include < doctest/doctest.h>
28
30
#include < optional>
31
+ #include < qcbor/qcbor_spiffy_decode.h>
29
32
#include < span>
33
+ #include < t_cose/t_cose_sign1_sign.h>
34
+ #include < t_cose/t_cose_sign1_verify.h>
30
35
31
36
using namespace std ;
32
37
using namespace ccf ::crypto;
@@ -190,6 +195,107 @@ ccf::crypto::Pem generate_self_signed_cert(
190
195
kp, name, {}, valid_from, certificate_validity_period_days);
191
196
}
192
197
198
+ std::string qcbor_buf_to_string (const UsefulBufC& buf)
199
+ {
200
+ return std::string (reinterpret_cast <const char *>(buf.ptr ), buf.len );
201
+ }
202
+
203
+ t_cose_err_t verify_detached (
204
+ EVP_PKEY* key, std::span<const uint8_t > buf, std::span<const uint8_t > payload)
205
+ {
206
+ t_cose_key cose_key;
207
+ cose_key.crypto_lib = T_COSE_CRYPTO_LIB_OPENSSL;
208
+ cose_key.k .key_ptr = key;
209
+
210
+ t_cose_sign1_verify_ctx verify_ctx;
211
+ t_cose_sign1_verify_init (&verify_ctx, T_COSE_OPT_TAG_REQUIRED);
212
+ t_cose_sign1_set_verification_key (&verify_ctx, cose_key);
213
+
214
+ q_useful_buf_c buf_;
215
+ buf_.ptr = buf.data ();
216
+ buf_.len = buf.size ();
217
+
218
+ q_useful_buf_c payload_;
219
+ payload_.ptr = payload.data ();
220
+ payload_.len = payload.size ();
221
+
222
+ t_cose_err_t error = t_cose_sign1_verify_detached (
223
+ &verify_ctx, buf_, NULL_Q_USEFUL_BUF_C, payload_, nullptr );
224
+
225
+ return error;
226
+ }
227
+
228
+ void require_match_headers (
229
+ const std::unordered_map<int64_t , std::string>& headers,
230
+ const std::vector<uint8_t >& cose_sign)
231
+ {
232
+ UsefulBufC msg{cose_sign.data (), cose_sign.size ()};
233
+
234
+ // 0. Init and verify COSE tag
235
+ QCBORDecodeContext ctx;
236
+ QCBORDecode_Init (&ctx, msg, QCBOR_DECODE_MODE_NORMAL);
237
+ QCBORDecode_EnterArray (&ctx, nullptr );
238
+ REQUIRE_EQ (QCBORDecode_GetError (&ctx), QCBOR_SUCCESS);
239
+ REQUIRE_EQ (QCBORDecode_GetNthTagOfLast (&ctx, 0 ), CBOR_TAG_COSE_SIGN1);
240
+
241
+ // 1. Protected headers
242
+ struct q_useful_buf_c protected_parameters;
243
+ QCBORDecode_EnterBstrWrapped (
244
+ &ctx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &protected_parameters);
245
+ QCBORDecode_EnterMap (&ctx, NULL );
246
+
247
+ QCBORItem header_items[headers.size () + 2 ];
248
+ size_t curr_id{0 };
249
+ for (const auto & kv : headers)
250
+ {
251
+ header_items[curr_id].label .int64 = kv.first ;
252
+ header_items[curr_id].uLabelType = QCBOR_TYPE_INT64;
253
+ header_items[curr_id].uDataType = QCBOR_TYPE_TEXT_STRING;
254
+
255
+ curr_id++;
256
+ }
257
+
258
+ // Verify 'alg' is default-encoded.
259
+ header_items[curr_id].label .int64 = 1 ;
260
+ header_items[curr_id].uLabelType = QCBOR_TYPE_INT64;
261
+ header_items[curr_id].uDataType = QCBOR_TYPE_INT64;
262
+
263
+ header_items[++curr_id].uLabelType = QCBOR_TYPE_NONE;
264
+
265
+ QCBORDecode_GetItemsInMap (&ctx, header_items);
266
+ REQUIRE_EQ (QCBORDecode_GetError (&ctx), QCBOR_SUCCESS);
267
+
268
+ curr_id = 0 ;
269
+ for (const auto & kv : headers)
270
+ {
271
+ REQUIRE_NE (header_items[curr_id].uDataType , QCBOR_TYPE_NONE);
272
+ REQUIRE_EQ (
273
+ qcbor_buf_to_string (header_items[curr_id].val .string ), kv.second );
274
+
275
+ curr_id++;
276
+ }
277
+
278
+ // 'alg'
279
+ REQUIRE_NE (header_items[curr_id].uDataType , QCBOR_TYPE_NONE);
280
+
281
+ QCBORDecode_ExitMap (&ctx);
282
+ QCBORDecode_ExitBstrWrapped (&ctx);
283
+
284
+ // 2. Unprotected headers (skip).
285
+ QCBORItem item;
286
+ QCBORDecode_VGetNextConsume (&ctx, &item);
287
+
288
+ // 3. Skip payload (detached);
289
+ QCBORDecode_GetNext (&ctx, &item);
290
+
291
+ // 4. skip signature (should be verified by cose verifier).
292
+ QCBORDecode_GetNext (&ctx, &item);
293
+
294
+ // 5. Decode can be completed.
295
+ QCBORDecode_ExitArray (&ctx);
296
+ REQUIRE_EQ (QCBORDecode_Finish (&ctx), QCBOR_SUCCESS);
297
+ }
298
+
193
299
TEST_CASE (" Check verifier handles nested certs for both PEM and DER inputs" )
194
300
{
195
301
auto cert_der = ccf::crypto::raw_from_b64 (nested_cert);
@@ -1109,4 +1215,44 @@ TEST_CASE("Sign and verify with RSA key")
1109
1215
mdtype,
1110
1216
verify_salt_legth));
1111
1217
}
1112
- }
1218
+ }
1219
+
1220
+ TEST_CASE (" COSE sign & verify" )
1221
+ {
1222
+ std::shared_ptr<KeyPair_OpenSSL> kp =
1223
+ std::dynamic_pointer_cast<KeyPair_OpenSSL>(
1224
+ ccf::crypto::make_key_pair (CurveID::SECP384R1));
1225
+
1226
+ std::vector<uint8_t > payload{1 , 10 , 42 , 43 , 44 , 45 , 100 };
1227
+ const std::unordered_map<int64_t , std::string> protected_headers = {
1228
+ {36 , " thirsty six" }, {47 , " hungry seven" }};
1229
+ auto cose_sign = cose_sign1 (*kp, protected_headers, payload);
1230
+
1231
+ if constexpr (false ) // enable to see the whole cose_sign as byte string
1232
+ {
1233
+ std::cout << " Public key: " << kp->public_key_pem ().str () << std::endl;
1234
+ std::cout << " Serialised cose: " << std::hex << std::uppercase
1235
+ << std::setw (2 ) << std::setfill (' 0' );
1236
+ for (uint8_t x : cose_sign)
1237
+ std::cout << static_cast <int >(x) << ' ' ;
1238
+ std::cout << std::endl;
1239
+ std::cout << " Raw payload: " ;
1240
+ for (uint8_t x : payload)
1241
+ std::cout << static_cast <int >(x) << ' ' ;
1242
+ std::cout << std::endl;
1243
+ }
1244
+
1245
+ require_match_headers (protected_headers, cose_sign);
1246
+
1247
+ REQUIRE_EQ (verify_detached (*kp, cose_sign, payload), T_COSE_SUCCESS);
1248
+
1249
+ // Wrong payload, must not pass verification.
1250
+ REQUIRE_EQ (
1251
+ verify_detached (*kp, cose_sign, std::vector<uint8_t >{1 , 2 , 3 }),
1252
+ T_COSE_ERR_SIG_VERIFY);
1253
+
1254
+ // Empty headers and payload handled correctly
1255
+ cose_sign = cose_sign1 (*kp, {}, {});
1256
+ require_match_headers ({}, cose_sign);
1257
+ REQUIRE_EQ (verify_detached (*kp, cose_sign, {}), T_COSE_SUCCESS);
1258
+ }
0 commit comments