1# JIT trust 2 3![JIT Trust logo](../doc-assets/jit-trust-logo.png) 4 5## Background 6 7Most systems using openssl rely on a system trust bundle that openssl was 8compiled to load at library init. This is a bit expensive, since it 9instantiates over 120 CA X.509 certs, but most modern Linux systems don't really 10notice the permanent use of 1MB or so of heap from init, the advantage is client 11connections have all the trusted root certs available in memory to perform 12validation. 13 14![Using system trust bundles](../doc-assets/jit-trust-system-trust.png) 15 16For the kind of systems that choose mbedtls, they will typically either be 17burdened by or not even have enough ram to take this approach. 18 19If the device only connects to endpoints that are signed by a specific 20CA, you can just prepare the connection with the known trusted CA, that's 21the approach the examples take. This method should still be used for critical 22connections to the cloud, for example provide the necessary CA cert in the 23Secure Streams policy, or at vhost creation time. 24 25![Using system trust bundles](../doc-assets/jit-trust-single-trust.png) 26 27However if you also have a browser type application that could connect anywhere, 28but you don't have heap spare to preload all the CAs, you need something like 29"JIT trust". 30 31## JIT trust overview 32 33The basic approach is to connect to the server to retrieve its certificates, 34then study the certificates to determine the identity of the missing trusted 35cert we should be trying to validate with. 36 37![JIT Trust overview](../doc-assets/jit-trust-overview.png) 38 39We attempt to get the trusted cert from some local or remote store, and retry 40the connection having instantiated the missing CA cert as trusted for that 41connection, if it is one that we do actually trust. If it lies about what CA it 42needs to validate, or we do not trust the one it asks for, subsequent 43connections will fail. 44 45If it asked for a trusted CA that we trust, and the relationship was valid, the 46tls negotiation should then complete successfully, and we can cache the CA cert 47and the host -> CA cert pre-trust requirement so future connections can work 48first time. 49 50## Subject Key Id and Authority Key Id 51 52All of the certificates publish a unique-enough personal "Subject Key ID" or 53SKID blob. These are typically 20-byte hashes based on the cert public key. 54 55When a server certificate is issued by the CA, an entry is made first in the 56certificate noting the SKID of the certificate that will be used to sign it, 57in an "Authority Key ID", or AKID, extension. The certificate is then signed by 58the parent certificate private key to prove it was issued by the real owner of 59the CA or intermediate certificate. 60 61![X.509 validation paths](../doc-assets/jit-trust-paths.png) 62 63Basically this AKID on a certificate is guiding the validator with 64information about which certificate it claims is next in the chain of trust 65leading back to a trusted CA. Lying about it doesn't help an attacker, 66because we're only using the AKID to get the CA certificate and then try to do 67the full signature check using it, if it's not really signed by the AKID cert it 68told, or anything else wrong, the actual validation will just fail. 69 70A chain that terminates in a CA certificate is complete, and can undergo full 71validation using the tls library. 72 73## Converting the Mozilla trust bundle for JIT trust 74 75Lws provides a bash script `./scripts/mozilla-trust-gen.sh` that can fetch the 76latest Mozilla CA trust bundle for certs usable for tls validation, and convert 77it to three different forms to allow maintaining the trust bundle in different 78ways for different kinds of device to consume. 79 80 - as a webroot directory, so you can server trusted DERs, with 81 symlink indexes to the CA certs by SKID and issuer/serial 82 83 - as an atomic binary blob, currently about 143KB, with structure 84 at the start pointing to DER certs and indexes inside 85 86 - a C-compiler friendly `uint8_t` array version of the blob, 87 so it can be compiled into .rodata directly if necessary. 88 89Currently there are 128 certs in the trust bundle, and the whole blob is about 90143KB uncompressed. 91 92## Considerations about maintaining the trust blob 93 94Mozilla update their trust bundle at intervals, and there have been at least 95three cases where they have removed or distrusted CAs from it by their own 96decision, because they have issued dangerous certificates, (like one for `*` 97that will validate anything at all). Certifacte owners may also revoke their 98own certificates for any reason and issue replacements. 99 100The certs in the trust bundle expire, currently 10/128 will expire within 3 101years and 50/128 over the next 10 years. So new and replacement certificates 102are also being added at intervals. 103 104Part of using the trust bundle is building in some way to update what is trusted 105over the lifetime of the device, which may exceed 10 years. 106 107Depending on the device, it may not be any problem to keep the trust blob in the 108firmware, and update the firmware ongoing every few months. So you could build 109it into the firmware using the C array include file (the minimal example takes 110this approach). 111 112Another device may have difficulty updating the firmware outside of emergencies, 113it could keep the trust blob in a separate area and update it separately. 114Having it as a single blob makes it easy to fetch and update. 115 116Finally constrained devices, say in ESP32 class, may not have space or desire 117to store the trust blob in the device at all, it could query a remote server on 118demand to check for any trusted CA matching a given AKID and retrieve and cache 119it in volatile ram. This would use the webroot produced by the script, via tls 120and a fixed CA cert outside this system. 121 122## Format of the JIT trust blob 123 124The trust blob layout is currently 125 126``` 12700: 54 42 4c 42 Magic "TBLB" 12804: 00 01 MSB-first trust blob layout version 12906: XX XX MSB-first count of certificates 13008: XX XX XX XX MSB-first trust blob generation unix time 1310c: XX XX XX XX MSB-first offset from blob start of cert length table 13210: XX XX XX XX MSB-first offset from blob start of SKID length table 13314: XX XX XX XX MSB-first offset from blob start of SKID table 13418: XX XX XX XX MSB-first total blob length 135 1361c: XX .. XX DER certs (start at +0x1c) 137 : XX .. XX DER cert length table (MSB-first 16-bit per cert) 138 : XX .. XX SKID length table (8-bit per cert) 139 : XX .. XX SKID table (variable per cert) 140``` 141 142## Enabling JIT Trust 143 144``` 145$ cmake .. -DLWS_WITH_TLS_JIT_TRUST=1 146``` 147 148## Minimal example for JIT Trust 149 150`minimal-examples/http-client/minimal-http-client-jit-trust` is built if JIT 151Trust is enabled at cmake and `-DLWS_WITH_MINIMAL_EXAMPLES=1`. This is based on 152minimal-http-client, except the loading of the system trust bundle is defeated, 153so by default it does not trust anything and cannot complete any tls connection. 154It includes the mozilla trust blob as a header file when built. 155 156It tries to do an http client connection twice, the first time fails but JIT 157Trust determines which trusted CA cert is missing, retreives it from the trust 158blob and creates the necessary temporary vhost with the correct CA cert(s) 159trusted. On the next retry, the connection succeeds. 160 161## Processing of x509 AKID and SKIDs 162 163We study each x509 cert sent by the server in turn. We parse out the SKID and 164AKID on each one and stash them (up to 4 deep). 165 166After the initial validation fails due to lack of any trusted CA, lws has 167collected all the AKID and SKIDs that were in certs sent by the server. Since 168these may be sent in any order, may be malicious, and may even contain the 169(untrusted) root CA, they are sorted into a trust path using the AKID and SKID 170relationships. 171 172To cover cross-signing and cases where the root cert(s) were wrongly sent by 173a misconfigured server, all of the AKIDs in the stash are queried against the 174trusted CA store. In cross-signing, multiple intermediates are provided with 175the same SKID, that all match the server certificate AKID parent. Since we 176might meet certificates that trust multiple valid CAs that can validate the 177certificate, we support up to three CA certs imported. 178 179A user `lws_system_ops` handler performs the query, so it can consist of any 180kind of backing store or remote lookup. Helpers are provided to query the JIT 181trust mozilla blob, so the system helper is small in the typical case, just 182calling lws helpers. 183 184The results (up to three CA certs to account for cross-signing scenarios) are 185collected and a 1hr TTL cache entry made for the hostname and the SKIDs of the 186matched CAs, if there is no existing JIT vhost with its tls context configured 187with the needed trusted CAs, one is created. 188 189When the connection is retried, lws checks the cache for the hostname having 190a binding to an existing JIT vhost, if that exists the connection proceeds 191bound to that. If there is a cache entry but no JIT vhost, one is created using 192the information in the cache entry. 193 194## Efficiency considerations 195 196From cold, the JIT Trust flow is 197 1981. A sacrificial connection is made to get the server certs 1992. Query the JIT Trust database for AKIDs mentioned in the certs (this may be 200done asynchronously) 2013. Create a temporary vhost with the appropriate trusted certs enabled in it, 202 and add an entry in the cache for this hostname to the SKIDs of the CAs 203 enabled on this temporary vhost 2044. Retry, querying the cache to bind the connection to the right temporary vhost 205 206An lws_cache in heap is maintained so step 1 can be skipped while hostname-> 207SKID items exist in the cache. If the items expire or are evicted, it just 208means we have to do step 1 again. 209 210For a short time, the vhost created in step 3 is allowed to exist when idle, ie 211when no connections are actively using it. In the case the vhost exists and 212the cache entry exists for the hostname, the connection can proceed successfully 213right away without steps 1 through 3. 214 215## APIs related to JIT Trust 216 217Systems that support JIT trust define an `lws_system_ops` callback 218that does whatever the system needs to do for attempting to acquire 219a trusted cert with a specified SKID or issuer/serial. 220 221``` 222int (*jit_trust_query)(struct lws_context *cx, const uint8_t *skid, size_t skid_len, void *got_opaque); 223``` 224 225The ops handler doesn't have to find the trusted cert immediately before 226returning, it is OK starting the process and later if successful calling a 227helper `lws_tls_jit_trust_got_cert_cb()` with the `got_opaque` from the query. 228This will cache the CA cert so it's available at the next connection retry for 229preloading. 230 231An helper suitable for `ops->jit_trust_query` using trust blob lookup in .rodata 232is provided in `lws_tls_jit_trust_blob_queury_skid()`, the callback above should 233be called with its results as shown in the minimal example. 234 235## Runtime tuning for JIT Trust 236 237The context creation info struct has a couple of runtime-tunable settings 238related to JIT Trust. 239 240`.jitt_cache_max_footprint`: default 0 means no limit, otherwise the hostname-> 241SKID cache is kept below this many bytes in heap, by evicting LRU entries. 242 243`.vh_idle_grace_ms`: default 0 means 5000ms, otherwise sets the length of time 244a JIT Trust vhost is allowed to exist when it has no connections using it. 245Notice that, eg, h2 connections have their own grace period when they become 246idle, to optimize reuse, this period does not start until any h2 network 247connection bound to the vhost has really closed. 248 249## Considerations around http redirects 250 251HTTP redirects are transactions that tell the client to go somewhere else to 252continue, typically a 301 response with a Location: header explaining where to 253go. 254 255JIT Trust supports redirects to hosts with the same or different trust 256requirements, each step in the redirect is treated as a new connection that will 257fail, try to create a vhost with the right trust and work on the retry. 258 259Lws rejects by default protocol downgrades (https -> http) on redirects, the 260example used a context option `LCCSCF_ACCEPT_TLS_DOWNGRADE_REDIRECTS` to 261override this. 262 263## Works out of the box on recent mbedtls and openssl 264 265No modifications are needed to either tls library. 266 267## Compatibility Testing 268 269A list of the top 100 sites each from the US and the ROW were combined to 270produce 156 unqiue domain names [1] 271 272The Mbedtls build of JIT trust minimal example was run against each of these 273doing a GET on path `/` and restricted to h1 (`--server xxx --h1`). In some 274cases, the server at the base domain name is broken or down, as verified using 275ssllabs.com as a second opinion. These domains only resolve properly using 276`www.` prefix. 277 278In some cases the sites check the user agent and return a 4xx, these are taken 279as success for this test, since there was no problem at the tls layer. 280 281|site|h1|h2|comment| 282|---|---|---|---| 283|adobe.com|✓|✓|| 284|allegro.pl|✓|✓|| 285|allrecipes.com|✓|✓|| 286|amazon.co.jp|✓|✓|| 287|amazon.com|✓|✓|| 288|amazon.co.uk|✓|✓|| 289|amazon.de|✓|✓|| 290|amazon.fr|✓|✓|| 291|amazon.in|✓|✓|| 292|amazon.it|✓|✓|| 293|aol.com|✓|✓|| 294|apartments.com|✓|✓|| 295|apple.com|✓|✓|| 296|ar.wikipedia.org|✓|✓|| 297|att.com|✓|✓|| 298|bankofamerica.com|✓|✓|| 299|bbc.com|✓|✓|| 300|bbc.co.uk|✓|✓|| 301|bestbuy.com|✕|✓|redirect-> `www.` then h1: timeout, h2: 403 forbidden... geolocated?| 302|booking.com|✓|✓|| 303|britannica.com|✓|✓|| 304|bulbagarden.net|✓|✓|| 305|businessinsider.com|✓|✓|| 306|ca.gov|✓|✓|| 307|caixa.gov.br|✕|✕|TLS trust works fine. Continuously redirects to self... sends set-cookie that we don't return yet| 308|capitalone.com|✓|✓|| 309|cbssports.com|✓|✓|| 310|cdc.gov|✓|✓|| 311|chase.com|✓|✓|| 312|chrome.google.com|✓|✓|| 313|cnbc.com|✓|✓|| 314|cnet.com|✓|✓|| 315|cnn.com|✓|✓|| 316|cookpad.com|✓|✓|| 317|costco.com|✕|✓|TLS trust works fine. But with or without `www.` server does not reply within 15s on h1, sends 403 OK on h2... Curl acts the same as we do, firefox works... geolocated?|| 318|craigslist.org|✓|✓|| 319|dailymotion.com|✓|✓|| 320|de.wikipedia.org|✓|✓|| 321|dictionary.com|✓|✓|| 322|ebay.com|✓|✓|| 323|ebay.co.uk|✓|✓|| 324|en.wikipedia.org|✓|✓|| 325|epicgames.com|✓|✓|| 326|espn.com|✓|✓|| 327|es.wikipedia.org|✓|✓|| 328|etsy.com|✓|✓|| 329|expedia.com|✓|✓|| 330|facebook.com|✓|✓|| 331|fandom.com|✓|✓|| 332|fedex.com|✓|✓|| 333|finance.yahoo.com|✓|✓|| 334|www.foodnetwork.com|✓|✓|`www.` served correctly, base domain is misconfigured with expired cert, confirmed with ssllabs + curl| 335|forbes.com|✓|✓|| 336|foxnews.com|✓|✓|| 337|fr.wikipedia.org|✓|✓|| 338|gamepedia.com|✓|✓|| 339|genius.com|✓|✓|| 340|glassdoor.com|✓|✓|| 341|globo.com|✓|✓|| 342|google.com|✓|✓|| 343|healthline.com|✓|✓|| 344|homedepot.com|✓|✓|| 345|hulu.com|✓|✓|| 346|hurriyet.com.tr|✓|✓|| 347|id.wikipedia.org|✓|✓|| 348|ign.com|✓|✓|| 349|ikea.com|✓|✓|`www.` served correctly, base domain is misconfigured with nonresponsive server, confirmed with ssllabs| 350|ilovepdf.com|✓|✓|| 351|imdb.com|✓|✓|| 352|indeed.com|✓|✓|| 353|indiatimes.com|✓|✓|| 354|instagram.com|✓|✓|| 355|investopedia.com|✓|✓|| 356|irs.gov|✓|✓|| 357|it.wikipedia.org|✓|✓|| 358|ivi.ru|✓|✓|| 359|ja.wikipedia.org|✓|✓|| 360|kakaku.com|✓|✓|| 361|khanacademy.org|✓|✓|| 362|kinopoisk.ru|✓|✓|| 363|leboncoin.fr|✓|✓|| 364|linkedin.com|✓|✓|| 365|live.com|✓|✓|| 366|lowes.com|✓|✓|| 367|macys.com|✕|✓|TLS trust works fine. Continuously redirects to self... `www.` same, curl acts same but OK if given -b -c, so akami cookie storage issue| 368|mail.ru|✓|✓|| 369|mail.yahoo.com|✓|✓|| 370|mapquest.com|✓|✓|| 371|mayoclinic.org|✓|✓|| 372|medicalnewstoday.com|✓|✓|| 373|mercadolivre.com.br|✓|✓|| 374|merriam-webster.com|✓|✓|| 375|microsoft.com|✓|✓|| 376|msn.com|✓|✓|| 377|namu.wiki|✓|✓|| 378|nbcnews.com|✓|✓|| 379|netflix.com|✓|✓|| 380|nih.gov|✓|✓|| 381|nl.wikipedia.org|✓|✓|| 382|ny.gov|✓|✓|| 383|nytimes.com|✓|✓|| 384|ok.ru|✓|✓|| 385|onet.pl|✓|| 386|orange.fr|✓|✓|| 387|paypal.com|✓|✓|| 388|pinterest.com|✓|✓|| 389|pixiv.net|✓|✓|| 390|play.google.com|✓|✓|| 391|pl.wikipedia.org|✓|✓|| 392|www.programme-tv.net|✓|✓|OK with `www.`, without `www.` TLS trust works fine but server does not reply, same with curl| 393|pt.wikipedia.org|✓|✓|| 394|quizlet.com|✓|✓|| 395|quora.com|✓|✓||| 396|rakuten.co.jp|✓|✓|| 397|realtor.com|✓|✓|| 398|reddit.com|✓|✓|| 399|reverso.net|✓|✓|| 400|roblox.com|✓|✓|| 401|rottentomatoes.com|✓|✓|| 402|ru.wikipedia.org|✓|✓|| 403|sahibinden.com|✓|✓|| 404|smallpdf.com|✓|✓|| 405|speedtest.net|✓|✓|| 406|spotify.com|✓|✓|| 407|steampowered.com|✓|✓|| 408|target.com|✓|✓|| 409|theguardian.com|✓|✓|| 410|tripadvisor.com|✓|✓|| 411|tr.wikipedia.org|✓|✓|| 412|twitch.tv|✓|✓|| 413|twitter.com|✓|✓|| 414|uol.com.br|✓|✓|| 415|ups.com|✓|✓|| 416|urbandictionary.com|✓|✓|| 417|usatoday.com|✓|✓|| 418|usnews.com|✕|✓|TLS trust works fine. Needs `www.` else server doesn't respond in 15s, sends 403 on h2, Curl acts the same, geolocated?| 419|usps.com|✓|✓|| 420|verizon.com|✓|✓|| 421|vk.com|✓|✓|| 422|walmart.com|✓|✓|| 423|washingtonpost.com|✓|✓|| 424|weather.com|✓|✓|| 425|webmd.com|✓|✓|| 426|whatsapp.com|✓|✓|| 427|wowhead.com|✓|✓|| 428|wp.pl|✓|✓|| 429|www.gov.uk|✓|✓|| 430|xfinity.com|✓|✓|| 431|yahoo.co.jp|✓|✓|| 432|yahoo.com|✓|✓|| 433|yandex.ru|✓|✓|| 434|yellowpages.com|✓|✓|| 435|yelp.com|✓|✓|| 436|youtube.com|✓|✓|| 437|zh.wikipedia.org|✓|✓|| 438|zillow.com|✓|✓|| 439 440[1] 441``` 442wget -O- https://ahrefs.com/blog/most-visited-websites/ | grep most-visited-websites-us | \ 443 sed -E 's/class="column-2">/|/g' | tr '|' '\n' | \ 444 sed 's/<.*//g' | grep -v Domain | grep -v Josh | sort | uniq 445``` 446 447