Skip to content

Commit ede6b5c

Browse files
maxtropetsCopilot
andauthored
Consolidate COSE header keys (#7619)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 32a744b commit ede6b5c

14 files changed

Lines changed: 193 additions & 168 deletions

File tree

src/crypto/cbor.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616

1717
namespace ccf::cbor
1818
{
19+
namespace tag
20+
{
21+
// https://www.rfc-editor.org/rfc/rfc8152.html#section-2
22+
static constexpr int64_t COSE_SIGN_1 = 18;
23+
}
24+
1925
struct ValueImpl;
2026
using Value = std::shared_ptr<ValueImpl>;
2127

src/crypto/cose.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "ccf/crypto/cose.h"
55

66
#include "crypto/cbor.h"
7+
#include "crypto/cose.h"
78

89
#include <stdexcept>
910
#include <vector>
@@ -19,7 +20,7 @@ namespace ccf::cose::edit
1920
[&]() { return parse(cose_input); }, "Failed to parse COSE_Sign1");
2021

2122
const auto& cose_envelope = rethrow_with_msg(
22-
[&]() -> auto& { return cose_cbor->tag_at(18); },
23+
[&]() -> auto& { return cose_cbor->tag_at(ccf::cbor::tag::COSE_SIGN_1); },
2324
"Failed to parse COSE_Sign1 tag");
2425

2526
const auto& phdr = rethrow_with_msg(
@@ -76,7 +77,8 @@ namespace ccf::cose::edit
7677
edited.push_back(payload);
7778
edited.push_back(signature);
7879

79-
auto edited_envelope = make_tagged(18, make_array(std::move(edited)));
80+
auto edited_envelope =
81+
make_tagged(ccf::cbor::tag::COSE_SIGN_1, make_array(std::move(edited)));
8082
return serialize(edited_envelope);
8183
}
8284
}

src/crypto/cose.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the Apache 2.0 License.
3+
4+
#pragma once
5+
6+
#include <cstdint>
7+
#include <string>
8+
9+
namespace ccf
10+
{
11+
namespace cose
12+
{
13+
namespace header
14+
{
15+
namespace iana // https://www.iana.org/assignments/cose/cose.xhtml
16+
{
17+
static constexpr int64_t ALG = 1;
18+
static constexpr int64_t CONTENT_TYPE = 3;
19+
static constexpr int64_t KID = 4;
20+
static constexpr int64_t CWT_CLAIMS = 15;
21+
static constexpr int64_t X5CHAIN = 33;
22+
static constexpr int64_t PREIMAGE_CONTENT_TYPE = 259;
23+
static constexpr int64_t VDS = 395;
24+
static constexpr int64_t VDP = 396;
25+
26+
// https://www.ietf.org/archive/id/draft-ietf-cose-merkle-tree-proofs-18.html#name-cose-header-parameter
27+
static constexpr int64_t INCLUSION_PROOFS = -1;
28+
}
29+
namespace custom
30+
{
31+
static constexpr std::string_view CCF_V1 = "ccf.v1";
32+
static constexpr std::string_view TX_ID = "txid";
33+
static constexpr std::string_view TX_RANGE_BEGIN = "epoch.start.txid";
34+
static constexpr std::string_view TX_RANGE_END = "epoch.end.txid";
35+
static constexpr std::string_view EPOCH_LAST_MERKLE_ROOT =
36+
"epoch.end.merkle.root";
37+
}
38+
}
39+
namespace value
40+
{
41+
// https://www.ietf.org/archive/id/draft-birkholz-cose-receipts-ccf-profile-05.html#section-2
42+
static constexpr int64_t CCF_LEDGER_SHA256 = 2;
43+
44+
static constexpr std::string_view CT_JSON = "application/json";
45+
static constexpr std::string_view CT_OCTET_STREAM =
46+
"application/octet-stream";
47+
}
48+
}
49+
namespace cwt::header
50+
{
51+
namespace iana // https://www.iana.org/assignments/cwt/cwt.xhtml
52+
{
53+
static constexpr int64_t ISS = 1;
54+
static constexpr int64_t SUB = 2;
55+
56+
/* Value is **PLAIN INTEGER**, as per
57+
* https://www.rfc-editor.org/rfc/rfc8392#section-2. Quote:
58+
*
59+
* The "NumericDate" term in this specification has the same meaning and
60+
* processing rules as the JWT "NumericDate" term defined in Section 2 of
61+
* [RFC7519], except that it is represented as a CBOR numericdate (from
62+
* Section 2.4.1 of [RFC7049]) instead of a JSON number. The encoding is
63+
* modified so that the leading tag 1 (epoch-based date/time) MUST be
64+
* omitted.
65+
*/
66+
static constexpr int64_t IAT = 6;
67+
}
68+
namespace custom
69+
{
70+
static constexpr std::string_view SVN = "svn";
71+
}
72+
}
73+
}

