3cebfc712584bf96a6002de0c8cb6fdd10afe902
[iotivity.git] / resource / csdk / connectivity / src / adapter_util / cacertprofile.c
1 //******************************************************************
2 // Copyright 2018 Cable Television Laboratories, Inc.
3 //
4 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
5 //
6 // Licensed under the Apache License, Version 2.0 (the "License");
7 // you may not use this file except in compliance with the License.
8 // You may obtain a copy of the License at
9 //
10 //      http://www.apache.org/licenses/LICENSE-2.0
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific lan guage governing permissions and
16 // limitations under the License.
17 //
18 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
19
20 #include <string.h>
21 #include <time.h>
22
23 #include <mbedtls/error.h>
24 #include <mbedtls/x509_crt.h>
25 #include <mbedtls/oid.h>
26
27 #include "cacommonutil.h"
28 #include "cacertprofile.h"
29 #include "experimental/logger.h"
30
31 #define TAG "OIC_CC_CERT_PROFILE"
32
33 #if defined(__WITH_DTLS__) || defined (__WITH_TLS__)
34 // OCF Compliant ID Cert profiles
35 static const mbedtls_x509_crt_profile s_certProfile = {
36     MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256),        // MD algorithms
37     MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECKEY) |        // Allowed key types
38     MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECDSA),
39     MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP256R1), // EC curves
40     0                                               // RSA minimum key length - not used because we only use EC key pairs
41 };
42
43 // OID for ID certificates (1.3.6.1.4.1.44924.1.6) suitable for mbedTLS check
44 static const char s_ekuIdOid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x06\x01\x04\x01\x82\xDE\x7C\x01\x06";
45
46
47 static const unsigned int s_eeKeyUsage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
48                                          MBEDTLS_X509_KU_KEY_AGREEMENT;
49
50 static const unsigned int s_eeNonKeyUsage = MBEDTLS_X509_KU_NON_REPUDIATION |
51                                             MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
52                                             MBEDTLS_X509_KU_KEY_ENCIPHERMENT |
53                                             MBEDTLS_X509_KU_KEY_CERT_SIGN |
54                                             MBEDTLS_X509_KU_CRL_SIGN |
55                                             MBEDTLS_X509_KU_ENCIPHER_ONLY |
56                                             MBEDTLS_X509_KU_DECIPHER_ONLY;
57
58 static const unsigned int s_caKeyUsage = MBEDTLS_X509_KU_KEY_CERT_SIGN |
59                                          MBEDTLS_X509_KU_CRL_SIGN;
60
61 static const unsigned int s_caNonKeyUsage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
62                                             MBEDTLS_X509_KU_NON_REPUDIATION  |
63                                             MBEDTLS_X509_KU_KEY_ENCIPHERMENT |
64                                             MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
65                                             MBEDTLS_X509_KU_KEY_AGREEMENT |
66                                             MBEDTLS_X509_KU_ENCIPHER_ONLY |
67                                             MBEDTLS_X509_KU_DECIPHER_ONLY;
68
69 static CertProfileResult FindEndEntityCert(const mbedtls_x509_crt *certChain, mbedtls_x509_crt const **eeCert)
70 {
71     *eeCert = NULL;
72
73     const mbedtls_x509_crt* curCert = certChain;
74     while (NULL != curCert)
75     {
76         if (0 == curCert->ca_istrue)
77         {
78             // first EE
79             if (NULL == *eeCert)
80             {
81                 *eeCert = curCert;
82             }
83             // more than 1 EE is an error condition
84             else
85             {
86                 *eeCert = NULL;
87                 OIC_LOG(ERROR, TAG, "More than 1 end entity cert in chain");
88                 return CP_MUL_EE_CERTS;
89             }
90         }
91         curCert = curCert->next;
92     }
93
94     if (NULL == *eeCert)
95     {
96         OIC_LOG(WARNING, TAG, "No end entity cert in chain");
97         return CP_NO_EE_CERT;
98     }
99
100     return CP_STATUS_OK;
101 }
102
103 static CertProfileResult CheckMdAlgorithm(const mbedtls_x509_crt_profile *profile, mbedtls_md_type_t mdAlgorithm)
104 {
105     if ((MBEDTLS_X509_ID_FLAG(mdAlgorithm) & profile->allowed_mds) != 0)
106     {
107         return CP_STATUS_OK;
108     }
109     return CP_STATUS_FAILED;
110 }
111
112 static CertProfileResult CheckPubKeyAlgorithm(const mbedtls_x509_crt_profile *profile, mbedtls_pk_type_t pkAlgorithm)
113 {
114     if ((MBEDTLS_X509_ID_FLAG(pkAlgorithm) & profile->allowed_pks) != 0)
115     {
116         return CP_STATUS_OK;
117     }
118     return CP_STATUS_FAILED;
119 }
120
121
122 // returns CP_STATUS_OK if pathlen is valid for cert type, othewise returns CP_STATUS_FAILED
123 static CertProfileResult CheckPathLen( CertType certType, int mbedMaxPathLen) {
124
125     // mbedtls max_pathlen behaviour:
126     // CA Cert: Expects RFC5280_val as encoding input
127     //          Provides RFC5280_val+1 as decoding output, where 0 = not present
128     // EE Cert: Does not encode
129     //          Provides 0 as decoding output
130
131     switch(certType)
132     {
133         case CERT_CA_ROOT:
134             return (0 == mbedMaxPathLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
135         case CERT_CA_INT:
136             return (1 == mbedMaxPathLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
137         case CERT_EE:
138             return (0 == mbedMaxPathLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
139         default:
140         {
141             OIC_LOG(WARNING, TAG, "Unkown cert type for pathlen check");
142             return CP_STATUS_FAILED;
143         }
144     }
145 }
146
147 // Check all cert entries that are common across root-ca, intermediate-ca, and ee certs
148 static CertProfileViolations ValidateCommonCertProfileEntries(const mbedtls_x509_crt *cert) {
149
150     CertProfileResult cpResult = CP_STATUS_OK;
151     CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
152
153     // Start with time windows
154     // notBefore: Required
155     // notAfter: Required
156     profileViolations = ValidateCertTimeWindow(cert);
157     if (CP_NO_VIOLATIONS != profileViolations)
158     {
159         OIC_LOG(ERROR, TAG, "Cert has expired or is not yet valid");
160     }
161
162     // signatureAlgorithm
163     // ecdsa-with-SHA256 (OID: 1.2.840.10045.4.3.2)
164     cpResult = CheckMdAlgorithm(&s_certProfile, cert->sig_md);
165     if (CP_STATUS_OK != cpResult)
166     {
167         OIC_LOG(ERROR, TAG, "Cert signature algorithm must be SHA256");
168         profileViolations |= CP_INVALID_SIG_ALGORITHM;
169     }
170
171     // Version: v3
172     // mbedTLS version 3 = x509 v3
173     if (3 != cert->version)
174     {
175         OIC_LOG(ERROR, TAG, "Cert is not x509 v3");
176         profileViolations |= CP_INVALID_VERSION;
177     }
178
179     // Subject Public Key Info
180     // id-ecPublicKey (OID: 1.2.840.10045.2.1) secp256r1 (OID:1.2.840.10045.3.1.7)
181     cpResult = CheckPubKeyAlgorithm(&s_certProfile, cert->sig_pk);
182     if (CP_STATUS_OK != cpResult)
183     {
184         OIC_LOG(ERROR, TAG, "Cert public key algorithm must be ECDSA");
185         profileViolations |= CP_INVALID_PUB_KEY_ALGORITHM;
186     }
187
188     // SerialNumber: SHALL be a positive integer, unique among all certificates issued by Root CA
189     // Not possible to validate SN uniqueness
190
191     // Issuer: SHALL match the Subject field of the issuing Root CA
192     // mbedTLS will check proper chaining during DTLS handshake
193
194     return profileViolations;
195 }
196
197
198 CertProfileViolations ValidateEndEntityCertProfile(const mbedtls_x509_crt *cert)
199 {
200
201     // OCF requirements exist for the following extensions, but w/o mbedTLS support
202     // * check for certificate policies, if present must be 1.3.6.1.4.1.51414.0.1.1
203     // * cRL Distributiojn Points
204
205     if (NULL == cert)
206     {
207         return CP_INVALID_CERT_INPUT;
208     }
209
210     int mbedRet = 0;
211     CertProfileResult cpResult = CP_STATUS_OK;
212     CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
213
214     // Check all common entries first
215     profileViolations = ValidateCommonCertProfileEntries(cert);
216
217     // keyUsage (REQUIRED/Critical)
218     // digitalSignature (0) and keyAgreement(4) bits SHALL be the only bits enabled
219     mbedRet = mbedtls_x509_crt_check_key_usage(cert, s_eeKeyUsage);
220     if (0 != mbedRet)
221     {
222         OIC_LOG(ERROR, TAG, "End entity cert key usage must include digitalSignature & keyAgreement");
223         profileViolations |= CP_INVALID_KEY_USAGE_MISSING;
224     }
225
226     if (0 != (cert->key_usage & s_eeNonKeyUsage))
227     {
228         OIC_LOG(ERROR, TAG, "End entity cert key usage must not include usages other than digitalSignature & keyAgreement");
229         profileViolations |= CP_INVALID_KEY_USAGE_EXTRA;
230     }
231
232     // basicConstraints (OPTIONAL/Critical)
233     // cA = FALSE
234     if (1 == cert->ca_istrue)
235     {
236         OIC_LOG(ERROR, TAG, "End entity cert marked as CA cert");
237         profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_CA;
238     }
239
240     // pathLenConstraint: not present
241     cpResult = CheckPathLen(CERT_EE, cert->max_pathlen);
242     if (CP_STATUS_OK != cpResult)
243     {
244         OIC_LOG(ERROR, TAG, "Invalid End entity max pathlen");
245         profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN;
246     }
247
248     // extendedKeyUsage (REQUIRED/Non-critical)
249     // Mandatory: serverAuthentication - 1.3.6.1.5.5.7.3.1
250     mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert,
251                                                         MBEDTLS_OID_SERVER_AUTH,
252                                                         MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH));
253     if (0 != mbedRet)
254     {
255         OIC_LOG(ERROR, TAG, "End entity cert extended key usage must include serverAuthentication");
256         profileViolations |= CP_INVALID_EKU_NO_SERVER_AUTH;
257     }
258
259     // extendedKeyUsage (REQUIRED/Non-critical)
260     // Mandatory: clientAuthentication - 1.3.6.1.5.5.7.3.2
261     mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert,
262                                                         MBEDTLS_OID_CLIENT_AUTH,
263                                                         MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH));
264     if (0 != mbedRet)
265     {
266         OIC_LOG(ERROR, TAG, "End entity cert extended key usage must include clientAuthentication");
267         profileViolations  |= CP_INVALID_EKU_NO_CLIENT_AUTH;
268     }
269
270     // extendedKeyUsage (REQUIRED/Non-critical)
271     // Mandatory: OCF Identity certificate - 1.3.6.1.4.1.44924.1.6
272     mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert, s_ekuIdOid, MBEDTLS_OID_SIZE(s_ekuIdOid));
273     if (0 != mbedRet)
274     {
275         OIC_LOG(ERROR, TAG, "End entity cert extended key usage must include OCF ID OID (1.3.6.1.4.1.44924.1.6");
276         profileViolations |= CP_INVALID_EKU_NO_OCF_ID_OID;
277     }
278
279     // extendedKeyUsage (REQUIRED/Non-critical)
280     // CAs SHALL NOT issue certificates with the anyExtendedKeyUsage OID (2.5.29.37.0)
281     mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert,
282                                                         MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE,
283                                                         MBEDTLS_OID_SIZE(MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE));
284     if (0 == mbedRet)
285     {
286         OIC_LOG(ERROR, TAG, "End entity cert extended key usage must not include anyExtendedKeyUsage");
287         profileViolations |= CP_INVALID_EKU_INCLUDES_ANY;
288     }
289
290     // subjectAlternativeName: No requirements for ID certs
291
292     return profileViolations;
293 }
294
295 CertProfileViolations ValidateIntermediateCACertProfile(const mbedtls_x509_crt *cert)
296 {
297
298     // OCF requirements exist for the following extensions, but w/o mbedTLS support
299     // * cRL Distribution Points
300
301     if (NULL == cert)
302     {
303         return CP_INVALID_CERT_INPUT;
304     }
305
306     int mbedRet = 0;
307     CertProfileResult cpResult = CP_STATUS_OK;
308     CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
309
310     // Check all common entries first
311     profileViolations = ValidateCommonCertProfileEntries(cert);
312
313     // keyUsage: REQUIRED/Critical
314     // keyCertSign (5) & cRLSign (6) bits SHALL be the only bits enabled
315     mbedRet = mbedtls_x509_crt_check_key_usage(cert, s_caKeyUsage);
316     if (0 != mbedRet)
317     {
318         OIC_LOG(ERROR, TAG, "Intermediate CA cert key usage must include keyCertSign & cRLSign");
319         profileViolations |= CP_INVALID_KEY_USAGE_MISSING;
320     }
321
322     if (0 != (cert->key_usage & s_caNonKeyUsage))
323     {
324         OIC_LOG(ERROR, TAG, "Intermediate CA cert key usage must not include usages other than keyCertSign & cRLSign");
325         profileViolations |= CP_INVALID_KEY_USAGE_EXTRA;
326     }
327
328     // basicConstraints REQUIRED/Critical
329     // cA = TRUE
330     if (1 != cert->ca_istrue)
331     {
332         OIC_LOG(ERROR, TAG, "Intermediate CA cert not marked as CA cert");
333         profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_CA;
334     }
335
336     // pathLenConstraint
337     // 0 - can only sign end-entity certs
338     cpResult = CheckPathLen(CERT_CA_INT, cert->max_pathlen);
339     if (CP_STATUS_OK != cpResult)
340     {
341         OIC_LOG(ERROR, TAG, "Invalid Intermediate CA max pathlen");
342         profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN;
343     }
344     return profileViolations;
345 }
346
347 CertProfileViolations ValidateRootCACertProfile(const mbedtls_x509_crt *cert)
348 {
349     if (NULL == cert)
350     {
351         return CP_INVALID_CERT_INPUT;
352     }
353
354     int mbedRet = 0;
355     CertProfileResult cpResult = CP_STATUS_OK;
356     CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
357
358     // Check all common entries first
359     profileViolations = ValidateCommonCertProfileEntries(cert);
360
361     // Issuer: SHALL match the Subject field
362     // Subject: SHALL match the Issuer field
363     if ( (cert->issuer_raw.len != cert->subject_raw.len) ||
364          (0 != memcmp(cert->issuer_raw.p, cert->subject_raw.p, cert->issuer_raw.len)))
365     {
366         OIC_LOG(ERROR, TAG, "Root CA cert subject must be the same as issuer");
367         profileViolations |= CP_INVALID_ISSUER_SUBJ_MISMATCH;
368
369     }
370
371     // keyUsage: REQUIRED/Critical
372     // keyCertSign (5) & cRLSign (6) bits SHALL be the only bits enabled
373     mbedRet = mbedtls_x509_crt_check_key_usage(cert, s_caKeyUsage);
374     if (0 != mbedRet)
375     {
376         OIC_LOG(ERROR, TAG, "Root CA cert key usage must include keyCertSign & cRLSign");
377         profileViolations |= CP_INVALID_KEY_USAGE_MISSING;
378     }
379     if (0 != (cert->key_usage & s_caNonKeyUsage))
380     {
381         OIC_LOG(ERROR, TAG, "Root CA cert key usage must not include usages other than keyCertSign & cRLSign");
382         profileViolations |= CP_INVALID_KEY_USAGE_EXTRA;
383     }
384
385     // basicConstraints REQUIRED Critical cA = TRUE
386     if (1 != cert->ca_istrue)
387     {
388         OIC_LOG(ERROR, TAG, "Root CA cert not marked as CA cert");
389         profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_CA;
390     }
391
392     // pathLenConstraint = not present (unlimited)
393     cpResult = CheckPathLen(CERT_CA_ROOT, cert->max_pathlen);
394     if (CP_STATUS_OK != cpResult)
395     {
396         OIC_LOG(ERROR, TAG, "Invalid Root CA max pathlen");
397         profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN;
398     }
399
400     return profileViolations;
401 }
402
403 int ValidateAuthCertChainProfiles(const mbedtls_x509_crt *certChain)
404 {
405     int numViolations = 0;
406     CertProfileResult cpResult = CP_STATUS_OK;
407     CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
408
409     const mbedtls_x509_crt* eeCert = NULL;
410     cpResult = FindEndEntityCert(certChain, &eeCert);
411     if ((CP_STATUS_OK != cpResult) || (NULL == eeCert))
412     {
413         return CP_INVALID_CERT_CHAIN;
414     }
415
416     const mbedtls_x509_crt* curCert = certChain;
417     while (NULL != curCert)
418     {
419         if (curCert == eeCert)
420         {
421             profileViolations = ValidateEndEntityCertProfile(curCert);
422         }
423         else
424         {
425             profileViolations = ValidateIntermediateCACertProfile(curCert);
426         }
427         if (CP_NO_VIOLATIONS != profileViolations)
428         {
429            numViolations++;
430         }
431         curCert = curCert->next;
432     }
433     return numViolations;
434 }
435
436 int ValidateRootCACertListProfiles(const mbedtls_x509_crt *certList)
437 {
438     int numViolations = 0;
439     CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
440
441     const mbedtls_x509_crt* curCert = certList;
442     while (NULL != curCert)
443     {
444         profileViolations = ValidateRootCACertProfile(curCert);
445         if (CP_NO_VIOLATIONS != profileViolations)
446         {
447            numViolations++;
448         }
449         curCert = curCert->next;
450     }
451     return numViolations;
452 }
453
454 CertProfileViolations ValidateCertTimeWindow(const mbedtls_x509_crt *cert)
455 {
456      CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
457
458     if (mbedtls_x509_time_is_future(&cert->valid_from))
459     {
460         OIC_LOG(ERROR, TAG, "Certificate is not yet valid");
461         profileViolations |= CP_NOT_YET_VALID;
462     }
463     if (mbedtls_x509_time_is_past(&cert->valid_to))
464     {
465         OIC_LOG(ERROR, TAG, "Certificate is no longer valid");
466         profileViolations |= CP_EXPIRED;
467     }
468     return profileViolations;
469 }
470
471 int CheckCertListTimeWindows(const mbedtls_x509_crt *certList)
472 {
473     int numInvalid = 0;
474     CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
475
476     const mbedtls_x509_crt* curCert = certList;
477     while (NULL != curCert)
478     {
479         profileViolations = ValidateCertTimeWindow(curCert);
480         if (CP_NO_VIOLATIONS != profileViolations)
481         {
482             numInvalid++;
483         }
484         curCert = curCert->next;
485     }
486     return numInvalid;
487 }
488
489 #endif // (__WITH_DTLS__) || defined (__WITH_TLS__)