Vanetza
Loading...
Searching...
No Matches
straight_verify_service.cpp
1#include <vanetza/common/its_aid.hpp>
2#include <vanetza/common/position_provider.hpp>
3#include <vanetza/common/runtime.hpp>
4#include <vanetza/security/backend.hpp>
5#include <vanetza/security/public_key.hpp>
6#include <vanetza/security/straight_verify_service.hpp>
7#include <vanetza/security/v2/basic_elements.hpp>
8#include <vanetza/security/v2/certificate_cache.hpp>
9#include <vanetza/security/v2/certificate_provider.hpp>
10#include <vanetza/security/v2/certificate_validator.hpp>
11#include <vanetza/security/v2/sign_header_policy.hpp>
12#include <vanetza/security/v2/verification.hpp>
13#include <vanetza/security/v3/asn1_conversions.hpp>
14#include <vanetza/security/v3/asn1_types.hpp>
15#include <vanetza/security/v3/certificate_cache.hpp>
16#include <vanetza/security/v3/certificate_provider.hpp>
17#include <vanetza/security/v3/certificate_validator.hpp>
18#include <vanetza/security/v3/hash.hpp>
19#include <vanetza/security/v3/sign_header_policy.hpp>
20#include <boost/optional.hpp>
21
22// asn1c quirk
23struct Vanetza_Security_Certificate : public Vanetza_Security_CertificateBase {};
24
25namespace vanetza
26{
27namespace security
28{
29
30namespace
31{
32
33bool assign_permissions(const v2::Certificate& certificate, VerifyConfirm& confirm)
34{
35 for (auto& subject_attribute : certificate.subject_attributes) {
36 if (get_type(subject_attribute) != v2::SubjectAttributeType::ITS_AID_SSP_List) {
37 continue;
38 }
39
40 auto& permissions = boost::get<std::list<v2::ItsAidSsp> >(subject_attribute);
41 for (auto& permission : permissions) {
42 if (permission.its_aid == confirm.its_aid) {
43 confirm.permissions = permission.service_specific_permissions;
44 return true;
45 }
46 }
47
48 break;
49 }
50
51 return false;
52}
53
54} // namespace
55
56
57StraightVerifyService::StraightVerifyService(const Runtime& runtime, Backend& backend, PositionProvider& position) :
58 m_runtime(runtime), m_backend(backend),m_position_provider(position)
59{
60}
61
62void StraightVerifyService::use_certificate_cache(v2::CertificateCache* cache)
63{
64 m_context_v2.m_cert_cache = cache;
65}
66
67void StraightVerifyService::use_certificate_provider(v2::CertificateProvider* provider)
68{
69 m_context_v2.m_cert_provider = provider;
70}
71
72void StraightVerifyService::use_certificate_validator(v2::CertificateValidator* validator)
73{
74 m_context_v2.m_cert_validator = validator;
75}
76
77void StraightVerifyService::use_sign_header_policy(v2::SignHeaderPolicy* policy)
78{
79 m_context_v2.m_sign_policy = policy;
80}
81
82void StraightVerifyService::use_certificate_provider(v3::CertificateProvider* provider)
83{
84 m_context_v3.m_cert_provider = provider;
85}
86
87void StraightVerifyService::use_certificate_validator(v3::CertificateValidator* validator)
88{
89 m_context_v3.m_cert_validator = validator;
90}
91
92void StraightVerifyService::use_sign_header_policy(v3::SignHeaderPolicy* policy)
93{
94 m_context_v3.m_sign_policy = policy;
95}
96
97VerifyConfirm StraightVerifyService::verify(const VerifyRequest& request)
98{
99 struct visitor : public boost::static_visitor<VerifyConfirm>
100 {
101 visitor(StraightVerifyService* service) : m_service(service)
102 {
103 }
104
105 VerifyConfirm operator()(const v2::SecuredMessage& msg)
106 {
107 return m_service->verify(msg);
108 }
109
110 VerifyConfirm operator()(const v3::SecuredMessage& msg)
111 {
112 return m_service->verify(msg);
113 }
114
115 StraightVerifyService* m_service = nullptr;
116 } visitor(this);
117
118 return boost::apply_visitor(visitor, request.secured_message);
119}
120
121VerifyConfirm StraightVerifyService::verify(const v2::SecuredMessage& secured_message)
122{
123 // TODO check if certificates in chain have been revoked for all CA certificates, ATs are never revoked
124 VerifyConfirm confirm;
125 using namespace v2;
126
127 if (PayloadType::Signed != secured_message.payload.type) {
128 confirm.report = VerificationReport::Unsigned_Message;
129 return confirm;
130 }
131
132 if (2 != secured_message.protocol_version()) {
133 confirm.report = VerificationReport::Incompatible_Protocol;
134 return confirm;
135 }
136
137 if (!m_context_v2.complete()) {
138 confirm.report = VerificationReport::Configuration_Problem;
139 return confirm;
140 }
141
142 v2::CertificateProvider& cert_provider = *m_context_v2.m_cert_provider;
143 v2::CertificateCache& cert_cache = *m_context_v2.m_cert_cache;
144 v2::CertificateValidator& cert_validator = *m_context_v2.m_cert_validator;
145 v2::SignHeaderPolicy& sign_policy = *m_context_v2.m_sign_policy;
146
147 const std::list<HashedId3>* requested_certs = secured_message.header_field<HeaderFieldType::Request_Unrecognized_Certificate>();
148 if (requested_certs) {
149 for (auto& requested_cert : *requested_certs) {
150 if (truncate(calculate_hash(cert_provider.own_certificate())) == requested_cert) {
151 sign_policy.request_certificate();
152 }
153
154 for (auto& cert : cert_provider.own_chain()) {
155 if (truncate(calculate_hash(cert)) == requested_cert) {
156 sign_policy.request_certificate_chain();
157 }
158 }
159 }
160 }
161
162 const IntX* its_aid = secured_message.header_field<HeaderFieldType::Its_Aid>();
163 if (!its_aid) {
164 // ITS-AID is required to be present, report as incompatible protocol, as that's the closest match
165 confirm.report = VerificationReport::Incompatible_Protocol;
166 return confirm;
167 }
168 confirm.its_aid = its_aid->get();
169
170 const SignerInfo* signer_info = secured_message.header_field<HeaderFieldType::Signer_Info>();
171 std::list<v2::Certificate> possible_certificates;
172 bool possible_certificates_from_cache = false;
173
174 // use a dummy hash for initialization
175 HashedId8 signer_hash;
176 signer_hash.fill(0x00);
177
178 if (signer_info) {
179 switch (get_type(*signer_info)) {
180 case SignerInfoType::Certificate:
181 possible_certificates.push_back(boost::get<v2::Certificate>(*signer_info));
182 signer_hash = calculate_hash(boost::get<v2::Certificate>(*signer_info));
183
184 if (confirm.its_aid == aid::CA && cert_cache.lookup(signer_hash, SubjectType::Authorization_Ticket).size() == 0) {
185 // Previously unknown certificate, send own certificate in next CAM
186 // See TS 103 097 v1.2.1, section 7.1, 1st bullet, 3rd dash
187 sign_policy.request_certificate();
188 }
189
190 break;
191 case SignerInfoType::Certificate_Digest_With_SHA256:
192 signer_hash = boost::get<HashedId8>(*signer_info);
193 possible_certificates.splice(possible_certificates.end(), cert_cache.lookup(signer_hash, SubjectType::Authorization_Ticket));
194 possible_certificates_from_cache = true;
195 break;
196 case SignerInfoType::Certificate_Chain:
197 {
198 std::list<v2::Certificate> chain = boost::get<std::list<v2::Certificate>>(*signer_info);
199 if (chain.size() == 0) {
200 confirm.report = VerificationReport::Signer_Certificate_Not_Found;
201 return confirm;
202 } else if (chain.size() > 3) {
203 // prevent DoS by sending very long chains, maximum length is three certificates, because:
204 // AT → AA → Root and no other signatures are allowed, sending the Root is optional
205 confirm.report = VerificationReport::Invalid_Certificate;
206 return confirm;
207 }
208 // pre-check chain certificates, otherwise they're not available for the ticket check
209 for (auto& cert : chain) {
210 // root certificates must already be known, otherwise the validation will fail anyway
211 if (cert.subject_info.subject_type == SubjectType::Authorization_Authority) {
212 // there's no need to report unknown signers at this point, see comment above
213 CertificateValidity validity = cert_validator.check_certificate(cert);
214
215 // we can abort early if there are invalid AA certificates in the chain
216 if (!validity) {
217 confirm.report = VerificationReport::Invalid_Certificate;
218 confirm.certificate_validity = validity;
219 return confirm;
220 }
221
222 // We won't cache outdated or premature certificates in the cache and abort early.
223 // This check isn't required as it would just fail below or in the consistency checks,
224 // but it's an optimization and saves us from polluting the cache with such certificates.
225 if (!check_certificate_time(cert, m_runtime.now()) || !check_certificate_region(cert, m_position_provider.position_fix())) {
226 confirm.report = VerificationReport::Invalid_Certificate;
227 return confirm;
228 }
229
230 cert_cache.insert(cert);
231 }
232 }
233 // last certificate must be the authorization ticket
234 signer_hash = calculate_hash(chain.back());
235 possible_certificates.push_back(chain.back());
236 }
237 break;
238 default:
239 confirm.report = VerificationReport::Unsupported_Signer_Identifier_Type;
240 return confirm;
241 break;
242 }
243 }
244
245 if (possible_certificates.size() == 0) {
246 confirm.report = VerificationReport::Signer_Certificate_Not_Found;
247 confirm.certificate_id = signer_hash;
248 sign_policy.request_unrecognized_certificate(signer_hash);
249 return confirm;
250 }
251
252 if (!check_generation_time(secured_message, m_runtime.now())) {
253 confirm.report = VerificationReport::Invalid_Timestamp;
254 return confirm;
255 }
256
257 // TODO check Duplicate_Message, Invalid_Mobility_Data, Unencrypted_Message, Decryption_Error
258
259 // check signature
260 const TrailerField* signature_field = secured_message.trailer_field(TrailerFieldType::Signature);
261 const v2::Signature* signature = boost::get<v2::Signature>(signature_field);
262
263 if (!signature) {
264 confirm.report = VerificationReport::Unsigned_Message;
265 return confirm;
266 }
267
268 if (PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256 != get_type(*signature)) {
269 confirm.report = VerificationReport::False_Signature;
270 return confirm;
271 }
272
273 // check the size of signature.R and siganture.s
274 auto ecdsa = extract_ecdsa_signature(*signature);
275 const auto field_len = field_size(PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
276 if (!ecdsa || ecdsa->s.size() != field_len) {
277 confirm.report = VerificationReport::False_Signature;
278 return confirm;
279 }
280
281 // verify payload signature with given signature
282 ByteBuffer payload = convert_for_signing(secured_message, secured_message.trailer_fields);
283 boost::optional<v2::Certificate> signer;
284
285 for (const auto& cert : possible_certificates) {
286 SubjectType subject_type = cert.subject_info.subject_type;
287 if (subject_type != SubjectType::Authorization_Ticket) {
288 confirm.report = VerificationReport::Invalid_Certificate;
289 confirm.certificate_validity = CertificateInvalidReason::Invalid_Signer;
290 return confirm;
291 }
292
293 boost::optional<ecdsa256::PublicKey> public_key = get_public_key(cert, m_backend);
294
295 // public key could not be extracted
296 if (!public_key) {
297 confirm.report = VerificationReport::Invalid_Certificate;
298 confirm.certificate_validity = CertificateInvalidReason::Missing_Public_Key;
299 return confirm;
300 }
301
302 if (m_backend.verify_data(public_key.get(), payload, *ecdsa)) {
303 signer = cert;
304 break;
305 }
306 }
307
308 if (!signer) {
309 // HashedId8 of authorization tickets is not guaranteed to be globally unique.
310 // The collision probability is rather low, but it might happen.
311 if (signer_info && get_type(*signer_info) == SignerInfoType::Certificate_Digest_With_SHA256) {
312 // assume a hash collision since we got only a digest with message
313 confirm.report = VerificationReport::Signer_Certificate_Not_Found;
314 } else {
315 // signature does not match the certificate received with this message
316 confirm.report = VerificationReport::False_Signature;
317 }
318
319 confirm.certificate_id = signer_hash;
320 sign_policy.request_unrecognized_certificate(signer_hash);
321 return confirm;
322 }
323
324 // we can only check the generation location after we have identified the correct certificate
325 if (!check_generation_location(secured_message, *signer)) {
326 confirm.report = VerificationReport::Invalid_Certificate;
327 confirm.certificate_validity = CertificateInvalidReason::Off_Region;
328 return confirm;
329 }
330
331 CertificateValidity cert_validity = CertificateValidity::valid();
332 if (!possible_certificates_from_cache) { // certificates from cache are already verified as trusted
333 cert_validity = cert_validator.check_certificate(*signer);
334 }
335
336 confirm.certificate_validity = cert_validity;
337
338 // if certificate could not be verified return correct DecapReport
339 if (!cert_validity) {
340 confirm.report = VerificationReport::Invalid_Certificate;
341
342 if (cert_validity.reason() == CertificateInvalidReason::Unknown_Signer) {
343 if (get_type(signer->signer_info) == SignerInfoType::Certificate_Digest_With_SHA256) {
344 auto signer_hash = boost::get<HashedId8>(signer->signer_info);
345 confirm.certificate_id = signer_hash;
346 sign_policy.request_unrecognized_certificate(signer_hash);
347 }
348 }
349
350 return confirm;
351 }
352
353 if (!check_certificate_time(*signer, m_runtime.now())) {
354 confirm.report = VerificationReport::Invalid_Certificate;
355 confirm.certificate_validity = CertificateInvalidReason::Off_Time_Period;
356 return confirm;
357 }
358
359 if (!check_certificate_region(*signer, m_position_provider.position_fix())) {
360 confirm.report = VerificationReport::Invalid_Certificate;
361 confirm.certificate_validity = CertificateInvalidReason::Off_Region;
362 return confirm;
363 }
364
365 // Assign permissions from the certificate based on the message AID already present in the confirm
366 // and reject the certificate if no permissions are present for the claimed AID.
367 if (!assign_permissions(*signer, confirm)) {
368 // This might seem weird, because the certificate itself is valid, but not for the received message.
369 confirm.report = VerificationReport::Invalid_Certificate;
370 confirm.certificate_validity = CertificateInvalidReason::Insufficient_ITS_AID;
371 return confirm;
372 }
373
374 // cache only certificates that are useful, one that mismatches its restrictions isn't
375 cert_cache.insert(*signer);
376
377 confirm.report = VerificationReport::Success;
378 return confirm;
379}
380
381VerifyConfirm StraightVerifyService::verify(const v3::SecuredMessage& msg)
382{
383 /*
384 * TS 103 097 v1.3.1 demands to assess the validity of signed data
385 * according to IEEE 1609.2 clause 5.2.
386 */
387 VerifyConfirm confirm;
388 confirm.report = VerificationReport::Incompatible_Protocol; /*< fallback error code */
389
390 if (!msg.is_signed()) {
391 confirm.report = VerificationReport::Unsigned_Message;
392 return confirm;
393 }
394
395 if (msg.protocol_version() != 3) {
396 confirm.report = VerificationReport::Incompatible_Protocol;
397 return confirm;
398 }
399
400 auto gen_time = msg.generation_time();
401 if (!gen_time) {
402 // TS 103 097 v1.3.1 demands generation time to be always present
403 confirm.report = VerificationReport::Invalid_Timestamp;
404 return confirm;
405 }
406 // TODO further generation time checks depending on application profile
407
408 auto signature = msg.signature();
409 if (!signature) {
410 confirm.report = VerificationReport::Unsigned_Message;
411 return confirm;
412 }
413
414 struct certificate_lookup_visitor : public boost::static_visitor<const v3::asn1::Certificate*> {
415 certificate_lookup_visitor(v3::CertificateProvider* provider) :
416 m_cache(provider != nullptr ? &provider->cache() : nullptr)
417 {
418 }
419
420 const v3::asn1::Certificate* operator()(const v3::asn1::HashedId8* digest)
421 {
422 // look up certificate matching digest in local storage
423 if (m_cache && digest) {
424 const v3::Certificate* found = m_cache->lookup(v3::convert(*digest));
425 return found ? found->content() : nullptr;
426 } else {
427 return nullptr;
428 }
429 }
430
431 const v3::asn1::Certificate* operator()(const v3::asn1::Certificate* cert)
432 {
433 return cert;
434 }
435
436 v3::CertificateCache* m_cache;
437 } certificate_lookup_visitor(m_context_v3.m_cert_provider);
438 auto signer_identifier = msg.signer_identifier();
439
440 // track "known" stations
441 auto maybe_digest = v3::get_certificate_id(signer_identifier);
442 if (m_context_v3.m_cert_provider && maybe_digest) {
443 bool was_unknown_station = m_context_v3.m_cert_provider->cache().announce(*maybe_digest);
444 if (was_unknown_station && msg.its_aid() == aid::CA && m_context_v3.m_sign_policy) {
445 // CAM from unknown station received, add our certificate to next outgoing message
446 m_context_v3.m_sign_policy->request_certificate();
447 }
448 }
449
450 // check if a ITS-AID 36 (CAM) with inline cert request shall be handled by us
451 if (msg.its_aid() == aid::CA && m_context_v3.m_sign_policy && m_context_v3.m_cert_provider) {
452 Vanetza_Security_SequenceOfHashedId3* requests = msg->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest;
453 if (requests) {
454 for (int i = 0; i < requests->list.count; ++i)
455 {
456 const Vanetza_Security_HashedId3_t* req_digest = requests->list.array[i];
457 const HashedId3 hid3 = create_hashed_id3(*req_digest);
458 m_context_v3.m_sign_policy->enqueue_p2p_request(hid3);
459 }
460 }
461
462 Vanetza_Security_Certificate* included_cert = msg->content->choice.signedData->tbsData->headerInfo.requestedCertificate;
463 if (included_cert) {
464 auto maybe_digest = v3::calculate_digest(*included_cert);
465 if (maybe_digest) {
466 m_context_v3.m_sign_policy->discard_p2p_request(truncate(*maybe_digest));
467 }
468 }
469 }
470
471 const v3::asn1::Certificate* certificate = boost::apply_visitor(certificate_lookup_visitor, signer_identifier);
472 if (!certificate) {
473 if (msg.its_aid() == aid::CA && m_context_v3.m_sign_policy && maybe_digest) {
474 // for received CAMs (having digest as signer identifier) with unknown AT we request the full AT certificate
475 m_context_v3.m_sign_policy->request_unrecognized_certificate(*maybe_digest);
476 }
477 confirm.report = VerificationReport::Signer_Certificate_Not_Found;
478 return confirm;
479 }
480
481 // code below can safely dereference certificate
482 assert(certificate != nullptr);
483
484 // check AT certificate's validity
485 if (m_context_v3.m_cert_validator) {
486 auto verdict = m_context_v3.m_cert_validator->valid_for_signing(v3::CertificateView { certificate }, msg.its_aid());
487 if (verdict != v3::CertificateValidator::Verdict::Valid) {
488 confirm.report = VerificationReport::Invalid_Certificate;
489 return confirm;
490 }
491 }
492
493 boost::optional<HashedId8> aa_digest;
494 switch (certificate->issuer.present)
495 {
496 case Vanetza_Security_IssuerIdentifier_PR_sha256AndDigest:
497 aa_digest = v3::convert(certificate->issuer.choice.sha256AndDigest);
498 break;
499 case Vanetza_Security_IssuerIdentifier_PR_sha384AndDigest:
500 aa_digest = v3::convert(certificate->issuer.choice.sha384AndDigest);
501 break;
502 default:
503 break;
504 }
505
506 if (aa_digest && m_context_v3.m_cert_provider) {
507 if (!m_context_v3.m_cert_provider->cache().is_known(*aa_digest)) {
508 if (m_context_v3.m_sign_policy) {
509 // request unknown AA certificate for any received message signed with AT issued by this AA
510 m_context_v3.m_sign_policy->request_unrecognized_certificate(*aa_digest);
511 }
512 }
513 // TODO: if AA is validated and fails, announce it in cache
514 }
515
516 auto public_key = v3::get_public_key(*certificate);
517 if (!public_key) {
518 confirm.report = VerificationReport::Invalid_Certificate;
519 confirm.certificate_validity = CertificateInvalidReason::Missing_Public_Key;
520 return confirm;
521 }
522
523 ByteBuffer msg_hash = v3::calculate_message_hash(m_backend, msg.hash_id(),
524 msg.signing_payload(), v3::CertificateView { certificate });
525 if (!m_backend.verify_digest(*public_key, msg_hash, *signature)) {
526 confirm.report = VerificationReport::False_Signature;
527 return confirm;
528 }
529
530 confirm.its_aid = msg.its_aid();
531 confirm.permissions = v3::get_app_permissions(*certificate, confirm.its_aid);
532 confirm.certificate_id = maybe_digest;
533 confirm.report = VerificationReport::Success;
534
535 if (m_context_v3.m_cert_provider && confirm.certificate_id) {
536 v3::CertificateCache& cache = m_context_v3.m_cert_provider->cache();
537 bool already_cached = cache.lookup(*confirm.certificate_id) != nullptr;
538 if (!already_cached && confirm.its_aid == aid::CA && m_context_v3.m_sign_policy) {
539 // CAM from unknown station received, add our certificate to next outgoing message
540 m_context_v3.m_sign_policy->request_certificate();
541 }
542
543 // update certificate cache with received certificate
544 if (v3::contains_certificate(signer_identifier)) {
545 cache.store(v3::Certificate { *certificate });
546 }
547 }
548
549 return confirm;
550}
551
552} // namespace security
553} // namespace vanetza
described in TS 103 097 v1.2.1 (2015-06), section 6.1