src/crypto/openssl/cose_sign.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "crypto/openssl/cose_sign.h"
55

66
#include "crypto/cbor.h"
7+
#include "crypto/cose.h"
78
#include "ds/internal_logger.h"
89

910
#include <openssl/evp.h>
@@ -38,7 +39,7 @@ namespace
3839
try
3940
{
4041
std::ignore = protected_headers->map_at(
41-
ccf::cbor::make_signed(ccf::crypto::COSE_PHEADER_KEY_ALG));
42+
ccf::cbor::make_signed(ccf::cose::header::iana::ALG));
4243
}
4344
catch (const CBORDecodeError& err)
4445
{
@@ -62,8 +63,7 @@ namespace
6263
auto& items = std::get<Map>(protected_headers->value).items;
6364
items.insert(
6465
items.begin(),
65-
{make_signed(ccf::crypto::COSE_PHEADER_KEY_ALG),
66-
make_signed(algorithm_id)});
66+
{make_signed(ccf::cose::header::iana::ALG), make_signed(algorithm_id)});
6767

6868
EVP_PKEY* evp_key = key;
6969
t_cose_key signing_key = {};
@@ -186,7 +186,8 @@ namespace ccf::crypto
186186
}
187187
cose_array.push_back(make_bytes(signature));
188188

