A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 78KB

4 lat temu
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Web Packaging Format Explainer (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="https://github.com/WICG/webpackage/blob/master/explainer.md">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. Web Packaging Format Explainer (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="https://github.com/WICG/webpackage/blob/master/explainer.md">Source originale du contenu</a></h3>
  445. <p>This document describes use cases for a new package format for web sites and
  446. applications and outlines such a format. It replaces the
  447. <del><a href="https://w3ctag.github.io/packaging-on-the-web/" rel="nofollow">W3C TAG's Web Packaging Draft</a></del>.
  448. It serves similar role as typical "Introduction" or "Using" and other
  449. non-normative sections of specs.</p>
  450. <h2><a href="#background" aria-hidden="true" class="anchor" id="user-content-background"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Background</h2>
  451. <p>Some new use cases for Web technology have motivated thinking about a multi-resource packaging format. Those new opportunities include:</p>
  452. <h3><a href="#local-sharing" aria-hidden="true" class="anchor" id="user-content-local-sharing"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Local Sharing</h3>
  453. <p>Local sharing is quite popular, especially in Emerging Markets countries, due to
  454. cost and limitations on cellular data and relatively spotty WiFi availability.
  455. It is typically done over local Bluetooth/WiFi, by either built-in OS features
  456. like <a href="https://en.wikipedia.org/wiki/Android_Beam" rel="nofollow">Android Beam</a> or with popular
  457. 3-rd party apps, such as
  458. <a href="https://play.google.com/store/apps/details?id=com.lenovo.anyshare.gps" rel="nofollow">ShareIt</a>
  459. or <a href="https://play.google.com/store/apps/details?id=cn.xender" rel="nofollow">Xender</a>).
  460. Typically, the locally stored media files and apps (APK files for Android for
  461. example) are shared this way. Extending sharing to bundles of content and web
  462. apps (Progressive Web Apps in particular) opens up new possibilities:</p>
  463. <h4><a href="#unsigned-snapshots" aria-hidden="true" class="anchor" id="user-content-unsigned-snapshots"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Unsigned snapshots</h4>
  464. <p>Any client can snapshot the page they're currently reading. This is currently
  465. done mostly by capturing a screenshot or saving into a browser-specific format
  466. like <a href="https://tools.ietf.org/html/rfc2557" rel="nofollow">MHTML</a>
  467. or <a href="https://en.wikipedia.org/wiki/Webarchive" rel="nofollow">Web Archive</a>, but these only
  468. support at most a single page per archive, and the second
  469. set <a href="https://xkcd.com/927/" rel="nofollow">aren't supported by other browsers</a>.</p>
  470. <h4><a href="#signed-applications" aria-hidden="true" class="anchor" id="user-content-signed-applications"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Signed applications</h4>
  471. <p>If the original origin signs a package, the contents can get full access to
  472. browser state and network requests for that origin. This lets people share full
  473. PWAs peer-to-peer.</p>
  474. <p>Local sharing tends to be popular where connectivity to the web is expensive:
  475. each byte costs money. This means the client may be stuck with an outdated
  476. version of a package for a significant amount of time, including that package's
  477. vulnerabilities. It may be feasible to periodically check for OCSP notification
  478. that a package's certificate has been revoked. We also need to design a cheap
  479. notification that the package is critically vulnerable and needs to be disabled
  480. until it can be updated.</p>
  481. <h3><a href="#physical-web" aria-hidden="true" class="anchor" id="user-content-physical-web"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Physical Web</h3>
  482. <p><a href="https://google.github.io/physical-web/" rel="nofollow">Beacons</a> and other physical web devices often want to 'broadcast' various content locally. Today, they broadcast a URL and make the user's device go to a web site. This delivers the trusted content to the user's browser (user can observe the address bar to verify) and allow web apps to talk back to their services. It can be useful to be able to broadcast a package containing several pages or even a simple web app, even without a need to immediately have a Web connection - for example, via Bluetooth. If combined with signature from the publisher, the loaded pages may be treated as if they were loaded via TLS connection with a valid certificate, in terms of the <a href="https://tools.ietf.org/html/rfc6454" rel="nofollow">origin-based security model</a>. For example, they can use <a href="https://fetch.spec.whatwg.org/#fetch-api" rel="nofollow"><code>fetch()</code></a> against its service or use "Add To Homescreen" for the convenience of the user.</p>
  483. <p>Physical web beacons may be located in a place that has no connectivity, and
  484. their owner may only visit to update them as often as their battery needs
  485. replacement: annually or less often. This leaves a large window during which a
  486. certificate could be compromised or a package could get out of date. We think
  487. there's no way to prevent a client from trusting its initial download of a
  488. package signed by a compromised certificate. When the client gets back to a
  489. public network, it should attempt to validate both the certificate and the
  490. package using the mechanisms alluded to under <a href="#local-sharing">Local Sharing</a>.</p>
  491. <h3><a href="#content-distributors-and-web-caches" aria-hidden="true" class="anchor" id="user-content-content-distributors-and-web-caches"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Content Distributors and Web caches.</h3>
  492. <p>Content distributors can provide the service of hosting web content that should
  493. be delivered at scale. This includes both hosting subresources (JS libraries,
  494. images) as well as entire content (<a href="https://developers.google.com/amp/cache/overview" rel="nofollow">Google
  495. AMP</a>) on a network of servers,
  496. often provided as a service by 3rd party. Unfortunately, the origin-based
  497. security model of the Web limits the ways 3rd-party caches/servers can be used.
  498. For example in the case of hosting JS subresources, the original document must
  499. explicitly trust the distributor's origin to serve the trusted script. The user
  500. agent must use protocol-based means to verify the subresource is coming from the
  501. trusted distributor. Another example is a content distributor that caches the
  502. whole content. Because the origin of the distributor is different from the
  503. origin of the site, the browser normally can't afford the origin treatment of
  504. the site to the loaded content. Look at how an article from USA Today is
  505. represented:</p>
  506. <p><a href="/WICG/webpackage/blob/master/buick.png" target="_blank"><img align="center" src="/WICG/webpackage/raw/master/buick.png"/></a></p>
  507. <p>Note the address bar indicating google.com. Also, since the content of USA Today is hosted in an iframe, it can't use all the functionality typically afforded to a top-level document:</p>
  508. <ul>
  509. <li>Can't request permissions</li>
  510. <li>Can't be added to homescreen</li>
  511. </ul>
  512. <p>Packages served to content distributors can staple an OCSP response and have a
  513. short expiration time, avoiding the problems with outdated packages under "Local
  514. Sharing".</p>
  515. <h2><a href="#goals-and-non-goals" aria-hidden="true" class="anchor" id="user-content-goals-and-non-goals"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Goals and non-goals</h2>
  516. <p>See
  517. <a href="https://wicg.github.io/webpackage/draft-yasskin-webpackage-use-cases.html#requirements" rel="nofollow">https://wicg.github.io/webpackage/draft-yasskin-webpackage-use-cases.html#requirements</a></p>
  518. <h2><a href="#proposal" aria-hidden="true" class="anchor" id="user-content-proposal"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Proposal</h2>
  519. <p>We propose to introduce a packaging format for the Web that would be able to contain multiple resources (HTML, CSS, Images, Media files, JS files etc) in a "bundle". That bundle can be distributed as regular Web resource (over HTTP[S]) and also by non-Web means, which includes local storage, local sharing, non-HTTP protocols like Bluetooth, etc. Being a single "bundle", it facilitates various modes of transfer. The <strong>packages may be nested</strong>, providing natural way to represent the bundles of resources corresponding to different origins and sites.</p>
  520. <p>In addition, that format would include optional <strong>signing</strong> of the resources, which can be used to verify authenticity and integrity of the content. Once and if verified (this may or may not require network connection), the content can be afforded the treatment of the claimed origin - for example showing a "green lock" with URL in a browser, or being able to send network request to the origin's server. This disconnects the verification of the origin from actual network connection and enables many new scenarios for the web content to be consumed, including time-shifted delivery (when content is delivered by an opportunistic restartable download for example), peer-to-peer sharing or caching on local file servers.</p>
  521. <p>Since the packaged "bundle" can be quite large (a game with a lot of resources or content of multiple web sites), efficient access to that content becomes important. For example, it would be often prohibitively expensive to "unpack" or somehow else pre-process such a large resource on the client device. Unpacking, for example, may require twice the space to be occupied in device's storage, which can be a problem, especially on low-end devices. We propose a optional <strong>Content Index</strong> structure that allows the bundle to be consumed (browsed) efficiently as is, without unpacking - by adding an index-like structure which provides direct offsets into the package.</p>
  522. <p>This is roughly based on the existing <a href="https://w3ctag.github.io/packaging-on-the-web/" rel="nofollow">Packaging on the Web</a> W3C TAG proposal, but we've made significant changes:</p>
  523. <ol>
  524. <li>Index of content to facilitate local resource fetching from the package.</li>
  525. <li>Package manifests that can refer to sub-package manifests to allow resources
  526. from multiple origins.</li>
  527. <li>Signature block, to cryptographically sign the content of the package.</li>
  528. <li>Removed the
  529. <a href="https://w3ctag.github.io/packaging-on-the-web/#fragment-identifiers" rel="nofollow">fragment-based URL schema</a>
  530. from the spec as it's complex with limited use cases.</li>
  531. </ol>
  532. <p>Note that this is just an explainer, <strong>not a specification</strong>. We'll add more
  533. precision when we translate it to a spec.</p>
  534. <h3><a href="#overall-format" aria-hidden="true" class="anchor" id="user-content-overall-format"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Overall format</h3>
  535. <p>The package is a <a href="https://tools.ietf.org/html/rfc7049" rel="nofollow">CBOR-encoded data item</a>
  536. with MIME type <code>application/package+cbor</code>. It logically contains a flat sequence
  537. of resources represented as HTTP responses. The package also includes metadata
  538. such as a manifest and an index to let consumers validate the resources and
  539. access them directly.</p>
  540. <p>The overall structure of the item is described by the following
  541. <a href="https://tools.ietf.org/html/draft-greevenbosch-appsawg-cbor-cddl" rel="nofollow">CDDL</a>:</p>
  542. <pre lang="cddl"><code>webpackage = [
  543. magic1: h'F0 9F 8C 90 F0 9F 93 A6', ; 🌐📦 in UTF-8.
  544. section-offsets: { * (($section-name .within tstr) =&gt; offset) },
  545. sections: ({ * $$section }) .within ({ * $section-name =&gt; any }),
  546. length: uint, ; Total number of bytes in the package.
  547. magic2: h'F0 9F 8C 90 F0 9F 93 A6', ; 🌐📦 in UTF-8.
  548. ]
  549. ; Offsets are measured from the first byte of the webpackage item to the first
  550. ; byte of the target item.
  551. offset = uint
  552. </code></pre>
  553. <p>Each section-offset points to a section with the same key, by holding the byte
  554. offset from the start of the webpackage item to the start of the section's name
  555. item.</p>
  556. <p>The length holds the total length in bytes of the <code>webpackage</code> item and must be
  557. encoded in the uint64_t format, which makes it possible to build self-extracting
  558. executables by appending a normal web package to the extractor executable.</p>
  559. <p>The defined section types are:</p>
  560. <ul>
  561. <li><a href="#main-content"><code>"indexed-content"</code></a>: The only required section.
  562. Maps resource keys (URLs possibly extended with HTTP headers) to
  563. HTTP2 responses. The mapping uses byte offsets to allow random
  564. access to any resource.</li>
  565. <li><a href="#manifest"><code>"manifest"</code></a>: Validates that resources came from the expected
  566. source. May refer to other manifests among the responses. If this section
  567. isn't provided, the resources are un-signed and can be loaded as untrusted
  568. data.</li>
  569. </ul>
  570. <p>More sections may be defined later. If an unexpected section is encountered, it
  571. is ignored.</p>
  572. <p>Note that this top-level information is <em>not signed</em>, and so can't be trusted.
  573. Only information in the manifest and below can be trusted.</p>
  574. <h3><a href="#main-content" aria-hidden="true" class="anchor" id="user-content-main-content"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Main content</h3>
  575. <p>The main content of a package is an index of HTTP requests pointing to HTTP
  576. responses. These request/response pairs hold the manifests of sub-packages and
  577. the resources in the package and all of its sub-packages. Both the requests and
  578. responses can appear in any order, usually chosen to optimize loading while the
  579. package is streamed.</p>
  580. <pre lang="cddl"><code>$section-name /= "indexed-content"
  581. $$section //= ("indexed-content" =&gt; [
  582. index: [* [resource-key, offset, ? length: uint] ],
  583. responses: [* [response-headers: http-headers, body: bstr]],
  584. ])
  585. resource-key = uri / http-headers
  586. ; http-headers is a byte string in HPACK format (RFC7541).
  587. ; The dynamic table begins empty for each instance of http-headers.
  588. http-headers = bstr
  589. </code></pre>
  590. <p>A <code>uri</code> <code>resource-key</code> is equivalent to an <code>http-headers</code> block with ":method"
  591. set to "GET" and with ":scheme", ":authority", and ":path" headers set from the
  592. URI as described in
  593. <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.3" rel="nofollow">RFC7540 section 8.1.2.3</a>.</p>
  594. <p>As an optimization, the <code>resource-key</code>s in the index store relative instead of
  595. absolute URLs. Each entry is resolved relative to the resolved version of the
  596. previous entry.</p>
  597. <p>TODO: Consider random access into large indices.</p>
  598. <p>In addition to the CDDL constraints:</p>
  599. <ul>
  600. <li>All byte strings must use a definite-length encoding so that package consumers
  601. can parse the content directly instead of concatenating the indefinite-length
  602. chunks first. The definite lengths here may also help a package consumer to
  603. quickly send resources to other threads for parsing.</li>
  604. <li>The index must not contain two resolved <code>resource-key</code>s with the
  605. same <a href="http://httpwg.org/specs/rfc7541.html#rfc.section.1.3" rel="nofollow">header list</a> after
  606. HPACK decoding.</li>
  607. <li>The <code>resource-key</code> must not contain any headers that aren't either ":method",
  608. ":scheme", ":authority", ":path", or listed in the
  609. <code>response-headers</code>'
  610. <a href="https://tools.ietf.org/html/rfc7231#section-7.1.4" rel="nofollow">"Vary" header</a>.</li>
  611. <li>The <code>resource-key</code> must contain at most one of each ":method", ":scheme",
  612. ":authority", ":path" header, in that order, before any other headers.
  613. Resolving the <code>resource-key</code> fills in any missing pseudo-headers from that
  614. set, ensuring that all resolved keys have exactly one of each.</li>
  615. </ul>
  616. <p>The optional <code>length</code> field in the index entries is redundant with the length
  617. prefixes on the <code>response-headers</code> and <code>body</code> in the content, but it can be used
  618. to issue <a href="https://tools.ietf.org/html/rfc7233" rel="nofollow">Range requests</a> for responses
  619. that appear late in the <code>content</code>.</p>
  620. <h3><a href="#manifest" aria-hidden="true" class="anchor" id="user-content-manifest"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Manifest</h3>
  621. <p>TODO: Now that this no longer contains
  622. a <a href="https://www.merriam-webster.com/dictionary/manifest#h3" rel="nofollow">manifest</a>,
  623. consider renaming it to something like "authenticity".</p>
  624. <p>A package's manifest contains some metadata for the
  625. package, <a href="#validating-resources">hashes</a> for all resources included in that
  626. package, and validity information for any <a href="#sub-packages">sub-packages</a> the
  627. package depends on. The manifest is signed, so that UAs can trust that it comes
  628. from its claimed origin.</p>
  629. <pre lang="cddl"><code>$section-name /= "manifest"
  630. $$section //= ("manifest" =&gt; signed-manifest)
  631. signed-manifest = {
  632. manifest: manifest,
  633. certificates: [+ certificate],
  634. signatures: [+ signature]
  635. }
  636. manifest = {
  637. metadata: manifest-metadata,
  638. resource-hashes: {* hash-algorithm =&gt; [hash-value]},
  639. ? subpackages: [* subpackage],
  640. }
  641. manifest-metadata = {
  642. date: time,
  643. origin: uri,
  644. * tstr =&gt; any,
  645. }
  646. ; From https://www.w3.org/TR/CSP3/#grammardef-hash-algorithm.
  647. hash-algorithm /= "sha256" / "sha384" / "sha512"
  648. ; Note that a hash value is not base64-encoded, unlike in CSP.
  649. hash-value = bstr
  650. ; X.509 format; see https://tools.ietf.org/html/rfc5280
  651. certificate = bstr
  652. signature = {
  653. ; RFC5280 says certificates can be identified by either the
  654. ; issuer-name-and-serial-number or by the subject key identifier. However,
  655. ; issuer names are complicated, and the subject key identifier only identifies
  656. ; the public key, not the certificate, so we identify certificates by their
  657. ; index in the certificates array instead.
  658. keyIndex: uint,
  659. ; Encoded as described in TLS 1.3,
  660. ; https://tlswg.github.io/tls13-spec/#signature-algorithms.
  661. signature: bstr,
  662. }
  663. </code></pre>
  664. <p>The metadata must include an absolute URL identifying
  665. the
  666. <a href="https://html.spec.whatwg.org/multipage/browsers.html#concept-origin" rel="nofollow">origin</a>
  667. vouching for the package and the date the package was created. It may contain
  668. more keys defined in <a href="https://www.w3.org/TR/appmanifest/" rel="nofollow">https://www.w3.org/TR/appmanifest/</a>.</p>
  669. <h4><a href="#manifest-signatures" aria-hidden="true" class="anchor" id="user-content-manifest-signatures"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Manifest signatures</h4>
  670. <p>The manifest is signed by a set of certificates, including at least one that is
  671. trusted to sign content from the
  672. manifest's
  673. <a href="https://html.spec.whatwg.org/multipage/browsers.html#concept-origin" rel="nofollow">origin</a>.
  674. Other certificates can sign to vouch for the package along other dimensions, for
  675. example that it was checked for malicious behavior by some authority.</p>
  676. <p>The signed sequence of bytes is the concatenation of the following byte strings.
  677. This matches the TLS1.3 format to avoid cross-protocol attacks when TLS
  678. certificates are used to sign manifests.</p>
  679. <ol>
  680. <li>A string that consists of octet 32 (0x20) repeated 64 times.</li>
  681. <li>A context string: the ASCII encoding of "Web Package Manifest".</li>
  682. <li>A single 0 byte which serves as a separator.</li>
  683. <li>The bytes of the <code>manifest</code> CBOR item.</li>
  684. </ol>
  685. <p>Each signature uses the <code>keyIndex</code> field to identify the certificate used to
  686. generate it.
  687. The <a href="https://tlswg.github.io/tls13-spec/#rfc.section.4.2.3" rel="nofollow">TLS 1.3 signing algorithm</a>
  688. is determined from the certificate's public key type:</p>
  689. <ul>
  690. <li>RSA, 2048 bits: rsa_pss_sha256</li>
  691. <li>secp256r1: ecdsa_secp256r1_sha256</li>
  692. <li>secp384r1: ecdsa_secp384r1_sha384</li>
  693. </ul>
  694. <p>The following key types are not supported, for the mentioned reason:</p>
  695. <ul>
  696. <li>secp521r1: <a href="https://crbug.com/477623" rel="nofollow">Chrome doesn't support this curve</a>, so
  697. certificates aren't using it on the web.</li>
  698. <li>ed25519 and ed448:
  699. The <a href="https://tools.ietf.org/html/draft-josefsson-tls-ed25519" rel="nofollow">RFC</a> for using
  700. these in certificates isn't yet final.</li>
  701. <li>RSA != 2048: Only 4096 has measurable usage, and it's very low.</li>
  702. </ul>
  703. <p>As a special case, if the package is being transferred from the manifest's
  704. origin under TLS, the UA may load it without checking that its own resources match
  705. the manifest. The UA still needs to validate resources provided by sub-manifests.</p>
  706. <h4><a href="#certificates" aria-hidden="true" class="anchor" id="user-content-certificates"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Certificates</h4>
  707. <p>The <code>signed-manifest.certificates</code> array should contain enough
  708. X.509 certificates to chain from the signing certificates, using the rules
  709. in <a href="https://tools.ietf.org/html/rfc5280" rel="nofollow">RFC5280</a>, to roots trusted by all
  710. expected consumers of the package.</p>
  711. <p><a href="#sub-packages'">Sub-packages</a> manifests can contain their own certificates or
  712. can rely on certificates in their parent packages.</p>
  713. <p>Requirements on the
  714. certificates' <a href="https://tools.ietf.org/html/rfc5280#section-4.2.1.3" rel="nofollow">Key Usage</a>
  715. and <a href="https://tools.ietf.org/html/rfc5280#section-4.2.1.12" rel="nofollow">Extended Key Usage</a>
  716. are TBD. It may or may not be important to prevent TLS serving certificates from
  717. being used to sign packages, in order to prevent cross-protocol attacks.</p>
  718. <h4><a href="#validating-resources" aria-hidden="true" class="anchor" id="user-content-validating-resources"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Validating resources</h4>
  719. <p>For a resource to be valid, then for each <code>hash-algorithm =&gt; [hash-value]</code> in
  720. <code>resource-hashes</code>, the resource's hash using that algorithm needs to appear in
  721. that list of <code>hash-value</code>s. Like
  722. in <a href="https://www.w3.org/TR/SRI/#agility" rel="nofollow">Subresource Integrity</a>, the UA will only
  723. check one of these, but it's up to the UA which one.</p>
  724. <p>The hash of a resource is the hash of its Canonical CBOR encoding using the
  725. following CDDL. Headers are decompressed before being encoded and hashed.</p>
  726. <pre lang="cddl"><code>resource = [
  727. request: [
  728. ':method', bstr,
  729. ':scheme', bstr,
  730. ':authority', bstr,
  731. ':path', bstr,
  732. * (header-name, header-value: bstr)
  733. ],
  734. response-headers: [
  735. ':status', bstr,
  736. * (header-name, header-value: bstr)
  737. ],
  738. response-body: bstr
  739. ]
  740. # Headers must be lower-case ascii per
  741. # http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2, and only
  742. # pseudo-headers can include ":".
  743. header-name = bstr .regexp "[\x21-\x39\x3b-\x40\x5b-\x7e]+"
  744. </code></pre>
  745. <p>This differs from <a href="https://w3c.github.io/webappsec-subresource-integrity" rel="nofollow">SRI</a>,
  746. which only hashes the body. Note: This will usually prevent a package from
  747. relying on some of its contents being transferred as normal network responses,
  748. unless its author can guarantee the network won't change or reorder the headers.</p>
  749. <h4><a href="#sub-packages" aria-hidden="true" class="anchor" id="user-content-sub-packages"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Sub-packages</h4>
  750. <p>A sub-package is represented by a <a href="#manifest">manifest</a> file in
  751. the <a href="#main-content"><code>"content"</code></a> section, which contains hashes of resources
  752. from another origin. The sub-package's resources are not otherwise distinguished
  753. from the rest of the resources in the package. Sub-packages can form an
  754. arbitrarily-deep tree.</p>
  755. <p>There are three possible forms of dependencies on sub-packages, of which we
  756. allow two. Because a sub-package is protected by its
  757. own <a href="#signatures">signature</a>, if the main package trusts the sub-package's
  758. server, it could avoid specifying a version of the sub-package at all. However,
  759. this opens the main package up to downgrade attacks, where the sub-package is
  760. replaced by an older, vulnerable version, so we don't allow this option.</p>
  761. <pre lang="cddl"><code>subpackage = [
  762. resource: resource-key,
  763. validation: {
  764. ? hash: hashes,
  765. ? notbefore: time,
  766. }
  767. ]
  768. </code></pre>
  769. <p>If the main package wants to load either the sub-package it was built with or
  770. any upgrade, it can specify the date of the original sub-package:</p>
  771. <pre lang="cbor-diag"><code>[32("https://example.com/loginsdk.package"), {"notbefore": 1(1486429554)}]
  772. </code></pre>
  773. <p>Constraining packages with their date makes it possible to link together
  774. sub-packages with common dependencies, even if the sub-packages were built at
  775. different times.</p>
  776. <p>If the main package wants to be certain it's loading the exact version of a
  777. sub-package that it was built with, it can constrain sub-package with a hash of its manifest:</p>
  778. <pre lang="cbor-diag"><code>[32("https://example.com/loginsdk.package"),
  779. {"hash": {"sha256": 22(b64'9qg0NGDuhsjeGwrcbaxMKZAvfzAHJ2d8L7NkDzXhgHk=')}}]
  780. </code></pre>
  781. <p>Note that because the sub-package may include sub-sub-packages by date, the top
  782. package may need to explicitly list those sub-sub-packages' hashes in order to
  783. be completely constrained.</p>
  784. <h2><a href="#examples" aria-hidden="true" class="anchor" id="user-content-examples"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Examples</h2>
  785. <p>Following are some example usages that correspond to these additions.
  786. The packages are written in
  787. <a href="https://tools.ietf.org/html/draft-greevenbosch-appsawg-cbor-cddl-10#appendix-G" rel="nofollow">CBOR's extended diagnostic notation</a>,
  788. with the extensions that:</p>
  789. <ol>
  790. <li><code>hpack({key:value,...})</code> is an <a href="https://tools.ietf.org/html/rfc7541" rel="nofollow">hpack</a>
  791. encoding of the described headers.</li>
  792. <li><code>DER(...)</code> is the DER encoding of a certificate described partially by the
  793. contents of the <code>...</code>.</li>
  794. </ol>
  795. <p>All examples are available in the <a href="/WICG/webpackage/blob/master/examples">examples</a> directory.</p>
  796. <h3><a href="#single-site-a-couple-of-web-pages-with-resources-in-a-package" aria-hidden="true" class="anchor" id="user-content-single-site-a-couple-of-web-pages-with-resources-in-a-package"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Single site: a couple of web pages with resources in a package.</h3>
  797. <p>The example web site contains two HTML pages and an image. This is straightforward case, demonstrating the following:</p>
  798. <ol>
  799. <li>The <code>section-offsets</code> section declares one main section starting 1 byte into
  800. the <code>sections</code> item. (The 1 byte is the map header for the <code>sections</code> item.)</li>
  801. <li>The <code>index</code> maps <a href="http://httpwg.org/specs/rfc7541.html" rel="nofollow">hpack</a>-encoded
  802. headers for each resource to the start of that resource, measured relative to
  803. the start of the <code>responses</code> item.</li>
  804. <li>Each resource contains <code>date</code>/<code>expires</code> headers that specify when the
  805. resource can be used by UA, similar to HTTP 1.1
  806. <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2" rel="nofollow">Expiration Model</a>.
  807. The actual expiration model is TBD and to be reflected in the spec. Note that
  808. we haven't yet described a way to set an <code>expires</code> value for the whole
  809. package at once.</li>
  810. <li>The length of the whole package always appears from the 10th to 18th bytes
  811. before the end of the package, in big-endian format.</li>
  812. </ol>
  813. <pre lang="cbor-diag"><code>['🌐📦',
  814. {"indexed-content": 1},
  815. {"indexed-content":
  816. [
  817. [ # Index.
  818. [hpack({
  819. :method: GET,
  820. :scheme: https
  821. :authority: example.com
  822. :path: /index.html
  823. }), 1],
  824. [hpack({
  825. :method: GET
  826. :scheme: https
  827. :authority: example.com
  828. :path: /otherPage.html
  829. }), 121],
  830. [hpack({
  831. :method: GET
  832. :scheme: https
  833. :authority: example.com
  834. :path: /images/world.png
  835. }), 243]
  836. ],
  837. [ # Resources.
  838. [
  839. hpack({
  840. :status: 200
  841. content-type: text/html
  842. date: Wed, 15 Nov 2016 06:25:24 GMT
  843. expires: Thu, 01 Jan 2017 16:00:00 GMT
  844. }),
  845. '&lt;body&gt;\n &lt;a href=\"otherPage.html\"&gt;Other page&lt;/a&gt;\n&lt;/body&gt;\n'
  846. ],
  847. [
  848. hpack({
  849. :status: 200
  850. content-type: text/html
  851. date: Wed, 15 Nov 2016 06:25:24 GMT
  852. expires: Thu, 01 Jan 2017 16:00:00 GMT
  853. }),
  854. '&lt;body&gt;\n Hello World! &lt;img src=\"images/world.png\"&gt;\n&lt;/body&gt;\n'
  855. ], [
  856. hpack({
  857. :status: 200
  858. content-type: image/png
  859. date: Wed, 15 Nov 2016 06:25:24 GMT
  860. expires: Thu, 01 Jan 2017 16:00:00 GMT
  861. }),
  862. '... binary png image ...'
  863. ]
  864. ]
  865. ]
  866. },
  867. 473, # Always 8 bytes long.
  868. '🌐📦'
  869. ]
  870. </code></pre>
  871. <h3><a href="#multiple-origins-a-web-page-with-a-resources-from-the-other-origin" aria-hidden="true" class="anchor" id="user-content-multiple-origins-a-web-page-with-a-resources-from-the-other-origin"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Multiple Origins: a web page with a resources from the other origin.</h3>
  872. <p>The example web site contains an HTML page and pulls a script from the
  873. well-known location (different origin). Note that there's no need to distinguish
  874. the resources from other origins vs the ones from the main origin. Since none of
  875. them are signed, the browser won't treat any as
  876. <a href="https://html.spec.whatwg.org/multipage/browsers.html#same-origin" rel="nofollow">same-origin</a>
  877. with their claimed origin.</p>
  878. <pre lang="cbor-diag"><code>['🌐📦',
  879. {"indexed-content": 1},
  880. {"indexed-content":
  881. [
  882. [
  883. [hpack({
  884. :method: GET
  885. :scheme: https
  886. :authority: example.com
  887. :path: /index.html
  888. }), 1],
  889. [hpack({
  890. :method: GET
  891. :scheme: https
  892. :authority: ajax.googleapis.com
  893. :path: /ajax/libs/jquery/3.1.0/jquery.min.js
  894. }), 179]
  895. ],
  896. [
  897. [
  898. hpack({
  899. :status: 200
  900. content-type: text/html
  901. date: Wed, 15 Nov 2016 06:25:24 GMT
  902. expires: Thu, 01 Jan 2017 16:00:00 GMT
  903. }),
  904. '&lt;head&gt;\n&lt;script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js\"&gt;&lt;/script&gt;\n&lt;body&gt;\n...\n&lt;/body&gt;\n'
  905. ],
  906. [
  907. hpack({
  908. :status: 200
  909. content-type: text/html
  910. date: Wed, 15 Nov 2016 06:25:24 GMT
  911. expires: Thu, 01 Jan 2017 16:00:00 GMT
  912. }),
  913. '... some JS code ...\n'
  914. ]
  915. ]
  916. ]
  917. },
  918. 396,
  919. '🌐📦'
  920. ]
  921. </code></pre>
  922. <h3><a href="#use-case-signed-package-one-origin" aria-hidden="true" class="anchor" id="user-content-use-case-signed-package-one-origin"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Use Case: Signed package, one origin.</h3>
  923. <p>The example contains example.com/index.html. The package is signed by the
  924. example.com publisher, using the same private key that example.com uses for
  925. HTTPS. The signed package ensures the verification of the origin even if the
  926. package is stored in a local file or obtained via other insecure ways like HTTP,
  927. or hosted on another origin's server.</p>
  928. <p>Some interesting things to notice in this package:</p>
  929. <ol>
  930. <li>The <code>"manifest"</code> map contains <code>"certificates"</code> and <code>"signatures"</code> arrays
  931. describing how the manifest is signed.</li>
  932. <li>The signature identifies the first element of <code>"certificates"</code> as the signing
  933. certificate.</li>
  934. <li>The elements of <code>"certificates"</code> are
  935. DER-encoded <a href="https://tools.ietf.org/html/rfc5280" rel="nofollow">X.509 certificates</a>.
  936. The <a href="/WICG/webpackage/blob/master/go/webpack/testdata/pki/example.com.cert">signing certificate</a> is
  937. trusted for <code>example.com</code>, and that certificate chains,
  938. using <a href="/WICG/webpackage/blob/master/go/webpack/testdata/pki/intermediate1.cert">other elements</a> of
  939. <code>"certificates"</code>, to
  940. a <a href="/WICG/webpackage/blob/master/go/webpack/testdata/pki/root1.cert">trusted root certificate</a>. The chain
  941. is built and trusted in the same way as TLS chains during normal web
  942. browsing.</li>
  943. <li>The signature algorithm is determined by the signing certificate's public key
  944. type, <code>prime256v1</code>, and isn't encoded separately in the signature block.</li>
  945. <li>The manifest contains a <code>"resource-hashes"</code> block, which contains the hashes,
  946. using the SHA384 algorithm in this case, of all resources in the package.
  947. Unlike in
  948. <a href="https://w3c.github.io/webappsec-subresource-integrity/" rel="nofollow">Subresource Integrity</a>,
  949. the hashes include the request and response headers.</li>
  950. <li>The inclusion of a certificate chain makes it possible to validate the
  951. package offline. Browsers detect revoked certificates and packages with known
  952. vulnerabilities by looking for separately signed files containing OCSP and
  953. recency information, but this package does not demonstrate how to attach
  954. those.</li>
  955. </ol>
  956. <pre lang="cbor-diag"><code>[
  957. '🌐📦',
  958. {
  959. "manifest": 1,
  960. "indexed-content": 2057
  961. },
  962. {
  963. "manifest": {
  964. "manifest": {
  965. "metadata": {
  966. "date": 1(1494583200),
  967. "origin": 32("https://example.com")
  968. },
  969. "resource-hashes": {
  970. "sha384": [
  971. h'3C3A03F7C3FC99494F6AAA25C3D11DA3C0D7097ABBF5A9476FB64741A769984E8B6801E71BB085E25D7134287B99BAAB',
  972. h'5AA8B83EE331F5F7D1EF2DF9B5AFC8B3A36AEC953F2715CE33ECCECD58627664D53241759778A8DC27BCAAE20F542F9F',
  973. h'D5B2A3EA8FE401F214DA8E3794BE97DE9666BAF012A4B515B8B67C85AAB141F8349C4CD4EE788C2B7A6D66177BC68171'
  974. ]
  975. }
  976. },
  977. "signatures": [
  978. {
  979. "keyIndex": 0,
  980. "signature": h'3044022015B1C8D46E4C6588F73D9D894D05377F382C4BC56E7CDE41ACEC1D81BF1EBF7E02204B812DACD001E0FD4AF968CF28EC6152299483D6D14D5DBE23FC1284ABB7A359'
  981. }
  982. ],
  983. "certificates": [
  984. DER(
  985. Certificate:
  986. ...
  987. Signature Algorithm: ecdsa-with-SHA256
  988. Issuer: C=US, O=Honest Achmed's, CN=Honest Achmed's Test Intermediate CA
  989. Validity
  990. Not Before: May 10 00:00:00 2017 GMT
  991. Not After : May 18 00:10:36 2018 GMT
  992. Subject: C=US, O=Test Example, CN=example.com
  993. Subject Public Key Info:
  994. Public Key Algorithm: id-ecPublicKey
  995. Public-Key: (256 bit)
  996. pub:
  997. ...
  998. ASN1 OID: prime256v1
  999. ...
  1000. ),
  1001. DER(
  1002. Certificate:
  1003. ...
  1004. Signature Algorithm: sha256WithRSAEncryption
  1005. Issuer: C=US, O=Honest Achmed's, CN=Honest Achmed's Test Root CA
  1006. Validity
  1007. Not Before: May 10 00:00:00 2017 GMT
  1008. Not After : May 18 00:10:36 2018 GMT
  1009. Subject: C=US, O=Honest Achmed's, CN=Honest Achmed's Test Intermediate CA
  1010. Subject Public Key Info:
  1011. Public Key Algorithm: id-ecPublicKey
  1012. Public-Key: (521 bit)
  1013. pub:
  1014. ...
  1015. ASN1 OID: secp521r1
  1016. ...
  1017. )
  1018. ]
  1019. },
  1020. "indexed-content": [
  1021. [
  1022. [ hpack({
  1023. :method: GET
  1024. :scheme: https
  1025. :authority: example.com
  1026. :path: /index.html
  1027. }), 1]
  1028. [ hpack({
  1029. :method: GET
  1030. :scheme: https
  1031. :authority: example.com
  1032. :path: /otherPage.html
  1033. }), 121],
  1034. [ hpack({
  1035. :method: GET
  1036. :scheme: https
  1037. :authority: example.com
  1038. :path: /images/world.png
  1039. }), 243]
  1040. ],
  1041. ],
  1042. [
  1043. [ hpack({
  1044. :status: 200
  1045. content-type: text/html
  1046. date: Wed, 15 Nov 2016 06:25:24 GMT
  1047. expires: Thu, 01 Jan 2017 16:00:00 GMT
  1048. }),
  1049. '&lt;body&gt;\n &lt;a href=\"otherPage.html\"&gt;Other page&lt;/a&gt;\n&lt;/body&gt;\n'
  1050. ]
  1051. [ hpack({
  1052. :status: 200
  1053. content-type: text/html
  1054. date: Wed, 15 Nov 2016 06:25:24 GMT
  1055. expires: Thu, 01 Jan 2017 16:00:00 GMT
  1056. }),
  1057. '&lt;body&gt;\n Hello World! &lt;img src=\"images/world.png\"&gt;\n&lt;/body&gt;\n'
  1058. ],
  1059. [ hpack({
  1060. :status: 200
  1061. content-type: image/png
  1062. date: Wed, 15 Nov 2016 06:25:24 GMT
  1063. expires: Thu, 01 Jan 2017 16:00:00 GMT
  1064. }),
  1065. '... binary png image ...'
  1066. ]
  1067. ]
  1068. ]
  1069. },
  1070. 2541,
  1071. '🌐📦'
  1072. ]
  1073. </code></pre>
  1074. <p>The process of validation:</p>
  1075. <ol>
  1076. <li>Verify that certificates identified by signature elements chain to trusted roots.</li>
  1077. <li>Find the subset of the signatures that correctly sign the manifest's bytes
  1078. using their identified certificates' public keys.</li>
  1079. <li>Parse the manifest and find its claimed origin.</li>
  1080. <li>Verify that at least one correct signature identifies a certificate that's
  1081. trusted for use by that origin.</li>
  1082. <li>When loading a resource, pick the strongest hash function in the
  1083. <code>"resource-hashes"</code> map, and use that to hash the Canonical CBOR
  1084. representation of its request headers, response headers, and body. Verify
  1085. that the resulting digest appears in that array in the <code>"resource-hashes"</code>
  1086. map.</li>
  1087. </ol>
  1088. <p><strong><em>Examples below here are out of date</em></strong></p>
  1089. <h3><a href="#use-case-signed-package-2-origins" aria-hidden="true" class="anchor" id="user-content-use-case-signed-package-2-origins"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Use Case: Signed package, 2 origins</h3>
  1090. <p>Lets add signing to the example mentioned above where a page uses a cross-origin JS library, hosted on <a href="https://ajax.googleapis.com" rel="nofollow">https://ajax.googleapis.com</a>. Since this package includes resources from 2 origins, this means there are 2 packages, one of them nested. Both of them should be signed by their respective publisher, since for the main page to be validated as secure (green lock, origin access) all resources that comprise it must be signed/validated - equivalent of them being loaded via HTTPS.</p>
  1091. <p>Important notes:</p>
  1092. <ol>
  1093. <li>Nested package with the JS library, obtained from googleapis.com, is separately signed by googleapis.com</li>
  1094. <li>Nested packages may have their own signatures and Content Index. They can be included verbatim as a <a href="https://w3ctag.github.io/packaging-on-the-web/#parts" rel="nofollow">part</a> of the outer package. Therefore, their index entries will be relative to the inner package. This does mean that accessing a part of a nested package will require multiple index lookups depending on how deeply nested a package is, as it will be necessary to locate the inner package using the outer package's Content Index.</li>
  1095. <li>Alternative for example.com would be to include the JS library into its own package and sign it as part of example.com, but this is useful example on how the nested signed package looks like.</li>
  1096. <li>The nested package has been indented for illustration purposes but would not be in an actual package.</li>
  1097. </ol>
  1098. <div class="highlight highlight-text-html-basic"><pre>Package-Signature: NNejtdEjGnea4VTvO7A/x+5ucZm+pGPkQ1TD32oT3oKGhPWeF0hASWjxQOXvfX5+; algorithm=sha384; certificate=urn:uuid:f47ac10b-58cc-4372-a567-0e02b2c3d479
  1099. Content-Type: application/package
  1100. Content-Location: https://example.org/examplePack.pack
  1101. Link: &lt;/<span class="pl-ent">index</span>.<span class="pl-e">html</span>&gt;; rel=describedby
  1102. Link: &lt;<span class="pl-ent">https:</span>//<span class="pl-e">ajax</span>.<span class="pl-e">googleapis</span>.<span class="pl-e">com</span>/<span class="pl-e">packs</span>/<span class="pl-e">jquery</span>_3.<span class="pl-e">1</span>.<span class="pl-e">0</span>.<span class="pl-e">pack</span>&gt;; rel=package; scope=/ajax/libs/jquery/3.1.0
  1103. Link: &lt;<span class="pl-ent">urn:uuid:d479c10b-58cc-4243-97a5-0e02b2c3f47a</span>&gt;; rel=index; offset=12014/2048
  1104. --j38n02qryf9n0eqny8cq0
  1105. Content-Location: /index.html
  1106. Content-Type: text/html
  1107. &lt;<span class="pl-ent">head</span>&gt;
  1108. &lt;<span class="pl-ent">script</span> <span class="pl-e">src</span>=<span class="pl-s"><span class="pl-pds">"</span>https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js<span class="pl-pds">"</span></span>&gt;&lt;/<span class="pl-ent">script</span>&gt;
  1109. &lt;<span class="pl-ent">body</span>&gt;
  1110. ...
  1111. &lt;/<span class="pl-ent">body</span>&gt;
  1112. --j38n02qryf9n0eqny8cq0
  1113. Package-Signature: A/xtdEjGnea4VTvNNejO7+5ucZm+pGPkQ1TD32oT3oKGhPWeF0hASWjx5+QOXvfX; algorithm=sha384; certificate=urn:uuid:7af4c10b-58cc-4372-8567-0e02b2c3dabc
  1114. Content-Location: https://ajax.googleapis.com/packs/jquery_3.1.0.pack
  1115. Content-Type: application/package
  1116. Link: &lt;<span class="pl-ent">urn:uuid:aaf4c10b-58cc-4372-8567-0e02b2c3daaa</span>&gt;; rel=index; offset=12014/2048
  1117. --klhfdlifhhiorefioeri1
  1118. Content-Location: /ajax/libs/jquery/3.1.0/jquery.min.js
  1119. Content-Type: application/javascript
  1120. ... some JS code ...
  1121. --klhfdlifhhiorefioeri1 (This is Content Index for ajax.googleapis.com subpackage)
  1122. Content-Location: urn:uuid:aaf4c10b-58cc-4372-8567-0e02b2c3daaa
  1123. Content-Type: application/package.index
  1124. /ajax/libs/jquery/3.1.0/jquery.min.js sha384-3dEjGnea4A/xtGPkQ1TDVTvNNejO7+5ucZm+pASWjx5+QOXvfX2oT3oKGhPWeF0h 102 3876
  1125. ... other entries ...
  1126. --klhfdlifhhiorefioeri1
  1127. Content-Location: urn:uuid:7af4c10b-58cc-4372-8567-0e02b2c3dabc
  1128. Content-Type: application/pkix-cert
  1129. ... certificate for ajax.googleapi.com ...
  1130. --klhfdlifhhiorefioeri1--
  1131. --j38n02qryf9n0eqny8cq0 (This is Content Index for example.com package)
  1132. Content-Location: urn:uuid:d479c10b-58cc-4243-97a5-0e02b2c3f47a
  1133. Content-Type: application/package.index
  1134. /index.html sha384-WeF0h3dEjGnea4ANejO7+5/xtGPkQ1TDVTvNucZm+pASWjx5+QOXvfX2oT3oKGhP 153 215
  1135. --j38n02qryf9n0eqny8cq0
  1136. Content-Location: urn:uuid:f47ac10b-58cc-4372-a567-0e02b2c3d479
  1137. Content-Type: application/pkcs7-mime
  1138. ... certificate for example.com ...
  1139. --j38n02qryf9n0eqny8cq0--
  1140. </pre></div>
  1141. <h2><a href="#faq" aria-hidden="true" class="anchor" id="user-content-faq"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>FAQ</h2>
  1142. <h3><a href="#why-signing-but-not-encryption-https-provides-both" aria-hidden="true" class="anchor" id="user-content-why-signing-but-not-encryption-https-provides-both"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Why signing but not encryption? HTTPS provides both...</h3>
  1143. <p>The signing part of the proposal addresses <em>integrity</em> and <em>authenticity</em> aspects of the security. It is enough for the resource to be signed to validate it belongs to the origin corresponding to the certificate used. This, in turn allows the browsers and other user agents to afford the 'origin treatment' to the resources in the package, because there is a guarantee that those resources were not tampered with.</p>
  1144. <h3><a href="#what-about-certificate-revocation-many-use-cases-assume-package-is-validated-while-offline" aria-hidden="true" class="anchor" id="user-content-what-about-certificate-revocation-many-use-cases-assume-package-is-validated-while-offline"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>What about certificate revocation? Many use cases assume package is validated while offline.</h3>
  1145. <p>Indeed, as is the case with web browsers as well, certificate revocation is not instant on the web. In case of packages that are consumed while device is offline (maybe for a long period of time), the revocation of the certificate may not reach device promptly. But then again, if the web resources were stored in a browser cache, or if pages were Saved As, and used when device is offline, there would be no way to receive the CRL or use OCSP for real-time certificate validation as well. Once the device is online, the certificate should be validated using best practices of the user agent and access revoked if needed.</p>
  1146. <h3><a href="#is-that-package-signature-a-mac-or-hmac-of-the-package" aria-hidden="true" class="anchor" id="user-content-is-that-package-signature-a-mac-or-hmac-of-the-package"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Is that Package-Signature a MAC or HMAC of the package?</h3>
  1147. <p>No, we don't use what commonly is called <a href="https://en.wikipedia.org/wiki/Message_authentication_code" rel="nofollow">MAC</a> here because the packages are not encrypted (and there is no strong use case motivating such encryption) so there is no symmetrical key and therefore the traditional concept of MAC is not applicable. However, the Package-Signature contains a <a href="https://en.wikipedia.org/wiki/Digital_signature" rel="nofollow">Digital Signature</a> which is a hash of the (Content Index + Package Header) signed with a private key of the publisher. The Content Index contains hashes for each resource part included in the package, so the Package-Signature validates each resource part as well.</p>
  1148. <h3><a href="#does-the-package-signature-cover-all-the-bits-of-the-package" aria-hidden="true" class="anchor" id="user-content-does-the-package-signature-cover-all-the-bits-of-the-package"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Does the Package-Signature cover all the bits of the package?</h3>
  1149. <p>Yes, the Package Header and Content Index are hashed and this hash, signed, is provided in the Package-Signature header. The Content Index, in turn, has hashes for all resources (Header+Body) so all bits of package are covered.</p>
  1150. <h3><a href="#are-subpackages-signed-as-well" aria-hidden="true" class="anchor" id="user-content-are-subpackages-signed-as-well"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Are subpackages signed as well?</h3>
  1151. <p>No. If a package contains subpackages, those subpackages are not covered by the package's signature or hashes and have to have their own Package-Signature header if they need to be signed. This reflects the fact that subpackages typically group resources from a different origin, with their own certificate. The [sub]packages are the units that are typically package resources from their respective origins and are therefore separately signed.</p>
  1152. <h3><a href="#what-happens-if-urnuuid-urls-collide" aria-hidden="true" class="anchor" id="user-content-what-happens-if-urnuuid-urls-collide"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>What happens if urn:uuid: URLs collide?</h3>
  1153. <p>Since they are Version 4 UUIDs, the chances of them colliding are vanishingly small.</p>
  1154. <h3><a href="#what-if-a-publisher-signed-a-package-with-a-js-library-and-later-discovered-a-vulnerability-in-it-on-the-web-they-would-just-replace-the-js-file-with-an-updated-one-what-is-the-story-in-case-of-packages" aria-hidden="true" class="anchor" id="user-content-what-if-a-publisher-signed-a-package-with-a-js-library-and-later-discovered-a-vulnerability-in-it-on-the-web-they-would-just-replace-the-js-file-with-an-updated-one-what-is-the-story-in-case-of-packages"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>What if a publisher signed a package with a JS library, and later discovered a vulnerability in it. On the Web, they would just replace the JS file with an updated one. What is the story in case of packages?</h3>
  1155. <p>The expiration headers in Package Headers section prescribe the 'useful lifetime' of the package, with UA optionally indicating the 'stale' state to the user and asking to upgrade, or automatically fetching a new one. While offline, the expiration may be ignored (not unlike Cache-Control: no-cache) but once user is online, the UA should verify both the certificate and if the package Content-Location contains an updated package (per Package Headers section) - and replace the package if necessary. In general, if the device is online and the package is expired, and the original location has updated package, the UA should obtain a new one (details TBD).</p>
  1156. <h3><a href="#why-is-there-a-content-index-that-specifies-where-each-part-is-and-also-mime-like-boundaries-that-separate-parts-in-the-package" aria-hidden="true" class="anchor" id="user-content-why-is-there-a-content-index-that-specifies-where-each-part-is-and-also-mime-like-boundaries-that-separate-parts-in-the-package"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Why is there a Content Index that specifies where each part is, and also MIME-like 'boundaries' that separate parts in the package?</h3>
  1157. <p>This is due to two main use cases of package loading:</p>
  1158. <ol>
  1159. <li>Loading the package from Web as part of the page or some other resource. In this case, the package is streamed from the server and <strong>boundaries</strong> allow to parse the package as it comes in and start using subresources as fast as possible. If the package has to be signed though, the package in its entirety has to be loaded first.</li>
  1160. <li>Loading a (potentially large) package offline. In that case, it is important to provide a fast access to subresources as they are requested, w/o unpacking the package (it takes double the storage at least to unpack and significant time). Using direct byte-offset Content Index allows to directly access resources in a potentially large package.</li>
  1161. </ol>
  1162. <h3><a href="#does-the-package-supply-a-full-chain-of-certificates-to-a-known-ca-root" aria-hidden="true" class="anchor" id="user-content-does-the-package-supply-a-full-chain-of-certificates-to-a-known-ca-root"><svg aria-hidden="true" class="octicon octicon-link" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"/></svg></a>Does the package supply a full chain of certificates to a known CA root?</h3>
  1163. <p>Not necessarily. Different devices have different sets of roots in their trust
  1164. stores, so there is not a single "correct" set of certificates to send that will
  1165. work best for all clients. Instead, for compatibility, packages may need to
  1166. include a set of certificates from which chains can be built to multiple roots,
  1167. or rely on clients to dynamically fetch additional intermediates when needed.</p>
  1168. <p>This becomes a tradeoff between the package size vs the set of clients that can
  1169. validate the signature offline. We expect that packaging tools will allow their
  1170. users to configure this tradeoff in appropriate ways.</p>
  1171. </article>
  1172. </section>
  1173. <nav id="jumpto">
  1174. <p>
  1175. <a href="/david/blog/">Accueil du blog</a> |
  1176. <a href="https://github.com/WICG/webpackage/blob/master/explainer.md">Source originale</a> |
  1177. <a href="/david/stream/2019/">Accueil du flux</a>
  1178. </p>
  1179. </nav>
  1180. <footer>
  1181. <div>
  1182. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  1183. <p>
  1184. Bonjour/Hi!
  1185. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  1186. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  1187. </p>
  1188. <p>
  1189. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  1190. </p>
  1191. <p>
  1192. Voici quelques articles choisis :
  1193. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  1194. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  1195. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  1196. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  1197. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  1198. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  1199. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  1200. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  1201. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  1202. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  1203. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  1204. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  1205. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  1206. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  1207. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  1208. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  1209. </p>
  1210. <p>
  1211. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  1212. </p>
  1213. <p>
  1214. Je ne traque pas ta navigation mais mon
  1215. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  1216. conserve des logs d’accès.
  1217. </p>
  1218. </div>
  1219. </footer>
  1220. <script type="text/javascript">
  1221. ;(_ => {
  1222. const jumper = document.getElementById('jumper')
  1223. jumper.addEventListener('click', e => {
  1224. e.preventDefault()
  1225. const anchor = e.target.getAttribute('href')
  1226. const targetEl = document.getElementById(anchor.substring(1))
  1227. targetEl.scrollIntoView({behavior: 'smooth'})
  1228. })
  1229. })()
  1230. </script>