Vanetza
Loading...
Searching...
No Matches
certificate.cpp
1#include <vanetza/asn1/security_profile.hpp>
2#include VANETZA_ASN1_SECURITY_HEADER(Certificate.h)
3#include <vanetza/security/hash_algorithm.hpp>
4#include <vanetza/security/sha.hpp>
5#include <vanetza/security/v3/asn1_conversions.hpp>
6#include <vanetza/security/v3/certificate.hpp>
7#include <vanetza/security/v3/distance.hpp>
8#include <vanetza/security/v3/geometry.hpp>
9#include <boost/optional/optional.hpp>
10#include <cassert>
11#include <cstdlib>
12#include <cstring>
13
14namespace vanetza
15{
16namespace security
17{
18namespace v3
19{
20
21namespace
22{
23HashAlgorithm digest_hash_algorithm(const asn1::EtsiTs103097Certificate& cert);
24bool copy_curve_point(PublicKey& to, const asn1::EccP256CurvePoint& from);
25bool copy_curve_point(PublicKey& to, const asn1::EccP384CurvePoint& from);
26ByteBuffer fetch_octets(const OCTET_STRING_t& octets);
27ByteBuffer get_x_coordinate(const asn1::EccP256CurvePoint& point);
28ByteBuffer get_x_coordinate(const asn1::EccP384CurvePoint& point);
29
30bool is_compressed(const Vanetza_Security_EccP256CurvePoint& point);
31bool is_compressed(const Vanetza_Security_EccP384CurvePoint& point);
32bool is_signature_x_only(const Vanetza_Security_Signature_t& sig);
35bool contains_permission(const Vanetza_Security_SequenceOfPsidSspRange_t& permissions, ItsAid aid);
36bool equal(const asn1::TwoDLocation& lhs, const asn1::TwoDLocation& rhs);
37bool equal(const asn1::RectangularRegion& lhs, const asn1::RectangularRegion& rhs);
38bool equal(const asn1::SequenceOfRectangularRegion& lhs, const asn1::SequenceOfRectangularRegion& rhs);
39bool is_within(const asn1::GeographicRegion& inner, const asn1::CircularRegion& outer);
40bool is_within(const asn1::GeographicRegion& inner, const asn1::SequenceOfRectangularRegion& outer);
41bool is_within(const asn1::GeographicRegion& inner, const asn1::PolygonalRegion& outer);
42bool is_within(const asn1::GeographicRegion& inner, const asn1::GeographicRegion& outer);
43bool make_x_only(Vanetza_Security_EccP256CurvePoint&);
44bool make_x_only(Vanetza_Security_EccP384CurvePoint&);
45bool make_signature_x_only(Vanetza_Security_Signature_t& sig);
46
47} // namespace
48
49CertificateView::CertificateView(const asn1::EtsiTs103097Certificate* cert) :
50 m_cert(cert)
51{
52}
53
54Certificate::Certificate() :
55 Wrapper(asn_DEF_Vanetza_Security_EtsiTs103097Certificate),
56 CertificateView { content() }
57{
58 assert(CertificateView::m_cert == Wrapper::m_struct);
59}
60
61Certificate::Certificate(const asn1::EtsiTs103097Certificate& cert) :
62 Wrapper(asn_DEF_Vanetza_Security_EtsiTs103097Certificate, &cert),
63 CertificateView { content() }
64{
65 assert(CertificateView::m_cert == Wrapper::m_struct);
66}
67
68Certificate::Certificate(const Certificate& other) :
69 Wrapper(other), CertificateView(content())
70{
71 assert(CertificateView::m_cert == Wrapper::m_struct);
72}
73
74Certificate& Certificate::operator=(const Certificate& other)
75{
76 Wrapper::operator=(other);
77 CertificateView::m_cert = content();
78 assert(CertificateView::m_cert == Wrapper::m_struct);
79 return *this;
80}
81
82Certificate::Certificate(Certificate&& other) :
83 Wrapper(std::move(other)), CertificateView(content())
84{
85 assert(CertificateView::m_cert == Wrapper::m_struct);
86}
87
88Certificate& Certificate::operator=(Certificate&& other)
89{
90 Wrapper::operator=(std::move(other));
91 CertificateView::m_cert = content();
92 assert(CertificateView::m_cert == Wrapper::m_struct);
93 return *this;
94}
95
96boost::optional<HashedId8> CertificateView::calculate_digest() const
97{
98 return m_cert ? v3::calculate_digest(*m_cert) : boost::none;
99}
100
102{
103 return m_cert ? v3::get_verification_key_type(*m_cert) : KeyType::Unspecified;
104}
105
106bool CertificateView::valid_at_location(const PositionFix& location, const LocationChecker* lc) const
107{
108 return m_cert ? lc ? lc->valid_at_location(*m_cert, location) : false : false;
109}
110
111bool CertificateView::valid_at_timepoint(const Clock::time_point& timepoint) const
112{
113 return m_cert ? v3::valid_at_timepoint(*m_cert, timepoint) : false;
114}
115
116bool valid_at_timepoint(const asn1::EtsiTs103097Certificate& cert, const Clock::time_point& timepoint)
117{
118 const asn1::ValidityPeriod& validity = cert.toBeSigned.validityPeriod;
119 Clock::time_point start { std::chrono::seconds(validity.start) };
120 Clock::time_point end = start;
121
122 switch (validity.duration.present)
123 {
124 case Vanetza_Security_Duration_PR_microseconds:
125 end += std::chrono::microseconds(validity.duration.choice.microseconds);
126 break;
127 case Vanetza_Security_Duration_PR_milliseconds:
128 end += std::chrono::milliseconds(validity.duration.choice.milliseconds);
129 break;
130 case Vanetza_Security_Duration_PR_seconds:
131 end += std::chrono::seconds(validity.duration.choice.seconds);
132 break;
133 case Vanetza_Security_Duration_PR_minutes:
134 end += std::chrono::minutes(validity.duration.choice.minutes);
135 break;
136 case Vanetza_Security_Duration_PR_hours:
137 end += std::chrono::hours(validity.duration.choice.hours);
138 break;
139 case Vanetza_Security_Duration_PR_sixtyHours:
140 end += std::chrono::hours(60) * validity.duration.choice.sixtyHours;
141 break;
142 case Vanetza_Security_Duration_PR_years:
143 // one year is considered 31556952 seconds according to IEEE 1609.2
144 end += std::chrono::seconds(31556952) * validity.duration.choice.years;
145 break;
146 default:
147 // leave end at start and thus forming an invalid range
148 break;
149 }
150
151 return timepoint >= start && timepoint < end;
152}
153
155{
156 return m_cert ? v3::valid_for_application(*m_cert, aid) : false;
157}
158
160{
161 if (!m_cert) {
162 return false;
163 }
164
165 if (const auto* seq = m_cert->toBeSigned.certIssuePermissions) {
166 for (int i = 0; i < seq->list.count; ++i) {
167 const auto* group = seq->list.array[i];
168 if (!group) {
169 continue;
170 }
171
172 const auto& subject_permissions = group->subjectPermissions;
173 if (subject_permissions.present == Vanetza_Security_SubjectPermissions_PR_all) {
174 return true;
175 } else if (
176 subject_permissions.present == Vanetza_Security_SubjectPermissions_PR_explicit &&
177 contains_permission(subject_permissions.choice.Explicit, aid)) {
178 return true;
179 }
180 }
181 }
182
183 return false;
184}
185
186boost::optional<std::uint8_t> CertificateView::assurance_level() const
187{
188 if (!m_cert || !m_cert->toBeSigned.assuranceLevel || m_cert->toBeSigned.assuranceLevel->size < 1) {
189 return boost::none;
190 }
191
192 return m_cert->toBeSigned.assuranceLevel->buf[0];
193}
194
196{
197 if (!m_cert || !issuer.m_cert) {
198 return false;
199 }
200
201 const auto* issuer_region = issuer.m_cert->toBeSigned.region;
202 const auto* subject_region = m_cert->toBeSigned.region;
203 if (!issuer_region) {
204 return true;
205 } else if (!subject_region) {
206 return false;
207 } else {
208 return is_within(*subject_region, *issuer_region);
209 }
210}
211
212bool valid_for_application(const asn1::EtsiTs103097Certificate& cert, ItsAid aid)
213{
214 const asn1::SequenceOfPsidSsp* permissions = cert.toBeSigned.appPermissions;
215 if (permissions) {
216 for (int i = 0; i < permissions->list.count; ++i) {
217 if (permissions->list.array[i]->psid == aid) {
218 return true;
219 }
220 }
221 }
222
223 // only explicitly allowed applications are valid
224 return false;
225}
226
227boost::optional<HashedId8> CertificateView::issuer_digest() const
228{
229 boost::optional<HashedId8> digest;
230 if (m_cert != nullptr) {
231 switch (m_cert->issuer.present)
232 {
233 case Vanetza_Security_IssuerIdentifier_PR_sha256AndDigest:
234 digest = create_hashed_id8(m_cert->issuer.choice.sha256AndDigest);
235 break;
236 case Vanetza_Security_IssuerIdentifier_PR_sha384AndDigest:
237 digest = create_hashed_id8(m_cert->issuer.choice.sha384AndDigest);
238 break;
239 default:
240 break;
241 }
242 }
243 return digest;
244}
245
247{
248 return m_cert->issuer.present == Vanetza_Security_IssuerIdentifier_PR_self;
249}
250
252{
253 return m_cert ? m_cert->toBeSigned.region != nullptr : false;
254}
255
257{
258 return m_cert && m_cert->toBeSigned.certIssuePermissions != nullptr;
259}
260
262{
263 return m_cert && m_cert->toBeSigned.certIssuePermissions == nullptr && m_cert->toBeSigned.appPermissions != nullptr;
264}
265
267{
268 return m_cert ? v3::is_canonical(*m_cert) : false;
269}
270
272{
273 StartAndEndValidity start_and_end;
274 start_and_end.start_validity = Time32(m_cert->toBeSigned.validityPeriod.start);
275 Time32 duration = 0;
276 switch (m_cert->toBeSigned.validityPeriod.duration.present)
277 {
278 case Vanetza_Security_Duration_PR_NOTHING:
279 break;
280 case Vanetza_Security_Duration_PR_microseconds:
281 duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.microseconds/1000000;
282 break;
283 case Vanetza_Security_Duration_PR_milliseconds:
284 duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.milliseconds/1000;
285 break;
286 case Vanetza_Security_Duration_PR_seconds:
287 duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.seconds;
288 break;
289 case Vanetza_Security_Duration_PR_minutes:
290 duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.minutes*60;
291 break;
292 case Vanetza_Security_Duration_PR_hours:
293 duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.hours*60*60;
294 break;
295 case Vanetza_Security_Duration_PR_sixtyHours:
296 duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.sixtyHours*60*60*60;
297 break;
298 case Vanetza_Security_Duration_PR_years:
299 duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.years*60*60*24*365;
300 break;
301 default:
302 break;
303 }
304 start_and_end.end_validity = start_and_end.start_validity + duration;
305 return start_and_end;
306}
307
308bool is_canonical(const asn1::EtsiTs103097Certificate& cert)
309{
310 bool compressed_point = true;
311 const Vanetza_Security_VerificationKeyIndicator& indicator = cert.toBeSigned.verifyKeyIndicator;
312 if (indicator.present == Vanetza_Security_VerificationKeyIndicator_PR_verificationKey) {
313 const Vanetza_Security_PublicVerificationKey& pubkey = indicator.choice.verificationKey;
314 switch (pubkey.present) {
315 case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256:
316 compressed_point = is_compressed(pubkey.choice.ecdsaNistP256);
317 break;
318 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1:
319 compressed_point = is_compressed(pubkey.choice.ecdsaBrainpoolP256r1);
320 break;
321 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1:
322 compressed_point = is_compressed(pubkey.choice.ecdsaBrainpoolP384r1);
323 break;
324 default:
325 break;
326 }
327 } else if (indicator.present == Vanetza_Security_VerificationKeyIndicator_PR_reconstructionValue) {
328 compressed_point = is_compressed(indicator.choice.reconstructionValue);
329 }
330
331 if (!compressed_point) {
332 return false;
333 } else if (cert.signature && !is_signature_x_only(*cert.signature)) {
334 return false;
335 } else {
336 return true;
337 }
338}
339
340ByteBuffer CertificateView::encode() const
341{
342 return m_cert ? asn1::encode_oer(asn_DEF_Vanetza_Security_EtsiTs103097Certificate, m_cert) : ByteBuffer {};
343}
344
345ByteBuffer Certificate::encode() const
346{
347 return Wrapper::encode();
348}
349
350boost::optional<Certificate> CertificateView::canonicalize() const
351{
352 return m_cert ? v3::canonicalize(*m_cert) : boost::none;
353}
354
355static boost::optional<Certificate> canonicalize(Certificate&& canonical)
356{
357 bool success = true;
358
359 if (canonical->toBeSigned.verifyKeyIndicator.present == Vanetza_Security_VerificationKeyIndicator_PR_verificationKey) {
360 Vanetza_Security_PublicVerificationKey& pubkey = canonical->toBeSigned.verifyKeyIndicator.choice.verificationKey;
361 switch (pubkey.present) {
362 case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256:
363 success &= compress(pubkey.choice.ecdsaNistP256);
364 break;
365 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1:
366 success &= compress(pubkey.choice.ecdsaBrainpoolP256r1);
367 break;
368 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1:
369 success &= compress(pubkey.choice.ecdsaBrainpoolP384r1);
370 break;
371 default:
372 break;
373 }
374 } else if (canonical->toBeSigned.verifyKeyIndicator.present == Vanetza_Security_VerificationKeyIndicator_PR_reconstructionValue) {
375 success &= compress(canonical->toBeSigned.verifyKeyIndicator.choice.reconstructionValue);
376 }
377
378 if (canonical->toBeSigned.encryptionKey) {
379 Vanetza_Security_BasePublicEncryptionKey& pubkey = canonical->toBeSigned.encryptionKey->publicKey;
380 switch (pubkey.present) {
381 case Vanetza_Security_BasePublicEncryptionKey_PR_eciesNistP256:
382 success &= compress(pubkey.choice.eciesNistP256);
383 break;
384 case Vanetza_Security_BasePublicEncryptionKey_PR_eciesBrainpoolP256r1:
385 success &= compress(pubkey.choice.eciesBrainpoolP256r1);
386 break;
387 default:
388 break;
389 }
390 }
391
392 if (canonical->signature) {
393 success &= make_signature_x_only(*canonical->signature);
394 }
395
396 if (success) {
397 assert(is_canonical(*canonical));
398 return canonical;
399 } else {
400 return boost::none;
401 }
402}
403
404boost::optional<Certificate> canonicalize(const asn1::EtsiTs103097Certificate& cert)
405{
406 try {
407 Certificate copy { cert };
408 return canonicalize(std::move(copy));
409 } catch (const std::exception&) {
410 return boost::none;
411 }
412}
413
414boost::optional<HashedId8> calculate_digest_internal(const asn1::EtsiTs103097Certificate& cert, HashAlgorithm hash_algo)
415{
416 boost::optional<HashedId8> digest;
417
418 try {
419 ByteBuffer buffer = asn1::encode_oer(asn_DEF_Vanetza_Security_EtsiTs103097Certificate, &cert);
420
421 switch (hash_algo)
422 {
423 case HashAlgorithm::SHA256:
424 digest = create_hashed_id8(calculate_sha256_digest(buffer.data(), buffer.size()));
425 break;
426 case HashAlgorithm::SHA384:
427 digest = create_hashed_id8(calculate_sha384_digest(buffer.data(), buffer.size()));
428 break;
429 default:
430 break;
431 }
432 } catch (const std::exception&) {
433 // cannot calculate digest of non-encodable certificate
434 }
435
436 return digest;
437}
438
439boost::optional<HashedId8> calculate_digest(const asn1::EtsiTs103097Certificate& cert)
440{
441 boost::optional<HashedId8> digest;
442 const HashAlgorithm hash_algo = digest_hash_algorithm(cert);
443 if (hash_algo != HashAlgorithm::Unspecified) {
444 if (is_canonical(cert)) {
445 digest = calculate_digest_internal(cert, hash_algo);
446 } else {
447 auto maybe_canonical_cert = canonicalize(cert);
448 if (maybe_canonical_cert) {
449 digest = calculate_digest_internal(*maybe_canonical_cert.value(), hash_algo);
450 }
451 }
452 }
453 return digest;
454}
455
456KeyType get_verification_key_type(const asn1::EtsiTs103097Certificate& cert)
457{
458 KeyType key_type = KeyType::Unspecified;
459
460 if (cert.toBeSigned.verifyKeyIndicator.present == Vanetza_Security_VerificationKeyIndicator_PR_verificationKey)
461 {
462 switch (cert.toBeSigned.verifyKeyIndicator.choice.verificationKey.present)
463 {
464 case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256:
465 key_type = KeyType::NistP256;
466 break;
467 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1:
468 key_type = KeyType::BrainpoolP256r1;
469 break;
470 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1:
471 key_type = KeyType::BrainpoolP384r1;
472 break;
473 default:
474 break;
475 }
476 }
477
478 return key_type;
479}
480
481boost::optional<PublicKey> get_public_key(const asn1::EtsiTs103097Certificate& cert)
482{
483 asn1::VerificationKeyIndicator indicator = cert.toBeSigned.verifyKeyIndicator;
484 if (indicator.present != Vanetza_Security_VerificationKeyIndicator_PR_verificationKey) {
485 return boost::none;
486 }
487
488 const asn1::PublicVerificationKey& input = cert.toBeSigned.verifyKeyIndicator.choice.verificationKey;
489 PublicKey output;
490 switch (input.present) {
491 case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256:
492 output.type = KeyType::NistP256;
493 if (copy_curve_point(output, input.choice.ecdsaNistP256)) {
494 return output;
495 } else {
496 return boost::none;
497 }
498 break;
499 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1:
500 output.type = KeyType::BrainpoolP256r1;
501 if (copy_curve_point(output, input.choice.ecdsaBrainpoolP256r1)) {
502 return output;
503 } else {
504 return boost::none;
505 }
506 break;
507 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1:
508 output.type = KeyType::BrainpoolP384r1;
509 if (copy_curve_point(output, input.choice.ecdsaBrainpoolP384r1)) {
510 return output;
511 } else {
512 return boost::none;
513 }
514 break;
515 default:
516 return boost::none;
517 break;
518 }
519}
520
521boost::optional<PublicKey> get_public_encryption_key(const asn1::EtsiTs103097Certificate& cert)
522{
523 const asn1::PublicEncryptionKey* enc_key = cert.toBeSigned.encryptionKey;
524 if (!enc_key || enc_key->supportedSymmAlg != Vanetza_Security_SymmAlgorithm_aes128Ccm) {
525 return boost::none;
526 }
527
528 PublicKey output;
529 switch (enc_key->publicKey.present) {
530 case Vanetza_Security_BasePublicEncryptionKey_PR_eciesNistP256:
531 output.type = KeyType::NistP256;
532 if (copy_curve_point(output, enc_key->publicKey.choice.eciesNistP256)) {
533 return output;
534 } else {
535 return boost::none;
536 }
537 break;
538 case Vanetza_Security_BasePublicEncryptionKey_PR_eciesBrainpoolP256r1:
539 output.type = KeyType::BrainpoolP256r1;
540 if (copy_curve_point(output, enc_key->publicKey.choice.eciesBrainpoolP256r1)) {
541 return output;
542 } else {
543 return boost::none;
544 }
545 break;
546 default:
547 return boost::none;
548 break;
549 }
550}
551
552boost::optional<Signature> get_signature(const asn1::EtsiTs103097Certificate& cert)
553{
554 if (!cert.signature) {
555 return boost::none;
556 }
557
558 const asn1::Signature* asn = cert.signature;
559 Signature sig;
560 switch (asn->present) {
561 case Vanetza_Security_Signature_PR_ecdsaNistP256Signature:
562 sig.type = KeyType::NistP256;
563 sig.r = get_x_coordinate(asn->choice.ecdsaNistP256Signature.rSig);
564 sig.s = fetch_octets(asn->choice.ecdsaNistP256Signature.sSig);
565 break;
566 case Vanetza_Security_Signature_PR_ecdsaBrainpoolP256r1Signature:
567 sig.type = KeyType::BrainpoolP256r1;
568 sig.r = get_x_coordinate(asn->choice.ecdsaBrainpoolP256r1Signature.rSig);
569 sig.s = fetch_octets(asn->choice.ecdsaBrainpoolP256r1Signature.sSig);
570 break;
571 case Vanetza_Security_Signature_PR_ecdsaBrainpoolP384r1Signature:
572 sig.type = KeyType::BrainpoolP384r1;
573 sig.r = get_x_coordinate(asn->choice.ecdsaBrainpoolP384r1Signature.rSig);
574 sig.s = fetch_octets(asn->choice.ecdsaBrainpoolP384r1Signature.sSig);
575 break;
576 default:
577 return boost::none;
578 }
579
580 return sig;
581}
582
583std::list<ItsAid> get_aids(const asn1::EtsiTs103097Certificate& cert)
584{
585 std::list<ItsAid> aids;
586 const asn1::SequenceOfPsidSsp* seq = cert.toBeSigned.appPermissions;
587 if (seq) {
588 for (int i = 0; i < seq->list.count; ++i) {
589 aids.push_back(seq->list.array[i]->psid);
590 }
591 }
592 return aids;
593}
594
595ByteBuffer get_app_permissions(const asn1::EtsiTs103097Certificate& cert, ItsAid aid)
596{
597 ByteBuffer perms;
598 const asn1::SequenceOfPsidSsp* seq = cert.toBeSigned.appPermissions;
599 if (seq) {
600 for (int i = 0; i < seq->list.count; ++i) {
601 if (seq->list.array[i]->psid == aid && seq->list.array[i]->ssp != nullptr) {
602 const asn1::ServiceSpecificPermissions& ssp = *seq->list.array[i]->ssp;
603 if (ssp.present == Vanetza_Security_ServiceSpecificPermissions_PR_bitmapSsp) {
604 const asn1::BitmapSsp& bitmap = ssp.choice.bitmapSsp;
605 perms.assign(bitmap.buf, bitmap.buf + bitmap.size);
606 break;
607 } else if (ssp.present == Vanetza_Security_ServiceSpecificPermissions_PR_opaque) {
608 const asn1::Opaque& opaque = ssp.choice.opaque;
609 perms.assign(opaque.buf, opaque.buf + opaque.size);
610 break;
611 }
612 }
613 }
614 }
615 return perms;
616}
617
618void add_psid_group_permission(asn1::PsidGroupPermissions* group_permission, ItsAid aid, const ByteBuffer& ssp, const ByteBuffer& bitmask)
619{
620 auto psid_range_scr = asn1::allocate<asn1::PsidSspRange>();
621 psid_range_scr->psid = aid;
622 psid_range_scr->sspRange = asn1::allocate<asn1::SspRange>();
623 psid_range_scr->sspRange->present = Vanetza_Security_SspRange_PR_bitmapSspRange;
624 OCTET_STRING_fromBuf(
625 &psid_range_scr->sspRange->choice.bitmapSspRange.sspValue,
626 reinterpret_cast<const char*>(ssp.data()),
627 ssp.size()
628 );
629 OCTET_STRING_fromBuf(
630 &psid_range_scr->sspRange->choice.bitmapSspRange.sspBitmask,
631 reinterpret_cast<const char*>(bitmask.data()),
632 bitmask.size()
633 );
634 ASN_SEQUENCE_ADD(&group_permission->subjectPermissions.choice.Explicit, psid_range_scr);
635}
636
637void Certificate::add_app_permission(ItsAid aid, const ByteBuffer& ssp)
638{
639 asn1::SequenceOfPsidSsp* seq = m_struct->toBeSigned.appPermissions;
640 if (!seq) {
641 seq = asn1::allocate<asn1::SequenceOfPsidSsp>();
642 m_struct->toBeSigned.appPermissions = seq;
643 }
644
645 auto psid_ptr = asn1::allocate<asn1::PsidSsp>();
646 psid_ptr->psid = aid;
647 if (!ssp.empty()) {
648 psid_ptr->ssp = asn1::allocate<asn1::ServiceSpecificPermissions>();
649 psid_ptr->ssp->present = Vanetza_Security_ServiceSpecificPermissions_PR_bitmapSsp;
650 OCTET_STRING_fromBuf(
651 &psid_ptr->ssp->choice.bitmapSsp,
652 reinterpret_cast<const char *>(ssp.data()),
653 ssp.size()
654 );
655 }
656 ASN_SEQUENCE_ADD(seq, psid_ptr);
657}
658
659void Certificate::add_cert_issue_permission(asn1::PsidGroupPermissions* group_permission)
660{
661 asn1::SequenceOfPsidGroupPermissions* seq = m_struct->toBeSigned.certIssuePermissions;
662 if (!seq) {
663 seq = asn1::allocate<asn1::SequenceOfPsidGroupPermissions>();
664 m_struct->toBeSigned.certIssuePermissions = seq;
665 }
666 ASN_SEQUENCE_ADD(seq, group_permission);
667}
668
669void Certificate::set_signature(const SomeEcdsaSignature& signature)
670{
671 if (!m_struct->signature) {
672 m_struct->signature = asn1::allocate<asn1::Signature>();
673 } else {
674 asn1::reset(m_struct->signature);
675 }
676
677 struct signature_visitor : public boost::static_visitor<>
678 {
679 asn1::Signature* sig;
680
681 explicit signature_visitor(asn1::Signature* s) : sig(s)
682 {
683 }
684
685 void operator()(const EcdsaSignature& signature) const
686 {
687 sig->present = Vanetza_Security_Signature_PR_ecdsaNistP256Signature;
688 assign(&sig->choice.ecdsaNistP256Signature.sSig, signature.s);
689 sig->choice.ecdsaNistP256Signature.rSig = to_asn1(signature.R);
690 }
691
692 void operator()(const EcdsaSignatureFuture& signature) const
693 {
694 this->operator()(signature.get());
695 }
696 };
697
698 boost::apply_visitor(signature_visitor(m_struct->signature), signature);
699}
700
701Certificate fake_certificate()
702{
703 Certificate cert;
704 cert->issuer.present = Vanetza_Security_IssuerIdentifier_PR_self;
705 cert->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_none;
706 std::array<char, 3> craca_id = { 0, 0, 0};
707 OCTET_STRING_fromBuf(&cert->toBeSigned.cracaId, craca_id.data(), craca_id.size());
708 cert->version = 3;
709 cert->toBeSigned.crlSeries = 0;
710 cert->toBeSigned.validityPeriod.start = 0;
711 cert->toBeSigned.validityPeriod.duration.present = Vanetza_Security_Duration_PR_minutes;
712 cert->toBeSigned.validityPeriod.duration.choice.minutes = 10080;
713 cert->toBeSigned.verifyKeyIndicator.present = Vanetza_Security_VerificationKeyIndicator_PR_verificationKey;
714 cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.present = Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256;
715 cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.present = Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0;
716 std::array<char, 32> dummy_r {}; // 32 octets for P256 signature
717 OCTET_STRING_fromBuf(
718 &cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.choice.x_only,
719 dummy_r.data(), dummy_r.size()
720 );
721 cert.add_app_permission(aid::CA, ByteBuffer({ 1, 0, 0 }));
722 return cert;
723}
724
725void serialize(OutputArchive& ar, const Certificate& certificate)
726{
727 ByteBuffer buffer = certificate.encode();
728 ar.save_binary(buffer.data(), buffer.size());
729}
730
731namespace
732{
733
734bool contains_permission(const Vanetza_Security_SequenceOfPsidSspRange_t& permissions, ItsAid aid)
735{
736 for (int i = 0; i < permissions.list.count; ++i) {
737 const auto* permission = permissions.list.array[i];
738 if (permission && permission->psid == aid) {
739 return true;
740 }
741 }
742
743 return false;
744}
745
746bool equal(const asn1::TwoDLocation& lhs, const asn1::TwoDLocation& rhs)
747{
748 return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude;
749}
750
751bool equal(const asn1::RectangularRegion& lhs, const asn1::RectangularRegion& rhs)
752{
753 return equal(lhs.northWest, rhs.northWest) && equal(lhs.southEast, rhs.southEast);
754}
755
756bool equal(const asn1::SequenceOfRectangularRegion& lhs, const asn1::SequenceOfRectangularRegion& rhs)
757{
758 if (lhs.list.count != rhs.list.count) {
759 return false;
760 }
761
762 for (int i = 0; i < lhs.list.count; ++i) {
763 if (!lhs.list.array[i] || !rhs.list.array[i] || !equal(*lhs.list.array[i], *rhs.list.array[i])) {
764 return false;
765 }
766 }
767
768 return true;
769}
770
771bool is_within(const asn1::GeographicRegion& inner, const asn1::CircularRegion& outer)
772{
773 if (inner.present != Vanetza_Security_GeographicRegion_PR_circularRegion) {
774 return false;
775 }
776
777 const auto& circle = inner.choice.circularRegion;
778 if (!is_valid(circle.center) || !is_valid(outer.center) || circle.radius < 0 || outer.radius < 0) {
779 return false;
780 }
781
782 PositionFix inner_center;
783 inner_center.latitude = convert_latitude(circle.center.latitude);
784 inner_center.longitude = convert_longitude(circle.center.longitude);
785 return distance(inner_center, outer.center) + circle.radius * units::si::meter <= outer.radius * units::si::meter;
786}
787
788bool is_within(const asn1::GeographicRegion& inner, const asn1::SequenceOfRectangularRegion& outer)
789{
790 return inner.present == Vanetza_Security_GeographicRegion_PR_rectangularRegion &&
791 equal(inner.choice.rectangularRegion, outer);
792}
793
794bool is_within(const asn1::GeographicRegion&, const asn1::PolygonalRegion&)
795{
796 return false;
797}
798
799bool is_within(const asn1::GeographicRegion& inner, const asn1::GeographicRegion& outer)
800{
801 switch (outer.present) {
802 case Vanetza_Security_GeographicRegion_PR_circularRegion:
803 return is_within(inner, outer.choice.circularRegion);
804 case Vanetza_Security_GeographicRegion_PR_rectangularRegion:
805 return is_within(inner, outer.choice.rectangularRegion);
806 case Vanetza_Security_GeographicRegion_PR_polygonalRegion:
807 return is_within(inner, outer.choice.polygonalRegion);
808 case Vanetza_Security_GeographicRegion_PR_NOTHING:
809 return true;
810 default:
811 return false;
812 }
813}
814
815bool copy_curve_point(PublicKey& to, const asn1::EccP256CurvePoint& from)
816{
817 bool copied = true;
818 switch (from.present) {
819 case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0:
820 to.compression = KeyCompression::Y0;
821 to.x = fetch_octets(from.choice.compressed_y_0);
822 break;
823 case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1:
824 to.compression = KeyCompression::Y1;
825 to.x = fetch_octets(from.choice.compressed_y_1);
826 break;
827 case Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256:
828 to.compression = KeyCompression::NoCompression;
829 to.x = fetch_octets(from.choice.uncompressedP256.x);
830 to.y = fetch_octets(from.choice.uncompressedP256.y);
831 break;
832 default:
833 copied = false;
834 break;
835 }
836
837 return copied;
838}
839
840bool copy_curve_point(PublicKey& to, const asn1::EccP384CurvePoint& from)
841{
842 bool copied = true;
843 switch (from.present) {
844 case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0:
845 to.compression = KeyCompression::Y0;
846 to.x = fetch_octets(from.choice.compressed_y_0);
847 break;
848 case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1:
849 to.compression = KeyCompression::Y1;
850 to.x = fetch_octets(from.choice.compressed_y_1);
851 break;
852 case Vanetza_Security_EccP384CurvePoint_PR_uncompressedP384:
853 to.compression = KeyCompression::NoCompression;
854 to.x = fetch_octets(from.choice.uncompressedP384.x);
855 to.y = fetch_octets(from.choice.uncompressedP384.y);
856 break;
857 default:
858 copied = false;
859 break;
860 }
861
862 return copied;
863}
864
865HashAlgorithm digest_hash_algorithm(const asn1::EtsiTs103097Certificate& cert)
866{
867 // IEEE 1609.2 clause 6.4.3: hash algorithm depends on the verification key indicator;
868 // a reconstruction value (implicit certificate) is always an EccP256CurvePoint and
869 // clause 5.3.2 mandates SHA-256 for implicit certificates
870 const asn1::VerificationKeyIndicator& indicator = cert.toBeSigned.verifyKeyIndicator;
871 switch (indicator.present) {
872 case Vanetza_Security_VerificationKeyIndicator_PR_verificationKey:
873 switch (indicator.choice.verificationKey.present) {
874 case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256:
875 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1:
876 return HashAlgorithm::SHA256;
877 case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1:
878 return HashAlgorithm::SHA384;
879 default:
880 return HashAlgorithm::Unspecified;
881 }
882 case Vanetza_Security_VerificationKeyIndicator_PR_reconstructionValue:
883 return HashAlgorithm::SHA256;
884 default:
885 return HashAlgorithm::Unspecified;
886 }
887}
888
889ByteBuffer fetch_octets(const OCTET_STRING_t& octets)
890{
891 ByteBuffer buffer(octets.size);
892 std::memcpy(buffer.data(), octets.buf, octets.size);
893 return buffer;
894}
895
896ByteBuffer get_x_coordinate(const asn1::EccP256CurvePoint& point)
897{
898 switch (point.present) {
899 case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0:
900 return fetch_octets(point.choice.compressed_y_0);
901 case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1:
902 return fetch_octets(point.choice.compressed_y_1);
903 case Vanetza_Security_EccP256CurvePoint_PR_x_only:
904 return fetch_octets(point.choice.x_only);
905 case Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256:
906 return fetch_octets(point.choice.uncompressedP256.x);
907 default:
908 return ByteBuffer {};
909 }
910}
911
912ByteBuffer get_x_coordinate(const asn1::EccP384CurvePoint& point)
913{
914 switch (point.present) {
915 case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0:
916 return fetch_octets(point.choice.compressed_y_0);
917 case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1:
918 return fetch_octets(point.choice.compressed_y_1);
919 case Vanetza_Security_EccP384CurvePoint_PR_x_only:
920 return fetch_octets(point.choice.x_only);
921 case Vanetza_Security_EccP384CurvePoint_PR_uncompressedP384:
922 return fetch_octets(point.choice.uncompressedP384.x);
923 default:
924 return ByteBuffer {};
925 }
926}
927
928bool is_compressed(const Vanetza_Security_EccP256CurvePoint& point)
929{
930 switch (point.present) {
931 case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0:
932 case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1:
933 return true;
934 default:
935 return false;
936 }
937}
938
939bool is_compressed(const Vanetza_Security_EccP384CurvePoint& point)
940{
941 switch (point.present) {
942 case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0:
943 case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1:
944 return true;
945 default:
946 return false;
947 }
948}
949
950bool is_signature_x_only(const Vanetza_Security_Signature_t& sig)
951{
952 switch (sig.present) {
953 case Vanetza_Security_Signature_PR_ecdsaNistP256Signature:
954 return sig.choice.ecdsaNistP256Signature.rSig.present == Vanetza_Security_EccP256CurvePoint_PR_x_only;
955 case Vanetza_Security_Signature_PR_ecdsaBrainpoolP256r1Signature:
956 return sig.choice.ecdsaBrainpoolP256r1Signature.rSig.present == Vanetza_Security_EccP256CurvePoint_PR_x_only;
957 case Vanetza_Security_Signature_PR_ecdsaBrainpoolP384r1Signature:
958 return sig.choice.ecdsaBrainpoolP384r1Signature.rSig.present == Vanetza_Security_EccP384CurvePoint_PR_x_only;
959 default:
960 return true; // not an ECDSA signature at all
961 }
962}
963
964bool compress(Vanetza_Security_EccP256CurvePoint& point)
965{
966 if (point.present == Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256) {
967 auto& unc = point.choice.uncompressedP256;
968 const bool y_odd = unc.y.size > 0 && unc.y.buf[unc.y.size - 1] & 0x01;
969 asn1::reset(&unc.y);
970 std::free(unc._asn_ctx.ptr);
971 unc._asn_ctx.ptr = nullptr;
972 if (y_odd) {
973 assert(&point.choice.uncompressedP256.x == &point.choice.compressed_y_1);
974 point.present = Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1;
975 } else {
976 assert(&point.choice.uncompressedP256.x == &point.choice.compressed_y_0);
977 point.present = Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0;
978 }
979 return true;
980 } else if (point.present == Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0 || point.present == Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1) {
981 return true;
982 } else {
983 return false;
984 }
985}
986
987bool compress(Vanetza_Security_EccP384CurvePoint& point)
988{
989 if (point.present == Vanetza_Security_EccP384CurvePoint_PR_uncompressedP384) {
990 auto& unc = point.choice.uncompressedP384;
991 const bool y_odd = unc.y.size > 0 && unc.y.buf[unc.y.size - 1] & 0x01;
992 asn1::reset(&unc.y);
993 std::free(unc._asn_ctx.ptr);
994 unc._asn_ctx.ptr = nullptr;
995 if (y_odd) {
996 assert(&point.choice.uncompressedP384.x == &point.choice.compressed_y_1);
997 point.present = Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1;
998 } else {
999 assert(&point.choice.uncompressedP384.x == &point.choice.compressed_y_0);
1000 point.present = Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0;
1001 }
1002 return true;
1003 } else if (point.present == Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0 || point.present == Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1) {
1004 return true;
1005 } else {
1006 return false;
1007 }
1008}
1009
1010bool make_x_only(Vanetza_Security_EccP256CurvePoint& point)
1011{
1012 if (point.present == Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256) {
1013 auto& unc = point.choice.uncompressedP256;
1014 asn1::reset(&unc.y);
1015 std::free(unc._asn_ctx.ptr);
1016 unc._asn_ctx.ptr = nullptr;
1017 assert(&unc.x == &point.choice.x_only);
1018 point.present = Vanetza_Security_EccP256CurvePoint_PR_x_only;
1019 return true;
1020 } else if (point.present == Vanetza_Security_EccP256CurvePoint_PR_x_only) {
1021 return true;
1022 } else {
1023 return false;
1024 }
1025}
1026
1027bool make_x_only(Vanetza_Security_EccP384CurvePoint& point)
1028{
1029 if (point.present == Vanetza_Security_EccP384CurvePoint_PR_uncompressedP384) {
1030 auto& unc = point.choice.uncompressedP384;
1031 asn1::reset(&unc.y);
1032 std::free(unc._asn_ctx.ptr);
1033 unc._asn_ctx.ptr = nullptr;
1034 assert(&unc.x == &point.choice.x_only);
1035 point.present = Vanetza_Security_EccP384CurvePoint_PR_x_only;
1036 return true;
1037 } else if (point.present == Vanetza_Security_EccP384CurvePoint_PR_x_only) {
1038 return true;
1039 } else {
1040 return false;
1041 }
1042}
1043
1044bool make_signature_x_only(Vanetza_Security_Signature& sig)
1045{
1046 switch (sig.present) {
1047 case Vanetza_Security_Signature_PR_ecdsaNistP256Signature:
1048 return make_x_only(sig.choice.ecdsaNistP256Signature.rSig);
1049 break;
1050 case Vanetza_Security_Signature_PR_ecdsaBrainpoolP256r1Signature:
1051 return make_x_only(sig.choice.ecdsaBrainpoolP256r1Signature.rSig);
1052 break;
1053 case Vanetza_Security_Signature_PR_ecdsaBrainpoolP384r1Signature:
1054 return make_x_only(sig.choice.ecdsaBrainpoolP384r1Signature.rSig);
1055 break;
1056 default:
1057 return false;
1058 break;
1059 }
1060}
1061
1062} // namespace
1063
1064} // namespace v3
1065} // namespace security
1066} // namespace vanetza
boost::optional< Certificate > canonicalize() const
bool valid_at_timepoint(const Clock::time_point &time_point) const
StartAndEndValidity get_start_and_end_validity() const
bool is_allowed_to_issue(ItsAid aid) const
bool valid_for_application(ItsAid aid) const
boost::optional< HashedId8 > calculate_digest() const
bool valid_at_location(const PositionFix &location, const LocationChecker *lc) const
bool region_is_within(const CertificateView &issuer) const
boost::optional< std::uint8_t > assurance_level() const
boost::optional< HashedId8 > issuer_digest() const
EcdsaSignature specified in TS 103 097 v1.2.1, section 4.2.9.
Definition signature.hpp:17
void add_cert_issue_permission(asn1::PsidGroupPermissions *group_permission)
add cert issuing permission
void add_app_permission(ItsAid aid, const ByteBuffer &ssp)
add application permissions as bitmap