189-
auto envelope = make_tagged(18, make_array(std::move(cose_array)));
189+
auto envelope = make_tagged(
190+
ccf::cbor::tag::COSE_SIGN_1, make_array(std::move(cose_array)));
190191
try
191192
{
192193
return serialize(envelope);

src/crypto/openssl/cose_sign.h

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,54 +10,6 @@
1010

1111
namespace ccf::crypto
1212
{
13-
// Standardised field: COSE tag.
14-
static constexpr int64_t COSE_TAG = 18;
15-
// Standardised field: algorithm used to sign.
16-
static constexpr int64_t COSE_PHEADER_KEY_ALG = 1;
17-
// Standardised: hash of the signing key.
18-
static constexpr int64_t COSE_PHEADER_KEY_ID = 4;
19-
// Standardised: CWT claims map.
20-
static constexpr int64_t COSE_PHEADER_KEY_CWT = 15;
21-
// Standardised: verifiable data structure.
22-
// https://www.ietf.org/archive/id/draft-ietf-cose-merkle-tree-proofs-18.html#name-cose-header-parameter
23-
static constexpr int64_t COSE_PHEADER_KEY_VDS = 395;
24-
static constexpr int64_t COSE_PHEADER_VDS_CCF_LEDGER_SHA256 = 2;
25-
// Standardised: issued at CWT claim. Value is **PLAIN INTEGER**, as per
26-
// https://www.rfc-editor.org/rfc/rfc8392#section-2. Quote:
27-
/* The "NumericDate" term in this specification has the same meaning and
28-
* processing rules as the JWT "NumericDate" term defined in Section 2 of
29-
* [RFC7519], except that it is represented as a CBOR numericdate (from
30-
* Section 2.4.1 of [RFC7049]) instead of a JSON number. The encoding is
31-
* modified so that the leading tag 1 (epoch-based date/time) MUST be
32-
* omitted.
33-
*/
34-
static constexpr int64_t COSE_PHEADER_KEY_IAT = 6;
35-
// Standardised: issuer CWT claim.
36-
// https://www.iana.org/assignments/cose/cose.xhtml#header-parameters
37-
/* The "iss" (issuer) claim identifies the principal that issued the CWT.
38-
* The "iss" value is a case-sensitive string containing a StringOrURI value.
39-
*/
40-
static constexpr int64_t COSE_PHEADER_KEY_ISS = 1;
41-
// Standardised: subject CWT claim.
42-
// https://www.iana.org/assignments/cose/cose.xhtml#header-parameters
43-
/* The "sub" (subject) claim identifies the principal that is the subject of
44-
* the CWT. The claims in a CWT are normally statements about the subject.
45-
* The "sub" value is a case-sensitive string containing a StringOrURI value.
46-
*/
47-
static constexpr int64_t COSE_PHEADER_KEY_SUB = 2;
48-
// CCF headers nested map key.
49-
static const std::string COSE_PHEADER_KEY_CCF = "ccf.v1";
50-
// CCF-specific: last signed TxID.
51-
static const std::string COSE_PHEADER_KEY_TXID = "txid";
52-
// CCF-specific: first TX in the range.
53-
static const std::string COSE_PHEADER_KEY_RANGE_BEGIN = "epoch.start.txid";
54-
// CCF-specific: last TX included in the range.
55-
static const std::string COSE_PHEADER_KEY_RANGE_END = "epoch.end.txid";
56-
// CCF-specific: last signed Merkle root hash in the range.
57-
static const std::string COSE_PHEADER_KEY_EPOCH_LAST_MERKLE_ROOT =
58-
"epoch.end.merkle.root";
59-
static const std::string COSE_PHEADER_UVM_SVN = "svn";
60-
6113
struct COSESignError : public std::runtime_error
6214
{
6315
COSESignError(const std::string& msg) : std::runtime_error(msg) {}

src/crypto/openssl/cose_verifier.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "x509_time.h"
1212

1313
#include <crypto/cbor.h>
14+
#include <crypto/cose.h>
1415
#include <openssl/evp.h>
1516
#include <openssl/ossl_typ.h>
1617
#include <openssl/x509.h>
@@ -28,7 +29,8 @@ namespace
2829
rethrow_with_msg([&]() { return parse(cose_msg); }, "Parse COSE CBOR");
2930

3031
const auto& cose_envelope = rethrow_with_msg(
31-
[&]() -> auto& { return cose_cbor->tag_at(18); }, "Parse COSE tag");
32+
[&]() -> auto& { return cose_cbor->tag_at(ccf::cbor::tag::COSE_SIGN_1); },
33+
"Parse COSE tag");
3234

3335
const auto& phdr_raw = rethrow_with_msg(
3436
[&]() -> auto& { return cose_envelope->array_at(0); },
@@ -39,7 +41,7 @@ namespace
3941

4042
const int64_t alg = rethrow_with_msg(
4143
[&]() {
42-
return phdr->map_at(make_signed(ccf::crypto::COSE_PHEADER_KEY_ALG))
44+
return phdr->map_at(make_signed(ccf::cose::header::iana::ALG))
4345
->as_signed();
4446
},
4547
"Retrieve alg from protected header");
@@ -206,7 +208,8 @@ namespace ccf::crypto
206208
rethrow_with_msg([&]() { return parse(cose_msg); }, "Parse COSE CBOR");
207209

208210
const auto& cose_envelope = rethrow_with_msg(
209-
[&]() -> auto& { return cose_cbor->tag_at(18); }, "Parse COSE tag");
211+
[&]() -> auto& { return cose_cbor->tag_at(ccf::cbor::tag::COSE_SIGN_1); },
212+
"Parse COSE tag");
210213

211214
const auto& phdr_raw = rethrow_with_msg(
212215
[&]() -> auto& { return cose_envelope->array_at(0); },
@@ -217,20 +220,22 @@ namespace ccf::crypto
217220

218221
const auto& ccf_claims = rethrow_with_msg(
219222
[&]() -> auto& {
220-
return phdr->map_at(make_string(ccf::crypto::COSE_PHEADER_KEY_CCF));
223+
return phdr->map_at(make_string(ccf::cose::header::custom::CCF_V1));
221224
},
222225
"Retrieve CCF claims");
223226

224227
auto from = rethrow_with_msg(
225228
[&]() {
226-
return ccf_claims->map_at(make_string(COSE_PHEADER_KEY_RANGE_BEGIN))
229+
return ccf_claims
230+
->map_at(make_string(ccf::cose::header::custom::TX_RANGE_BEGIN))
227231
->as_string();
228232
},
229233
"Retrieve epoch range begin");
230234

231235
auto to = rethrow_with_msg(
232236
[&]() {
233-
return ccf_claims->map_at(make_string(COSE_PHEADER_KEY_RANGE_END))
237+
return ccf_claims
238+
->map_at(make_string(ccf::cose::header::custom::TX_RANGE_END))
234239
->as_string();
235240
},
236241
"Retrieve epoch range end");

src/crypto/test/cose.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ TEST_CASE("Check unprotected header")
177177
auto csp_set = ccf::cose::edit::set_unprotected_header(csp, desc);
178178

179179
auto edited = parse(csp_set);
180-
const auto& uhdr = edited->tag_at(18)->array_at(1);
180+
const auto& uhdr =
181+
edited->tag_at(ccf::cbor::tag::COSE_SIGN_1)->array_at(1);
181182

182183
std::vector<MapItem> ref;
183184
if (std::holds_alternative<ccf::cose::edit::pos::InArray>(position))
@@ -207,7 +208,8 @@ TEST_CASE("Check unprotected header")
207208
csp, ccf::cose::edit::desc::Empty{});
208209

209210
auto edited = parse(csp_set_empty);
210-
const auto& uhdr = edited->tag_at(18)->array_at(1);
211+
const auto& uhdr =
212+
edited->tag_at(ccf::cbor::tag::COSE_SIGN_1)->array_at(1);
211213

212214
auto ref_map = make_map({});
213215

src/crypto/test/crypto.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "ccf/ds/x509_time_fmt.h"
1717
#include "crypto/cbor.h"
1818
#include "crypto/certs.h"
19+
#include "crypto/cose.h"
1920
#include "crypto/csr.h"
2021
#include "crypto/openssl/cose_sign.h"
2122
#include "crypto/openssl/cose_verifier.h"
@@ -230,7 +231,7 @@ void require_match_headers(
230231
{
231232
auto decoded = ccf::cbor::parse(cose_sign);
232233

233-
const auto& as_cose = decoded->tag_at(18);
234+
const auto& as_cose = decoded->tag_at(ccf::cbor::tag::COSE_SIGN_1);
234235
const auto& raw_phdr = as_cose->array_at(0)->as_bytes();
235236

236237
auto phdr = ccf::cbor::parse(raw_phdr);

src/endpoints/authentication/cose_auth.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ namespace ccf
4040
[&]() { return parse(cose_sign1); }, "Parse COSE CBOR");
4141

4242
const auto& cose_envelope = rethrow_with_msg(
43-
[&]() -> auto& { return cose_cbor->tag_at(18); }, "Parse COSE tag");
43+
[&]() -> auto& {
44+
return cose_cbor->tag_at(ccf::cbor::tag::COSE_SIGN_1);
45+
},
46+
"Parse COSE tag");
4447

4548
const auto& phdr_raw = rethrow_with_msg(
4649
[&]() -> auto& { return cose_envelope->array_at(0); },
@@ -54,13 +57,13 @@ namespace ccf
5457

5558
parsed.alg = rethrow_with_msg(
5659
[&]() {
57-
return phdr->map_at(make_signed(headers::PARAM_ALG))->as_signed();
60+
return phdr->map_at(make_signed(header::iana::ALG))->as_signed();
5861
},
5962
"Parse alg in protected header");
6063

6164
parsed.kid = buf_to_string(rethrow_with_msg(
6265
[&]() {
63-
return phdr->map_at(make_signed(headers::PARAM_KID))->as_bytes();
66+
return phdr->map_at(make_signed(header::iana::KID))->as_bytes();
6467
},
6568
"Parse kid in protected header"));
6669

@@ -124,7 +127,10 @@ namespace ccf
124127
[&]() { return parse(cose_sign1); }, "Parse COSE CBOR");
125128

126129
const auto& cose_envelope = rethrow_with_msg(
127-
[&]() -> auto& { return cose_cbor->tag_at(18); }, "Parse COSE tag");
130+
[&]() -> auto& {
131+
return cose_cbor->tag_at(ccf::cbor::tag::COSE_SIGN_1);
132+
},
133+
"Parse COSE tag");
128134

129135
const auto& phdr_raw = rethrow_with_msg(
130136
[&]() -> auto& { return cose_envelope->array_at(0); },
@@ -138,13 +144,13 @@ namespace ccf
138144

139145
parsed.alg = rethrow_with_msg(
140146
[&]() {
141-
return phdr->map_at(make_signed(headers::PARAM_ALG))->as_signed();
147+
return phdr->map_at(make_signed(header::iana::ALG))->as_signed();
142148
},
143149
"Parse alg in protected header");
144150

145151
parsed.kid = buf_to_string(rethrow_with_msg(
146152
[&]() {
147-
return phdr->map_at(make_signed(headers::PARAM_KID))->as_bytes();
153+
return phdr->map_at(make_signed(header::iana::KID))->as_bytes();
148154
},
149155
"Parse kid in protected header"));
150156

0 commit comments

Comments
 (0)