Add a nice note for general MXF errors.
[libdcp.git] / src / certificate.cc
1 /*
2     Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     libdcp is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34
35 /** @file  src/certificate.cc
36  *  @brief Certificate class
37  */
38
39
40 #include "certificate.h"
41 #include "compose.hpp"
42 #include "exceptions.h"
43 #include "util.h"
44 #include "dcp_assert.h"
45 #include <asdcp/KM_util.h>
46 #include <libxml++/nodes/element.h>
47 #include <openssl/x509.h>
48 #include <openssl/ssl.h>
49 #include <openssl/asn1.h>
50 #include <openssl/err.h>
51 #include <boost/algorithm/string.hpp>
52 #include <cerrno>
53 #include <iostream>
54 #include <algorithm>
55
56
57 using std::list;
58 using std::string;
59 using std::ostream;
60 using std::min;
61 using namespace dcp;
62
63
64 static string const begin_certificate = "-----BEGIN CERTIFICATE-----";
65 static string const end_certificate = "-----END CERTIFICATE-----";
66
67
68 Certificate::Certificate (X509* c)
69         : _certificate (c)
70 {
71
72 }
73
74
75 Certificate::Certificate (string cert)
76 {
77         auto const s = read_string (cert);
78         if (!s.empty()) {
79                 throw MiscError ("unexpected data after certificate");
80         }
81 }
82
83
84 Certificate::Certificate (Certificate const & other)
85 {
86         if (other._certificate) {
87                 read_string (other.certificate (true));
88         }
89 }
90
91
92 string
93 Certificate::read_string (string cert)
94 {
95         /* Reformat cert so that it has line breaks every 64 characters.
96            See http://comments.gmane.org/gmane.comp.encryption.openssl.user/55593
97         */
98
99         list<string> lines;
100         string line;
101
102         for (size_t i = 0; i < cert.length(); ++i) {
103                 line += cert[i];
104                 if (cert[i] == '\r' || cert[i] == '\n') {
105                         boost::algorithm::trim (line);
106                         lines.push_back (line);
107                         line = "";
108                 }
109         }
110
111         if (!line.empty()) {
112                 boost::algorithm::trim (line);
113                 lines.push_back (line);
114         }
115
116         auto i = lines.begin ();
117
118         /* BEGIN */
119         while (i != lines.end() && *i != begin_certificate) {
120                 ++i;
121         }
122
123         if (i == lines.end()) {
124                 throw MiscError ("missing BEGIN line in certificate");
125         }
126
127         /* Skip over the BEGIN line */
128         ++i;
129
130         /* The base64 data */
131         bool got_end = false;
132         string base64 = "";
133         while (i != lines.end()) {
134                 if (*i == end_certificate) {
135                         got_end = true;
136                         break;
137                 }
138                 base64 += *i;
139                 ++i;
140         }
141
142         if (!got_end) {
143                 throw MiscError ("missing END line in certificate");
144         }
145
146         /* Skip over the END line */
147         ++i;
148
149         /* Make up the fixed version */
150
151         string fixed = begin_certificate + "\n";
152         while (!base64.empty ()) {
153                 size_t const t = min (size_t(64), base64.length());
154                 fixed += base64.substr (0, t) + "\n";
155                 base64 = base64.substr (t, base64.length() - t);
156         }
157
158         fixed += end_certificate;
159
160         auto bio = BIO_new_mem_buf (const_cast<char *> (fixed.c_str ()), -1);
161         if (!bio) {
162                 throw MiscError ("could not create memory BIO");
163         }
164
165         _certificate = PEM_read_bio_X509 (bio, 0, 0, 0);
166         if (!_certificate) {
167                 throw MiscError ("could not read X509 certificate from memory BIO");
168         }
169
170         BIO_free (bio);
171
172         string extra;
173
174         while (i != lines.end()) {
175                 if (!i->empty()) {
176                         extra += *i + "\n";
177                 }
178                 ++i;
179         }
180
181         return extra;
182 }
183
184
185 Certificate::~Certificate ()
186 {
187         X509_free (_certificate);
188         RSA_free (_public_key);
189 }
190
191
192 Certificate &
193 Certificate::operator= (Certificate const & other)
194 {
195         if (this == &other) {
196                 return *this;
197         }
198
199         X509_free (_certificate);
200         _certificate = 0;
201         RSA_free (_public_key);
202         _public_key = 0;
203
204         read_string (other.certificate(true));
205
206         return *this;
207 }
208
209
210 string
211 Certificate::certificate (bool with_begin_end) const
212 {
213         DCP_ASSERT (_certificate);
214
215         auto bio = BIO_new (BIO_s_mem());
216         if (!bio) {
217                 throw MiscError ("could not create memory BIO");
218         }
219
220         PEM_write_bio_X509 (bio, _certificate);
221
222         string s;
223         char* data;
224         long int const data_length = BIO_get_mem_data (bio, &data);
225         for (long int i = 0; i < data_length; ++i) {
226                 s += data[i];
227         }
228
229         BIO_free (bio);
230
231         if (!with_begin_end) {
232                 boost::replace_all (s, begin_certificate + "\n", "");
233                 boost::replace_all (s, "\n" + end_certificate + "\n", "");
234         }
235
236         return s;
237 }
238
239
240 string
241 Certificate::issuer () const
242 {
243         DCP_ASSERT (_certificate);
244         return name_for_xml (X509_get_issuer_name(_certificate));
245 }
246
247
248 string
249 Certificate::issuer_common_name() const
250 {
251         DCP_ASSERT(_certificate);
252
253         return get_name_part(X509_get_issuer_name(_certificate), NID_commonName);
254 }
255
256
257 string
258 Certificate::issuer_organization_name() const
259 {
260         DCP_ASSERT(_certificate);
261
262         return get_name_part(X509_get_issuer_name(_certificate), NID_organizationName);
263 }
264
265
266 string
267 Certificate::issuer_organizational_unit_name() const
268 {
269         DCP_ASSERT(_certificate);
270
271         return get_name_part(X509_get_issuer_name(_certificate), NID_organizationalUnitName);
272 }
273
274
275 string
276 Certificate::asn_to_utf8 (ASN1_STRING* s)
277 {
278         unsigned char* buf = 0;
279         ASN1_STRING_to_UTF8 (&buf, s);
280         string const u (reinterpret_cast<char *> (buf));
281         OPENSSL_free (buf);
282         return u;
283 }
284
285
286 string
287 Certificate::get_name_part (X509_NAME* n, int nid)
288 {
289         int p = -1;
290         p = X509_NAME_get_index_by_NID (n, nid, p);
291         if (p == -1) {
292                 return "";
293         }
294         return asn_to_utf8 (X509_NAME_ENTRY_get_data(X509_NAME_get_entry(n, p)));
295 }
296
297
298 string
299 Certificate::name_for_xml (X509_NAME* name)
300 {
301         assert (name);
302
303         auto bio = BIO_new (BIO_s_mem ());
304         if (!bio) {
305                 throw MiscError ("could not create memory BIO");
306         }
307
308         X509_NAME_print_ex (bio, name, 0, XN_FLAG_RFC2253);
309         int n = BIO_pending (bio);
310         char* result = new char[n + 1];
311         n = BIO_read (bio, result, n);
312         result[n] = '\0';
313
314         BIO_free (bio);
315
316         string s = result;
317         delete[] result;
318
319         return s;
320 }
321
322 string
323 Certificate::subject () const
324 {
325         DCP_ASSERT (_certificate);
326
327         return name_for_xml (X509_get_subject_name(_certificate));
328 }
329
330
331 string
332 Certificate::subject_common_name () const
333 {
334         DCP_ASSERT (_certificate);
335
336         return get_name_part (X509_get_subject_name(_certificate), NID_commonName);
337 }
338
339
340 string
341 Certificate::subject_organization_name () const
342 {
343         DCP_ASSERT (_certificate);
344
345         return get_name_part (X509_get_subject_name(_certificate), NID_organizationName);
346 }
347
348
349 string
350 Certificate::subject_organizational_unit_name () const
351 {
352         DCP_ASSERT (_certificate);
353
354         return get_name_part (X509_get_subject_name(_certificate), NID_organizationalUnitName);
355 }
356
357
358 static
359 LocalTime
360 convert_time (ASN1_TIME const * time)
361 {
362         LocalTime t;
363         char const * s = (char const *) time->data;
364
365         if (time->type == V_ASN1_UTCTIME) {
366                 return LocalTime::from_asn1_utc_time (s);
367         } else if (time->type == V_ASN1_GENERALIZEDTIME) {
368                 return LocalTime::from_asn1_generalized_time (s);
369         }
370
371         DCP_ASSERT (false);
372         return {};
373 }
374
375
376 LocalTime
377 Certificate::not_before () const
378 {
379         DCP_ASSERT (_certificate);
380 #if OPENSSL_VERSION_NUMBER > 0x10100000L
381         return convert_time(X509_get0_notBefore(_certificate));
382 #else
383         return convert_time(X509_get_notBefore(_certificate));
384 #endif
385 }
386
387
388 LocalTime
389 Certificate::not_after () const
390 {
391         DCP_ASSERT (_certificate);
392 #if OPENSSL_VERSION_NUMBER > 0x10100000L
393         return convert_time(X509_get0_notAfter(_certificate));
394 #else
395         return convert_time(X509_get_notAfter(_certificate));
396 #endif
397 }
398
399
400 string
401 Certificate::serial () const
402 {
403         DCP_ASSERT (_certificate);
404
405         auto s = X509_get_serialNumber (_certificate);
406         DCP_ASSERT (s);
407
408         auto b = ASN1_INTEGER_to_BN (s, 0);
409         char* c = BN_bn2dec (b);
410         BN_free (b);
411
412         string st (c);
413         OPENSSL_free (c);
414
415         return st;
416 }
417
418
419 string
420 Certificate::thumbprint () const
421 {
422         DCP_ASSERT (_certificate);
423
424         uint8_t buffer[8192];
425         uint8_t* p = buffer;
426
427 #if OPENSSL_VERSION_NUMBER > 0x10100000L
428         i2d_re_X509_tbs(_certificate, &p);
429 #else
430         i2d_X509_CINF (_certificate->cert_info, &p);
431 #endif
432         unsigned int const length = p - buffer;
433         if (length > sizeof (buffer)) {
434                 throw MiscError ("buffer too small to generate thumbprint");
435         }
436
437         SHA_CTX sha;
438         SHA1_Init (&sha);
439         SHA1_Update (&sha, buffer, length);
440         uint8_t digest[20];
441         SHA1_Final (digest, &sha);
442
443         char digest_base64[64];
444         return Kumu::base64encode (digest, 20, digest_base64, 64);
445 }
446
447
448 RSA *
449 Certificate::public_key () const
450 {
451         DCP_ASSERT (_certificate);
452
453         if (_public_key) {
454                 return _public_key;
455         }
456
457         auto key = X509_get_pubkey (_certificate);
458         if (!key) {
459                 throw MiscError ("could not get public key from certificate");
460         }
461
462         _public_key = EVP_PKEY_get1_RSA (key);
463         if (!_public_key) {
464                 throw MiscError (String::compose ("could not get RSA public key (%1)", ERR_error_string (ERR_get_error(), 0)));
465         }
466
467         return _public_key;
468 }
469
470
471 static bool string_is_utf8 (X509_NAME* n, int nid)
472 {
473         int p = -1;
474         p = X509_NAME_get_index_by_NID (n, nid, p);
475         return p != -1 && X509_NAME_ENTRY_get_data(X509_NAME_get_entry(n, p))->type == V_ASN1_UTF8STRING;
476 }
477
478
479 bool
480 Certificate::has_utf8_strings () const
481 {
482         auto n = X509_get_subject_name (_certificate);
483         return string_is_utf8(n, NID_commonName) ||
484                 string_is_utf8(n, NID_organizationName) ||
485                 string_is_utf8(n, NID_organizationalUnitName);
486 }
487
488
489 bool
490 dcp::operator== (Certificate const & a, Certificate const & b)
491 {
492         return a.certificate() == b.certificate();
493 }
494
495
496 bool
497 dcp::operator< (Certificate const & a, Certificate const & b)
498 {
499         return a.certificate() < b.certificate();
500 }
501
502
503 ostream&
504 dcp::operator<< (ostream& s, Certificate const & c)
505 {
506         s << c.certificate();
507         return s;
508 }