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